Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / NodeInterface.html
1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-data.NodeInterface'>/**
2 </span> * @class Ext.data.NodeInterface
3  * This class is meant to be used as a set of methods that are applied to the prototype of a
4  * Record to decorate it with a Node API. This means that models used in conjunction with a tree
5  * will have all of the tree related methods available on the model. In general this class will
6  * not be used directly by the developer.
7  */
8 Ext.define('Ext.data.NodeInterface', {
9     requires: ['Ext.data.Field'],
10     
11     statics: {
12 <span id='Ext-data.NodeInterface-method-decorate'>        /**
13 </span>         * This method allows you to decorate a Record's prototype to implement the NodeInterface.
14          * This adds a set of methods, new events, new properties and new fields on every Record
15          * with the same Model as the passed Record.
16          * @param {Ext.data.Record} record The Record you want to decorate the prototype of.
17          * @static
18          */
19         decorate: function(record) {
20             if (!record.isNode) {
21                 // Apply the methods and fields to the prototype
22                 // @TODO: clean this up to use proper class system stuff
23                 var mgr = Ext.ModelManager,
24                     modelName = record.modelName,
25                     modelClass = mgr.getModel(modelName),
26                     idName = modelClass.prototype.idProperty,
27                     instances = Ext.Array.filter(mgr.all.getArray(), function(item) {
28                         return item.modelName == modelName;
29                     }),
30                     iln = instances.length,
31                     newFields = [],
32                     i, instance, jln, j, newField;
33
34                 // Start by adding the NodeInterface methods to the Model's prototype
35                 modelClass.override(this.getPrototypeBody());
36                 newFields = this.applyFields(modelClass, [
37                     {name: idName,      type: 'string',  defaultValue: null},
38                     {name: 'parentId',  type: 'string',  defaultValue: null},
39                     {name: 'index',     type: 'int',     defaultValue: null},
40                     {name: 'depth',     type: 'int',     defaultValue: 0}, 
41                     {name: 'expanded',  type: 'bool',    defaultValue: false, persist: false},
42                     {name: 'checked',   type: 'auto',    defaultValue: null},
43                     {name: 'leaf',      type: 'bool',    defaultValue: false, persist: false},
44                     {name: 'cls',       type: 'string',  defaultValue: null, persist: false},
45                     {name: 'iconCls',   type: 'string',  defaultValue: null, persist: false},
46                     {name: 'root',      type: 'boolean', defaultValue: false, persist: false},
47                     {name: 'isLast',    type: 'boolean', defaultValue: false, persist: false},
48                     {name: 'isFirst',   type: 'boolean', defaultValue: false, persist: false},
49                     {name: 'allowDrop', type: 'boolean', defaultValue: true, persist: false},
50                     {name: 'allowDrag', type: 'boolean', defaultValue: true, persist: false},
51                     {name: 'loaded',    type: 'boolean', defaultValue: false, persist: false},
52                     {name: 'loading',   type: 'boolean', defaultValue: false, persist: false},
53                     {name: 'href',      type: 'string',  defaultValue: null, persist: false},
54                     {name: 'hrefTarget',type: 'string',  defaultValue: null, persist: false},
55                     {name: 'qtip',      type: 'string',  defaultValue: null, persist: false},
56                     {name: 'qtitle',    type: 'string',  defaultValue: null, persist: false}
57                 ]);
58
59                 jln = newFields.length;
60                 // Set default values to all instances already out there
61                 for (i = 0; i &lt; iln; i++) {
62                     instance = instances[i];
63                     for (j = 0; j &lt; jln; j++) {
64                         newField = newFields[j];
65                         if (instance.get(newField.name) === undefined) {
66                             instance.data[newField.name] = newField.defaultValue;
67                         }
68                     }
69                 }
70             }
71             
72             Ext.applyIf(record, {
73                 firstChild: null,
74                 lastChild: null,
75                 parentNode: null,
76                 previousSibling: null,
77                 nextSibling: null,
78                 childNodes: []
79             });
80             // Commit any fields so the record doesn't show as dirty initially
81             record.commit(true);
82             
83             record.enableBubble([
84 <span id='Ext-data.NodeInterface-event-append'>                /**
85 </span>                 * @event append
86                  * Fires when a new child node is appended
87                  * @param {Node} this This node
88                  * @param {Node} node The newly appended node
89                  * @param {Number} index The index of the newly appended node
90                  */
91                 &quot;append&quot;,
92
93 <span id='Ext-data.NodeInterface-event-remove'>                /**
94 </span>                 * @event remove
95                  * Fires when a child node is removed
96                  * @param {Node} this This node
97                  * @param {Node} node The removed node
98                  */
99                 &quot;remove&quot;,
100
101 <span id='Ext-data.NodeInterface-event-move'>                /**
102 </span>                 * @event move
103                  * Fires when this node is moved to a new location in the tree
104                  * @param {Node} this This node
105                  * @param {Node} oldParent The old parent of this node
106                  * @param {Node} newParent The new parent of this node
107                  * @param {Number} index The index it was moved to
108                  */
109                 &quot;move&quot;,
110
111 <span id='Ext-data.NodeInterface-event-insert'>                /**
112 </span>                 * @event insert
113                  * Fires when a new child node is inserted.
114                  * @param {Node} this This node
115                  * @param {Node} node The child node inserted
116                  * @param {Node} refNode The child node the node was inserted before
117                  */
118                 &quot;insert&quot;,
119
120 <span id='Ext-data.NodeInterface-event-beforeappend'>                /**
121 </span>                 * @event beforeappend
122                  * Fires before a new child is appended, return false to cancel the append.
123                  * @param {Node} this This node
124                  * @param {Node} node The child node to be appended
125                  */
126                 &quot;beforeappend&quot;,
127
128 <span id='Ext-data.NodeInterface-event-beforeremove'>                /**
129 </span>                 * @event beforeremove
130                  * Fires before a child is removed, return false to cancel the remove.
131                  * @param {Node} this This node
132                  * @param {Node} node The child node to be removed
133                  */
134                 &quot;beforeremove&quot;,
135
136 <span id='Ext-data.NodeInterface-event-beforemove'>                /**
137 </span>                 * @event beforemove
138                  * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
139                  * @param {Node} this This node
140                  * @param {Node} oldParent The parent of this node
141                  * @param {Node} newParent The new parent this node is moving to
142                  * @param {Number} index The index it is being moved to
143                  */
144                 &quot;beforemove&quot;,
145
146 <span id='Ext-data.NodeInterface-event-beforeinsert'>                 /**
147 </span>                  * @event beforeinsert
148                   * Fires before a new child is inserted, return false to cancel the insert.
149                   * @param {Node} this This node
150                   * @param {Node} node The child node to be inserted
151                   * @param {Node} refNode The child node the node is being inserted before
152                   */
153                 &quot;beforeinsert&quot;,
154                 
155 <span id='Ext-data.NodeInterface-event-expand'>                /**
156 </span>                 * @event expand
157                  * Fires when this node is expanded.
158                  * @param {Node} this The expanding node
159                  */
160                 &quot;expand&quot;,
161                 
162 <span id='Ext-data.NodeInterface-event-collapse'>                /**
163 </span>                 * @event collapse
164                  * Fires when this node is collapsed.
165                  * @param {Node} this The collapsing node
166                  */
167                 &quot;collapse&quot;,
168                 
169 <span id='Ext-data.NodeInterface-event-beforeexpand'>                /**
170 </span>                 * @event beforeexpand
171                  * Fires before this node is expanded.
172                  * @param {Node} this The expanding node
173                  */
174                 &quot;beforeexpand&quot;,
175                 
176 <span id='Ext-data.NodeInterface-event-beforecollapse'>                /**
177 </span>                 * @event beforecollapse
178                  * Fires before this node is collapsed.
179                  * @param {Node} this The collapsing node
180                  */
181                 &quot;beforecollapse&quot;,
182                 
183 <span id='Ext-data.NodeInterface-event-beforecollapse'>                /**
184 </span>                 * @event beforecollapse
185                  * Fires before this node is collapsed.
186                  * @param {Node} this The collapsing node
187                  */
188                 &quot;sort&quot;
189             ]);
190             
191             return record;
192         },
193         
194         applyFields: function(modelClass, addFields) {
195             var modelPrototype = modelClass.prototype,
196                 fields = modelPrototype.fields,
197                 keys = fields.keys,
198                 ln = addFields.length,
199                 addField, i, name,
200                 newFields = [];
201                 
202             for (i = 0; i &lt; ln; i++) {
203                 addField = addFields[i];
204                 if (!Ext.Array.contains(keys, addField.name)) {
205                     addField = Ext.create('data.field', addField);
206                     
207                     newFields.push(addField);
208                     fields.add(addField);
209                 }
210             }
211             
212             return newFields;
213         },
214         
215         getPrototypeBody: function() {
216             return {
217                 isNode: true,
218
219 <span id='Ext-data.NodeInterface-method-createNode'>                /**
220 </span>                 * Ensures that the passed object is an instance of a Record with the NodeInterface applied
221                  * @return {Boolean}
222                  */
223                 createNode: function(node) {
224                     if (Ext.isObject(node) &amp;&amp; !node.isModel) {
225                         node = Ext.ModelManager.create(node, this.modelName);
226                     }
227                     // Make sure the node implements the node interface
228                     return Ext.data.NodeInterface.decorate(node);
229                 },
230                 
231 <span id='Ext-data.NodeInterface-method-isLeaf'>                /**
232 </span>                 * Returns true if this node is a leaf
233                  * @return {Boolean}
234                  */
235                 isLeaf : function() {
236                     return this.get('leaf') === true;
237                 },
238
239 <span id='Ext-data.NodeInterface-method-setFirstChild'>                /**
240 </span>                 * Sets the first child of this node
241                  * @private
242                  * @param {Ext.data.NodeInterface} node
243                  */
244                 setFirstChild : function(node) {
245                     this.firstChild = node;
246                 },
247
248 <span id='Ext-data.NodeInterface-method-setLastChild'>                /**
249 </span>                 * Sets the last child of this node
250                  * @private
251                  * @param {Ext.data.NodeInterface} node
252                  */
253                 setLastChild : function(node) {
254                     this.lastChild = node;
255                 },
256
257 <span id='Ext-data.NodeInterface-method-updateInfo'>                /**
258 </span>                 * Updates general data of this node like isFirst, isLast, depth. This
259                  * method is internally called after a node is moved. This shouldn't
260                  * have to be called by the developer unless they are creating custom
261                  * Tree plugins.
262                  * @return {Boolean}
263                  */
264                 updateInfo: function(silent) {
265                     var me = this,
266                         isRoot = me.isRoot(),
267                         parentNode = me.parentNode,
268                         isFirst = (!parentNode ? true : parentNode.firstChild == me),
269                         isLast = (!parentNode ? true : parentNode.lastChild == me),
270                         depth = 0,
271                         parent = me,
272                         children = me.childNodes,
273                         len = children.length,
274                         i = 0;
275
276                     while (parent.parentNode) {
277                         ++depth;
278                         parent = parent.parentNode;
279                     }                                            
280                     
281                     me.beginEdit();
282                     me.set({
283                         isFirst: isFirst,
284                         isLast: isLast,
285                         depth: depth,
286                         index: parentNode ? parentNode.indexOf(me) : 0,
287                         parentId: parentNode ? parentNode.getId() : null
288                     });
289                     me.endEdit(silent);
290                     if (silent) {
291                         me.commit();
292                     }
293                     
294                     for (i = 0; i &lt; len; i++) {
295                         children[i].updateInfo(silent);
296                     }
297                 },
298
299 <span id='Ext-data.NodeInterface-method-isLast'>                /**
300 </span>                 * Returns true if this node is the last child of its parent
301                  * @return {Boolean}
302                  */
303                 isLast : function() {
304                    return this.get('isLast');
305                 },
306
307 <span id='Ext-data.NodeInterface-method-isFirst'>                /**
308 </span>                 * Returns true if this node is the first child of its parent
309                  * @return {Boolean}
310                  */
311                 isFirst : function() {
312                    return this.get('isFirst');
313                 },
314
315 <span id='Ext-data.NodeInterface-method-hasChildNodes'>                /**
316 </span>                 * Returns true if this node has one or more child nodes, else false.
317                  * @return {Boolean}
318                  */
319                 hasChildNodes : function() {
320                     return !this.isLeaf() &amp;&amp; this.childNodes.length &gt; 0;
321                 },
322
323 <span id='Ext-data.NodeInterface-method-isExpandable'>                /**
324 </span>                 * Returns true if this node has one or more child nodes, or if the &lt;tt&gt;expandable&lt;/tt&gt;
325                  * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
326                  * @return {Boolean}
327                  */
328                 isExpandable : function() {
329                     return this.get('expandable') || this.hasChildNodes();
330                 },
331
332 <span id='Ext-data.NodeInterface-method-appendChild'>                /**
333 </span>                 * &lt;p&gt;Insert node(s) as the last child node of this node.&lt;/p&gt;
334                  * &lt;p&gt;If the node was previously a child node of another parent node, it will be removed from that node first.&lt;/p&gt;
335                  * @param {Node/Array} node The node or Array of nodes to append
336                  * @return {Node} The appended node if single append, or null if an array was passed
337                  */
338                 appendChild : function(node, suppressEvents, suppressNodeUpdate) {
339                     var me = this,
340                         i, ln,
341                         index,
342                         oldParent,
343                         ps;
344
345                     // if passed an array or multiple args do them one by one
346                     if (Ext.isArray(node)) {
347                         for (i = 0, ln = node.length; i &lt; ln; i++) {
348                             me.appendChild(node[i]);
349                         }
350                     } else {
351                         // Make sure it is a record
352                         node = me.createNode(node);
353                         
354                         if (suppressEvents !== true &amp;&amp; me.fireEvent(&quot;beforeappend&quot;, me, node) === false) {
355                             return false;                         
356                         }
357
358                         index = me.childNodes.length;
359                         oldParent = node.parentNode;
360
361                         // it's a move, make sure we move it cleanly
362                         if (oldParent) {
363                             if (suppressEvents !== true &amp;&amp; node.fireEvent(&quot;beforemove&quot;, node, oldParent, me, index) === false) {
364                                 return false;
365                             }
366                             oldParent.removeChild(node, null, false, true);
367                         }
368
369                         index = me.childNodes.length;
370                         if (index === 0) {
371                             me.setFirstChild(node);
372                         }
373
374                         me.childNodes.push(node);
375                         node.parentNode = me;
376                         node.nextSibling = null;
377
378                         me.setLastChild(node);
379                                                 
380                         ps = me.childNodes[index - 1];
381                         if (ps) {
382                             node.previousSibling = ps;
383                             ps.nextSibling = node;
384                             ps.updateInfo(suppressNodeUpdate);
385                         } else {
386                             node.previousSibling = null;
387                         }
388
389                         node.updateInfo(suppressNodeUpdate);
390                         
391                         // As soon as we append a child to this node, we are loaded
392                         if (!me.isLoaded()) {
393                             me.set('loaded', true);                            
394                         }
395                         // If this node didnt have any childnodes before, update myself
396                         else if (me.childNodes.length === 1) {
397                             me.set('loaded', me.isLoaded());
398                         }
399                         
400                         if (suppressEvents !== true) {
401                             me.fireEvent(&quot;append&quot;, me, node, index);
402
403                             if (oldParent) {
404                                 node.fireEvent(&quot;move&quot;, node, oldParent, me, index);
405                             }                            
406                         }
407
408                         return node;
409                     }
410                 },
411                 
412 <span id='Ext-data.NodeInterface-method-getBubbleTarget'>                /**
413 </span>                 * Returns the bubble target for this node
414                  * @private
415                  * @return {Object} The bubble target
416                  */
417                 getBubbleTarget: function() {
418                     return this.parentNode;
419                 },
420
421 <span id='Ext-data.NodeInterface-method-removeChild'>                /**
422 </span>                 * Removes a child node from this node.
423                  * @param {Node} node The node to remove
424                  * @param {Boolean} destroy &lt;tt&gt;true&lt;/tt&gt; to destroy the node upon removal. Defaults to &lt;tt&gt;false&lt;/tt&gt;.
425                  * @return {Node} The removed node
426                  */
427                 removeChild : function(node, destroy, suppressEvents, suppressNodeUpdate) {
428                     var me = this,
429                         index = me.indexOf(node);
430                     
431                     if (index == -1 || (suppressEvents !== true &amp;&amp; me.fireEvent(&quot;beforeremove&quot;, me, node) === false)) {
432                         return false;
433                     }
434
435                     // remove it from childNodes collection
436                     me.childNodes.splice(index, 1);
437
438                     // update child refs
439                     if (me.firstChild == node) {
440                         me.setFirstChild(node.nextSibling);
441                     }
442                     if (me.lastChild == node) {
443                         me.setLastChild(node.previousSibling);
444                     }
445                     
446                     // update siblings
447                     if (node.previousSibling) {
448                         node.previousSibling.nextSibling = node.nextSibling;
449                         node.previousSibling.updateInfo(suppressNodeUpdate);
450                     }
451                     if (node.nextSibling) {
452                         node.nextSibling.previousSibling = node.previousSibling;
453                         node.nextSibling.updateInfo(suppressNodeUpdate);
454                     }
455
456                     if (suppressEvents !== true) {
457                         me.fireEvent(&quot;remove&quot;, me, node);
458                     }
459                     
460                     
461                     // If this node suddenly doesnt have childnodes anymore, update myself
462                     if (!me.childNodes.length) {
463                         me.set('loaded', me.isLoaded());
464                     }
465                     
466                     if (destroy) {
467                         node.destroy(true);
468                     } else {
469                         node.clear();
470                     }
471
472                     return node;
473                 },
474
475 <span id='Ext-data.NodeInterface-method-copy'>                /**
476 </span>                 * Creates a copy (clone) of this Node.
477                  * @param {String} id (optional) A new id, defaults to this Node's id. See &lt;code&gt;{@link #id}&lt;/code&gt;.
478                  * @param {Boolean} deep (optional) &lt;p&gt;If passed as &lt;code&gt;true&lt;/code&gt;, all child Nodes are recursively copied into the new Node.&lt;/p&gt;
479                  * &lt;p&gt;If omitted or false, the copy will have no child Nodes.&lt;/p&gt;
480                  * @return {Node} A copy of this Node.
481                  */
482                 copy: function(newId, deep) {
483                     var me = this,
484                         result = me.callOverridden(arguments),
485                         len = me.childNodes ? me.childNodes.length : 0,
486                         i;
487
488                     // Move child nodes across to the copy if required
489                     if (deep) {
490                         for (i = 0; i &lt; len; i++) {
491                             result.appendChild(me.childNodes[i].copy(true));
492                         }
493                     }
494                     return result;
495                 },
496
497 <span id='Ext-data.NodeInterface-method-clear'>                /**
498 </span>                 * Clear the node.
499                  * @private
500                  * @param {Boolean} destroy True to destroy the node.
501                  */
502                 clear : function(destroy) {
503                     var me = this;
504                     
505                     // clear any references from the node
506                     me.parentNode = me.previousSibling = me.nextSibling = null;
507                     if (destroy) {
508                         me.firstChild = me.lastChild = null;
509                     }
510                 },
511
512 <span id='Ext-data.NodeInterface-method-destroy'>                /**
513 </span>                 * Destroys the node.
514                  */
515                 destroy : function(silent) {
516                     /*
517                      * Silent is to be used in a number of cases
518                      * 1) When setRoot is called.
519                      * 2) When destroy on the tree is called
520                      * 3) For destroying child nodes on a node
521                      */
522                     var me = this;
523                     
524                     if (silent === true) {
525                         me.clear(true);
526                         Ext.each(me.childNodes, function(n) {
527                             n.destroy(true);
528                         });
529                         me.childNodes = null;
530                     } else {
531                         me.remove(true);
532                     }
533
534                     me.callOverridden();
535                 },
536
537 <span id='Ext-data.NodeInterface-method-insertBefore'>                /**
538 </span>                 * Inserts the first node before the second node in this nodes childNodes collection.
539                  * @param {Node} node The node to insert
540                  * @param {Node} refNode The node to insert before (if null the node is appended)
541                  * @return {Node} The inserted node
542                  */
543                 insertBefore : function(node, refNode, suppressEvents) {
544                     var me = this,
545                         index     = me.indexOf(refNode),
546                         oldParent = node.parentNode,
547                         refIndex  = index,
548                         ps;
549                     
550                     if (!refNode) { // like standard Dom, refNode can be null for append
551                         return me.appendChild(node);
552                     }
553                     
554                     // nothing to do
555                     if (node == refNode) {
556                         return false;
557                     }
558
559                     // Make sure it is a record with the NodeInterface
560                     node = me.createNode(node);
561                     
562                     if (suppressEvents !== true &amp;&amp; me.fireEvent(&quot;beforeinsert&quot;, me, node, refNode) === false) {
563                         return false;
564                     }
565                     
566                     // when moving internally, indexes will change after remove
567                     if (oldParent == me &amp;&amp; me.indexOf(node) &lt; index) {
568                         refIndex--;
569                     }
570
571                     // it's a move, make sure we move it cleanly
572                     if (oldParent) {
573                         if (suppressEvents !== true &amp;&amp; node.fireEvent(&quot;beforemove&quot;, node, oldParent, me, index, refNode) === false) {
574                             return false;
575                         }
576                         oldParent.removeChild(node);
577                     }
578
579                     if (refIndex === 0) {
580                         me.setFirstChild(node);
581                     }
582
583                     me.childNodes.splice(refIndex, 0, node);
584                     node.parentNode = me;
585                     
586                     node.nextSibling = refNode;
587                     refNode.previousSibling = node;
588                     
589                     ps = me.childNodes[refIndex - 1];
590                     if (ps) {
591                         node.previousSibling = ps;
592                         ps.nextSibling = node;
593                         ps.updateInfo();
594                     } else {
595                         node.previousSibling = null;
596                     }
597                     
598                     node.updateInfo();
599                     
600                     if (!me.isLoaded()) {
601                         me.set('loaded', true);                            
602                     }    
603                     // If this node didnt have any childnodes before, update myself
604                     else if (me.childNodes.length === 1) {
605                         me.set('loaded', me.isLoaded());
606                     }
607
608                     if (suppressEvents !== true) {
609                         me.fireEvent(&quot;insert&quot;, me, node, refNode);
610
611                         if (oldParent) {
612                             node.fireEvent(&quot;move&quot;, node, oldParent, me, refIndex, refNode);
613                         }                        
614                     }
615
616                     return node;
617                 },
618                 
619 <span id='Ext-data.NodeInterface-method-insertChild'>                /**
620 </span>                 * Insert a node into this node
621                  * @param {Number} index The zero-based index to insert the node at
622                  * @param {Ext.data.Model} node The node to insert
623                  * @return {Ext.data.Record} The record you just inserted
624                  */    
625                 insertChild: function(index, node) {
626                     var sibling = this.childNodes[index];
627                     if (sibling) {
628                         return this.insertBefore(node, sibling);
629                     }
630                     else {
631                         return this.appendChild(node);
632                     }
633                 },
634
635 <span id='Ext-data.NodeInterface-method-remove'>                /**
636 </span>                 * Removes this node from its parent
637                  * @param {Boolean} destroy &lt;tt&gt;true&lt;/tt&gt; to destroy the node upon removal. Defaults to &lt;tt&gt;false&lt;/tt&gt;.
638                  * @return {Node} this
639                  */
640                 remove : function(destroy, suppressEvents) {
641                     var parentNode = this.parentNode;
642
643                     if (parentNode) {
644                         parentNode.removeChild(this, destroy, suppressEvents, true);
645                     }
646                     return this;
647                 },
648
649 <span id='Ext-data.NodeInterface-method-removeAll'>                /**
650 </span>                 * Removes all child nodes from this node.
651                  * @param {Boolean} destroy &lt;tt&gt;true&lt;/tt&gt; to destroy the node upon removal. Defaults to &lt;tt&gt;false&lt;/tt&gt;.
652                  * @return {Node} this
653                  */
654                 removeAll : function(destroy, suppressEvents) {
655                     var cn = this.childNodes,
656                         n;
657
658                     while ((n = cn[0])) {
659                         this.removeChild(n, destroy, suppressEvents);
660                     }
661                     return this;
662                 },
663
664 <span id='Ext-data.NodeInterface-method-getChildAt'>                /**
665 </span>                 * Returns the child node at the specified index.
666                  * @param {Number} index
667                  * @return {Node}
668                  */
669                 getChildAt : function(index) {
670                     return this.childNodes[index];
671                 },
672
673 <span id='Ext-data.NodeInterface-method-replaceChild'>                /**
674 </span>                 * Replaces one child node in this node with another.
675                  * @param {Node} newChild The replacement node
676                  * @param {Node} oldChild The node to replace
677                  * @return {Node} The replaced node
678                  */
679                 replaceChild : function(newChild, oldChild, suppressEvents) {
680                     var s = oldChild ? oldChild.nextSibling : null;
681                     
682                     this.removeChild(oldChild, suppressEvents);
683                     this.insertBefore(newChild, s, suppressEvents);
684                     return oldChild;
685                 },
686
687 <span id='Ext-data.NodeInterface-method-indexOf'>                /**
688 </span>                 * Returns the index of a child node
689                  * @param {Node} node
690                  * @return {Number} The index of the node or -1 if it was not found
691                  */
692                 indexOf : function(child) {
693                     return Ext.Array.indexOf(this.childNodes, child);
694                 },
695
696 <span id='Ext-data.NodeInterface-method-getDepth'>                /**
697 </span>                 * Returns depth of this node (the root node has a depth of 0)
698                  * @return {Number}
699                  */
700                 getDepth : function() {
701                     return this.get('depth');
702                 },
703
704 <span id='Ext-data.NodeInterface-method-bubble'>                /**
705 </span>                 * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
706                  * will be the args provided or the current node. If the function returns false at any point,
707                  * the bubble is stopped.
708                  * @param {Function} fn The function to call
709                  * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the function is executed. Defaults to the current Node.
710                  * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
711                  */
712                 bubble : function(fn, scope, args) {
713                     var p = this;
714                     while (p) {
715                         if (fn.apply(scope || p, args || [p]) === false) {
716                             break;
717                         }
718                         p = p.parentNode;
719                     }
720                 },
721
722                 //&lt;deprecated since=0.99&gt;
723                 cascade: function() {
724                     if (Ext.isDefined(Ext.global.console)) {
725                         Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
726                     }
727                     return this.cascadeBy.apply(this, arguments);
728                 },
729                 //&lt;/deprecated&gt;
730
731 <span id='Ext-data.NodeInterface-method-cascadeBy'>                /**
732 </span>                 * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
733                  * will be the args provided or the current node. If the function returns false at any point,
734                  * the cascade is stopped on that branch.
735                  * @param {Function} fn The function to call
736                  * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the function is executed. Defaults to the current Node.
737                  * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
738                  */
739                 cascadeBy : function(fn, scope, args) {
740                     if (fn.apply(scope || this, args || [this]) !== false) {
741                         var childNodes = this.childNodes,
742                             length     = childNodes.length,
743                             i;
744
745                         for (i = 0; i &lt; length; i++) {
746                             childNodes[i].cascadeBy(fn, scope, args);
747                         }
748                     }
749                 },
750
751 <span id='Ext-data.NodeInterface-method-eachChild'>                /**
752 </span>                 * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
753                  * will be the args provided or the current node. If the function returns false at any point,
754                  * the iteration stops.
755                  * @param {Function} fn The function to call
756                  * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the function is executed. Defaults to the current Node in the iteration.
757                  * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
758                  */
759                 eachChild : function(fn, scope, args) {
760                     var childNodes = this.childNodes,
761                         length     = childNodes.length,
762                         i;
763
764                     for (i = 0; i &lt; length; i++) {
765                         if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
766                             break;
767                         }
768                     }
769                 },
770
771 <span id='Ext-data.NodeInterface-method-findChild'>                /**
772 </span>                 * Finds the first child that has the attribute with the specified value.
773                  * @param {String} attribute The attribute name
774                  * @param {Mixed} value The value to search for
775                  * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
776                  * @return {Node} The found child or null if none was found
777                  */
778                 findChild : function(attribute, value, deep) {
779                     return this.findChildBy(function() {
780                         return this.get(attribute) == value;
781                     }, null, deep);
782                 },
783
784 <span id='Ext-data.NodeInterface-method-findChildBy'>                /**
785 </span>                 * Finds the first child by a custom function. The child matches if the function passed returns &lt;code&gt;true&lt;/code&gt;.
786                  * @param {Function} fn A function which must return &lt;code&gt;true&lt;/code&gt; if the passed Node is the required Node.
787                  * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the function is executed. Defaults to the Node being tested.
788                  * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
789                  * @return {Node} The found child or null if none was found
790                  */
791                 findChildBy : function(fn, scope, deep) {
792                     var cs = this.childNodes,
793                         len = cs.length,
794                         i = 0, n, res;
795
796                     for (; i &lt; len; i++) {
797                         n = cs[i];
798                         if (fn.call(scope || n, n) === true) {
799                             return n;
800                         }
801                         else if (deep) {
802                             res = n.findChildBy(fn, scope, deep);
803                             if (res !== null) {
804                                 return res;
805                             }
806                         }
807                     }
808
809                     return null;
810                 },
811
812 <span id='Ext-data.NodeInterface-method-contains'>                /**
813 </span>                 * Returns true if this node is an ancestor (at any point) of the passed node.
814                  * @param {Node} node
815                  * @return {Boolean}
816                  */
817                 contains : function(node) {
818                     return node.isAncestor(this);
819                 },
820
821 <span id='Ext-data.NodeInterface-method-isAncestor'>                /**
822 </span>                 * Returns true if the passed node is an ancestor (at any point) of this node.
823                  * @param {Node} node
824                  * @return {Boolean}
825                  */
826                 isAncestor : function(node) {
827                     var p = this.parentNode;
828                     while (p) {
829                         if (p == node) {
830                             return true;
831                         }
832                         p = p.parentNode;
833                     }
834                     return false;
835                 },
836
837 <span id='Ext-data.NodeInterface-method-sort'>                /**
838 </span>                 * Sorts this nodes children using the supplied sort function.
839                  * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
840                  * @param {Boolean} recursive Whether or not to apply this sort recursively
841                  * @param {Boolean} suppressEvent Set to true to not fire a sort event.
842                  */
843                 sort : function(sortFn, recursive, suppressEvent) {
844                     var cs  = this.childNodes,
845                         ln = cs.length,
846                         i, n;
847                     
848                     if (ln &gt; 0) {
849                         Ext.Array.sort(cs, sortFn);
850                         for (i = 0; i &lt; ln; i++) {
851                             n = cs[i];
852                             n.previousSibling = cs[i-1];
853                             n.nextSibling = cs[i+1];
854                         
855                             if (i === 0) {
856                                 this.setFirstChild(n);
857                                 n.updateInfo();
858                             }
859                             if (i == ln - 1) {
860                                 this.setLastChild(n);
861                                 n.updateInfo();
862                             }
863                             if (recursive &amp;&amp; !n.isLeaf()) {
864                                 n.sort(sortFn, true, true);
865                             }
866                         }
867                         
868                         if (suppressEvent !== true) {
869                             this.fireEvent('sort', this, cs);
870                         }
871                     }
872                 },
873                         
874 <span id='Ext-data.NodeInterface-method-isExpanded'>                /**
875 </span>                 * Returns true if this node is expaned
876                  * @return {Boolean}
877                  */        
878                 isExpanded: function() {
879                     return this.get('expanded');
880                 },
881                 
882 <span id='Ext-data.NodeInterface-method-isLoaded'>                /**
883 </span>                 * Returns true if this node is loaded
884                  * @return {Boolean}
885                  */ 
886                 isLoaded: function() {
887                     return this.get('loaded');
888                 },
889
890 <span id='Ext-data.NodeInterface-method-isLoading'>                /**
891 </span>                 * Returns true if this node is loading
892                  * @return {Boolean}
893                  */ 
894                 isLoading: function() {
895                     return this.get('loading');
896                 },
897                                 
898 <span id='Ext-data.NodeInterface-method-isRoot'>                /**
899 </span>                 * Returns true if this node is the root node
900                  * @return {Boolean}
901                  */ 
902                 isRoot: function() {
903                     return !this.parentNode;
904                 },
905                 
906 <span id='Ext-data.NodeInterface-method-isVisible'>                /**
907 </span>                 * Returns true if this node is visible
908                  * @return {Boolean}
909                  */ 
910                 isVisible: function() {
911                     var parent = this.parentNode;
912                     while (parent) {
913                         if (!parent.isExpanded()) {
914                             return false;
915                         }
916                         parent = parent.parentNode;
917                     }
918                     return true;
919                 },
920                 
921 <span id='Ext-data.NodeInterface-method-expand'>                /**
922 </span>                 * Expand this node.
923                  * @param {Function} recursive (Optional) True to recursively expand all the children
924                  * @param {Function} callback (Optional) The function to execute once the expand completes
925                  * @param {Object} scope (Optional) The scope to run the callback in
926                  */
927                 expand: function(recursive, callback, scope) {
928                     var me = this;
929
930                     // all paths must call the callback (eventually) or things like
931                     // selectPath fail
932
933                     // First we start by checking if this node is a parent
934                     if (!me.isLeaf()) {
935                         // Now we check if this record is already expanding or expanded
936                         if (!me.isLoading() &amp;&amp; !me.isExpanded()) {
937                             // The TreeStore actually listens for the beforeexpand method and checks
938                             // whether we have to asynchronously load the children from the server
939                             // first. Thats why we pass a callback function to the event that the
940                             // store can call once it has loaded and parsed all the children.
941                             me.fireEvent('beforeexpand', me, function(records) {
942                                 me.set('expanded', true); 
943                                 me.fireEvent('expand', me, me.childNodes, false);
944                                 
945                                 // Call the expandChildren method if recursive was set to true 
946                                 if (recursive) {
947                                     me.expandChildren(true, callback, scope);
948                                 }
949                                 else {
950                                     Ext.callback(callback, scope || me, [me.childNodes]);                                
951                                 }
952                             }, me);                            
953                         }
954                         // If it is is already expanded but we want to recursively expand then call expandChildren
955                         else if (recursive) {
956                             me.expandChildren(true, callback, scope);
957                         }
958                         else {
959                             Ext.callback(callback, scope || me, [me.childNodes]);
960                         }
961
962                         // TODO - if the node isLoading, we probably need to defer the
963                         // callback until it is loaded (e.g., selectPath would need us
964                         // to not make the callback until the childNodes exist).
965                     }
966                     // If it's not then we fire the callback right away
967                     else {
968                         Ext.callback(callback, scope || me); // leaf = no childNodes
969                     }
970                 },
971                 
972 <span id='Ext-data.NodeInterface-method-expandChildren'>                /**
973 </span>                 * Expand all the children of this node.
974                  * @param {Function} recursive (Optional) True to recursively expand all the children
975                  * @param {Function} callback (Optional) The function to execute once all the children are expanded
976                  * @param {Object} scope (Optional) The scope to run the callback in
977                  */
978                 expandChildren: function(recursive, callback, scope) {
979                     var me = this,
980                         i = 0,
981                         nodes = me.childNodes,
982                         ln = nodes.length,
983                         node,
984                         expanding = 0;
985
986                     for (; i &lt; ln; ++i) {
987                         node = nodes[i];
988                         if (!node.isLeaf() &amp;&amp; !node.isExpanded()) {
989                             expanding++;
990                             nodes[i].expand(recursive, function () {
991                                 expanding--;
992                                 if (callback &amp;&amp; !expanding) {
993                                     Ext.callback(callback, scope || me, me.childNodes); 
994                                 }
995                             });                            
996                         }
997                     }
998                     
999                     if (!expanding &amp;&amp; callback) {
1000                         Ext.callback(callback, scope || me, me.childNodes);
1001                     }
1002                 },
1003
1004 <span id='Ext-data.NodeInterface-method-collapse'>                /**
1005 </span>                 * Collapse this node.
1006                  * @param {Function} recursive (Optional) True to recursively collapse all the children
1007                  * @param {Function} callback (Optional) The function to execute once the collapse completes
1008                  * @param {Object} scope (Optional) The scope to run the callback in
1009                  */
1010                 collapse: function(recursive, callback, scope) {
1011                     var me = this;
1012
1013                     // First we start by checking if this node is a parent
1014                     if (!me.isLeaf()) {
1015                         // Now we check if this record is already collapsing or collapsed
1016                         if (!me.collapsing &amp;&amp; me.isExpanded()) {
1017                             me.fireEvent('beforecollapse', me, function(records) {
1018                                 me.set('expanded', false); 
1019                                 me.fireEvent('collapse', me, me.childNodes, false);
1020                                 
1021                                 // Call the collapseChildren method if recursive was set to true 
1022                                 if (recursive) {
1023                                     me.collapseChildren(true, callback, scope);
1024                                 }
1025                                 else {
1026                                     Ext.callback(callback, scope || me, [me.childNodes]);                                
1027                                 }
1028                             }, me);                            
1029                         }
1030                         // If it is is already collapsed but we want to recursively collapse then call collapseChildren
1031                         else if (recursive) {
1032                             me.collapseChildren(true, callback, scope);
1033                         }
1034                     }
1035                     // If it's not then we fire the callback right away
1036                     else {
1037                         Ext.callback(callback, scope || me, me.childNodes); 
1038                     }
1039                 },
1040                 
1041 <span id='Ext-data.NodeInterface-method-collapseChildren'>                /**
1042 </span>                 * Collapse all the children of this node.
1043                  * @param {Function} recursive (Optional) True to recursively collapse all the children
1044                  * @param {Function} callback (Optional) The function to execute once all the children are collapsed
1045                  * @param {Object} scope (Optional) The scope to run the callback in
1046                  */
1047                 collapseChildren: function(recursive, callback, scope) {
1048                     var me = this,
1049                         i = 0,
1050                         nodes = me.childNodes,
1051                         ln = nodes.length,
1052                         node,
1053                         collapsing = 0;
1054
1055                     for (; i &lt; ln; ++i) {
1056                         node = nodes[i];
1057                         if (!node.isLeaf() &amp;&amp; node.isExpanded()) {
1058                             collapsing++;
1059                             nodes[i].collapse(recursive, function () {
1060                                 collapsing--;
1061                                 if (callback &amp;&amp; !collapsing) {
1062                                     Ext.callback(callback, scope || me, me.childNodes); 
1063                                 }
1064                             });                            
1065                         }
1066                     }
1067                     
1068                     if (!collapsing &amp;&amp; callback) {
1069                         Ext.callback(callback, scope || me, me.childNodes);
1070                     }
1071                 }
1072             };
1073         }
1074     }
1075 });</pre></pre></body></html>