]>
git.proxmox.com Git - sencha-touch.git/blob - src/src/data/NodeInterface.js
2 * @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. This class also creates extra fields on the model if
7 * they do not exist, to help maintain the tree state and UI. These fields are:
30 Ext
.define('Ext.data.NodeInterface', {
31 requires
: ['Ext.data.Field', 'Ext.data.ModelManager'],
33 alternateClassName
: 'Ext.data.Node',
36 * @property nextSibling
37 * A reference to this node's next sibling node. `null` if this node does not have a next sibling.
41 * @property previousSibling
42 * A reference to this node's previous sibling node. `null` if this node does not have a previous sibling.
46 * @property parentNode
47 * A reference to this node's parent node. `null` if this node is the root node.
52 * A reference to this node's last child node. `null` if this node has no children.
56 * @property firstChild
57 * A reference to this node's first child node. `null` if this node has no children.
61 * @property childNodes
62 * An array of this nodes children. Array will be empty if this node has no children.
67 * This method allows you to decorate a Record's prototype to implement the NodeInterface.
68 * This adds a set of methods, new events, new properties and new fields on every Record
69 * with the same Model as the passed Record.
70 * @param {Ext.data.Model} record The Record you want to decorate the prototype of.
73 decorate: function(record
) {
75 // Apply the methods and fields to the prototype
76 var mgr
= Ext
.data
.ModelManager
,
77 modelName
= record
.modelName
,
78 modelClass
= mgr
.getModel(modelName
),
82 // Start by adding the NodeInterface methods to the Model's prototype
83 modelClass
.override(this.getPrototypeBody());
85 newFields
= this.applyFields(modelClass
, [
86 {name
: 'parentId', type
: 'string', defaultValue
: null},
87 {name
: 'index', type
: 'int', defaultValue
: 0},
88 {name
: 'depth', type
: 'int', defaultValue
: 0, persist
: false},
89 {name
: 'expanded', type
: 'bool', defaultValue
: false, persist
: false},
90 {name
: 'expandable', type
: 'bool', defaultValue
: true, persist
: false},
91 {name
: 'checked', type
: 'auto', defaultValue
: null},
92 {name
: 'leaf', type
: 'bool', defaultValue
: false, persist
: false},
93 {name
: 'cls', type
: 'string', defaultValue
: null, persist
: false},
94 {name
: 'iconCls', type
: 'string', defaultValue
: null, persist
: false},
95 {name
: 'root', type
: 'boolean', defaultValue
: false, persist
: false},
96 {name
: 'isLast', type
: 'boolean', defaultValue
: false, persist
: false},
97 {name
: 'isFirst', type
: 'boolean', defaultValue
: false, persist
: false},
98 {name
: 'allowDrop', type
: 'boolean', defaultValue
: true, persist
: false},
99 {name
: 'allowDrag', type
: 'boolean', defaultValue
: true, persist
: false},
100 {name
: 'loaded', type
: 'boolean', defaultValue
: false, persist
: false},
101 {name
: 'loading', type
: 'boolean', defaultValue
: false, persist
: false},
102 {name
: 'href', type
: 'string', defaultValue
: null, persist
: false},
103 {name
: 'hrefTarget', type
: 'string', defaultValue
: null, persist
: false},
104 {name
: 'qtip', type
: 'string', defaultValue
: null, persist
: false},
105 {name
: 'qtitle', type
: 'string', defaultValue
: null, persist
: false}
108 len
= newFields
.length
;
110 // We set a dirty flag on the fields collection of the model. Any reader that
111 // will read in data for this model will update their extractor functions.
112 modelClass
.getFields().isDirty
= true;
114 // Set default values
115 for (i
= 0; i
< len
; ++i
) {
116 newField
= newFields
[i
];
117 if (record
.get(newField
.getName()) === undefined) {
118 record
.data
[newField
.getName()] = newField
.getDefaultValue();
123 if (!record
.isDecorated
) {
124 record
.isDecorated
= true;
126 Ext
.applyIf(record
, {
130 previousSibling
: null,
135 record
.enableBubble([
138 * Fires when a new child node is appended.
139 * @param {Ext.data.NodeInterface} this This node.
140 * @param {Ext.data.NodeInterface} node The newly appended node.
141 * @param {Number} index The index of the newly appended node.
147 * Fires when a child node is removed.
148 * @param {Ext.data.NodeInterface} this This node.
149 * @param {Ext.data.NodeInterface} node The removed node.
155 * Fires when this node is moved to a new location in the tree.
156 * @param {Ext.data.NodeInterface} this This node.
157 * @param {Ext.data.NodeInterface} oldParent The old parent of this node.
158 * @param {Ext.data.NodeInterface} newParent The new parent of this node.
159 * @param {Number} index The index it was moved to.
165 * Fires when a new child node is inserted.
166 * @param {Ext.data.NodeInterface} this This node.
167 * @param {Ext.data.NodeInterface} node The child node inserted.
168 * @param {Ext.data.NodeInterface} refNode The child node the node was inserted before.
173 * @event beforeappend
174 * Fires before a new child is appended, return `false` to cancel the append.
175 * @param {Ext.data.NodeInterface} this This node.
176 * @param {Ext.data.NodeInterface} node The child node to be appended.
181 * @event beforeremove
182 * Fires before a child is removed, return `false` to cancel the remove.
183 * @param {Ext.data.NodeInterface} this This node.
184 * @param {Ext.data.NodeInterface} node The child node to be removed.
190 * Fires before this node is moved to a new location in the tree. Return `false` to cancel the move.
191 * @param {Ext.data.NodeInterface} this This node.
192 * @param {Ext.data.NodeInterface} oldParent The parent of this node.
193 * @param {Ext.data.NodeInterface} newParent The new parent this node is moving to.
194 * @param {Number} index The index it is being moved to.
199 * @event beforeinsert
200 * Fires before a new child is inserted, return false to cancel the insert.
201 * @param {Ext.data.NodeInterface} this This node
202 * @param {Ext.data.NodeInterface} node The child node to be inserted
203 * @param {Ext.data.NodeInterface} refNode The child node the node is being inserted before
209 * Fires when this node is expanded.
210 * @param {Ext.data.NodeInterface} this The expanding node.
216 * Fires when this node is collapsed.
217 * @param {Ext.data.NodeInterface} this The collapsing node.
222 * @event beforeexpand
223 * Fires before this node is expanded.
224 * @param {Ext.data.NodeInterface} this The expanding node.
229 * @event beforecollapse
230 * Fires before this node is collapsed.
231 * @param {Ext.data.NodeInterface} this The collapsing node.
237 * Fires when this node's childNodes are sorted.
238 * @param {Ext.data.NodeInterface} this This node.
239 * @param {Ext.data.NodeInterface[]} childNodes The childNodes of this node.
250 applyFields: function(modelClass
, addFields
) {
251 var modelPrototype
= modelClass
.prototype,
252 fields
= modelPrototype
.fields
,
254 ln
= addFields
.length
,
258 for (i
= 0; i
< ln
; i
++) {
259 addField
= addFields
[i
];
260 if (!Ext
.Array
.contains(keys
, addField
.name
)) {
261 addField
= Ext
.create('Ext.data.Field', addField
);
263 newFields
.push(addField
);
264 fields
.add(addField
);
271 getPrototypeBody: function() {
276 * Ensures that the passed object is an instance of a Record with the NodeInterface applied
280 createNode: function(node
) {
281 if (Ext
.isObject(node
) && !node
.isModel
) {
282 node
= Ext
.data
.ModelManager
.create(node
, this.modelName
);
284 // Make sure the node implements the node interface
285 return Ext
.data
.NodeInterface
.decorate(node
);
289 * Returns true if this node is a leaf
292 isLeaf : function() {
293 return this.get('leaf') === true;
297 * Sets the first child of this node
299 * @param {Ext.data.NodeInterface} node
301 setFirstChild : function(node
) {
302 this.firstChild
= node
;
306 * Sets the last child of this node
308 * @param {Ext.data.NodeInterface} node
310 setLastChild : function(node
) {
311 this.lastChild
= node
;
315 * Updates general data of this node like isFirst, isLast, depth. This
316 * method is internally called after a node is moved. This shouldn't
317 * have to be called by the developer unless they are creating custom
321 updateInfo: function(silent
) {
323 parentNode
= me
.parentNode
,
324 isFirst
= (!parentNode
? true : parentNode
.firstChild
== me
),
325 isLast
= (!parentNode
? true : parentNode
.lastChild
== me
),
328 children
= me
.childNodes
,
329 ln
= children
.length
,
332 while (parent
.parentNode
) {
334 parent
= parent
.parentNode
;
342 index
: parentNode
? parentNode
.indexOf(me
) : 0,
343 parentId
: parentNode
? parentNode
.getId() : null
350 for (i
= 0; i
< ln
; i
++) {
351 children
[i
].updateInfo(silent
);
356 * Returns `true` if this node is the last child of its parent.
359 isLast : function() {
360 return this.get('isLast');
364 * Returns `true` if this node is the first child of its parent.
367 isFirst : function() {
368 return this.get('isFirst');
372 * Returns `true` if this node has one or more child nodes, else `false`.
375 hasChildNodes : function() {
376 return !this.isLeaf() && this.childNodes
.length
> 0;
380 * Returns `true` if this node has one or more child nodes, or if the `expandable`
381 * node attribute is explicitly specified as `true`, otherwise returns `false`.
384 isExpandable : function() {
387 if (me
.get('expandable')) {
388 return !(me
.isLeaf() || (me
.isLoaded() && !me
.hasChildNodes()));
394 * Insert node(s) as the last child node of this node.
396 * If the node was previously a child node of another parent node, it will be removed from that node first.
398 * @param {Ext.data.NodeInterface/Ext.data.NodeInterface[]} node The node or Array of nodes to append.
399 * @return {Ext.data.NodeInterface} The appended node if single append, or `null` if an array was passed.
401 appendChild : function(node
, suppressEvents
, suppressNodeUpdate
) {
408 // if passed an array or multiple args do them one by one
409 if (Ext
.isArray(node
)) {
410 for (i
= 0, ln
= node
.length
; i
< ln
; i
++) {
411 me
.appendChild(node
[i
], suppressEvents
, suppressNodeUpdate
);
414 // Make sure it is a record
415 node
= me
.createNode(node
);
417 if (suppressEvents
!== true && me
.fireEvent("beforeappend", me
, node
) === false) {
421 index
= me
.childNodes
.length
;
422 oldParent
= node
.parentNode
;
424 // it's a move, make sure we move it cleanly
426 if (suppressEvents
!== true && node
.fireEvent("beforemove", node
, oldParent
, me
, index
) === false) {
429 oldParent
.removeChild(node
, null, false, true);
432 index
= me
.childNodes
.length
;
434 me
.setFirstChild(node
);
437 me
.childNodes
.push(node
);
438 node
.parentNode
= me
;
439 node
.nextSibling
= null;
441 me
.setLastChild(node
);
443 ps
= me
.childNodes
[index
- 1];
445 node
.previousSibling
= ps
;
446 ps
.nextSibling
= node
;
447 ps
.updateInfo(suppressNodeUpdate
);
449 node
.previousSibling
= null;
452 node
.updateInfo(suppressNodeUpdate
);
454 // As soon as we append a child to this node, we are loaded
455 if (!me
.isLoaded()) {
456 me
.set('loaded', true);
458 // If this node didn't have any childnodes before, update myself
459 else if (me
.childNodes
.length
=== 1) {
460 me
.set('loaded', me
.isLoaded());
463 if (suppressEvents
!== true) {
464 me
.fireEvent("append", me
, node
, index
);
467 node
.fireEvent("move", node
, oldParent
, me
, index
);
476 * Returns the bubble target for this node.
478 * @return {Object} The bubble target.
480 getBubbleTarget: function() {
481 return this.parentNode
;
485 * Removes a child node from this node.
486 * @param {Ext.data.NodeInterface} node The node to remove.
487 * @param {Boolean} [destroy=false] `true` to destroy the node upon removal.
488 * @return {Ext.data.NodeInterface} The removed node.
490 removeChild : function(node
, destroy
, suppressEvents
, suppressNodeUpdate
) {
492 index
= me
.indexOf(node
);
494 if (index
== -1 || (suppressEvents
!== true && me
.fireEvent("beforeremove", me
, node
) === false)) {
498 // remove it from childNodes collection
499 Ext
.Array
.erase(me
.childNodes
, index
, 1);
502 if (me
.firstChild
== node
) {
503 me
.setFirstChild(node
.nextSibling
);
505 if (me
.lastChild
== node
) {
506 me
.setLastChild(node
.previousSibling
);
509 if (suppressEvents
!== true) {
510 me
.fireEvent("remove", me
, node
);
514 if (node
.previousSibling
) {
515 node
.previousSibling
.nextSibling
= node
.nextSibling
;
516 node
.previousSibling
.updateInfo(suppressNodeUpdate
);
518 if (node
.nextSibling
) {
519 node
.nextSibling
.previousSibling
= node
.previousSibling
;
520 node
.nextSibling
.updateInfo(suppressNodeUpdate
);
523 // If this node suddenly doesn't have childnodes anymore, update myself
524 if (!me
.childNodes
.length
) {
525 me
.set('loaded', me
.isLoaded());
538 * Creates a copy (clone) of this Node.
539 * @param {String} [newId] A new id, defaults to this Node's id.
540 * @param {Boolean} [deep] If passed as `true`, all child Nodes are recursively copied into the new Node.
541 * If omitted or `false`, the copy will have no child Nodes.
542 * @return {Ext.data.NodeInterface} A copy of this Node.
544 copy: function(newId
, deep
) {
546 result
= me
.callOverridden(arguments
),
547 len
= me
.childNodes
? me
.childNodes
.length
: 0,
550 // Move child nodes across to the copy if required
552 for (i
= 0; i
< len
; i
++) {
553 result
.appendChild(me
.childNodes
[i
].copy(true));
562 * @param {Boolean} destroy `true` to destroy the node.
564 clear : function(destroy
) {
567 // clear any references from the node
568 me
.parentNode
= me
.previousSibling
= me
.nextSibling
= null;
570 me
.firstChild
= me
.lastChild
= null;
577 destroy : function(silent
) {
579 * Silent is to be used in a number of cases
580 * 1) When setRoot is called.
581 * 2) When destroy on the tree is called
582 * 3) For destroying child nodes on a node
585 options
= me
.destroyOptions
;
587 if (silent
=== true) {
589 Ext
.each(me
.childNodes
, function(n
) {
592 me
.childNodes
= null;
593 delete me
.destroyOptions
;
594 me
.callOverridden([options
]);
596 me
.destroyOptions
= silent
;
597 // overridden method will be called, since remove will end up calling destroy(true);
603 * Inserts the first node before the second node in this nodes `childNodes` collection.
604 * @param {Ext.data.NodeInterface} node The node to insert.
605 * @param {Ext.data.NodeInterface} refNode The node to insert before (if `null` the node is appended).
606 * @return {Ext.data.NodeInterface} The inserted node.
608 insertBefore : function(node
, refNode
, suppressEvents
) {
610 index
= me
.indexOf(refNode
),
611 oldParent
= node
.parentNode
,
615 if (!refNode
) { // like standard Dom, refNode can be null for append
616 return me
.appendChild(node
);
620 if (node
== refNode
) {
624 // Make sure it is a record with the NodeInterface
625 node
= me
.createNode(node
);
627 if (suppressEvents
!== true && me
.fireEvent("beforeinsert", me
, node
, refNode
) === false) {
631 // when moving internally, indexes will change after remove
632 if (oldParent
== me
&& me
.indexOf(node
) < index
) {
636 // it's a move, make sure we move it cleanly
638 if (suppressEvents
!== true && node
.fireEvent("beforemove", node
, oldParent
, me
, index
, refNode
) === false) {
641 oldParent
.removeChild(node
);
644 if (refIndex
=== 0) {
645 me
.setFirstChild(node
);
648 Ext
.Array
.splice(me
.childNodes
, refIndex
, 0, node
);
649 node
.parentNode
= me
;
651 node
.nextSibling
= refNode
;
652 refNode
.previousSibling
= node
;
654 ps
= me
.childNodes
[refIndex
- 1];
656 node
.previousSibling
= ps
;
657 ps
.nextSibling
= node
;
660 node
.previousSibling
= null;
665 if (!me
.isLoaded()) {
666 me
.set('loaded', true);
668 // If this node didn't have any childnodes before, update myself
669 else if (me
.childNodes
.length
=== 1) {
670 me
.set('loaded', me
.isLoaded());
673 if (suppressEvents
!== true) {
674 me
.fireEvent("insert", me
, node
, refNode
);
677 node
.fireEvent("move", node
, oldParent
, me
, refIndex
, refNode
);
685 * Insert a node into this node.
686 * @param {Number} index The zero-based index to insert the node at.
687 * @param {Ext.data.Model} node The node to insert.
688 * @return {Ext.data.Model} The record you just inserted.
690 insertChild: function(index
, node
) {
691 var sibling
= this.childNodes
[index
];
693 return this.insertBefore(node
, sibling
);
696 return this.appendChild(node
);
701 * Removes this node from its parent.
702 * @param {Boolean} [destroy=false] `true` to destroy the node upon removal.
703 * @return {Ext.data.NodeInterface} this
705 remove : function(destroy
, suppressEvents
) {
706 var parentNode
= this.parentNode
;
709 parentNode
.removeChild(this, destroy
, suppressEvents
, true);
715 * Removes all child nodes from this node.
716 * @param {Boolean} [destroy=false] `true` to destroy the node upon removal.
717 * @return {Ext.data.NodeInterface} this
719 removeAll : function(destroy
, suppressEvents
) {
720 var cn
= this.childNodes
,
723 while ((n
= cn
[0])) {
724 this.removeChild(n
, destroy
, suppressEvents
);
730 * Returns the child node at the specified index.
731 * @param {Number} index
732 * @return {Ext.data.NodeInterface}
734 getChildAt : function(index
) {
735 return this.childNodes
[index
];
739 * Replaces one child node in this node with another.
740 * @param {Ext.data.NodeInterface} newChild The replacement node.
741 * @param {Ext.data.NodeInterface} oldChild The node to replace.
742 * @return {Ext.data.NodeInterface} The replaced node.
744 replaceChild : function(newChild
, oldChild
, suppressEvents
) {
745 var s
= oldChild
? oldChild
.nextSibling
: null;
747 this.removeChild(oldChild
, suppressEvents
);
748 this.insertBefore(newChild
, s
, suppressEvents
);
753 * Returns the index of a child node.
754 * @param {Ext.data.NodeInterface} child
755 * @return {Number} The index of the node or -1 if it was not found.
757 indexOf : function(child
) {
758 return Ext
.Array
.indexOf(this.childNodes
, child
);
762 * Gets the hierarchical path from the root of the current node.
763 * @param {String} field (optional) The field to construct the path from. Defaults to the model `idProperty`.
764 * @param {String} [separator=/] (optional) A separator to use.
765 * @return {String} The node path
767 getPath: function(field
, separator
) {
768 field
= field
|| this.idProperty
;
769 separator
= separator
|| '/';
771 var path
= [this.get(field
)],
772 parent
= this.parentNode
;
775 path
.unshift(parent
.get(field
));
776 parent
= parent
.parentNode
;
778 return separator
+ path
.join(separator
);
782 * Returns depth of this node (the root node has a depth of 0).
785 getDepth : function() {
786 return this.get('depth');
790 * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
791 * will be the args provided or the current node. If the function returns `false` at any point,
792 * the bubble is stopped.
793 * @param {Function} fn The function to call.
794 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to the current Node.
795 * @param {Array} args (optional) The args to call the function with (default to passing the current Node).
797 bubble : function(fn
, scope
, args
) {
800 if (fn
.apply(scope
|| p
, args
|| [p
]) === false) {
807 //<deprecated since=0.99>
808 cascade: function() {
809 Ext
.Logger
.deprecate('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
811 return this.cascadeBy
.apply(this, arguments
);
816 * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
817 * will be the args provided or the current node. If the function returns false at any point,
818 * the cascade is stopped on that branch.
819 * @param {Function} fn The function to call
820 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to the current Node.
821 * @param {Array} args (optional) The args to call the function with (default to passing the current Node).
823 cascadeBy : function(fn
, scope
, args
) {
824 if (fn
.apply(scope
|| this, args
|| [this]) !== false) {
825 var childNodes
= this.childNodes
,
826 length
= childNodes
.length
,
829 for (i
= 0; i
< length
; i
++) {
830 childNodes
[i
].cascadeBy(fn
, scope
, args
);
836 * Iterates the child nodes of this node, calling the specified function with each node. The arguments to the function
837 * will be the args provided or the current node. If the function returns false at any point,
838 * the iteration stops.
839 * @param {Function} fn The function to call.
840 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to the current Node in the iteration.
841 * @param {Array} args (optional) The args to call the function with (default to passing the current Node).
843 eachChild : function(fn
, scope
, args
) {
844 var childNodes
= this.childNodes
,
845 length
= childNodes
.length
,
848 for (i
= 0; i
< length
; i
++) {
849 if (fn
.apply(scope
|| this, args
|| [childNodes
[i
]]) === false) {
856 * Finds the first child that has the attribute with the specified value.
857 * @param {String} attribute The attribute name.
858 * @param {Object} value The value to search for.
859 * @param {Boolean} deep (Optional) `true` to search through nodes deeper than the immediate children.
860 * @return {Ext.data.NodeInterface} The found child or `null` if none was found.
862 findChild : function(attribute
, value
, deep
) {
863 return this.findChildBy(function() {
864 return this.get(attribute
) == value
;
869 * Finds the first child by a custom function. The child matches if the function passed returns `true`.
870 * @param {Function} fn A function which must return `true` if the passed Node is the required Node.
871 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to the Node being tested.
872 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children.
873 * @return {Ext.data.NodeInterface} The found child or null if `none` was found.
875 findChildBy : function(fn
, scope
, deep
) {
876 var cs
= this.childNodes
,
880 for (; i
< len
; i
++) {
882 if (fn
.call(scope
|| n
, n
) === true) {
886 res
= n
.findChildBy(fn
, scope
, deep
);
897 * Returns `true` if this node is an ancestor (at any point) of the passed node.
898 * @param {Ext.data.NodeInterface} node
901 contains : function(node
) {
902 return node
.isAncestor(this);
906 * Returns `true` if the passed node is an ancestor (at any point) of this node.
907 * @param {Ext.data.NodeInterface} node
910 isAncestor : function(node
) {
911 var p
= this.parentNode
;
922 * Sorts this nodes children using the supplied sort function.
923 * @param {Function} sortFn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
924 * @param {Boolean} recursive Whether or not to apply this sort recursively.
925 * @param {Boolean} suppressEvent Set to true to not fire a sort event.
927 sort: function(sortFn
, recursive
, suppressEvent
) {
928 var cs
= this.childNodes
,
933 Ext
.Array
.sort(cs
, sortFn
);
934 for (i
= 0; i
< ln
; i
++) {
936 n
.previousSibling
= cs
[i
-1];
937 n
.nextSibling
= cs
[i
+1];
940 this.setFirstChild(n
);
943 this.setLastChild(n
);
946 n
.updateInfo(suppressEvent
);
948 if (recursive
&& !n
.isLeaf()) {
949 n
.sort(sortFn
, true, true);
953 this.notifyStores('afterEdit', ['sorted'], {sorted
: 'sorted'});
955 if (suppressEvent
!== true) {
956 this.fireEvent('sort', this, cs
);
962 * Returns `true` if this node is expanded.
965 isExpanded: function() {
966 return this.get('expanded');
970 * Returns `true` if this node is loaded.
973 isLoaded: function() {
974 return this.get('loaded');
978 * Returns `true` if this node is loading.
981 isLoading: function() {
982 return this.get('loading');
986 * Returns `true` if this node is the root node.
990 return !this.parentNode
;
994 * Returns `true` if this node is visible.
997 isVisible: function() {
998 var parent
= this.parentNode
;
1000 if (!parent
.isExpanded()) {
1003 parent
= parent
.parentNode
;
1010 * @param {Function} recursive (Optional) `true` to recursively expand all the children.
1011 * @param {Function} callback (Optional) The function to execute once the expand completes.
1012 * @param {Object} scope (Optional) The scope to run the callback in.
1014 expand: function(recursive
, callback
, scope
) {
1018 if (me
.isLoading()) {
1019 me
.on('expand', function() {
1020 me
.expand(recursive
, callback
, scope
);
1021 }, me
, {single
: true});
1024 if (!me
.isExpanded()) {
1025 // The TreeStore actually listens for the beforeexpand method and checks
1026 // whether we have to asynchronously load the children from the server
1027 // first. Thats why we pass a callback function to the event that the
1028 // store can call once it has loaded and parsed all the children.
1029 me
.fireAction('expand', [this], function() {
1030 me
.set('expanded', true);
1031 Ext
.callback(callback
, scope
|| me
, [me
.childNodes
]);
1035 Ext
.callback(callback
, scope
|| me
, [me
.childNodes
]);
1039 Ext
.callback(callback
, scope
|| me
);
1044 * Collapse this node.
1045 * @param {Function} recursive (Optional) `true` to recursively collapse all the children.
1046 * @param {Function} callback (Optional) The function to execute once the collapse completes.
1047 * @param {Object} scope (Optional) The scope to run the callback in.
1049 collapse: function(recursive
, callback
, scope
) {
1052 // First we start by checking if this node is a parent
1053 if (!me
.isLeaf() && me
.isExpanded()) {
1054 this.fireAction('collapse', [me
], function() {
1055 me
.set('expanded', false);
1056 Ext
.callback(callback
, scope
|| me
, [me
.childNodes
]);
1059 Ext
.callback(callback
, scope
|| me
, [me
.childNodes
]);