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