Roo/tree/TreePanel.js
[roojs1] / Roo / tree / TreePanel.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13 /**
14  * @class Roo.tree.TreePanel
15  * @extends Roo.data.Tree
16
17  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
18  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
19  * @cfg {Boolean} enableDD true to enable drag and drop
20  * @cfg {Boolean} enableDrag true to enable just drag
21  * @cfg {Boolean} enableDrop true to enable just drop
22  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
23  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
24  * @cfg {String} ddGroup The DD group this TreePanel belongs to
25  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
26  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
27  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
28  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
29  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
35  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
36  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
37  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
38  * 
39  * @constructor
40  * @param {String/HTMLElement/Element} el The container element
41  * @param {Object} config
42  */
43 Roo.tree.TreePanel = function(el, config){
44     var root = false;
45     var loader = false;
46     if (config.root) {
47         root = config.root;
48         delete config.root;
49     }
50     if (config.loader) {
51         loader = config.loader;
52         delete config.loader;
53     }
54     
55     Roo.apply(this, config);
56     Roo.tree.TreePanel.superclass.constructor.call(this);
57     this.el = Roo.get(el);
58     this.el.addClass('x-tree');
59     //console.log(root);
60     if (root) {
61         this.setRootNode( Roo.factory(root, Roo.tree));
62     }
63     if (loader) {
64         this.loader = Roo.factory(loader, Roo.tree);
65     }
66    /**
67     * Read-only. The id of the container element becomes this TreePanel's id.
68     */
69     this.id = this.el.id;
70     this.addEvents({
71         /**
72         * @event beforeload
73         * Fires before a node is loaded, return false to cancel
74         * @param {Node} node The node being loaded
75         */
76         "beforeload" : true,
77         /**
78         * @event load
79         * Fires when a node is loaded
80         * @param {Node} node The node that was loaded
81         */
82         "load" : true,
83         /**
84         * @event textchange
85         * Fires when the text for a node is changed
86         * @param {Node} node The node
87         * @param {String} text The new text
88         * @param {String} oldText The old text
89         */
90         "textchange" : true,
91         /**
92         * @event beforeexpand
93         * Fires before a node is expanded, return false to cancel.
94         * @param {Node} node The node
95         * @param {Boolean} deep
96         * @param {Boolean} anim
97         */
98         "beforeexpand" : true,
99         /**
100         * @event beforecollapse
101         * Fires before a node is collapsed, return false to cancel.
102         * @param {Node} node The node
103         * @param {Boolean} deep
104         * @param {Boolean} anim
105         */
106         "beforecollapse" : true,
107         /**
108         * @event expand
109         * Fires when a node is expanded
110         * @param {Node} node The node
111         */
112         "expand" : true,
113         /**
114         * @event disabledchange
115         * Fires when the disabled status of a node changes
116         * @param {Node} node The node
117         * @param {Boolean} disabled
118         */
119         "disabledchange" : true,
120         /**
121         * @event collapse
122         * Fires when a node is collapsed
123         * @param {Node} node The node
124         */
125         "collapse" : true,
126         /**
127         * @event beforeclick
128         * Fires before click processing on a node. Return false to cancel the default action.
129         * @param {Node} node The node
130         * @param {Roo.EventObject} e The event object
131         */
132         "beforeclick":true,
133         /**
134         * @event checkchange
135         * Fires when a node with a checkbox's checked property changes
136         * @param {Node} this This node
137         * @param {Boolean} checked
138         */
139         "checkchange":true,
140         /**
141         * @event click
142         * Fires when a node is clicked
143         * @param {Node} node The node
144         * @param {Roo.EventObject} e The event object
145         */
146         "click":true,
147         /**
148         * @event dblclick
149         * Fires when a node is double clicked
150         * @param {Node} node The node
151         * @param {Roo.EventObject} e The event object
152         */
153         "dblclick":true,
154         /**
155         * @event contextmenu
156         * Fires when a node is right clicked
157         * @param {Node} node The node
158         * @param {Roo.EventObject} e The event object
159         */
160         "contextmenu":true,
161         /**
162         * @event beforechildrenrendered
163         * Fires right before the child nodes for a node are rendered
164         * @param {Node} node The node
165         */
166         "beforechildrenrendered":true,
167         /**
168         * @event startdrag
169         * Fires when a node starts being dragged
170         * @param {Roo.tree.TreePanel} this
171         * @param {Roo.tree.TreeNode} node
172         * @param {event} e The raw browser event
173         */ 
174        "startdrag" : true,
175        /**
176         * @event enddrag
177         * Fires when a drag operation is complete
178         * @param {Roo.tree.TreePanel} this
179         * @param {Roo.tree.TreeNode} node
180         * @param {event} e The raw browser event
181         */
182        "enddrag" : true,
183        /**
184         * @event dragdrop
185         * Fires when a dragged node is dropped on a valid DD target
186         * @param {Roo.tree.TreePanel} this
187         * @param {Roo.tree.TreeNode} node
188         * @param {DD} dd The dd it was dropped on
189         * @param {event} e The raw browser event
190         */
191        "dragdrop" : true,
192        /**
193         * @event beforenodedrop
194         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
195         * passed to handlers has the following properties:<br />
196         * <ul style="padding:5px;padding-left:16px;">
197         * <li>tree - The TreePanel</li>
198         * <li>target - The node being targeted for the drop</li>
199         * <li>data - The drag data from the drag source</li>
200         * <li>point - The point of the drop - append, above or below</li>
201         * <li>source - The drag source</li>
202         * <li>rawEvent - Raw mouse event</li>
203         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
204         * to be inserted by setting them on this object.</li>
205         * <li>cancel - Set this to true to cancel the drop.</li>
206         * </ul>
207         * @param {Object} dropEvent
208         */
209        "beforenodedrop" : true,
210        /**
211         * @event nodedrop
212         * Fires after a DD object is dropped on a node in this tree. The dropEvent
213         * passed to handlers has the following properties:<br />
214         * <ul style="padding:5px;padding-left:16px;">
215         * <li>tree - The TreePanel</li>
216         * <li>target - The node being targeted for the drop</li>
217         * <li>data - The drag data from the drag source</li>
218         * <li>point - The point of the drop - append, above or below</li>
219         * <li>source - The drag source</li>
220         * <li>rawEvent - Raw mouse event</li>
221         * <li>dropNode - Dropped node(s).</li>
222         * </ul>
223         * @param {Object} dropEvent
224         */
225        "nodedrop" : true,
226         /**
227         * @event nodedragover
228         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
229         * passed to handlers has the following properties:<br />
230         * <ul style="padding:5px;padding-left:16px;">
231         * <li>tree - The TreePanel</li>
232         * <li>target - The node being targeted for the drop</li>
233         * <li>data - The drag data from the drag source</li>
234         * <li>point - The point of the drop - append, above or below</li>
235         * <li>source - The drag source</li>
236         * <li>rawEvent - Raw mouse event</li>
237         * <li>dropNode - Drop node(s) provided by the source.</li>
238         * <li>cancel - Set this to true to signal drop not allowed.</li>
239         * </ul>
240         * @param {Object} dragOverEvent
241         */
242        "nodedragover" : true
243         
244     });
245     if(this.singleExpand){
246        this.on("beforeexpand", this.restrictExpand, this);
247     }
248     if (this.editor) {
249         this.editor.tree = this;
250         this.editor = Roo.factory(this.editor,Roo.tree);
251     }
252    
253 };
254 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
255     rootVisible : true,
256     animate: Roo.enableFx,
257     lines : true,
258     enableDD : false,
259     hlDrop : Roo.enableFx,
260   
261     renderer: false,
262     
263     rendererTip: false,
264     // private
265     restrictExpand : function(node){
266         var p = node.parentNode;
267         if(p){
268             if(p.expandedChild && p.expandedChild.parentNode == p){
269                 p.expandedChild.collapse();
270             }
271             p.expandedChild = node;
272         }
273     },
274
275     // private override
276     setRootNode : function(node){
277         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
278         if(!this.rootVisible){
279             node.ui = new Roo.tree.RootTreeNodeUI(node);
280         }
281         return node;
282     },
283
284     /**
285      * Returns the container element for this TreePanel
286      */
287     getEl : function(){
288         return this.el;
289     },
290
291     /**
292      * Returns the default TreeLoader for this TreePanel
293      */
294     getLoader : function(){
295         return this.loader;
296     },
297
298     /**
299      * Expand all nodes
300      */
301     expandAll : function(){
302         this.root.expand(true);
303     },
304
305     /**
306      * Collapse all nodes
307      */
308     collapseAll : function(){
309         this.root.collapse(true);
310     },
311
312     /**
313      * Returns the selection model used by this TreePanel
314      */
315     getSelectionModel : function(){
316         if(!this.selModel){
317             this.selModel = new Roo.tree.DefaultSelectionModel();
318         }
319         return this.selModel;
320     },
321
322     /**
323      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
324      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
325      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
326      * @return {Array}
327      */
328     getChecked : function(a, startNode){
329         startNode = startNode || this.root;
330         var r = [];
331         var f = function(){
332             if(this.attributes.checked){
333                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
334             }
335         }
336         startNode.cascade(f);
337         return r;
338     },
339
340     /**
341      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
342      * @param {String} path
343      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
344      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
345      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
346      */
347     expandPath : function(path, attr, callback){
348         attr = attr || "id";
349         var keys = path.split(this.pathSeparator);
350         var curNode = this.root;
351         if(curNode.attributes[attr] != keys[1]){ // invalid root
352             if(callback){
353                 callback(false, null);
354             }
355             return;
356         }
357         var index = 1;
358         var f = function(){
359             if(++index == keys.length){
360                 if(callback){
361                     callback(true, curNode);
362                 }
363                 return;
364             }
365             var c = curNode.findChild(attr, keys[index]);
366             if(!c){
367                 if(callback){
368                     callback(false, curNode);
369                 }
370                 return;
371             }
372             curNode = c;
373             c.expand(false, false, f);
374         };
375         curNode.expand(false, false, f);
376     },
377
378     /**
379      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
380      * @param {String} path
381      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
382      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
383      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
384      */
385     selectPath : function(path, attr, callback){
386         attr = attr || "id";
387         var keys = path.split(this.pathSeparator);
388         var v = keys.pop();
389         if(keys.length > 0){
390             var f = function(success, node){
391                 if(success && node){
392                     var n = node.findChild(attr, v);
393                     if(n){
394                         n.select();
395                         if(callback){
396                             callback(true, n);
397                         }
398                     }else if(callback){
399                         callback(false, n);
400                     }
401                 }else{
402                     if(callback){
403                         callback(false, n);
404                     }
405                 }
406             };
407             this.expandPath(keys.join(this.pathSeparator), attr, f);
408         }else{
409             this.root.select();
410             if(callback){
411                 callback(true, this.root);
412             }
413         }
414     },
415
416     getTreeEl : function(){
417         return this.el;
418     },
419
420     /**
421      * Trigger rendering of this TreePanel
422      */
423     render : function(){
424         if (this.innerCt) {
425             return this; // stop it rendering more than once!!
426         }
427         
428         this.innerCt = this.el.createChild({tag:"ul",
429                cls:"x-tree-root-ct " +
430                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
431
432         if(this.containerScroll){
433             Roo.dd.ScrollManager.register(this.el);
434         }
435         if((this.enableDD || this.enableDrop) && !this.dropZone){
436            /**
437             * The dropZone used by this tree if drop is enabled
438             * @type Roo.tree.TreeDropZone
439             */
440              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
441                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
442            });
443         }
444         if((this.enableDD || this.enableDrag) && !this.dragZone){
445            /**
446             * The dragZone used by this tree if drag is enabled
447             * @type Roo.tree.TreeDragZone
448             */
449             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
450                ddGroup: this.ddGroup || "TreeDD",
451                scroll: this.ddScroll
452            });
453         }
454         this.getSelectionModel().init(this);
455         if (!this.root) {
456             console.log("ROOT not set in tree");
457             return;
458         }
459         this.root.render();
460         if(!this.rootVisible){
461             this.root.renderChildren();
462         }
463         return this;
464     }
465 });