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