Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / docs / source / TreeNode.html
1 <html>\r
2 <head>\r
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    \r
4   <title>The source code</title>\r
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
7 </head>\r
8 <body  onload="prettyPrint();">\r
9     <pre class="prettyprint lang-js"><div id="cls-Ext.tree.TreeNode"></div>/**\r
10  * @class Ext.tree.TreeNode\r
11  * @extends Ext.data.Node\r
12  * @cfg {String} text The text for this node\r
13  * @cfg {Boolean} expanded true to start the node expanded\r
14  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)\r
15  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)\r
16  * @cfg {Boolean} disabled true to start the node disabled\r
17  * @cfg {String} icon The path to an icon for the node. The preferred way to do this\r
18  * is to use the cls or iconCls attributes and add the icon via a CSS background image.\r
19  * @cfg {String} cls A css class to be added to the node\r
20  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images\r
21  * @cfg {String} href URL of the link used for the node (defaults to #)\r
22  * @cfg {String} hrefTarget target frame for the link\r
23  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).\r
24  * @cfg {String} qtip An Ext QuickTip for the node\r
25  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty\r
26  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)\r
27  * @cfg {Boolean} singleClickExpand True for single click expand on this node\r
28  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)\r
29  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox\r
30  * (defaults to undefined with no checkbox rendered)\r
31  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)\r
32  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)\r
33  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)\r
34  * @cfg {Boolean} editable False to not allow this node to be edited by an {@link Ext.tree.TreeEditor} (defaults to true)\r
35  * @constructor\r
36  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node\r
37  */\r
38 Ext.tree.TreeNode = function(attributes){\r
39     attributes = attributes || {};\r
40     if(Ext.isString(attributes)){\r
41         attributes = {text: attributes};\r
42     }\r
43     this.childrenRendered = false;\r
44     this.rendered = false;\r
45     Ext.tree.TreeNode.superclass.constructor.call(this, attributes);\r
46     this.expanded = attributes.expanded === true;\r
47     this.isTarget = attributes.isTarget !== false;\r
48     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;\r
49     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;\r
50 \r
51     /**\r
52      * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.\r
53      * @type String\r
54      */\r
55     this.text = attributes.text;\r
56     /**\r
57      * True if this node is disabled.\r
58      * @type Boolean\r
59      */\r
60     this.disabled = attributes.disabled === true;\r
61     /**\r
62      * True if this node is hidden.\r
63      * @type Boolean\r
64      */\r
65     this.hidden = attributes.hidden === true;\r
66 \r
67     this.addEvents(\r
68         /**\r
69         * @event textchange\r
70         * Fires when the text for this node is changed\r
71         * @param {Node} this This node\r
72         * @param {String} text The new text\r
73         * @param {String} oldText The old text\r
74         */\r
75         'textchange',\r
76         /**\r
77         * @event beforeexpand\r
78         * Fires before this node is expanded, return false to cancel.\r
79         * @param {Node} this This node\r
80         * @param {Boolean} deep\r
81         * @param {Boolean} anim\r
82         */\r
83         'beforeexpand',\r
84         /**\r
85         * @event beforecollapse\r
86         * Fires before this node is collapsed, return false to cancel.\r
87         * @param {Node} this This node\r
88         * @param {Boolean} deep\r
89         * @param {Boolean} anim\r
90         */\r
91         'beforecollapse',\r
92         /**\r
93         * @event expand\r
94         * Fires when this node is expanded\r
95         * @param {Node} this This node\r
96         */\r
97         'expand',\r
98         /**\r
99         * @event disabledchange\r
100         * Fires when the disabled status of this node changes\r
101         * @param {Node} this This node\r
102         * @param {Boolean} disabled\r
103         */\r
104         'disabledchange',\r
105         /**\r
106         * @event collapse\r
107         * Fires when this node is collapsed\r
108         * @param {Node} this This node\r
109         */\r
110         'collapse',\r
111         /**\r
112         * @event beforeclick\r
113         * Fires before click processing. Return false to cancel the default action.\r
114         * @param {Node} this This node\r
115         * @param {Ext.EventObject} e The event object\r
116         */\r
117         'beforeclick',\r
118         /**\r
119         * @event click\r
120         * Fires when this node is clicked\r
121         * @param {Node} this This node\r
122         * @param {Ext.EventObject} e The event object\r
123         */\r
124         'click',\r
125         /**\r
126         * @event checkchange\r
127         * Fires when a node with a checkbox's checked property changes\r
128         * @param {Node} this This node\r
129         * @param {Boolean} checked\r
130         */\r
131         'checkchange',\r
132         /**\r
133         * @event beforedblclick\r
134         * Fires before double click processing. Return false to cancel the default action.\r
135         * @param {Node} this This node\r
136         * @param {Ext.EventObject} e The event object\r
137         */\r
138         'beforedblclick',\r
139         /**\r
140         * @event dblclick\r
141         * Fires when this node is double clicked\r
142         * @param {Node} this This node\r
143         * @param {Ext.EventObject} e The event object\r
144         */\r
145         'dblclick',\r
146         /**\r
147         * @event contextmenu\r
148         * Fires when this node is right clicked\r
149         * @param {Node} this This node\r
150         * @param {Ext.EventObject} e The event object\r
151         */\r
152         'contextmenu',\r
153         /**\r
154         * @event beforechildrenrendered\r
155         * Fires right before the child nodes for this node are rendered\r
156         * @param {Node} this This node\r
157         */\r
158         'beforechildrenrendered'\r
159     );\r
160 \r
161     var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;\r
162 \r
163     /**\r
164      * Read-only. The UI for this node\r
165      * @type TreeNodeUI\r
166      */\r
167     this.ui = new uiClass(this);\r
168 };\r
169 Ext.extend(Ext.tree.TreeNode, Ext.data.Node, {\r
170     preventHScroll : true,\r
171     /**\r
172      * Returns true if this node is expanded\r
173      * @return {Boolean}\r
174      */\r
175     isExpanded : function(){\r
176         return this.expanded;\r
177     },\r
178 \r
179 /**\r
180  * Returns the UI object for this node.\r
181  * @return {TreeNodeUI} The object which is providing the user interface for this tree\r
182  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance\r
183  * of {@link Ext.tree.TreeNodeUI}\r
184  */\r
185     getUI : function(){\r
186         return this.ui;\r
187     },\r
188 \r
189     getLoader : function(){\r
190         var owner;\r
191         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : (this.loader = new Ext.tree.TreeLoader()));\r
192     },\r
193 \r
194     // private override\r
195     setFirstChild : function(node){\r
196         var of = this.firstChild;\r
197         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);\r
198         if(this.childrenRendered && of && node != of){\r
199             of.renderIndent(true, true);\r
200         }\r
201         if(this.rendered){\r
202             this.renderIndent(true, true);\r
203         }\r
204     },\r
205 \r
206     // private override\r
207     setLastChild : function(node){\r
208         var ol = this.lastChild;\r
209         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);\r
210         if(this.childrenRendered && ol && node != ol){\r
211             ol.renderIndent(true, true);\r
212         }\r
213         if(this.rendered){\r
214             this.renderIndent(true, true);\r
215         }\r
216     },\r
217 \r
218     // these methods are overridden to provide lazy rendering support\r
219     // private override\r
220     appendChild : function(n){\r
221         if(!n.render && !Ext.isArray(n)){\r
222             n = this.getLoader().createNode(n);\r
223         }\r
224         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);\r
225         if(node && this.childrenRendered){\r
226             node.render();\r
227         }\r
228         this.ui.updateExpandIcon();\r
229         return node;\r
230     },\r
231 \r
232     // private override\r
233     removeChild : function(node, destroy){\r
234         this.ownerTree.getSelectionModel().unselect(node);\r
235         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);\r
236         // if it's been rendered remove dom node\r
237         if(node.ui.rendered){\r
238             node.ui.remove();\r
239         }\r
240         if(this.childNodes.length < 1){\r
241             this.collapse(false, false);\r
242         }else{\r
243             this.ui.updateExpandIcon();\r
244         }\r
245         if(!this.firstChild && !this.isHiddenRoot()) {\r
246             this.childrenRendered = false;\r
247         }\r
248         return node;\r
249     },\r
250 \r
251     // private override\r
252     insertBefore : function(node, refNode){\r
253         if(!node.render){\r
254             node = this.getLoader().createNode(node);\r
255         }\r
256         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);\r
257         if(newNode && refNode && this.childrenRendered){\r
258             node.render();\r
259         }\r
260         this.ui.updateExpandIcon();\r
261         return newNode;\r
262     },\r
263 \r
264     /**\r
265      * Sets the text for this node\r
266      * @param {String} text\r
267      */\r
268     setText : function(text){\r
269         var oldText = this.text;\r
270         this.text = this.attributes.text = text;\r
271         if(this.rendered){ // event without subscribing\r
272             this.ui.onTextChange(this, text, oldText);\r
273         }\r
274         this.fireEvent('textchange', this, text, oldText);\r
275     },\r
276 \r
277     /**\r
278      * Triggers selection of this node\r
279      */\r
280     select : function(){\r
281         var t = this.getOwnerTree();\r
282         if(t){\r
283             t.getSelectionModel().select(this);\r
284         }\r
285     },\r
286 \r
287     /**\r
288      * Triggers deselection of this node\r
289      * @param {Boolean} silent (optional) True to stop selection change events from firing.\r
290      */\r
291     unselect : function(silent){\r
292         var t = this.getOwnerTree();\r
293         if(t){\r
294             t.getSelectionModel().unselect(this, silent);\r
295         }\r
296     },\r
297 \r
298     /**\r
299      * Returns true if this node is selected\r
300      * @return {Boolean}\r
301      */\r
302     isSelected : function(){\r
303         var t = this.getOwnerTree();\r
304         return t ? t.getSelectionModel().isSelected(this) : false;\r
305     },\r
306 \r
307     /**\r
308      * Expand this node.\r
309      * @param {Boolean} deep (optional) True to expand all children as well\r
310      * @param {Boolean} anim (optional) false to cancel the default animation\r
311      * @param {Function} callback (optional) A callback to be called when\r
312      * expanding this node completes (does not wait for deep expand to complete).\r
313      * Called with 1 parameter, this node.\r
314      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.\r
315      */\r
316     expand : function(deep, anim, callback, scope){\r
317         if(!this.expanded){\r
318             if(this.fireEvent('beforeexpand', this, deep, anim) === false){\r
319                 return;\r
320             }\r
321             if(!this.childrenRendered){\r
322                 this.renderChildren();\r
323             }\r
324             this.expanded = true;\r
325             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){\r
326                 this.ui.animExpand(function(){\r
327                     this.fireEvent('expand', this);\r
328                     this.runCallback(callback, scope || this, [this]);\r
329                     if(deep === true){\r
330                         this.expandChildNodes(true);\r
331                     }\r
332                 }.createDelegate(this));\r
333                 return;\r
334             }else{\r
335                 this.ui.expand();\r
336                 this.fireEvent('expand', this);\r
337                 this.runCallback(callback, scope || this, [this]);\r
338             }\r
339         }else{\r
340            this.runCallback(callback, scope || this, [this]);\r
341         }\r
342         if(deep === true){\r
343             this.expandChildNodes(true);\r
344         }\r
345     },\r
346 \r
347     runCallback : function(cb, scope, args){\r
348         if(Ext.isFunction(cb)){\r
349             cb.apply(scope, args);\r
350         }\r
351     },\r
352 \r
353     isHiddenRoot : function(){\r
354         return this.isRoot && !this.getOwnerTree().rootVisible;\r
355     },\r
356 \r
357     /**\r
358      * Collapse this node.\r
359      * @param {Boolean} deep (optional) True to collapse all children as well\r
360      * @param {Boolean} anim (optional) false to cancel the default animation\r
361      * @param {Function} callback (optional) A callback to be called when\r
362      * expanding this node completes (does not wait for deep expand to complete).\r
363      * Called with 1 parameter, this node.\r
364      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.\r
365      */\r
366     collapse : function(deep, anim, callback, scope){\r
367         if(this.expanded && !this.isHiddenRoot()){\r
368             if(this.fireEvent('beforecollapse', this, deep, anim) === false){\r
369                 return;\r
370             }\r
371             this.expanded = false;\r
372             if((this.getOwnerTree().animate && anim !== false) || anim){\r
373                 this.ui.animCollapse(function(){\r
374                     this.fireEvent('collapse', this);\r
375                     this.runCallback(callback, scope || this, [this]);\r
376                     if(deep === true){\r
377                         this.collapseChildNodes(true);\r
378                     }\r
379                 }.createDelegate(this));\r
380                 return;\r
381             }else{\r
382                 this.ui.collapse();\r
383                 this.fireEvent('collapse', this);\r
384                 this.runCallback(callback, scope || this, [this]);\r
385             }\r
386         }else if(!this.expanded){\r
387             this.runCallback(callback, scope || this, [this]);\r
388         }\r
389         if(deep === true){\r
390             var cs = this.childNodes;\r
391             for(var i = 0, len = cs.length; i < len; i++) {\r
392                 cs[i].collapse(true, false);\r
393             }\r
394         }\r
395     },\r
396 \r
397     // private\r
398     delayedExpand : function(delay){\r
399         if(!this.expandProcId){\r
400             this.expandProcId = this.expand.defer(delay, this);\r
401         }\r
402     },\r
403 \r
404     // private\r
405     cancelExpand : function(){\r
406         if(this.expandProcId){\r
407             clearTimeout(this.expandProcId);\r
408         }\r
409         this.expandProcId = false;\r
410     },\r
411 \r
412     /**\r
413      * Toggles expanded/collapsed state of the node\r
414      */\r
415     toggle : function(){\r
416         if(this.expanded){\r
417             this.collapse();\r
418         }else{\r
419             this.expand();\r
420         }\r
421     },\r
422 \r
423     /**\r
424      * Ensures all parent nodes are expanded, and if necessary, scrolls\r
425      * the node into view.\r
426      * @param {Function} callback (optional) A function to call when the node has been made visible.\r
427      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.\r
428      */\r
429     ensureVisible : function(callback, scope){\r
430         var tree = this.getOwnerTree();\r
431         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){\r
432             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime\r
433             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);\r
434             this.runCallback(callback, scope || this, [this]);\r
435         }.createDelegate(this));\r
436     },\r
437 \r
438     /**\r
439      * Expand all child nodes\r
440      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes\r
441      */\r
442     expandChildNodes : function(deep){\r
443         var cs = this.childNodes;\r
444         for(var i = 0, len = cs.length; i < len; i++) {\r
445                 cs[i].expand(deep);\r
446         }\r
447     },\r
448 \r
449     /**\r
450      * Collapse all child nodes\r
451      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes\r
452      */\r
453     collapseChildNodes : function(deep){\r
454         var cs = this.childNodes;\r
455         for(var i = 0, len = cs.length; i < len; i++) {\r
456                 cs[i].collapse(deep);\r
457         }\r
458     },\r
459 \r
460     /**\r
461      * Disables this node\r
462      */\r
463     disable : function(){\r
464         this.disabled = true;\r
465         this.unselect();\r
466         if(this.rendered && this.ui.onDisableChange){ // event without subscribing\r
467             this.ui.onDisableChange(this, true);\r
468         }\r
469         this.fireEvent('disabledchange', this, true);\r
470     },\r
471 \r
472     /**\r
473      * Enables this node\r
474      */\r
475     enable : function(){\r
476         this.disabled = false;\r
477         if(this.rendered && this.ui.onDisableChange){ // event without subscribing\r
478             this.ui.onDisableChange(this, false);\r
479         }\r
480         this.fireEvent('disabledchange', this, false);\r
481     },\r
482 \r
483     // private\r
484     renderChildren : function(suppressEvent){\r
485         if(suppressEvent !== false){\r
486             this.fireEvent('beforechildrenrendered', this);\r
487         }\r
488         var cs = this.childNodes;\r
489         for(var i = 0, len = cs.length; i < len; i++){\r
490             cs[i].render(true);\r
491         }\r
492         this.childrenRendered = true;\r
493     },\r
494 \r
495     // private\r
496     sort : function(fn, scope){\r
497         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);\r
498         if(this.childrenRendered){\r
499             var cs = this.childNodes;\r
500             for(var i = 0, len = cs.length; i < len; i++){\r
501                 cs[i].render(true);\r
502             }\r
503         }\r
504     },\r
505 \r
506     // private\r
507     render : function(bulkRender){\r
508         this.ui.render(bulkRender);\r
509         if(!this.rendered){\r
510             // make sure it is registered\r
511             this.getOwnerTree().registerNode(this);\r
512             this.rendered = true;\r
513             if(this.expanded){\r
514                 this.expanded = false;\r
515                 this.expand(false, false);\r
516             }\r
517         }\r
518     },\r
519 \r
520     // private\r
521     renderIndent : function(deep, refresh){\r
522         if(refresh){\r
523             this.ui.childIndent = null;\r
524         }\r
525         this.ui.renderIndent();\r
526         if(deep === true && this.childrenRendered){\r
527             var cs = this.childNodes;\r
528             for(var i = 0, len = cs.length; i < len; i++){\r
529                 cs[i].renderIndent(true, refresh);\r
530             }\r
531         }\r
532     },\r
533 \r
534     beginUpdate : function(){\r
535         this.childrenRendered = false;\r
536     },\r
537 \r
538     endUpdate : function(){\r
539         if(this.expanded && this.rendered){\r
540             this.renderChildren();\r
541         }\r
542     },\r
543 \r
544     destroy : function(){\r
545         this.unselect(true);\r
546         Ext.tree.TreeNode.superclass.destroy.call(this);\r
547         Ext.destroy(this.ui, this.loader);\r
548         this.ui = this.loader = null;\r
549     },\r
550 \r
551     // private\r
552     onIdChange : function(id){\r
553         this.ui.onIdChange(id);\r
554     }\r
555 });\r
556 \r
557 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;</pre>    \r
558 </body>\r
559 </html>