Upgrade to ExtJS 3.3.0 - Released 10/06/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.3.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 = Ext.extend(Ext.data.Node, {
45     
46     constructor : function(attributes){
47         attributes = attributes || {};
48         if(Ext.isString(attributes)){
49             attributes = {text: attributes};
50         }
51         this.childrenRendered = false;
52         this.rendered = false;
53         Ext.tree.TreeNode.superclass.constructor.call(this, attributes);
54         this.expanded = attributes.expanded === true;
55         this.isTarget = attributes.isTarget !== false;
56         this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
57         this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
58
59         /**
60          * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.
61          * @type String
62          */
63         this.text = attributes.text;
64         /**
65          * True if this node is disabled.
66          * @type Boolean
67          */
68         this.disabled = attributes.disabled === true;
69         /**
70          * True if this node is hidden.
71          * @type Boolean
72          */
73         this.hidden = attributes.hidden === true;
74     
75         this.addEvents(
76             /**
77             * @event textchange
78             * Fires when the text for this node is changed
79             * @param {Node} this This node
80             * @param {String} text The new text
81             * @param {String} oldText The old text
82             */
83             'textchange',
84             /**
85             * @event beforeexpand
86             * Fires before this node is expanded, return false to cancel.
87             * @param {Node} this This node
88             * @param {Boolean} deep
89             * @param {Boolean} anim
90             */
91             'beforeexpand',
92             /**
93             * @event beforecollapse
94             * Fires before this node is collapsed, return false to cancel.
95             * @param {Node} this This node
96             * @param {Boolean} deep
97             * @param {Boolean} anim
98             */
99             'beforecollapse',
100             /**
101             * @event expand
102             * Fires when this node is expanded
103             * @param {Node} this This node
104             */
105             'expand',
106             /**
107             * @event disabledchange
108             * Fires when the disabled status of this node changes
109             * @param {Node} this This node
110             * @param {Boolean} disabled
111             */
112             'disabledchange',
113             /**
114             * @event collapse
115             * Fires when this node is collapsed
116             * @param {Node} this This node
117             */
118             'collapse',
119             /**
120             * @event beforeclick
121             * Fires before click processing. Return false to cancel the default action.
122             * @param {Node} this This node
123             * @param {Ext.EventObject} e The event object
124             */
125             'beforeclick',
126             /**
127             * @event click
128             * Fires when this node is clicked
129             * @param {Node} this This node
130             * @param {Ext.EventObject} e The event object
131             */
132             'click',
133             /**
134             * @event checkchange
135             * Fires when a node with a checkbox's checked property changes
136             * @param {Node} this This node
137             * @param {Boolean} checked
138             */
139             'checkchange',
140             /**
141             * @event beforedblclick
142             * Fires before double click processing. Return false to cancel the default action.
143             * @param {Node} this This node
144             * @param {Ext.EventObject} e The event object
145             */
146             'beforedblclick',
147             /**
148             * @event dblclick
149             * Fires when this node is double clicked
150             * @param {Node} this This node
151             * @param {Ext.EventObject} e The event object
152             */
153             'dblclick',
154             /**
155             * @event contextmenu
156             * Fires when this node is right clicked
157             * @param {Node} this This node
158             * @param {Ext.EventObject} e The event object
159             */
160             'contextmenu',
161             /**
162             * @event beforechildrenrendered
163             * Fires right before the child nodes for this node are rendered
164             * @param {Node} this This node
165             */
166             'beforechildrenrendered'
167         );
168     
169         var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;
170     
171         /**
172          * Read-only. The UI for this node
173          * @type TreeNodeUI
174          */
175         this.ui = new uiClass(this);    
176     },
177     
178     preventHScroll : true,
179     /**
180      * Returns true if this node is expanded
181      * @return {Boolean}
182      */
183     isExpanded : function(){
184         return this.expanded;
185     },
186
187 /**
188  * Returns the UI object for this node.
189  * @return {TreeNodeUI} The object which is providing the user interface for this tree
190  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance
191  * of {@link Ext.tree.TreeNodeUI}
192  */
193     getUI : function(){
194         return this.ui;
195     },
196
197     getLoader : function(){
198         var owner;
199         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : (this.loader = new Ext.tree.TreeLoader()));
200     },
201
202     // private override
203     setFirstChild : function(node){
204         var of = this.firstChild;
205         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);
206         if(this.childrenRendered && of && node != of){
207             of.renderIndent(true, true);
208         }
209         if(this.rendered){
210             this.renderIndent(true, true);
211         }
212     },
213
214     // private override
215     setLastChild : function(node){
216         var ol = this.lastChild;
217         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);
218         if(this.childrenRendered && ol && node != ol){
219             ol.renderIndent(true, true);
220         }
221         if(this.rendered){
222             this.renderIndent(true, true);
223         }
224     },
225
226     // these methods are overridden to provide lazy rendering support
227     // private override
228     appendChild : function(n){
229         if(!n.render && !Ext.isArray(n)){
230             n = this.getLoader().createNode(n);
231         }
232         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);
233         if(node && this.childrenRendered){
234             node.render();
235         }
236         this.ui.updateExpandIcon();
237         return node;
238     },
239
240     // private override
241     removeChild : function(node, destroy){
242         this.ownerTree.getSelectionModel().unselect(node);
243         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);
244         // only update the ui if we're not destroying
245         if(!destroy){
246             var rendered = node.ui.rendered;
247             // if it's been rendered remove dom node
248             if(rendered){
249                 node.ui.remove();
250             }
251             if(rendered && this.childNodes.length < 1){
252                 this.collapse(false, false);
253             }else{
254                 this.ui.updateExpandIcon();
255             }
256             if(!this.firstChild && !this.isHiddenRoot()){
257                 this.childrenRendered = false;
258             }
259         }
260         return node;
261     },
262
263     // private override
264     insertBefore : function(node, refNode){
265         if(!node.render){
266             node = this.getLoader().createNode(node);
267         }
268         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);
269         if(newNode && refNode && this.childrenRendered){
270             node.render();
271         }
272         this.ui.updateExpandIcon();
273         return newNode;
274     },
275
276     /**
277      * Sets the text for this node
278      * @param {String} text
279      */
280     setText : function(text){
281         var oldText = this.text;
282         this.text = this.attributes.text = text;
283         if(this.rendered){ // event without subscribing
284             this.ui.onTextChange(this, text, oldText);
285         }
286         this.fireEvent('textchange', this, text, oldText);
287     },
288     
289     /**
290      * Sets the icon class for this node.
291      * @param {String} cls
292      */
293     setIconCls : function(cls){
294         var old = this.attributes.iconCls;
295         this.attributes.iconCls = cls;
296         if(this.rendered){
297             this.ui.onIconClsChange(this, cls, old);
298         }
299     },
300     
301     /**
302      * Sets the tooltip for this node.
303      * @param {String} tip The text for the tip
304      * @param {String} title (Optional) The title for the tip
305      */
306     setTooltip : function(tip, title){
307         this.attributes.qtip = tip;
308         this.attributes.qtipTitle = title;
309         if(this.rendered){
310             this.ui.onTipChange(this, tip, title);
311         }
312     },
313     
314     /**
315      * Sets the icon for this node.
316      * @param {String} icon
317      */
318     setIcon : function(icon){
319         this.attributes.icon = icon;
320         if(this.rendered){
321             this.ui.onIconChange(this, icon);
322         }
323     },
324     
325     /**
326      * Sets the href for the node.
327      * @param {String} href The href to set
328      * @param {String} (Optional) target The target of the href
329      */
330     setHref : function(href, target){
331         this.attributes.href = href;
332         this.attributes.hrefTarget = target;
333         if(this.rendered){
334             this.ui.onHrefChange(this, href, target);
335         }
336     },
337     
338     /**
339      * Sets the class on this node.
340      * @param {String} cls
341      */
342     setCls : function(cls){
343         var old = this.attributes.cls;
344         this.attributes.cls = cls;
345         if(this.rendered){
346             this.ui.onClsChange(this, cls, old);
347         }
348     },
349
350     /**
351      * Triggers selection of this node
352      */
353     select : function(){
354         var t = this.getOwnerTree();
355         if(t){
356             t.getSelectionModel().select(this);
357         }
358     },
359
360     /**
361      * Triggers deselection of this node
362      * @param {Boolean} silent (optional) True to stop selection change events from firing.
363      */
364     unselect : function(silent){
365         var t = this.getOwnerTree();
366         if(t){
367             t.getSelectionModel().unselect(this, silent);
368         }
369     },
370
371     /**
372      * Returns true if this node is selected
373      * @return {Boolean}
374      */
375     isSelected : function(){
376         var t = this.getOwnerTree();
377         return t ? t.getSelectionModel().isSelected(this) : false;
378     },
379
380     /**
381      * Expand this node.
382      * @param {Boolean} deep (optional) True to expand all children as well
383      * @param {Boolean} anim (optional) false to cancel the default animation
384      * @param {Function} callback (optional) A callback to be called when
385      * expanding this node completes (does not wait for deep expand to complete).
386      * Called with 1 parameter, this node.
387      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
388      */
389     expand : function(deep, anim, callback, scope){
390         if(!this.expanded){
391             if(this.fireEvent('beforeexpand', this, deep, anim) === false){
392                 return;
393             }
394             if(!this.childrenRendered){
395                 this.renderChildren();
396             }
397             this.expanded = true;
398             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
399                 this.ui.animExpand(function(){
400                     this.fireEvent('expand', this);
401                     this.runCallback(callback, scope || this, [this]);
402                     if(deep === true){
403                         this.expandChildNodes(true, true);
404                     }
405                 }.createDelegate(this));
406                 return;
407             }else{
408                 this.ui.expand();
409                 this.fireEvent('expand', this);
410                 this.runCallback(callback, scope || this, [this]);
411             }
412         }else{
413            this.runCallback(callback, scope || this, [this]);
414         }
415         if(deep === true){
416             this.expandChildNodes(true);
417         }
418     },
419
420     runCallback : function(cb, scope, args){
421         if(Ext.isFunction(cb)){
422             cb.apply(scope, args);
423         }
424     },
425
426     isHiddenRoot : function(){
427         return this.isRoot && !this.getOwnerTree().rootVisible;
428     },
429
430     /**
431      * Collapse this node.
432      * @param {Boolean} deep (optional) True to collapse all children as well
433      * @param {Boolean} anim (optional) false to cancel the default animation
434      * @param {Function} callback (optional) A callback to be called when
435      * expanding this node completes (does not wait for deep expand to complete).
436      * Called with 1 parameter, this node.
437      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
438      */
439     collapse : function(deep, anim, callback, scope){
440         if(this.expanded && !this.isHiddenRoot()){
441             if(this.fireEvent('beforecollapse', this, deep, anim) === false){
442                 return;
443             }
444             this.expanded = false;
445             if((this.getOwnerTree().animate && anim !== false) || anim){
446                 this.ui.animCollapse(function(){
447                     this.fireEvent('collapse', this);
448                     this.runCallback(callback, scope || this, [this]);
449                     if(deep === true){
450                         this.collapseChildNodes(true);
451                     }
452                 }.createDelegate(this));
453                 return;
454             }else{
455                 this.ui.collapse();
456                 this.fireEvent('collapse', this);
457                 this.runCallback(callback, scope || this, [this]);
458             }
459         }else if(!this.expanded){
460             this.runCallback(callback, scope || this, [this]);
461         }
462         if(deep === true){
463             var cs = this.childNodes;
464             for(var i = 0, len = cs.length; i < len; i++) {
465                 cs[i].collapse(true, false);
466             }
467         }
468     },
469
470     // private
471     delayedExpand : function(delay){
472         if(!this.expandProcId){
473             this.expandProcId = this.expand.defer(delay, this);
474         }
475     },
476
477     // private
478     cancelExpand : function(){
479         if(this.expandProcId){
480             clearTimeout(this.expandProcId);
481         }
482         this.expandProcId = false;
483     },
484
485     /**
486      * Toggles expanded/collapsed state of the node
487      */
488     toggle : function(){
489         if(this.expanded){
490             this.collapse();
491         }else{
492             this.expand();
493         }
494     },
495
496     /**
497      * Ensures all parent nodes are expanded, and if necessary, scrolls
498      * the node into view.
499      * @param {Function} callback (optional) A function to call when the node has been made visible.
500      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
501      */
502     ensureVisible : function(callback, scope){
503         var tree = this.getOwnerTree();
504         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){
505             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime
506             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);
507             this.runCallback(callback, scope || this, [this]);
508         }.createDelegate(this));
509     },
510
511     /**
512      * Expand all child nodes
513      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
514      */
515     expandChildNodes : function(deep, anim) {
516         var cs = this.childNodes,
517             i,
518             len = cs.length;
519         for (i = 0; i < len; i++) {
520                 cs[i].expand(deep, anim);
521         }
522     },
523
524     /**
525      * Collapse all child nodes
526      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
527      */
528     collapseChildNodes : function(deep){
529         var cs = this.childNodes;
530         for(var i = 0, len = cs.length; i < len; i++) {
531                 cs[i].collapse(deep);
532         }
533     },
534
535     /**
536      * Disables this node
537      */
538     disable : function(){
539         this.disabled = true;
540         this.unselect();
541         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
542             this.ui.onDisableChange(this, true);
543         }
544         this.fireEvent('disabledchange', this, true);
545     },
546
547     /**
548      * Enables this node
549      */
550     enable : function(){
551         this.disabled = false;
552         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
553             this.ui.onDisableChange(this, false);
554         }
555         this.fireEvent('disabledchange', this, false);
556     },
557
558     // private
559     renderChildren : function(suppressEvent){
560         if(suppressEvent !== false){
561             this.fireEvent('beforechildrenrendered', this);
562         }
563         var cs = this.childNodes;
564         for(var i = 0, len = cs.length; i < len; i++){
565             cs[i].render(true);
566         }
567         this.childrenRendered = true;
568     },
569
570     // private
571     sort : function(fn, scope){
572         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);
573         if(this.childrenRendered){
574             var cs = this.childNodes;
575             for(var i = 0, len = cs.length; i < len; i++){
576                 cs[i].render(true);
577             }
578         }
579     },
580
581     // private
582     render : function(bulkRender){
583         this.ui.render(bulkRender);
584         if(!this.rendered){
585             // make sure it is registered
586             this.getOwnerTree().registerNode(this);
587             this.rendered = true;
588             if(this.expanded){
589                 this.expanded = false;
590                 this.expand(false, false);
591             }
592         }
593     },
594
595     // private
596     renderIndent : function(deep, refresh){
597         if(refresh){
598             this.ui.childIndent = null;
599         }
600         this.ui.renderIndent();
601         if(deep === true && this.childrenRendered){
602             var cs = this.childNodes;
603             for(var i = 0, len = cs.length; i < len; i++){
604                 cs[i].renderIndent(true, refresh);
605             }
606         }
607     },
608
609     beginUpdate : function(){
610         this.childrenRendered = false;
611     },
612
613     endUpdate : function(){
614         if(this.expanded && this.rendered){
615             this.renderChildren();
616         }
617     },
618
619     //inherit docs
620     destroy : function(silent){
621         if(silent === true){
622             this.unselect(true);
623         }
624         Ext.tree.TreeNode.superclass.destroy.call(this, silent);
625         Ext.destroy(this.ui, this.loader);
626         this.ui = this.loader = null;
627     },
628
629     // private
630     onIdChange : function(id){
631         this.ui.onIdChange(id);
632     }
633 });
634
635 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;</pre>    
636 </body>
637 </html>