Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / src / widgets / tree / TreePanel.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.tree.TreePanel\r
9  * @extends Ext.Panel\r
10  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>\r
11  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata\r
12  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>\r
13  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be\r
14  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.\r
15  * <p>An example of tree rendered to an existing div:</p><pre><code>\r
16 var tree = new Ext.tree.TreePanel({\r
17     renderTo: 'tree-div',\r
18     useArrows: true,\r
19     autoScroll: true,\r
20     animate: true,\r
21     enableDD: true,\r
22     containerScroll: true,\r
23     border: false,\r
24     // auto create TreeLoader\r
25     dataUrl: 'get-nodes.php',\r
26 \r
27     root: {\r
28         nodeType: 'async',\r
29         text: 'Ext JS',\r
30         draggable: false,\r
31         id: 'source'\r
32     }\r
33 });\r
34 \r
35 tree.getRootNode().expand();\r
36  * </code></pre>\r
37  * <p>The example above would work with a data packet similar to this:</p><pre><code>\r
38 [{\r
39     "text": "adapter",\r
40     "id": "source\/adapter",\r
41     "cls": "folder"\r
42 }, {\r
43     "text": "dd",\r
44     "id": "source\/dd",\r
45     "cls": "folder"\r
46 }, {\r
47     "text": "debug.js",\r
48     "id": "source\/debug.js",\r
49     "leaf": true,\r
50     "cls": "file"\r
51 }]\r
52  * </code></pre>\r
53  * <p>An example of tree within a Viewport:</p><pre><code>\r
54 new Ext.Viewport({\r
55     layout: 'border',\r
56     items: [{\r
57         region: 'west',\r
58         collapsible: true,\r
59         title: 'Navigation',\r
60         xtype: 'treepanel',\r
61         width: 200,\r
62         autoScroll: true,\r
63         split: true,\r
64         loader: new Ext.tree.TreeLoader(),\r
65         root: new Ext.tree.AsyncTreeNode({\r
66             expanded: true,\r
67             children: [{\r
68                 text: 'Menu Option 1',\r
69                 leaf: true\r
70             }, {\r
71                 text: 'Menu Option 2',\r
72                 leaf: true\r
73             }, {\r
74                 text: 'Menu Option 3',\r
75                 leaf: true\r
76             }]\r
77         }),\r
78         rootVisible: false,\r
79         listeners: {\r
80             click: function(n) {\r
81                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');\r
82             }\r
83         }\r
84     }, {\r
85         region: 'center',\r
86         xtype: 'tabpanel',\r
87         // remaining code not shown ...\r
88     }]\r
89 });\r
90 </code></pre>\r
91  *\r
92  * @cfg {Ext.tree.TreeNode} root The root node for the tree.\r
93  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)\r
94  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)\r
95  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop\r
96  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag\r
97  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop\r
98  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance\r
99  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance\r
100  * @cfg {String} ddGroup The DD group this TreePanel belongs to\r
101  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)\r
102  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling\r
103  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager\r
104  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})\r
105  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)\r
106  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})\r
107  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded\r
108  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})\r
109  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting\r
110  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel\r
111  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)\r
112  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)\r
113  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
114  *\r
115  * @constructor\r
116  * @param {Object} config\r
117  * @xtype treepanel\r
118  */\r
119 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {\r
120     rootVisible : true,\r
121     animate: Ext.enableFx,\r
122     lines : true,\r
123     enableDD : false,\r
124     hlDrop : Ext.enableFx,\r
125     pathSeparator: "/",\r
126 \r
127     initComponent : function(){\r
128         Ext.tree.TreePanel.superclass.initComponent.call(this);\r
129 \r
130         if(!this.eventModel){\r
131             this.eventModel = new Ext.tree.TreeEventModel(this);\r
132         }\r
133 \r
134         // initialize the loader\r
135         var l = this.loader;\r
136         if(!l){\r
137             l = new Ext.tree.TreeLoader({\r
138                 dataUrl: this.dataUrl,\r
139                 requestMethod: this.requestMethod\r
140             });\r
141         }else if(typeof l == 'object' && !l.load){\r
142             l = new Ext.tree.TreeLoader(l);\r
143         }\r
144         this.loader = l;\r
145 \r
146         this.nodeHash = {};\r
147 \r
148         /**\r
149         * The root node of this tree.\r
150         * @type Ext.tree.TreeNode\r
151         * @property root\r
152         */\r
153         if(this.root){\r
154             var r = this.root;\r
155             delete this.root;\r
156             this.setRootNode(r);\r
157         }\r
158 \r
159 \r
160         this.addEvents(\r
161 \r
162             /**\r
163             * @event append\r
164             * Fires when a new child node is appended to a node in this tree.\r
165             * @param {Tree} tree The owner tree\r
166             * @param {Node} parent The parent node\r
167             * @param {Node} node The newly appended node\r
168             * @param {Number} index The index of the newly appended node\r
169             */\r
170            "append",\r
171            /**\r
172             * @event remove\r
173             * Fires when a child node is removed from a node in this tree.\r
174             * @param {Tree} tree The owner tree\r
175             * @param {Node} parent The parent node\r
176             * @param {Node} node The child node removed\r
177             */\r
178            "remove",\r
179            /**\r
180             * @event movenode\r
181             * Fires when a node is moved to a new location in the tree\r
182             * @param {Tree} tree The owner tree\r
183             * @param {Node} node The node moved\r
184             * @param {Node} oldParent The old parent of this node\r
185             * @param {Node} newParent The new parent of this node\r
186             * @param {Number} index The index it was moved to\r
187             */\r
188            "movenode",\r
189            /**\r
190             * @event insert\r
191             * Fires when a new child node is inserted in a node in this tree.\r
192             * @param {Tree} tree The owner tree\r
193             * @param {Node} parent The parent node\r
194             * @param {Node} node The child node inserted\r
195             * @param {Node} refNode The child node the node was inserted before\r
196             */\r
197            "insert",\r
198            /**\r
199             * @event beforeappend\r
200             * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
201             * @param {Tree} tree The owner tree\r
202             * @param {Node} parent The parent node\r
203             * @param {Node} node The child node to be appended\r
204             */\r
205            "beforeappend",\r
206            /**\r
207             * @event beforeremove\r
208             * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
209             * @param {Tree} tree The owner tree\r
210             * @param {Node} parent The parent node\r
211             * @param {Node} node The child node to be removed\r
212             */\r
213            "beforeremove",\r
214            /**\r
215             * @event beforemovenode\r
216             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
217             * @param {Tree} tree The owner tree\r
218             * @param {Node} node The node being moved\r
219             * @param {Node} oldParent The parent of the node\r
220             * @param {Node} newParent The new parent the node is moving to\r
221             * @param {Number} index The index it is being moved to\r
222             */\r
223            "beforemovenode",\r
224            /**\r
225             * @event beforeinsert\r
226             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
227             * @param {Tree} tree The owner tree\r
228             * @param {Node} parent The parent node\r
229             * @param {Node} node The child node to be inserted\r
230             * @param {Node} refNode The child node the node is being inserted before\r
231             */\r
232             "beforeinsert",\r
233 \r
234             /**\r
235             * @event beforeload\r
236             * Fires before a node is loaded, return false to cancel\r
237             * @param {Node} node The node being loaded\r
238             */\r
239             "beforeload",\r
240             /**\r
241             * @event load\r
242             * Fires when a node is loaded\r
243             * @param {Node} node The node that was loaded\r
244             */\r
245             "load",\r
246             /**\r
247             * @event textchange\r
248             * Fires when the text for a node is changed\r
249             * @param {Node} node The node\r
250             * @param {String} text The new text\r
251             * @param {String} oldText The old text\r
252             */\r
253             "textchange",\r
254             /**\r
255             * @event beforeexpandnode\r
256             * Fires before a node is expanded, return false to cancel.\r
257             * @param {Node} node The node\r
258             * @param {Boolean} deep\r
259             * @param {Boolean} anim\r
260             */\r
261             "beforeexpandnode",\r
262             /**\r
263             * @event beforecollapsenode\r
264             * Fires before a node is collapsed, return false to cancel.\r
265             * @param {Node} node The node\r
266             * @param {Boolean} deep\r
267             * @param {Boolean} anim\r
268             */\r
269             "beforecollapsenode",\r
270             /**\r
271             * @event expandnode\r
272             * Fires when a node is expanded\r
273             * @param {Node} node The node\r
274             */\r
275             "expandnode",\r
276             /**\r
277             * @event disabledchange\r
278             * Fires when the disabled status of a node changes\r
279             * @param {Node} node The node\r
280             * @param {Boolean} disabled\r
281             */\r
282             "disabledchange",\r
283             /**\r
284             * @event collapsenode\r
285             * Fires when a node is collapsed\r
286             * @param {Node} node The node\r
287             */\r
288             "collapsenode",\r
289             /**\r
290             * @event beforeclick\r
291             * Fires before click processing on a node. Return false to cancel the default action.\r
292             * @param {Node} node The node\r
293             * @param {Ext.EventObject} e The event object\r
294             */\r
295             "beforeclick",\r
296             /**\r
297             * @event click\r
298             * Fires when a node is clicked\r
299             * @param {Node} node The node\r
300             * @param {Ext.EventObject} e The event object\r
301             */\r
302             "click",\r
303             /**\r
304             * @event checkchange\r
305             * Fires when a node with a checkbox's checked property changes\r
306             * @param {Node} this This node\r
307             * @param {Boolean} checked\r
308             */\r
309             "checkchange",\r
310             /**\r
311             * @event dblclick\r
312             * Fires when a node is double clicked\r
313             * @param {Node} node The node\r
314             * @param {Ext.EventObject} e The event object\r
315             */\r
316             "dblclick",\r
317             /**\r
318             * @event contextmenu\r
319             * Fires when a node is right clicked. To display a context menu in response to this\r
320             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add\r
321             * a handler for this event:<pre><code>\r
322 new Ext.tree.TreePanel({\r
323     title: 'My TreePanel',\r
324     root: new Ext.tree.AsyncTreeNode({\r
325         text: 'The Root',\r
326         children: [\r
327             { text: 'Child node 1', leaf: true },\r
328             { text: 'Child node 2', leaf: true }\r
329         ]\r
330     }),\r
331     contextMenu: new Ext.menu.Menu({\r
332         items: [{\r
333             id: 'delete-node',\r
334             text: 'Delete Node'\r
335         }],\r
336         listeners: {\r
337             itemclick: function(item) {\r
338                 switch (item.id) {\r
339                     case 'delete-node':\r
340                         var n = item.parentMenu.contextNode;\r
341                         if (n.parentNode) {\r
342                             n.remove();\r
343                         }\r
344                         break;\r
345                 }\r
346             }\r
347         }\r
348     }),\r
349     listeners: {\r
350         contextmenu: function(node, e) {\r
351 //          Register the context node with the menu so that a Menu Item's handler function can access\r
352 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.\r
353             node.select();\r
354             var c = node.getOwnerTree().contextMenu;\r
355             c.contextNode = node;\r
356             c.showAt(e.getXY());\r
357         }\r
358     }\r
359 });\r
360 </code></pre>\r
361             * @param {Node} node The node\r
362             * @param {Ext.EventObject} e The event object\r
363             */\r
364             "contextmenu",\r
365             /**\r
366             * @event beforechildrenrendered\r
367             * Fires right before the child nodes for a node are rendered\r
368             * @param {Node} node The node\r
369             */\r
370             "beforechildrenrendered",\r
371            /**\r
372              * @event startdrag\r
373              * Fires when a node starts being dragged\r
374              * @param {Ext.tree.TreePanel} this\r
375              * @param {Ext.tree.TreeNode} node\r
376              * @param {event} e The raw browser event\r
377              */\r
378             "startdrag",\r
379             /**\r
380              * @event enddrag\r
381              * Fires when a drag operation is complete\r
382              * @param {Ext.tree.TreePanel} this\r
383              * @param {Ext.tree.TreeNode} node\r
384              * @param {event} e The raw browser event\r
385              */\r
386             "enddrag",\r
387             /**\r
388              * @event dragdrop\r
389              * Fires when a dragged node is dropped on a valid DD target\r
390              * @param {Ext.tree.TreePanel} this\r
391              * @param {Ext.tree.TreeNode} node\r
392              * @param {DD} dd The dd it was dropped on\r
393              * @param {event} e The raw browser event\r
394              */\r
395             "dragdrop",\r
396             /**\r
397              * @event beforenodedrop\r
398              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent\r
399              * passed to handlers has the following properties:<br />\r
400              * <ul style="padding:5px;padding-left:16px;">\r
401              * <li>tree - The TreePanel</li>\r
402              * <li>target - The node being targeted for the drop</li>\r
403              * <li>data - The drag data from the drag source</li>\r
404              * <li>point - The point of the drop - append, above or below</li>\r
405              * <li>source - The drag source</li>\r
406              * <li>rawEvent - Raw mouse event</li>\r
407              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)\r
408              * to be inserted by setting them on this object.</li>\r
409              * <li>cancel - Set this to true to cancel the drop.</li>\r
410              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true\r
411              * will prevent the animated "repair" from appearing.</li>\r
412              * </ul>\r
413              * @param {Object} dropEvent\r
414              */\r
415             "beforenodedrop",\r
416             /**\r
417              * @event nodedrop\r
418              * Fires after a DD object is dropped on a node in this tree. The dropEvent\r
419              * passed to handlers has the following properties:<br />\r
420              * <ul style="padding:5px;padding-left:16px;">\r
421              * <li>tree - The TreePanel</li>\r
422              * <li>target - The node being targeted for the drop</li>\r
423              * <li>data - The drag data from the drag source</li>\r
424              * <li>point - The point of the drop - append, above or below</li>\r
425              * <li>source - The drag source</li>\r
426              * <li>rawEvent - Raw mouse event</li>\r
427              * <li>dropNode - Dropped node(s).</li>\r
428              * </ul>\r
429              * @param {Object} dropEvent\r
430              */\r
431             "nodedrop",\r
432              /**\r
433              * @event nodedragover\r
434              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent\r
435              * passed to handlers has the following properties:<br />\r
436              * <ul style="padding:5px;padding-left:16px;">\r
437              * <li>tree - The TreePanel</li>\r
438              * <li>target - The node being targeted for the drop</li>\r
439              * <li>data - The drag data from the drag source</li>\r
440              * <li>point - The point of the drop - append, above or below</li>\r
441              * <li>source - The drag source</li>\r
442              * <li>rawEvent - Raw mouse event</li>\r
443              * <li>dropNode - Drop node(s) provided by the source.</li>\r
444              * <li>cancel - Set this to true to signal drop not allowed.</li>\r
445              * </ul>\r
446              * @param {Object} dragOverEvent\r
447              */\r
448             "nodedragover"\r
449         );\r
450         if(this.singleExpand){\r
451             this.on("beforeexpandnode", this.restrictExpand, this);\r
452         }\r
453     },\r
454 \r
455     // private\r
456     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){\r
457         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){\r
458             ename = ename+'node';\r
459         }\r
460         // args inline for performance while bubbling events\r
461         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);\r
462     },\r
463 \r
464 \r
465     /**\r
466      * Returns this root node for this tree\r
467      * @return {Node}\r
468      */\r
469     getRootNode : function(){\r
470         return this.root;\r
471     },\r
472 \r
473     /**\r
474      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the\r
475      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.\r
476      * @param {Node} node\r
477      * @return {Node}\r
478      */\r
479     setRootNode : function(node){\r
480         Ext.destroy(this.root);\r
481         if(!node.render){ // attributes passed\r
482             node = this.loader.createNode(node);\r
483         }\r
484         this.root = node;\r
485         node.ownerTree = this;\r
486         node.isRoot = true;\r
487         this.registerNode(node);\r
488         if(!this.rootVisible){\r
489             var uiP = node.attributes.uiProvider;\r
490             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);\r
491         }\r
492         if (this.innerCt) {\r
493             this.innerCt.update('');\r
494             this.afterRender();\r
495         }\r
496         return node;\r
497     },\r
498 \r
499     /**\r
500      * Gets a node in this tree by its id\r
501      * @param {String} id\r
502      * @return {Node}\r
503      */\r
504     getNodeById : function(id){\r
505         return this.nodeHash[id];\r
506     },\r
507 \r
508     // private\r
509     registerNode : function(node){\r
510         this.nodeHash[node.id] = node;\r
511     },\r
512 \r
513     // private\r
514     unregisterNode : function(node){\r
515         delete this.nodeHash[node.id];\r
516     },\r
517 \r
518     // private\r
519     toString : function(){\r
520         return "[Tree"+(this.id?" "+this.id:"")+"]";\r
521     },\r
522 \r
523     // private\r
524     restrictExpand : function(node){\r
525         var p = node.parentNode;\r
526         if(p){\r
527             if(p.expandedChild && p.expandedChild.parentNode == p){\r
528                 p.expandedChild.collapse();\r
529             }\r
530             p.expandedChild = node;\r
531         }\r
532     },\r
533 \r
534     /**\r
535      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")\r
536      * @param {String} attribute (optional) Defaults to null (return the actual nodes)\r
537      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root\r
538      * @return {Array}\r
539      */\r
540     getChecked : function(a, startNode){\r
541         startNode = startNode || this.root;\r
542         var r = [];\r
543         var f = function(){\r
544             if(this.attributes.checked){\r
545                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));\r
546             }\r
547         };\r
548         startNode.cascade(f);\r
549         return r;\r
550     },\r
551 \r
552     /**\r
553      * Returns the container element for this TreePanel.\r
554      * @return {Element} The container element for this TreePanel.\r
555      */\r
556     getEl : function(){\r
557         return this.el;\r
558     },\r
559 \r
560     /**\r
561      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.\r
562      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.\r
563      */\r
564     getLoader : function(){\r
565         return this.loader;\r
566     },\r
567 \r
568     /**\r
569      * Expand all nodes\r
570      */\r
571     expandAll : function(){\r
572         this.root.expand(true);\r
573     },\r
574 \r
575     /**\r
576      * Collapse all nodes\r
577      */\r
578     collapseAll : function(){\r
579         this.root.collapse(true);\r
580     },\r
581 \r
582     /**\r
583      * Returns the selection model used by this TreePanel.\r
584      * @return {TreeSelectionModel} The selection model used by this TreePanel\r
585      */\r
586     getSelectionModel : function(){\r
587         if(!this.selModel){\r
588             this.selModel = new Ext.tree.DefaultSelectionModel();\r
589         }\r
590         return this.selModel;\r
591     },\r
592 \r
593     /**\r
594      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
595      * @param {String} path\r
596      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
597      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with\r
598      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.\r
599      */\r
600     expandPath : function(path, attr, callback){\r
601         attr = attr || "id";\r
602         var keys = path.split(this.pathSeparator);\r
603         var curNode = this.root;\r
604         if(curNode.attributes[attr] != keys[1]){ // invalid root\r
605             if(callback){\r
606                 callback(false, null);\r
607             }\r
608             return;\r
609         }\r
610         var index = 1;\r
611         var f = function(){\r
612             if(++index == keys.length){\r
613                 if(callback){\r
614                     callback(true, curNode);\r
615                 }\r
616                 return;\r
617             }\r
618             var c = curNode.findChild(attr, keys[index]);\r
619             if(!c){\r
620                 if(callback){\r
621                     callback(false, curNode);\r
622                 }\r
623                 return;\r
624             }\r
625             curNode = c;\r
626             c.expand(false, false, f);\r
627         };\r
628         curNode.expand(false, false, f);\r
629     },\r
630 \r
631     /**\r
632      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
633      * @param {String} path\r
634      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
635      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with\r
636      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.\r
637      */\r
638     selectPath : function(path, attr, callback){\r
639         attr = attr || "id";\r
640         var keys = path.split(this.pathSeparator);\r
641         var v = keys.pop();\r
642         if(keys.length > 0){\r
643             var f = function(success, node){\r
644                 if(success && node){\r
645                     var n = node.findChild(attr, v);\r
646                     if(n){\r
647                         n.select();\r
648                         if(callback){\r
649                             callback(true, n);\r
650                         }\r
651                     }else if(callback){\r
652                         callback(false, n);\r
653                     }\r
654                 }else{\r
655                     if(callback){\r
656                         callback(false, n);\r
657                     }\r
658                 }\r
659             };\r
660             this.expandPath(keys.join(this.pathSeparator), attr, f);\r
661         }else{\r
662             this.root.select();\r
663             if(callback){\r
664                 callback(true, this.root);\r
665             }\r
666         }\r
667     },\r
668 \r
669     /**\r
670      * Returns the underlying Element for this tree\r
671      * @return {Ext.Element} The Element\r
672      */\r
673     getTreeEl : function(){\r
674         return this.body;\r
675     },\r
676 \r
677     // private\r
678     onRender : function(ct, position){\r
679         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);\r
680         this.el.addClass('x-tree');\r
681         this.innerCt = this.body.createChild({tag:"ul",\r
682                cls:"x-tree-root-ct " +\r
683                (this.useArrows ? 'x-tree-arrows' : this.lines ? "x-tree-lines" : "x-tree-no-lines")});\r
684     },\r
685 \r
686     // private\r
687     initEvents : function(){\r
688         Ext.tree.TreePanel.superclass.initEvents.call(this);\r
689 \r
690         if(this.containerScroll){\r
691             Ext.dd.ScrollManager.register(this.body);\r
692         }\r
693         if((this.enableDD || this.enableDrop) && !this.dropZone){\r
694            /**\r
695             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})\r
696             * @property dropZone\r
697             * @type Ext.tree.TreeDropZone\r
698             */\r
699              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {\r
700                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true\r
701            });\r
702         }\r
703         if((this.enableDD || this.enableDrag) && !this.dragZone){\r
704            /**\r
705             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})\r
706             * @property dragZone\r
707             * @type Ext.tree.TreeDragZone\r
708             */\r
709             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {\r
710                ddGroup: this.ddGroup || "TreeDD",\r
711                scroll: this.ddScroll\r
712            });\r
713         }\r
714         this.getSelectionModel().init(this);\r
715     },\r
716 \r
717     // private\r
718     afterRender : function(){\r
719         Ext.tree.TreePanel.superclass.afterRender.call(this);\r
720         this.root.render();\r
721         if(!this.rootVisible){\r
722             this.root.renderChildren();\r
723         }\r
724     },\r
725 \r
726     onDestroy : function(){\r
727         if(this.rendered){\r
728             this.body.removeAllListeners();\r
729             Ext.dd.ScrollManager.unregister(this.body);\r
730             if(this.dropZone){\r
731                 this.dropZone.unreg();\r
732             }\r
733             if(this.dragZone){\r
734                this.dragZone.unreg();\r
735             }\r
736         }\r
737         this.root.destroy();\r
738         this.nodeHash = null;\r
739         Ext.tree.TreePanel.superclass.onDestroy.call(this);\r
740     }\r
741 \r
742     /**\r
743      * @cfg {String/Number} activeItem\r
744      * @hide\r
745      */\r
746     /**\r
747      * @cfg {Boolean} autoDestroy\r
748      * @hide\r
749      */\r
750     /**\r
751      * @cfg {Object/String/Function} autoLoad\r
752      * @hide\r
753      */\r
754     /**\r
755      * @cfg {Boolean} autoWidth\r
756      * @hide\r
757      */\r
758     /**\r
759      * @cfg {Boolean/Number} bufferResize\r
760      * @hide\r
761      */\r
762     /**\r
763      * @cfg {String} defaultType\r
764      * @hide\r
765      */\r
766     /**\r
767      * @cfg {Object} defaults\r
768      * @hide\r
769      */\r
770     /**\r
771      * @cfg {Boolean} hideBorders\r
772      * @hide\r
773      */\r
774     /**\r
775      * @cfg {Mixed} items\r
776      * @hide\r
777      */\r
778     /**\r
779      * @cfg {String} layout\r
780      * @hide\r
781      */\r
782     /**\r
783      * @cfg {Object} layoutConfig\r
784      * @hide\r
785      */\r
786     /**\r
787      * @cfg {Boolean} monitorResize\r
788      * @hide\r
789      */\r
790     /**\r
791      * @property items\r
792      * @hide\r
793      */\r
794     /**\r
795      * @method cascade\r
796      * @hide\r
797      */\r
798     /**\r
799      * @method doLayout\r
800      * @hide\r
801      */\r
802     /**\r
803      * @method find\r
804      * @hide\r
805      */\r
806     /**\r
807      * @method findBy\r
808      * @hide\r
809      */\r
810     /**\r
811      * @method findById\r
812      * @hide\r
813      */\r
814     /**\r
815      * @method findByType\r
816      * @hide\r
817      */\r
818     /**\r
819      * @method getComponent\r
820      * @hide\r
821      */\r
822     /**\r
823      * @method getLayout\r
824      * @hide\r
825      */\r
826     /**\r
827      * @method getUpdater\r
828      * @hide\r
829      */\r
830     /**\r
831      * @method insert\r
832      * @hide\r
833      */\r
834     /**\r
835      * @method load\r
836      * @hide\r
837      */\r
838     /**\r
839      * @method remove\r
840      * @hide\r
841      */\r
842     /**\r
843      * @event add\r
844      * @hide\r
845      */\r
846     /**\r
847      * @method removeAll\r
848      * @hide\r
849      */\r
850     /**\r
851      * @event afterLayout\r
852      * @hide\r
853      */\r
854     /**\r
855      * @event beforeadd\r
856      * @hide\r
857      */\r
858     /**\r
859      * @event beforeremove\r
860      * @hide\r
861      */\r
862     /**\r
863      * @event remove\r
864      * @hide\r
865      */\r
866 \r
867 \r
868 \r
869     /**\r
870      * @cfg {String} allowDomMove  @hide\r
871      */\r
872     /**\r
873      * @cfg {String} autoEl @hide\r
874      */\r
875     /**\r
876      * @cfg {String} applyTo  @hide\r
877      */\r
878     /**\r
879      * @cfg {String} contentEl  @hide\r
880      */\r
881     /**\r
882      * @cfg {String} disabledClass  @hide\r
883      */\r
884     /**\r
885      * @cfg {String} elements  @hide\r
886      */\r
887     /**\r
888      * @cfg {String} html  @hide\r
889      */\r
890     /**\r
891      * @cfg {Boolean} preventBodyReset\r
892      * @hide\r
893      */\r
894     /**\r
895      * @property disabled\r
896      * @hide\r
897      */\r
898     /**\r
899      * @method applyToMarkup\r
900      * @hide\r
901      */\r
902     /**\r
903      * @method enable\r
904      * @hide\r
905      */\r
906     /**\r
907      * @method disable\r
908      * @hide\r
909      */\r
910     /**\r
911      * @method setDisabled\r
912      * @hide\r
913      */\r
914 });\r
915 \r
916 Ext.tree.TreePanel.nodeTypes = {};\r
917 \r
918 Ext.reg('treepanel', Ext.tree.TreePanel);