4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-data-NodeInterface'>/**
19 </span> * This class is used as a set of methods that are applied to the prototype of a
20 * Model to decorate it with a Node API. This means that models used in conjunction with a tree
21 * will have all of the tree related methods available on the model. In general this class will
22 * not be used directly by the developer. This class also creates extra fields on the model if
23 * they do not exist, to help maintain the tree state and UI. These fields are documented as
26 Ext.define('Ext.data.NodeInterface', {
27 requires: ['Ext.data.Field'],
29 <span id='Ext-data-NodeInterface-cfg-parentId'> /**
30 </span> * @cfg {String} parentId
34 <span id='Ext-data-NodeInterface-cfg-index'> /**
35 </span> * @cfg {Number} index
36 * The position of the node inside its parent. When parent has 4 children and the node is third amongst them,
40 <span id='Ext-data-NodeInterface-cfg-depth'> /**
41 </span> * @cfg {Number} depth
42 * The number of parents this node has. A root node has depth 0, a child of it depth 1, and so on...
45 <span id='Ext-data-NodeInterface-cfg-expanded'> /**
46 </span> * @cfg {Boolean} [expanded=false]
47 * True if the node is expanded.
50 <span id='Ext-data-NodeInterface-cfg-expandable'> /**
51 </span> * @cfg {Boolean} [expandable=false]
52 * Set to true to allow for expanding/collapsing of this node.
55 <span id='Ext-data-NodeInterface-cfg-checked'> /**
56 </span> * @cfg {Boolean} [checked=null]
57 * Set to true or false to show a checkbox alongside this node.
60 <span id='Ext-data-NodeInterface-cfg-leaf'> /**
61 </span> * @cfg {Boolean} [leaf=false]
62 * Set to true to indicate that this child can have no children. The expand icon/arrow will then not be
63 * rendered for this node.
66 <span id='Ext-data-NodeInterface-cfg-cls'> /**
67 </span> * @cfg {String} cls
68 * CSS class to apply for this node.
71 <span id='Ext-data-NodeInterface-cfg-iconCls'> /**
72 </span> * @cfg {String} iconCls
73 * CSS class to apply for this node's icon.
76 <span id='Ext-data-NodeInterface-cfg-icon'> /**
77 </span> * @cfg {String} icon
78 * URL for this node's icon.
81 <span id='Ext-data-NodeInterface-cfg-root'> /**
82 </span> * @cfg {Boolean} root
83 * True if this is the root node.
86 <span id='Ext-data-NodeInterface-cfg-isLast'> /**
87 </span> * @cfg {Boolean} isLast
88 * True if this is the last node.
91 <span id='Ext-data-NodeInterface-cfg-isFirst'> /**
92 </span> * @cfg {Boolean} isFirst
93 * True if this is the first node.
96 <span id='Ext-data-NodeInterface-cfg-allowDrop'> /**
97 </span> * @cfg {Boolean} [allowDrop=true]
98 * Set to false to deny dropping on this node.
101 <span id='Ext-data-NodeInterface-cfg-allowDrag'> /**
102 </span> * @cfg {Boolean} [allowDrag=true]
103 * Set to false to deny dragging of this node.
106 <span id='Ext-data-NodeInterface-cfg-loaded'> /**
107 </span> * @cfg {Boolean} [loaded=false]
108 * True if the node has finished loading.
111 <span id='Ext-data-NodeInterface-cfg-loading'> /**
112 </span> * @cfg {Boolean} [loading=false]
113 * True if the node is currently loading.
116 <span id='Ext-data-NodeInterface-cfg-href'> /**
117 </span> * @cfg {String} href
118 * An URL for a link that's created when this config is specified.
121 <span id='Ext-data-NodeInterface-cfg-hrefTarget'> /**
122 </span> * @cfg {String} hrefTarget
123 * Target for link. Only applicable when {@link #href} also specified.
126 <span id='Ext-data-NodeInterface-cfg-qtip'> /**
127 </span> * @cfg {String} qtip
128 * Tooltip text to show on this node.
131 <span id='Ext-data-NodeInterface-cfg-qtitle'> /**
132 </span> * @cfg {String} qtitle
136 <span id='Ext-data-NodeInterface-cfg-text'> /**
137 </span> * @cfg {String} text
138 * The text for to show on node label.
141 <span id='Ext-data-NodeInterface-cfg-children'> /**
142 </span> * @cfg {Ext.data.NodeInterface[]} children
143 * Array of child nodes.
147 <span id='Ext-data-NodeInterface-property-nextSibling'> /**
148 </span> * @property nextSibling
149 * A reference to this node's next sibling node. `null` if this node does not have a next sibling.
152 <span id='Ext-data-NodeInterface-property-previousSibling'> /**
153 </span> * @property previousSibling
154 * A reference to this node's previous sibling node. `null` if this node does not have a previous sibling.
157 <span id='Ext-data-NodeInterface-property-parentNode'> /**
158 </span> * @property parentNode
159 * A reference to this node's parent node. `null` if this node is the root node.
162 <span id='Ext-data-NodeInterface-property-lastChild'> /**
163 </span> * @property lastChild
164 * A reference to this node's last child node. `null` if this node has no children.
167 <span id='Ext-data-NodeInterface-property-firstChild'> /**
168 </span> * @property firstChild
169 * A reference to this node's first child node. `null` if this node has no children.
172 <span id='Ext-data-NodeInterface-property-childNodes'> /**
173 </span> * @property childNodes
174 * An array of this nodes children. Array will be empty if this node has no chidren.
178 <span id='Ext-data-NodeInterface-static-method-decorate'> /**
179 </span> * This method allows you to decorate a Record's prototype to implement the NodeInterface.
180 * This adds a set of methods, new events, new properties and new fields on every Record
181 * with the same Model as the passed Record.
182 * @param {Ext.data.Model} record The Record you want to decorate the prototype of.
185 decorate: function(record) {
186 if (!record.isNode) {
187 // Apply the methods and fields to the prototype
188 // @TODO: clean this up to use proper class system stuff
189 var mgr = Ext.ModelManager,
190 modelName = record.modelName,
191 modelClass = mgr.getModel(modelName),
192 idName = modelClass.prototype.idProperty,
196 // Start by adding the NodeInterface methods to the Model's prototype
197 modelClass.override(this.getPrototypeBody());
198 newFields = this.applyFields(modelClass, [
199 {name: idName, type: 'string', defaultValue: null},
200 {name: 'parentId', type: 'string', defaultValue: null},
201 {name: 'index', type: 'int', defaultValue: null},
202 {name: 'depth', type: 'int', defaultValue: 0},
203 {name: 'expanded', type: 'bool', defaultValue: false, persist: false},
204 {name: 'expandable', type: 'bool', defaultValue: true, persist: false},
205 {name: 'checked', type: 'auto', defaultValue: null},
206 {name: 'leaf', type: 'bool', defaultValue: false, persist: false},
207 {name: 'cls', type: 'string', defaultValue: null, persist: false},
208 {name: 'iconCls', type: 'string', defaultValue: null, persist: false},
209 {name: 'icon', type: 'string', defaultValue: null, persist: false},
210 {name: 'root', type: 'boolean', defaultValue: false, persist: false},
211 {name: 'isLast', type: 'boolean', defaultValue: false, persist: false},
212 {name: 'isFirst', type: 'boolean', defaultValue: false, persist: false},
213 {name: 'allowDrop', type: 'boolean', defaultValue: true, persist: false},
214 {name: 'allowDrag', type: 'boolean', defaultValue: true, persist: false},
215 {name: 'loaded', type: 'boolean', defaultValue: false, persist: false},
216 {name: 'loading', type: 'boolean', defaultValue: false, persist: false},
217 {name: 'href', type: 'string', defaultValue: null, persist: false},
218 {name: 'hrefTarget', type: 'string', defaultValue: null, persist: false},
219 {name: 'qtip', type: 'string', defaultValue: null, persist: false},
220 {name: 'qtitle', type: 'string', defaultValue: null, persist: false}
223 len = newFields.length;
224 // Set default values
225 for (i = 0; i < len; ++i) {
226 newField = newFields[i];
227 if (record.get(newField.name) === undefined) {
228 record.data[newField.name] = newField.defaultValue;
233 Ext.applyIf(record, {
237 previousSibling: null,
241 // Commit any fields so the record doesn't show as dirty initially
244 record.enableBubble([
245 <span id='Ext-data-NodeInterface-event-append'> /**
246 </span> * @event append
247 * Fires when a new child node is appended
248 * @param {Ext.data.NodeInterface} this This node
249 * @param {Ext.data.NodeInterface} node The newly appended node
250 * @param {Number} index The index of the newly appended node
254 <span id='Ext-data-NodeInterface-event-remove'> /**
255 </span> * @event remove
256 * Fires when a child node is removed
257 * @param {Ext.data.NodeInterface} this This node
258 * @param {Ext.data.NodeInterface} node The removed node
262 <span id='Ext-data-NodeInterface-event-move'> /**
263 </span> * @event move
264 * Fires when this node is moved to a new location in the tree
265 * @param {Ext.data.NodeInterface} this This node
266 * @param {Ext.data.NodeInterface} oldParent The old parent of this node
267 * @param {Ext.data.NodeInterface} newParent The new parent of this node
268 * @param {Number} index The index it was moved to
272 <span id='Ext-data-NodeInterface-event-insert'> /**
273 </span> * @event insert
274 * Fires when a new child node is inserted.
275 * @param {Ext.data.NodeInterface} this This node
276 * @param {Ext.data.NodeInterface} node The child node inserted
277 * @param {Ext.data.NodeInterface} refNode The child node the node was inserted before
281 <span id='Ext-data-NodeInterface-event-beforeappend'> /**
282 </span> * @event beforeappend
283 * Fires before a new child is appended, return false to cancel the append.
284 * @param {Ext.data.NodeInterface} this This node
285 * @param {Ext.data.NodeInterface} node The child node to be appended
287 "beforeappend",
289 <span id='Ext-data-NodeInterface-event-beforeremove'> /**
290 </span> * @event beforeremove
291 * Fires before a child is removed, return false to cancel the remove.
292 * @param {Ext.data.NodeInterface} this This node
293 * @param {Ext.data.NodeInterface} node The child node to be removed
295 "beforeremove",
297 <span id='Ext-data-NodeInterface-event-beforemove'> /**
298 </span> * @event beforemove
299 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
300 * @param {Ext.data.NodeInterface} this This node
301 * @param {Ext.data.NodeInterface} oldParent The parent of this node
302 * @param {Ext.data.NodeInterface} newParent The new parent this node is moving to
303 * @param {Number} index The index it is being moved to
305 "beforemove",
307 <span id='Ext-data-NodeInterface-event-beforeinsert'> /**
308 </span> * @event beforeinsert
309 * Fires before a new child is inserted, return false to cancel the insert.
310 * @param {Ext.data.NodeInterface} this This node
311 * @param {Ext.data.NodeInterface} node The child node to be inserted
312 * @param {Ext.data.NodeInterface} refNode The child node the node is being inserted before
314 "beforeinsert",
316 <span id='Ext-data-NodeInterface-event-expand'> /**
317 </span> * @event expand
318 * Fires when this node is expanded.
319 * @param {Ext.data.NodeInterface} this The expanding node
323 <span id='Ext-data-NodeInterface-event-collapse'> /**
324 </span> * @event collapse
325 * Fires when this node is collapsed.
326 * @param {Ext.data.NodeInterface} this The collapsing node
328 "collapse",
330 <span id='Ext-data-NodeInterface-event-beforeexpand'> /**
331 </span> * @event beforeexpand
332 * Fires before this node is expanded.
333 * @param {Ext.data.NodeInterface} this The expanding node
335 "beforeexpand",
337 <span id='Ext-data-NodeInterface-event-beforecollapse'> /**
338 </span> * @event beforecollapse
339 * Fires before this node is collapsed.
340 * @param {Ext.data.NodeInterface} this The collapsing node
342 "beforecollapse",
344 <span id='Ext-data-NodeInterface-event-sort'> /**
345 </span> * @event sort
346 * Fires when this node's childNodes are sorted.
347 * @param {Ext.data.NodeInterface} this This node.
348 * @param {Ext.data.NodeInterface[]} childNodes The childNodes of this node.
356 applyFields: function(modelClass, addFields) {
357 var modelPrototype = modelClass.prototype,
358 fields = modelPrototype.fields,
360 ln = addFields.length,
364 for (i = 0; i < ln; i++) {
365 addField = addFields[i];
366 if (!Ext.Array.contains(keys, addField.name)) {
367 addField = Ext.create('data.field', addField);
369 newFields.push(addField);
370 fields.add(addField);
377 getPrototypeBody: function() {
381 <span id='Ext-data-NodeInterface-method-createNode'> /**
382 </span> * Ensures that the passed object is an instance of a Record with the NodeInterface applied
385 createNode: function(node) {
386 if (Ext.isObject(node) && !node.isModel) {
387 node = Ext.ModelManager.create(node, this.modelName);
389 // Make sure the node implements the node interface
390 return Ext.data.NodeInterface.decorate(node);
393 <span id='Ext-data-NodeInterface-method-isLeaf'> /**
394 </span> * Returns true if this node is a leaf
397 isLeaf : function() {
398 return this.get('leaf') === true;
401 <span id='Ext-data-NodeInterface-method-setFirstChild'> /**
402 </span> * Sets the first child of this node
404 * @param {Ext.data.NodeInterface} node
406 setFirstChild : function(node) {
407 this.firstChild = node;
410 <span id='Ext-data-NodeInterface-method-setLastChild'> /**
411 </span> * Sets the last child of this node
413 * @param {Ext.data.NodeInterface} node
415 setLastChild : function(node) {
416 this.lastChild = node;
419 <span id='Ext-data-NodeInterface-method-updateInfo'> /**
420 </span> * Updates general data of this node like isFirst, isLast, depth. This
421 * method is internally called after a node is moved. This shouldn't
422 * have to be called by the developer unless they are creating custom
426 updateInfo: function(silent) {
428 isRoot = me.isRoot(),
429 parentNode = me.parentNode,
430 isFirst = (!parentNode ? true : parentNode.firstChild == me),
431 isLast = (!parentNode ? true : parentNode.lastChild == me),
434 children = me.childNodes,
435 len = children.length,
438 while (parent.parentNode) {
440 parent = parent.parentNode;
448 index: parentNode ? parentNode.indexOf(me) : 0,
449 parentId: parentNode ? parentNode.getId() : null
456 for (i = 0; i < len; i++) {
457 children[i].updateInfo(silent);
461 <span id='Ext-data-NodeInterface-method-isLast'> /**
462 </span> * Returns true if this node is the last child of its parent
465 isLast : function() {
466 return this.get('isLast');
469 <span id='Ext-data-NodeInterface-method-isFirst'> /**
470 </span> * Returns true if this node is the first child of its parent
473 isFirst : function() {
474 return this.get('isFirst');
477 <span id='Ext-data-NodeInterface-method-hasChildNodes'> /**
478 </span> * Returns true if this node has one or more child nodes, else false.
481 hasChildNodes : function() {
482 return !this.isLeaf() && this.childNodes.length > 0;
485 <span id='Ext-data-NodeInterface-method-isExpandable'> /**
486 </span> * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
487 * node attribute is explicitly specified as true, otherwise returns false.
490 isExpandable : function() {
493 if (me.get('expandable')) {
494 return !(me.isLeaf() || (me.isLoaded() && !me.hasChildNodes()));
499 <span id='Ext-data-NodeInterface-method-appendChild'> /**
500 </span> * Inserts node(s) as the last child node of this node.
502 * If the node was previously a child node of another parent node, it will be removed from that node first.
504 * @param {Ext.data.NodeInterface/Ext.data.NodeInterface[]} node The node or Array of nodes to append
505 * @return {Ext.data.NodeInterface} The appended node if single append, or null if an array was passed
507 appendChild : function(node, suppressEvents, suppressNodeUpdate) {
514 // if passed an array or multiple args do them one by one
515 if (Ext.isArray(node)) {
516 for (i = 0, ln = node.length; i < ln; i++) {
517 me.appendChild(node[i]);
520 // Make sure it is a record
521 node = me.createNode(node);
523 if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
527 index = me.childNodes.length;
528 oldParent = node.parentNode;
530 // it's a move, make sure we move it cleanly
532 if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index) === false) {
535 oldParent.removeChild(node, null, false, true);
538 index = me.childNodes.length;
540 me.setFirstChild(node);
543 me.childNodes.push(node);
544 node.parentNode = me;
545 node.nextSibling = null;
547 me.setLastChild(node);
549 ps = me.childNodes[index - 1];
551 node.previousSibling = ps;
552 ps.nextSibling = node;
553 ps.updateInfo(suppressNodeUpdate);
555 node.previousSibling = null;
558 node.updateInfo(suppressNodeUpdate);
560 // As soon as we append a child to this node, we are loaded
561 if (!me.isLoaded()) {
562 me.set('loaded', true);
564 // If this node didnt have any childnodes before, update myself
565 else if (me.childNodes.length === 1) {
566 me.set('loaded', me.isLoaded());
569 if (suppressEvents !== true) {
570 me.fireEvent("append", me, node, index);
573 node.fireEvent("move", node, oldParent, me, index);
581 <span id='Ext-data-NodeInterface-method-getBubbleTarget'> /**
582 </span> * Returns the bubble target for this node
584 * @return {Object} The bubble target
586 getBubbleTarget: function() {
587 return this.parentNode;
590 <span id='Ext-data-NodeInterface-method-removeChild'> /**
591 </span> * Removes a child node from this node.
592 * @param {Ext.data.NodeInterface} node The node to remove
593 * @param {Boolean} [destroy=false] True to destroy the node upon removal.
594 * @return {Ext.data.NodeInterface} The removed node
596 removeChild : function(node, destroy, suppressEvents, suppressNodeUpdate) {
598 index = me.indexOf(node);
600 if (index == -1 || (suppressEvents !== true && me.fireEvent("beforeremove", me, node) === false)) {
604 // remove it from childNodes collection
605 Ext.Array.erase(me.childNodes, index, 1);
608 if (me.firstChild == node) {
609 me.setFirstChild(node.nextSibling);
611 if (me.lastChild == node) {
612 me.setLastChild(node.previousSibling);
616 if (node.previousSibling) {
617 node.previousSibling.nextSibling = node.nextSibling;
618 node.previousSibling.updateInfo(suppressNodeUpdate);
620 if (node.nextSibling) {
621 node.nextSibling.previousSibling = node.previousSibling;
622 node.nextSibling.updateInfo(suppressNodeUpdate);
625 if (suppressEvents !== true) {
626 me.fireEvent("remove", me, node);
630 // If this node suddenly doesnt have childnodes anymore, update myself
631 if (!me.childNodes.length) {
632 me.set('loaded', me.isLoaded());
644 <span id='Ext-data-NodeInterface-method-copy'> /**
645 </span> * Creates a copy (clone) of this Node.
646 * @param {String} [id] A new id, defaults to this Node's id.
647 * @param {Boolean} [deep=false] True to recursively copy all child Nodes into the new Node.
648 * False to copy without child Nodes.
649 * @return {Ext.data.NodeInterface} A copy of this Node.
651 copy: function(newId, deep) {
653 result = me.callOverridden(arguments),
654 len = me.childNodes ? me.childNodes.length : 0,
657 // Move child nodes across to the copy if required
659 for (i = 0; i < len; i++) {
660 result.appendChild(me.childNodes[i].copy(true));
666 <span id='Ext-data-NodeInterface-method-clear'> /**
667 </span> * Clears the node.
669 * @param {Boolean} [destroy=false] True to destroy the node.
671 clear : function(destroy) {
674 // clear any references from the node
675 me.parentNode = me.previousSibling = me.nextSibling = null;
677 me.firstChild = me.lastChild = null;
681 <span id='Ext-data-NodeInterface-method-destroy'> /**
682 </span> * Destroys the node.
684 destroy : function(silent) {
686 * Silent is to be used in a number of cases
687 * 1) When setRoot is called.
688 * 2) When destroy on the tree is called
689 * 3) For destroying child nodes on a node
692 options = me.destroyOptions;
694 if (silent === true) {
696 Ext.each(me.childNodes, function(n) {
699 me.childNodes = null;
700 delete me.destroyOptions;
701 me.callOverridden([options]);
703 me.destroyOptions = silent;
704 // overridden method will be called, since remove will end up calling destroy(true);
709 <span id='Ext-data-NodeInterface-method-insertBefore'> /**
710 </span> * Inserts the first node before the second node in this nodes childNodes collection.
711 * @param {Ext.data.NodeInterface} node The node to insert
712 * @param {Ext.data.NodeInterface} refNode The node to insert before (if null the node is appended)
713 * @return {Ext.data.NodeInterface} The inserted node
715 insertBefore : function(node, refNode, suppressEvents) {
717 index = me.indexOf(refNode),
718 oldParent = node.parentNode,
722 if (!refNode) { // like standard Dom, refNode can be null for append
723 return me.appendChild(node);
727 if (node == refNode) {
731 // Make sure it is a record with the NodeInterface
732 node = me.createNode(node);
734 if (suppressEvents !== true && me.fireEvent("beforeinsert", me, node, refNode) === false) {
738 // when moving internally, indexes will change after remove
739 if (oldParent == me && me.indexOf(node) < index) {
743 // it's a move, make sure we move it cleanly
745 if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index, refNode) === false) {
748 oldParent.removeChild(node);
751 if (refIndex === 0) {
752 me.setFirstChild(node);
755 Ext.Array.splice(me.childNodes, refIndex, 0, node);
756 node.parentNode = me;
758 node.nextSibling = refNode;
759 refNode.previousSibling = node;
761 ps = me.childNodes[refIndex - 1];
763 node.previousSibling = ps;
764 ps.nextSibling = node;
767 node.previousSibling = null;
772 if (!me.isLoaded()) {
773 me.set('loaded', true);
775 // If this node didnt have any childnodes before, update myself
776 else if (me.childNodes.length === 1) {
777 me.set('loaded', me.isLoaded());
780 if (suppressEvents !== true) {
781 me.fireEvent("insert", me, node, refNode);
784 node.fireEvent("move", node, oldParent, me, refIndex, refNode);
791 <span id='Ext-data-NodeInterface-method-insertChild'> /**
792 </span> * Insert a node into this node
793 * @param {Number} index The zero-based index to insert the node at
794 * @param {Ext.data.Model} node The node to insert
795 * @return {Ext.data.Model} The record you just inserted
797 insertChild: function(index, node) {
798 var sibling = this.childNodes[index];
800 return this.insertBefore(node, sibling);
803 return this.appendChild(node);
807 <span id='Ext-data-NodeInterface-method-remove'> /**
808 </span> * Removes this node from its parent
809 * @param {Boolean} [destroy=false] True to destroy the node upon removal.
810 * @return {Ext.data.NodeInterface} this
812 remove : function(destroy, suppressEvents) {
813 var parentNode = this.parentNode;
816 parentNode.removeChild(this, destroy, suppressEvents, true);
821 <span id='Ext-data-NodeInterface-method-removeAll'> /**
822 </span> * Removes all child nodes from this node.
823 * @param {Boolean} [destroy=false] <True to destroy the node upon removal.
824 * @return {Ext.data.NodeInterface} this
826 removeAll : function(destroy, suppressEvents) {
827 var cn = this.childNodes,
830 while ((n = cn[0])) {
831 this.removeChild(n, destroy, suppressEvents);
836 <span id='Ext-data-NodeInterface-method-getChildAt'> /**
837 </span> * Returns the child node at the specified index.
838 * @param {Number} index
839 * @return {Ext.data.NodeInterface}
841 getChildAt : function(index) {
842 return this.childNodes[index];
845 <span id='Ext-data-NodeInterface-method-replaceChild'> /**
846 </span> * Replaces one child node in this node with another.
847 * @param {Ext.data.NodeInterface} newChild The replacement node
848 * @param {Ext.data.NodeInterface} oldChild The node to replace
849 * @return {Ext.data.NodeInterface} The replaced node
851 replaceChild : function(newChild, oldChild, suppressEvents) {
852 var s = oldChild ? oldChild.nextSibling : null;
854 this.removeChild(oldChild, suppressEvents);
855 this.insertBefore(newChild, s, suppressEvents);
859 <span id='Ext-data-NodeInterface-method-indexOf'> /**
860 </span> * Returns the index of a child node
861 * @param {Ext.data.NodeInterface} node
862 * @return {Number} The index of the node or -1 if it was not found
864 indexOf : function(child) {
865 return Ext.Array.indexOf(this.childNodes, child);
868 <span id='Ext-data-NodeInterface-method-getPath'> /**
869 </span> * Gets the hierarchical path from the root of the current node.
870 * @param {String} [field] The field to construct the path from. Defaults to the model idProperty.
871 * @param {String} [separator="/"] A separator to use.
872 * @return {String} The node path
874 getPath: function(field, separator) {
875 field = field || this.idProperty;
876 separator = separator || '/';
878 var path = [this.get(field)],
879 parent = this.parentNode;
882 path.unshift(parent.get(field));
883 parent = parent.parentNode;
885 return separator + path.join(separator);
888 <span id='Ext-data-NodeInterface-method-getDepth'> /**
889 </span> * Returns depth of this node (the root node has a depth of 0)
892 getDepth : function() {
893 return this.get('depth');
896 <span id='Ext-data-NodeInterface-method-bubble'> /**
897 </span> * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
898 * will be the args provided or the current node. If the function returns false at any point,
899 * the bubble is stopped.
900 * @param {Function} fn The function to call
901 * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the current Node.
902 * @param {Array} [args] The args to call the function with. Defaults to passing the current Node.
904 bubble : function(fn, scope, args) {
907 if (fn.apply(scope || p, args || [p]) === false) {
914 //<deprecated since=0.99>
915 cascade: function() {
916 if (Ext.isDefined(Ext.global.console)) {
917 Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
919 return this.cascadeBy.apply(this, arguments);
921 //</deprecated>
923 <span id='Ext-data-NodeInterface-method-cascadeBy'> /**
924 </span> * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
925 * will be the args provided or the current node. If the function returns false at any point,
926 * the cascade is stopped on that branch.
927 * @param {Function} fn The function to call
928 * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the current Node.
929 * @param {Array} [args] The args to call the function with. Defaults to passing the current Node.
931 cascadeBy : function(fn, scope, args) {
932 if (fn.apply(scope || this, args || [this]) !== false) {
933 var childNodes = this.childNodes,
934 length = childNodes.length,
937 for (i = 0; i < length; i++) {
938 childNodes[i].cascadeBy(fn, scope, args);
943 <span id='Ext-data-NodeInterface-method-eachChild'> /**
944 </span> * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
945 * will be the args provided or the current node. If the function returns false at any point,
946 * the iteration stops.
947 * @param {Function} fn The function to call
948 * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the current Node in iteration.
949 * @param {Array} [args] The args to call the function with. Defaults to passing the current Node.
951 eachChild : function(fn, scope, args) {
952 var childNodes = this.childNodes,
953 length = childNodes.length,
956 for (i = 0; i < length; i++) {
957 if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
963 <span id='Ext-data-NodeInterface-method-findChild'> /**
964 </span> * Finds the first child that has the attribute with the specified value.
965 * @param {String} attribute The attribute name
966 * @param {Object} value The value to search for
967 * @param {Boolean} [deep=false] True to search through nodes deeper than the immediate children
968 * @return {Ext.data.NodeInterface} The found child or null if none was found
970 findChild : function(attribute, value, deep) {
971 return this.findChildBy(function() {
972 return this.get(attribute) == value;
976 <span id='Ext-data-NodeInterface-method-findChildBy'> /**
977 </span> * Finds the first child by a custom function. The child matches if the function passed returns true.
978 * @param {Function} fn A function which must return true if the passed Node is the required Node.
979 * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the Node being tested.
980 * @param {Boolean} [deep=false] True to search through nodes deeper than the immediate children
981 * @return {Ext.data.NodeInterface} The found child or null if none was found
983 findChildBy : function(fn, scope, deep) {
984 var cs = this.childNodes,
988 for (; i < len; i++) {
990 if (fn.call(scope || n, n) === true) {
994 res = n.findChildBy(fn, scope, deep);
1004 <span id='Ext-data-NodeInterface-method-contains'> /**
1005 </span> * Returns true if this node is an ancestor (at any point) of the passed node.
1006 * @param {Ext.data.NodeInterface} node
1009 contains : function(node) {
1010 return node.isAncestor(this);
1013 <span id='Ext-data-NodeInterface-method-isAncestor'> /**
1014 </span> * Returns true if the passed node is an ancestor (at any point) of this node.
1015 * @param {Ext.data.NodeInterface} node
1018 isAncestor : function(node) {
1019 var p = this.parentNode;
1029 <span id='Ext-data-NodeInterface-method-sort'> /**
1030 </span> * Sorts this nodes children using the supplied sort function.
1031 * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
1032 * @param {Boolean} [recursive=false] True to apply this sort recursively
1033 * @param {Boolean} [suppressEvent=false] True to not fire a sort event.
1035 sort : function(sortFn, recursive, suppressEvent) {
1036 var cs = this.childNodes,
1041 Ext.Array.sort(cs, sortFn);
1042 for (i = 0; i < ln; i++) {
1044 n.previousSibling = cs[i-1];
1045 n.nextSibling = cs[i+1];
1048 this.setFirstChild(n);
1052 this.setLastChild(n);
1055 if (recursive && !n.isLeaf()) {
1056 n.sort(sortFn, true, true);
1060 if (suppressEvent !== true) {
1061 this.fireEvent('sort', this, cs);
1066 <span id='Ext-data-NodeInterface-method-isExpanded'> /**
1067 </span> * Returns true if this node is expaned
1070 isExpanded: function() {
1071 return this.get('expanded');
1074 <span id='Ext-data-NodeInterface-method-isLoaded'> /**
1075 </span> * Returns true if this node is loaded
1078 isLoaded: function() {
1079 return this.get('loaded');
1082 <span id='Ext-data-NodeInterface-method-isLoading'> /**
1083 </span> * Returns true if this node is loading
1086 isLoading: function() {
1087 return this.get('loading');
1090 <span id='Ext-data-NodeInterface-method-isRoot'> /**
1091 </span> * Returns true if this node is the root node
1094 isRoot: function() {
1095 return !this.parentNode;
1098 <span id='Ext-data-NodeInterface-method-isVisible'> /**
1099 </span> * Returns true if this node is visible
1102 isVisible: function() {
1103 var parent = this.parentNode;
1105 if (!parent.isExpanded()) {
1108 parent = parent.parentNode;
1113 <span id='Ext-data-NodeInterface-method-expand'> /**
1114 </span> * Expand this node.
1115 * @param {Boolean} [recursive=false] True to recursively expand all the children
1116 * @param {Function} [callback] The function to execute once the expand completes
1117 * @param {Object} [scope] The scope to run the callback in
1119 expand: function(recursive, callback, scope) {
1122 // all paths must call the callback (eventually) or things like
1125 // First we start by checking if this node is a parent
1127 // If it's loaded, wait until it loads before proceeding
1128 if (me.isLoading()) {
1129 me.on('expand', function(){
1130 me.expand(recursive, callback, scope);
1131 }, me, {single: true});
1133 // Now we check if this record is already expanding or expanded
1134 if (!me.isExpanded()) {
1135 // The TreeStore actually listens for the beforeexpand method and checks
1136 // whether we have to asynchronously load the children from the server
1137 // first. Thats why we pass a callback function to the event that the
1138 // store can call once it has loaded and parsed all the children.
1139 me.fireEvent('beforeexpand', me, function(){
1140 me.set('expanded', true);
1141 me.fireEvent('expand', me, me.childNodes, false);
1143 // Call the expandChildren method if recursive was set to true
1145 me.expandChildren(true, callback, scope);
1147 Ext.callback(callback, scope || me, [me.childNodes]);
1150 } else if (recursive) {
1151 // If it is is already expanded but we want to recursively expand then call expandChildren
1152 me.expandChildren(true, callback, scope);
1154 Ext.callback(callback, scope || me, [me.childNodes]);
1158 // If it's not then we fire the callback right away
1159 Ext.callback(callback, scope || me); // leaf = no childNodes
1163 <span id='Ext-data-NodeInterface-method-expandChildren'> /**
1164 </span> * Expand all the children of this node.
1165 * @param {Boolean} [recursive=false] True to recursively expand all the children
1166 * @param {Function} [callback] The function to execute once all the children are expanded
1167 * @param {Object} [scope] The scope to run the callback in
1169 expandChildren: function(recursive, callback, scope) {
1172 nodes = me.childNodes,
1177 for (; i < ln; ++i) {
1179 if (!node.isLeaf() && !node.isExpanded()) {
1181 nodes[i].expand(recursive, function () {
1183 if (callback && !expanding) {
1184 Ext.callback(callback, scope || me, [me.childNodes]);
1190 if (!expanding && callback) {
1191 Ext.callback(callback, scope || me, [me.childNodes]); }
1194 <span id='Ext-data-NodeInterface-method-collapse'> /**
1195 </span> * Collapse this node.
1196 * @param {Boolean} [recursive=false] True to recursively collapse all the children
1197 * @param {Function} [callback] The function to execute once the collapse completes
1198 * @param {Object} [scope] The scope to run the callback in
1200 collapse: function(recursive, callback, scope) {
1203 // First we start by checking if this node is a parent
1205 // Now we check if this record is already collapsing or collapsed
1206 if (!me.collapsing && me.isExpanded()) {
1207 me.fireEvent('beforecollapse', me, function() {
1208 me.set('expanded', false);
1209 me.fireEvent('collapse', me, me.childNodes, false);
1211 // Call the collapseChildren method if recursive was set to true
1213 me.collapseChildren(true, callback, scope);
1216 Ext.callback(callback, scope || me, [me.childNodes]);
1220 // If it is is already collapsed but we want to recursively collapse then call collapseChildren
1221 else if (recursive) {
1222 me.collapseChildren(true, callback, scope);
1225 // If it's not then we fire the callback right away
1227 Ext.callback(callback, scope || me, [me.childNodes]);
1231 <span id='Ext-data-NodeInterface-method-collapseChildren'> /**
1232 </span> * Collapse all the children of this node.
1233 * @param {Function} [recursive=false] True to recursively collapse all the children
1234 * @param {Function} [callback] The function to execute once all the children are collapsed
1235 * @param {Object} [scope] The scope to run the callback in
1237 collapseChildren: function(recursive, callback, scope) {
1240 nodes = me.childNodes,
1245 for (; i < ln; ++i) {
1247 if (!node.isLeaf() && node.isExpanded()) {
1249 nodes[i].collapse(recursive, function () {
1251 if (callback && !collapsing) {
1252 Ext.callback(callback, scope || me, [me.childNodes]);
1258 if (!collapsing && callback) {
1259 Ext.callback(callback, scope || me, [me.childNodes]);