X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..f562e4c6e5fac7bcb445985b99acbea4d706e6f0:/src/data/NodeInterface.js diff --git a/src/data/NodeInterface.js b/src/data/NodeInterface.js index 621c03ca..e3233dfe 100644 --- a/src/data/NodeInterface.js +++ b/src/data/NodeInterface.js @@ -13,21 +13,170 @@ If you are unsure which license is appropriate for your use, please contact the */ /** - * @class Ext.data.NodeInterface - * This class is meant to be used as a set of methods that are applied to the prototype of a - * Record to decorate it with a Node API. This means that models used in conjunction with a tree + * This class is used as a set of methods that are applied to the prototype of a + * Model to decorate it with a Node API. This means that models used in conjunction with a tree * will have all of the tree related methods available on the model. In general this class will - * not be used directly by the developer. + * not be used directly by the developer. This class also creates extra fields on the model if + * they do not exist, to help maintain the tree state and UI. These fields are documented as + * config options. */ Ext.define('Ext.data.NodeInterface', { requires: ['Ext.data.Field'], - + + /** + * @cfg {String} parentId + * ID of parent node. + */ + + /** + * @cfg {Number} index + * The position of the node inside its parent. When parent has 4 children and the node is third amongst them, + * index will be 2. + */ + + /** + * @cfg {Number} depth + * The number of parents this node has. A root node has depth 0, a child of it depth 1, and so on... + */ + + /** + * @cfg {Boolean} [expanded=false] + * True if the node is expanded. + */ + + /** + * @cfg {Boolean} [expandable=false] + * Set to true to allow for expanding/collapsing of this node. + */ + + /** + * @cfg {Boolean} [checked=null] + * Set to true or false to show a checkbox alongside this node. + */ + + /** + * @cfg {Boolean} [leaf=false] + * Set to true to indicate that this child can have no children. The expand icon/arrow will then not be + * rendered for this node. + */ + + /** + * @cfg {String} cls + * CSS class to apply for this node. + */ + + /** + * @cfg {String} iconCls + * CSS class to apply for this node's icon. + */ + + /** + * @cfg {String} icon + * URL for this node's icon. + */ + + /** + * @cfg {Boolean} root + * True if this is the root node. + */ + + /** + * @cfg {Boolean} isLast + * True if this is the last node. + */ + + /** + * @cfg {Boolean} isFirst + * True if this is the first node. + */ + + /** + * @cfg {Boolean} [allowDrop=true] + * Set to false to deny dropping on this node. + */ + + /** + * @cfg {Boolean} [allowDrag=true] + * Set to false to deny dragging of this node. + */ + + /** + * @cfg {Boolean} [loaded=false] + * True if the node has finished loading. + */ + + /** + * @cfg {Boolean} [loading=false] + * True if the node is currently loading. + */ + + /** + * @cfg {String} href + * An URL for a link that's created when this config is specified. + */ + + /** + * @cfg {String} hrefTarget + * Target for link. Only applicable when {@link #href} also specified. + */ + + /** + * @cfg {String} qtip + * Tooltip text to show on this node. + */ + + /** + * @cfg {String} qtitle + * Tooltip title. + */ + + /** + * @cfg {String} text + * The text for to show on node label. + */ + + /** + * @cfg {Ext.data.NodeInterface[]} children + * Array of child nodes. + */ + + + /** + * @property nextSibling + * A reference to this node's next sibling node. `null` if this node does not have a next sibling. + */ + + /** + * @property previousSibling + * A reference to this node's previous sibling node. `null` if this node does not have a previous sibling. + */ + + /** + * @property parentNode + * A reference to this node's parent node. `null` if this node is the root node. + */ + + /** + * @property lastChild + * A reference to this node's last child node. `null` if this node has no children. + */ + + /** + * @property firstChild + * A reference to this node's first child node. `null` if this node has no children. + */ + + /** + * @property childNodes + * An array of this nodes children. Array will be empty if this node has no chidren. + */ + statics: { /** * This method allows you to decorate a Record's prototype to implement the NodeInterface. * This adds a set of methods, new events, new properties and new fields on every Record * with the same Model as the passed Record. - * @param {Ext.data.Record} record The Record you want to decorate the prototype of. + * @param {Ext.data.Model} record The Record you want to decorate the prototype of. * @static */ decorate: function(record) { @@ -47,13 +196,14 @@ Ext.define('Ext.data.NodeInterface', { {name: idName, type: 'string', defaultValue: null}, {name: 'parentId', type: 'string', defaultValue: null}, {name: 'index', type: 'int', defaultValue: null}, - {name: 'depth', type: 'int', defaultValue: 0}, + {name: 'depth', type: 'int', defaultValue: 0}, {name: 'expanded', type: 'bool', defaultValue: false, persist: false}, {name: 'expandable', type: 'bool', defaultValue: true, persist: false}, {name: 'checked', type: 'auto', defaultValue: null}, {name: 'leaf', type: 'bool', defaultValue: false, persist: false}, {name: 'cls', type: 'string', defaultValue: null, persist: false}, {name: 'iconCls', type: 'string', defaultValue: null, persist: false}, + {name: 'icon', type: 'string', defaultValue: null, persist: false}, {name: 'root', type: 'boolean', defaultValue: false, persist: false}, {name: 'isLast', type: 'boolean', defaultValue: false, persist: false}, {name: 'isFirst', type: 'boolean', defaultValue: false, persist: false}, @@ -76,7 +226,7 @@ Ext.define('Ext.data.NodeInterface', { } } } - + Ext.applyIf(record, { firstChild: null, lastChild: null, @@ -87,13 +237,13 @@ Ext.define('Ext.data.NodeInterface', { }); // Commit any fields so the record doesn't show as dirty initially record.commit(true); - + record.enableBubble([ /** * @event append * Fires when a new child node is appended - * @param {Node} this This node - * @param {Node} node The newly appended node + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} node The newly appended node * @param {Number} index The index of the newly appended node */ "append", @@ -101,17 +251,17 @@ Ext.define('Ext.data.NodeInterface', { /** * @event remove * Fires when a child node is removed - * @param {Node} this This node - * @param {Node} node The removed node + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} node The removed node */ "remove", /** * @event move * Fires when this node is moved to a new location in the tree - * @param {Node} this This node - * @param {Node} oldParent The old parent of this node - * @param {Node} newParent The new parent of this node + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} oldParent The old parent of this node + * @param {Ext.data.NodeInterface} newParent The new parent of this node * @param {Number} index The index it was moved to */ "move", @@ -119,34 +269,34 @@ Ext.define('Ext.data.NodeInterface', { /** * @event insert * Fires when a new child node is inserted. - * @param {Node} this This node - * @param {Node} node The child node inserted - * @param {Node} refNode The child node the node was inserted before + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} node The child node inserted + * @param {Ext.data.NodeInterface} refNode The child node the node was inserted before */ "insert", /** * @event beforeappend * Fires before a new child is appended, return false to cancel the append. - * @param {Node} this This node - * @param {Node} node The child node to be appended + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} node The child node to be appended */ "beforeappend", /** * @event beforeremove * Fires before a child is removed, return false to cancel the remove. - * @param {Node} this This node - * @param {Node} node The child node to be removed + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} node The child node to be removed */ "beforeremove", /** * @event beforemove * Fires before this node is moved to a new location in the tree. Return false to cancel the move. - * @param {Node} this This node - * @param {Node} oldParent The parent of this node - * @param {Node} newParent The new parent this node is moving to + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} oldParent The parent of this node + * @param {Ext.data.NodeInterface} newParent The new parent this node is moving to * @param {Number} index The index it is being moved to */ "beforemove", @@ -154,52 +304,52 @@ Ext.define('Ext.data.NodeInterface', { /** * @event beforeinsert * Fires before a new child is inserted, return false to cancel the insert. - * @param {Node} this This node - * @param {Node} node The child node to be inserted - * @param {Node} refNode The child node the node is being inserted before + * @param {Ext.data.NodeInterface} this This node + * @param {Ext.data.NodeInterface} node The child node to be inserted + * @param {Ext.data.NodeInterface} refNode The child node the node is being inserted before */ "beforeinsert", - + /** * @event expand * Fires when this node is expanded. - * @param {Node} this The expanding node + * @param {Ext.data.NodeInterface} this The expanding node */ "expand", - + /** * @event collapse * Fires when this node is collapsed. - * @param {Node} this The collapsing node + * @param {Ext.data.NodeInterface} this The collapsing node */ "collapse", - + /** * @event beforeexpand * Fires before this node is expanded. - * @param {Node} this The expanding node + * @param {Ext.data.NodeInterface} this The expanding node */ "beforeexpand", - + /** * @event beforecollapse * Fires before this node is collapsed. - * @param {Node} this The collapsing node + * @param {Ext.data.NodeInterface} this The collapsing node */ "beforecollapse", - + /** * @event sort * Fires when this node's childNodes are sorted. - * @param {Node} this This node. - * @param {Array} The childNodes of this node. + * @param {Ext.data.NodeInterface} this This node. + * @param {Ext.data.NodeInterface[]} childNodes The childNodes of this node. */ "sort" ]); - + return record; }, - + applyFields: function(modelClass, addFields) { var modelPrototype = modelClass.prototype, fields = modelPrototype.fields, @@ -207,20 +357,20 @@ Ext.define('Ext.data.NodeInterface', { ln = addFields.length, addField, i, name, newFields = []; - + for (i = 0; i < ln; i++) { addField = addFields[i]; if (!Ext.Array.contains(keys, addField.name)) { addField = Ext.create('data.field', addField); - + newFields.push(addField); fields.add(addField); } } - + return newFields; }, - + getPrototypeBody: function() { return { isNode: true, @@ -236,7 +386,7 @@ Ext.define('Ext.data.NodeInterface', { // Make sure the node implements the node interface return Ext.data.NodeInterface.decorate(node); }, - + /** * Returns true if this node is a leaf * @return {Boolean} @@ -285,8 +435,8 @@ Ext.define('Ext.data.NodeInterface', { while (parent.parentNode) { ++depth; parent = parent.parentNode; - } - + } + me.beginEdit(); me.set({ isFirst: isFirst, @@ -299,7 +449,7 @@ Ext.define('Ext.data.NodeInterface', { if (silent) { me.commit(); } - + for (i = 0; i < len; i++) { children[i].updateInfo(silent); } @@ -331,12 +481,12 @@ Ext.define('Ext.data.NodeInterface', { /** * Returns true if this node has one or more child nodes, or if the expandable - * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false. + * node attribute is explicitly specified as true, otherwise returns false. * @return {Boolean} */ isExpandable : function() { var me = this; - + if (me.get('expandable')) { return !(me.isLeaf() || (me.isLoaded() && !me.hasChildNodes())); } @@ -344,10 +494,12 @@ Ext.define('Ext.data.NodeInterface', { }, /** - *

Insert node(s) as the last child node of this node.

- *

If the node was previously a child node of another parent node, it will be removed from that node first.

- * @param {Node/Array} node The node or Array of nodes to append - * @return {Node} The appended node if single append, or null if an array was passed + * Inserts node(s) as the last child node of this node. + * + * If the node was previously a child node of another parent node, it will be removed from that node first. + * + * @param {Ext.data.NodeInterface/Ext.data.NodeInterface[]} node The node or Array of nodes to append + * @return {Ext.data.NodeInterface} The appended node if single append, or null if an array was passed */ appendChild : function(node, suppressEvents, suppressNodeUpdate) { var me = this, @@ -364,9 +516,9 @@ Ext.define('Ext.data.NodeInterface', { } else { // Make sure it is a record node = me.createNode(node); - + if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) { - return false; + return false; } index = me.childNodes.length; @@ -390,7 +542,7 @@ Ext.define('Ext.data.NodeInterface', { node.nextSibling = null; me.setLastChild(node); - + ps = me.childNodes[index - 1]; if (ps) { node.previousSibling = ps; @@ -401,28 +553,28 @@ Ext.define('Ext.data.NodeInterface', { } node.updateInfo(suppressNodeUpdate); - + // As soon as we append a child to this node, we are loaded if (!me.isLoaded()) { - me.set('loaded', true); + me.set('loaded', true); } // If this node didnt have any childnodes before, update myself else if (me.childNodes.length === 1) { me.set('loaded', me.isLoaded()); } - + if (suppressEvents !== true) { me.fireEvent("append", me, node, index); if (oldParent) { node.fireEvent("move", node, oldParent, me, index); - } + } } return node; } }, - + /** * Returns the bubble target for this node * @private @@ -434,14 +586,14 @@ Ext.define('Ext.data.NodeInterface', { /** * Removes a child node from this node. - * @param {Node} node The node to remove - * @param {Boolean} destroy true to destroy the node upon removal. Defaults to false. - * @return {Node} The removed node + * @param {Ext.data.NodeInterface} node The node to remove + * @param {Boolean} [destroy=false] True to destroy the node upon removal. + * @return {Ext.data.NodeInterface} The removed node */ removeChild : function(node, destroy, suppressEvents, suppressNodeUpdate) { var me = this, index = me.indexOf(node); - + if (index == -1 || (suppressEvents !== true && me.fireEvent("beforeremove", me, node) === false)) { return false; } @@ -456,7 +608,7 @@ Ext.define('Ext.data.NodeInterface', { if (me.lastChild == node) { me.setLastChild(node.previousSibling); } - + // update siblings if (node.previousSibling) { node.previousSibling.nextSibling = node.nextSibling; @@ -470,13 +622,13 @@ Ext.define('Ext.data.NodeInterface', { if (suppressEvents !== true) { me.fireEvent("remove", me, node); } - - + + // If this node suddenly doesnt have childnodes anymore, update myself if (!me.childNodes.length) { me.set('loaded', me.isLoaded()); } - + if (destroy) { node.destroy(true); } else { @@ -488,10 +640,10 @@ Ext.define('Ext.data.NodeInterface', { /** * Creates a copy (clone) of this Node. - * @param {String} id (optional) A new id, defaults to this Node's id. See {@link #id}. - * @param {Boolean} deep (optional)

If passed as true, all child Nodes are recursively copied into the new Node.

- *

If omitted or false, the copy will have no child Nodes.

- * @return {Node} A copy of this Node. + * @param {String} [id] A new id, defaults to this Node's id. + * @param {Boolean} [deep=false] True to recursively copy all child Nodes into the new Node. + * False to copy without child Nodes. + * @return {Ext.data.NodeInterface} A copy of this Node. */ copy: function(newId, deep) { var me = this, @@ -509,13 +661,13 @@ Ext.define('Ext.data.NodeInterface', { }, /** - * Clear the node. + * Clears the node. * @private - * @param {Boolean} destroy True to destroy the node. + * @param {Boolean} [destroy=false] True to destroy the node. */ clear : function(destroy) { var me = this; - + // clear any references from the node me.parentNode = me.previousSibling = me.nextSibling = null; if (destroy) { @@ -535,7 +687,7 @@ Ext.define('Ext.data.NodeInterface', { */ var me = this, options = me.destroyOptions; - + if (silent === true) { me.clear(true); Ext.each(me.childNodes, function(n) { @@ -553,9 +705,9 @@ Ext.define('Ext.data.NodeInterface', { /** * Inserts the first node before the second node in this nodes childNodes collection. - * @param {Node} node The node to insert - * @param {Node} refNode The node to insert before (if null the node is appended) - * @return {Node} The inserted node + * @param {Ext.data.NodeInterface} node The node to insert + * @param {Ext.data.NodeInterface} refNode The node to insert before (if null the node is appended) + * @return {Ext.data.NodeInterface} The inserted node */ insertBefore : function(node, refNode, suppressEvents) { var me = this, @@ -563,11 +715,11 @@ Ext.define('Ext.data.NodeInterface', { oldParent = node.parentNode, refIndex = index, ps; - + if (!refNode) { // like standard Dom, refNode can be null for append return me.appendChild(node); } - + // nothing to do if (node == refNode) { return false; @@ -575,11 +727,11 @@ Ext.define('Ext.data.NodeInterface', { // Make sure it is a record with the NodeInterface node = me.createNode(node); - + if (suppressEvents !== true && me.fireEvent("beforeinsert", me, node, refNode) === false) { return false; } - + // when moving internally, indexes will change after remove if (oldParent == me && me.indexOf(node) < index) { refIndex--; @@ -599,10 +751,10 @@ Ext.define('Ext.data.NodeInterface', { Ext.Array.splice(me.childNodes, refIndex, 0, node); node.parentNode = me; - + node.nextSibling = refNode; refNode.previousSibling = node; - + ps = me.childNodes[refIndex - 1]; if (ps) { node.previousSibling = ps; @@ -611,12 +763,12 @@ Ext.define('Ext.data.NodeInterface', { } else { node.previousSibling = null; } - + node.updateInfo(); - + if (!me.isLoaded()) { - me.set('loaded', true); - } + me.set('loaded', true); + } // If this node didnt have any childnodes before, update myself else if (me.childNodes.length === 1) { me.set('loaded', me.isLoaded()); @@ -627,18 +779,18 @@ Ext.define('Ext.data.NodeInterface', { if (oldParent) { node.fireEvent("move", node, oldParent, me, refIndex, refNode); - } + } } return node; }, - + /** * Insert a node into this node * @param {Number} index The zero-based index to insert the node at * @param {Ext.data.Model} node The node to insert - * @return {Ext.data.Record} The record you just inserted - */ + * @return {Ext.data.Model} The record you just inserted + */ insertChild: function(index, node) { var sibling = this.childNodes[index]; if (sibling) { @@ -651,8 +803,8 @@ Ext.define('Ext.data.NodeInterface', { /** * Removes this node from its parent - * @param {Boolean} destroy true to destroy the node upon removal. Defaults to false. - * @return {Node} this + * @param {Boolean} [destroy=false] True to destroy the node upon removal. + * @return {Ext.data.NodeInterface} this */ remove : function(destroy, suppressEvents) { var parentNode = this.parentNode; @@ -665,8 +817,8 @@ Ext.define('Ext.data.NodeInterface', { /** * Removes all child nodes from this node. - * @param {Boolean} destroy true to destroy the node upon removal. Defaults to false. - * @return {Node} this + * @param {Boolean} [destroy=false] this reference) in which the function is executed. Defaults to the current Node. - * @param {Array} args (optional) The args to call the function with (default to passing the current Node) + * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the current Node. + * @param {Array} [args] The args to call the function with. Defaults to passing the current Node. */ bubble : function(fn, scope, args) { var p = this; @@ -750,8 +922,8 @@ Ext.define('Ext.data.NodeInterface', { * will be the args provided or the current node. If the function returns false at any point, * the cascade is stopped on that branch. * @param {Function} fn The function to call - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the current Node. - * @param {Array} args (optional) The args to call the function with (default to passing the current Node) + * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the current Node. + * @param {Array} [args] The args to call the function with. Defaults to passing the current Node. */ cascadeBy : function(fn, scope, args) { if (fn.apply(scope || this, args || [this]) !== false) { @@ -770,8 +942,8 @@ Ext.define('Ext.data.NodeInterface', { * will be the args provided or the current node. If the function returns false at any point, * the iteration stops. * @param {Function} fn The function to call - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the current Node in the iteration. - * @param {Array} args (optional) The args to call the function with (default to passing the current Node) + * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the current Node in iteration. + * @param {Array} [args] The args to call the function with. Defaults to passing the current Node. */ eachChild : function(fn, scope, args) { var childNodes = this.childNodes, @@ -788,9 +960,9 @@ Ext.define('Ext.data.NodeInterface', { /** * Finds the first child that has the attribute with the specified value. * @param {String} attribute The attribute name - * @param {Mixed} value The value to search for - * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children - * @return {Node} The found child or null if none was found + * @param {Object} value The value to search for + * @param {Boolean} [deep=false] True to search through nodes deeper than the immediate children + * @return {Ext.data.NodeInterface} The found child or null if none was found */ findChild : function(attribute, value, deep) { return this.findChildBy(function() { @@ -799,11 +971,11 @@ Ext.define('Ext.data.NodeInterface', { }, /** - * Finds the first child by a custom function. The child matches if the function passed returns true. - * @param {Function} fn A function which must return true if the passed Node is the required Node. - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the Node being tested. - * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children - * @return {Node} The found child or null if none was found + * Finds the first child by a custom function. The child matches if the function passed returns true. + * @param {Function} fn A function which must return true if the passed Node is the required Node. + * @param {Object} [scope] The scope (this reference) in which the function is executed. Defaults to the Node being tested. + * @param {Boolean} [deep=false] True to search through nodes deeper than the immediate children + * @return {Ext.data.NodeInterface} The found child or null if none was found */ findChildBy : function(fn, scope, deep) { var cs = this.childNodes, @@ -828,7 +1000,7 @@ Ext.define('Ext.data.NodeInterface', { /** * Returns true if this node is an ancestor (at any point) of the passed node. - * @param {Node} node + * @param {Ext.data.NodeInterface} node * @return {Boolean} */ contains : function(node) { @@ -837,7 +1009,7 @@ Ext.define('Ext.data.NodeInterface', { /** * Returns true if the passed node is an ancestor (at any point) of this node. - * @param {Node} node + * @param {Ext.data.NodeInterface} node * @return {Boolean} */ isAncestor : function(node) { @@ -854,21 +1026,21 @@ Ext.define('Ext.data.NodeInterface', { /** * Sorts this nodes children using the supplied sort function. * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order. - * @param {Boolean} recursive Whether or not to apply this sort recursively - * @param {Boolean} suppressEvent Set to true to not fire a sort event. + * @param {Boolean} [recursive=false] True to apply this sort recursively + * @param {Boolean} [suppressEvent=false] True to not fire a sort event. */ sort : function(sortFn, recursive, suppressEvent) { var cs = this.childNodes, ln = cs.length, i, n; - + if (ln > 0) { Ext.Array.sort(cs, sortFn); for (i = 0; i < ln; i++) { n = cs[i]; n.previousSibling = cs[i-1]; n.nextSibling = cs[i+1]; - + if (i === 0) { this.setFirstChild(n); n.updateInfo(); @@ -881,25 +1053,25 @@ Ext.define('Ext.data.NodeInterface', { n.sort(sortFn, true, true); } } - + if (suppressEvent !== true) { this.fireEvent('sort', this, cs); } } }, - + /** * Returns true if this node is expaned * @return {Boolean} - */ + */ isExpanded: function() { return this.get('expanded'); }, - + /** * Returns true if this node is loaded * @return {Boolean} - */ + */ isLoaded: function() { return this.get('loaded'); }, @@ -907,23 +1079,23 @@ Ext.define('Ext.data.NodeInterface', { /** * Returns true if this node is loading * @return {Boolean} - */ + */ isLoading: function() { return this.get('loading'); }, - + /** * Returns true if this node is the root node * @return {Boolean} - */ + */ isRoot: function() { return !this.parentNode; }, - + /** * Returns true if this node is visible * @return {Boolean} - */ + */ isVisible: function() { var parent = this.parentNode; while (parent) { @@ -934,12 +1106,12 @@ Ext.define('Ext.data.NodeInterface', { } return true; }, - + /** * Expand this node. - * @param {Function} recursive (Optional) True to recursively expand all the children - * @param {Function} callback (Optional) The function to execute once the expand completes - * @param {Object} scope (Optional) The scope to run the callback in + * @param {Boolean} [recursive=false] True to recursively expand all the children + * @param {Function} [callback] The function to execute once the expand completes + * @param {Object} [scope] The scope to run the callback in */ expand: function(recursive, callback, scope) { var me = this; @@ -949,48 +1121,47 @@ Ext.define('Ext.data.NodeInterface', { // First we start by checking if this node is a parent if (!me.isLeaf()) { - // Now we check if this record is already expanding or expanded - if (!me.isLoading() && !me.isExpanded()) { - // The TreeStore actually listens for the beforeexpand method and checks - // whether we have to asynchronously load the children from the server - // first. Thats why we pass a callback function to the event that the - // store can call once it has loaded and parsed all the children. - me.fireEvent('beforeexpand', me, function() { - me.set('expanded', true); - me.fireEvent('expand', me, me.childNodes, false); - - // Call the expandChildren method if recursive was set to true - if (recursive) { - me.expandChildren(true, callback, scope); - } - else { - Ext.callback(callback, scope || me, [me.childNodes]); - } - }, me); - } - // If it is is already expanded but we want to recursively expand then call expandChildren - else if (recursive) { - me.expandChildren(true, callback, scope); - } - else { - Ext.callback(callback, scope || me, [me.childNodes]); + // If it's loaded, wait until it loads before proceeding + if (me.isLoading()) { + me.on('expand', function(){ + me.expand(recursive, callback, scope); + }, me, {single: true}); + } else { + // Now we check if this record is already expanding or expanded + if (!me.isExpanded()) { + // The TreeStore actually listens for the beforeexpand method and checks + // whether we have to asynchronously load the children from the server + // first. Thats why we pass a callback function to the event that the + // store can call once it has loaded and parsed all the children. + me.fireEvent('beforeexpand', me, function(){ + me.set('expanded', true); + me.fireEvent('expand', me, me.childNodes, false); + + // Call the expandChildren method if recursive was set to true + if (recursive) { + me.expandChildren(true, callback, scope); + } else { + Ext.callback(callback, scope || me, [me.childNodes]); + } + }, me); + } else if (recursive) { + // If it is is already expanded but we want to recursively expand then call expandChildren + me.expandChildren(true, callback, scope); + } else { + Ext.callback(callback, scope || me, [me.childNodes]); + } } - - // TODO - if the node isLoading, we probably need to defer the - // callback until it is loaded (e.g., selectPath would need us - // to not make the callback until the childNodes exist). - } - // If it's not then we fire the callback right away - else { + } else { + // If it's not then we fire the callback right away Ext.callback(callback, scope || me); // leaf = no childNodes } }, - + /** * Expand all the children of this node. - * @param {Function} recursive (Optional) True to recursively expand all the children - * @param {Function} callback (Optional) The function to execute once all the children are expanded - * @param {Object} scope (Optional) The scope to run the callback in + * @param {Boolean} [recursive=false] True to recursively expand all the children + * @param {Function} [callback] The function to execute once all the children are expanded + * @param {Object} [scope] The scope to run the callback in */ expandChildren: function(recursive, callback, scope) { var me = this, @@ -1007,21 +1178,21 @@ Ext.define('Ext.data.NodeInterface', { nodes[i].expand(recursive, function () { expanding--; if (callback && !expanding) { - Ext.callback(callback, scope || me, [me.childNodes]); + Ext.callback(callback, scope || me, [me.childNodes]); } - }); + }); } } - + if (!expanding && callback) { Ext.callback(callback, scope || me, [me.childNodes]); } }, /** * Collapse this node. - * @param {Function} recursive (Optional) True to recursively collapse all the children - * @param {Function} callback (Optional) The function to execute once the collapse completes - * @param {Object} scope (Optional) The scope to run the callback in + * @param {Boolean} [recursive=false] True to recursively collapse all the children + * @param {Function} [callback] The function to execute once the collapse completes + * @param {Object} [scope] The scope to run the callback in */ collapse: function(recursive, callback, scope) { var me = this; @@ -1031,17 +1202,17 @@ Ext.define('Ext.data.NodeInterface', { // Now we check if this record is already collapsing or collapsed if (!me.collapsing && me.isExpanded()) { me.fireEvent('beforecollapse', me, function() { - me.set('expanded', false); + me.set('expanded', false); me.fireEvent('collapse', me, me.childNodes, false); - - // Call the collapseChildren method if recursive was set to true + + // Call the collapseChildren method if recursive was set to true if (recursive) { me.collapseChildren(true, callback, scope); } else { - Ext.callback(callback, scope || me, [me.childNodes]); + Ext.callback(callback, scope || me, [me.childNodes]); } - }, me); + }, me); } // If it is is already collapsed but we want to recursively collapse then call collapseChildren else if (recursive) { @@ -1050,15 +1221,15 @@ Ext.define('Ext.data.NodeInterface', { } // If it's not then we fire the callback right away else { - Ext.callback(callback, scope || me, [me.childNodes]); + Ext.callback(callback, scope || me, [me.childNodes]); } }, - + /** * Collapse all the children of this node. - * @param {Function} recursive (Optional) True to recursively collapse all the children - * @param {Function} callback (Optional) The function to execute once all the children are collapsed - * @param {Object} scope (Optional) The scope to run the callback in + * @param {Function} [recursive=false] True to recursively collapse all the children + * @param {Function} [callback] The function to execute once all the children are collapsed + * @param {Object} [scope] The scope to run the callback in */ collapseChildren: function(recursive, callback, scope) { var me = this, @@ -1075,12 +1246,12 @@ Ext.define('Ext.data.NodeInterface', { nodes[i].collapse(recursive, function () { collapsing--; if (callback && !collapsing) { - Ext.callback(callback, scope || me, [me.childNodes]); + Ext.callback(callback, scope || me, [me.childNodes]); } - }); + }); } } - + if (!collapsing && callback) { Ext.callback(callback, scope || me, [me.childNodes]); }