--- /dev/null
+<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-data.NodeInterface'>/**
+</span> * @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
+ * will have all of the tree related methods available on the model. In general this class will
+ * not be used directly by the developer.
+ */
+Ext.define('Ext.data.NodeInterface', {
+ requires: ['Ext.data.Field'],
+
+ statics: {
+<span id='Ext-data.NodeInterface-method-decorate'> /**
+</span> * 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.
+ * @static
+ */
+ decorate: function(record) {
+ if (!record.isNode) {
+ // Apply the methods and fields to the prototype
+ // @TODO: clean this up to use proper class system stuff
+ var mgr = Ext.ModelManager,
+ modelName = record.modelName,
+ modelClass = mgr.getModel(modelName),
+ idName = modelClass.prototype.idProperty,
+ instances = Ext.Array.filter(mgr.all.getArray(), function(item) {
+ return item.modelName == modelName;
+ }),
+ iln = instances.length,
+ newFields = [],
+ i, instance, jln, j, newField;
+
+ // Start by adding the NodeInterface methods to the Model's prototype
+ modelClass.override(this.getPrototypeBody());
+ newFields = this.applyFields(modelClass, [
+ {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: 'expanded', type: 'bool', defaultValue: false, 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: 'root', type: 'boolean', defaultValue: false, persist: false},
+ {name: 'isLast', type: 'boolean', defaultValue: false, persist: false},
+ {name: 'isFirst', type: 'boolean', defaultValue: false, persist: false},
+ {name: 'allowDrop', type: 'boolean', defaultValue: true, persist: false},
+ {name: 'allowDrag', type: 'boolean', defaultValue: true, persist: false},
+ {name: 'loaded', type: 'boolean', defaultValue: false, persist: false},
+ {name: 'loading', type: 'boolean', defaultValue: false, persist: false},
+ {name: 'href', type: 'string', defaultValue: null, persist: false},
+ {name: 'hrefTarget',type: 'string', defaultValue: null, persist: false},
+ {name: 'qtip', type: 'string', defaultValue: null, persist: false},
+ {name: 'qtitle', type: 'string', defaultValue: null, persist: false}
+ ]);
+
+ jln = newFields.length;
+ // Set default values to all instances already out there
+ for (i = 0; i < iln; i++) {
+ instance = instances[i];
+ for (j = 0; j < jln; j++) {
+ newField = newFields[j];
+ if (instance.get(newField.name) === undefined) {
+ instance.data[newField.name] = newField.defaultValue;
+ }
+ }
+ }
+ }
+
+ Ext.applyIf(record, {
+ firstChild: null,
+ lastChild: null,
+ parentNode: null,
+ previousSibling: null,
+ nextSibling: null,
+ childNodes: []
+ });
+ // Commit any fields so the record doesn't show as dirty initially
+ record.commit(true);
+
+ record.enableBubble([
+<span id='Ext-data.NodeInterface-event-append'> /**
+</span> * @event append
+ * Fires when a new child node is appended
+ * @param {Node} this This node
+ * @param {Node} node The newly appended node
+ * @param {Number} index The index of the newly appended node
+ */
+ "append",
+
+<span id='Ext-data.NodeInterface-event-remove'> /**
+</span> * @event remove
+ * Fires when a child node is removed
+ * @param {Node} this This node
+ * @param {Node} node The removed node
+ */
+ "remove",
+
+<span id='Ext-data.NodeInterface-event-move'> /**
+</span> * @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 {Number} index The index it was moved to
+ */
+ "move",
+
+<span id='Ext-data.NodeInterface-event-insert'> /**
+</span> * @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
+ */
+ "insert",
+
+<span id='Ext-data.NodeInterface-event-beforeappend'> /**
+</span> * @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
+ */
+ "beforeappend",
+
+<span id='Ext-data.NodeInterface-event-beforeremove'> /**
+</span> * @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
+ */
+ "beforeremove",
+
+<span id='Ext-data.NodeInterface-event-beforemove'> /**
+</span> * @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 {Number} index The index it is being moved to
+ */
+ "beforemove",
+
+<span id='Ext-data.NodeInterface-event-beforeinsert'> /**
+</span> * @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
+ */
+ "beforeinsert",
+
+<span id='Ext-data.NodeInterface-event-expand'> /**
+</span> * @event expand
+ * Fires when this node is expanded.
+ * @param {Node} this The expanding node
+ */
+ "expand",
+
+<span id='Ext-data.NodeInterface-event-collapse'> /**
+</span> * @event collapse
+ * Fires when this node is collapsed.
+ * @param {Node} this The collapsing node
+ */
+ "collapse",
+
+<span id='Ext-data.NodeInterface-event-beforeexpand'> /**
+</span> * @event beforeexpand
+ * Fires before this node is expanded.
+ * @param {Node} this The expanding node
+ */
+ "beforeexpand",
+
+<span id='Ext-data.NodeInterface-event-beforecollapse'> /**
+</span> * @event beforecollapse
+ * Fires before this node is collapsed.
+ * @param {Node} this The collapsing node
+ */
+ "beforecollapse",
+
+<span id='Ext-data.NodeInterface-event-beforecollapse'> /**
+</span> * @event beforecollapse
+ * Fires before this node is collapsed.
+ * @param {Node} this The collapsing node
+ */
+ "sort"
+ ]);
+
+ return record;
+ },
+
+ applyFields: function(modelClass, addFields) {
+ var modelPrototype = modelClass.prototype,
+ fields = modelPrototype.fields,
+ keys = fields.keys,
+ 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,
+
+<span id='Ext-data.NodeInterface-method-createNode'> /**
+</span> * Ensures that the passed object is an instance of a Record with the NodeInterface applied
+ * @return {Boolean}
+ */
+ createNode: function(node) {
+ if (Ext.isObject(node) && !node.isModel) {
+ node = Ext.ModelManager.create(node, this.modelName);
+ }
+ // Make sure the node implements the node interface
+ return Ext.data.NodeInterface.decorate(node);
+ },
+
+<span id='Ext-data.NodeInterface-method-isLeaf'> /**
+</span> * Returns true if this node is a leaf
+ * @return {Boolean}
+ */
+ isLeaf : function() {
+ return this.get('leaf') === true;
+ },
+
+<span id='Ext-data.NodeInterface-method-setFirstChild'> /**
+</span> * Sets the first child of this node
+ * @private
+ * @param {Ext.data.NodeInterface} node
+ */
+ setFirstChild : function(node) {
+ this.firstChild = node;
+ },
+
+<span id='Ext-data.NodeInterface-method-setLastChild'> /**
+</span> * Sets the last child of this node
+ * @private
+ * @param {Ext.data.NodeInterface} node
+ */
+ setLastChild : function(node) {
+ this.lastChild = node;
+ },
+
+<span id='Ext-data.NodeInterface-method-updateInfo'> /**
+</span> * Updates general data of this node like isFirst, isLast, depth. This
+ * method is internally called after a node is moved. This shouldn't
+ * have to be called by the developer unless they are creating custom
+ * Tree plugins.
+ * @return {Boolean}
+ */
+ updateInfo: function(silent) {
+ var me = this,
+ isRoot = me.isRoot(),
+ parentNode = me.parentNode,
+ isFirst = (!parentNode ? true : parentNode.firstChild == me),
+ isLast = (!parentNode ? true : parentNode.lastChild == me),
+ depth = 0,
+ parent = me,
+ children = me.childNodes,
+ len = children.length,
+ i = 0;
+
+ while (parent.parentNode) {
+ ++depth;
+ parent = parent.parentNode;
+ }
+
+ me.beginEdit();
+ me.set({
+ isFirst: isFirst,
+ isLast: isLast,
+ depth: depth,
+ index: parentNode ? parentNode.indexOf(me) : 0,
+ parentId: parentNode ? parentNode.getId() : null
+ });
+ me.endEdit(silent);
+ if (silent) {
+ me.commit();
+ }
+
+ for (i = 0; i < len; i++) {
+ children[i].updateInfo(silent);
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-isLast'> /**
+</span> * Returns true if this node is the last child of its parent
+ * @return {Boolean}
+ */
+ isLast : function() {
+ return this.get('isLast');
+ },
+
+<span id='Ext-data.NodeInterface-method-isFirst'> /**
+</span> * Returns true if this node is the first child of its parent
+ * @return {Boolean}
+ */
+ isFirst : function() {
+ return this.get('isFirst');
+ },
+
+<span id='Ext-data.NodeInterface-method-hasChildNodes'> /**
+</span> * Returns true if this node has one or more child nodes, else false.
+ * @return {Boolean}
+ */
+ hasChildNodes : function() {
+ return !this.isLeaf() && this.childNodes.length > 0;
+ },
+
+<span id='Ext-data.NodeInterface-method-isExpandable'> /**
+</span> * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
+ * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
+ * @return {Boolean}
+ */
+ isExpandable : function() {
+ return this.get('expandable') || this.hasChildNodes();
+ },
+
+<span id='Ext-data.NodeInterface-method-appendChild'> /**
+</span> * <p>Insert node(s) as the last child node of this node.</p>
+ * <p>If the node was previously a child node of another parent node, it will be removed from that node first.</p>
+ * @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
+ */
+ appendChild : function(node, suppressEvents, suppressNodeUpdate) {
+ var me = this,
+ i, ln,
+ index,
+ oldParent,
+ ps;
+
+ // if passed an array or multiple args do them one by one
+ if (Ext.isArray(node)) {
+ for (i = 0, ln = node.length; i < ln; i++) {
+ me.appendChild(node[i]);
+ }
+ } else {
+ // Make sure it is a record
+ node = me.createNode(node);
+
+ if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
+ return false;
+ }
+
+ index = me.childNodes.length;
+ oldParent = node.parentNode;
+
+ // it's a move, make sure we move it cleanly
+ if (oldParent) {
+ if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index) === false) {
+ return false;
+ }
+ oldParent.removeChild(node, null, false, true);
+ }
+
+ index = me.childNodes.length;
+ if (index === 0) {
+ me.setFirstChild(node);
+ }
+
+ me.childNodes.push(node);
+ node.parentNode = me;
+ node.nextSibling = null;
+
+ me.setLastChild(node);
+
+ ps = me.childNodes[index - 1];
+ if (ps) {
+ node.previousSibling = ps;
+ ps.nextSibling = node;
+ ps.updateInfo(suppressNodeUpdate);
+ } else {
+ node.previousSibling = null;
+ }
+
+ node.updateInfo(suppressNodeUpdate);
+
+ // As soon as we append a child to this node, we are loaded
+ if (!me.isLoaded()) {
+ 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;
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-getBubbleTarget'> /**
+</span> * Returns the bubble target for this node
+ * @private
+ * @return {Object} The bubble target
+ */
+ getBubbleTarget: function() {
+ return this.parentNode;
+ },
+
+<span id='Ext-data.NodeInterface-method-removeChild'> /**
+</span> * Removes a child node from this node.
+ * @param {Node} node The node to remove
+ * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
+ * @return {Node} 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;
+ }
+
+ // remove it from childNodes collection
+ me.childNodes.splice(index, 1);
+
+ // update child refs
+ if (me.firstChild == node) {
+ me.setFirstChild(node.nextSibling);
+ }
+ if (me.lastChild == node) {
+ me.setLastChild(node.previousSibling);
+ }
+
+ // update siblings
+ if (node.previousSibling) {
+ node.previousSibling.nextSibling = node.nextSibling;
+ node.previousSibling.updateInfo(suppressNodeUpdate);
+ }
+ if (node.nextSibling) {
+ node.nextSibling.previousSibling = node.previousSibling;
+ node.nextSibling.updateInfo(suppressNodeUpdate);
+ }
+
+ 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 {
+ node.clear();
+ }
+
+ return node;
+ },
+
+<span id='Ext-data.NodeInterface-method-copy'> /**
+</span> * Creates a copy (clone) of this Node.
+ * @param {String} id (optional) A new id, defaults to this Node's id. See <code>{@link #id}</code>.
+ * @param {Boolean} deep (optional) <p>If passed as <code>true</code>, all child Nodes are recursively copied into the new Node.</p>
+ * <p>If omitted or false, the copy will have no child Nodes.</p>
+ * @return {Node} A copy of this Node.
+ */
+ copy: function(newId, deep) {
+ var me = this,
+ result = me.callOverridden(arguments),
+ len = me.childNodes ? me.childNodes.length : 0,
+ i;
+
+ // Move child nodes across to the copy if required
+ if (deep) {
+ for (i = 0; i < len; i++) {
+ result.appendChild(me.childNodes[i].copy(true));
+ }
+ }
+ return result;
+ },
+
+<span id='Ext-data.NodeInterface-method-clear'> /**
+</span> * Clear the node.
+ * @private
+ * @param {Boolean} destroy 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) {
+ me.firstChild = me.lastChild = null;
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-destroy'> /**
+</span> * Destroys the node.
+ */
+ destroy : function(silent) {
+ /*
+ * Silent is to be used in a number of cases
+ * 1) When setRoot is called.
+ * 2) When destroy on the tree is called
+ * 3) For destroying child nodes on a node
+ */
+ var me = this;
+
+ if (silent === true) {
+ me.clear(true);
+ Ext.each(me.childNodes, function(n) {
+ n.destroy(true);
+ });
+ me.childNodes = null;
+ } else {
+ me.remove(true);
+ }
+
+ me.callOverridden();
+ },
+
+<span id='Ext-data.NodeInterface-method-insertBefore'> /**
+</span> * 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
+ */
+ insertBefore : function(node, refNode, suppressEvents) {
+ var me = this,
+ index = me.indexOf(refNode),
+ 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;
+ }
+
+ // 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--;
+ }
+
+ // it's a move, make sure we move it cleanly
+ if (oldParent) {
+ if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index, refNode) === false) {
+ return false;
+ }
+ oldParent.removeChild(node);
+ }
+
+ if (refIndex === 0) {
+ me.setFirstChild(node);
+ }
+
+ me.childNodes.splice(refIndex, 0, node);
+ node.parentNode = me;
+
+ node.nextSibling = refNode;
+ refNode.previousSibling = node;
+
+ ps = me.childNodes[refIndex - 1];
+ if (ps) {
+ node.previousSibling = ps;
+ ps.nextSibling = node;
+ ps.updateInfo();
+ } else {
+ node.previousSibling = null;
+ }
+
+ node.updateInfo();
+
+ if (!me.isLoaded()) {
+ 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("insert", me, node, refNode);
+
+ if (oldParent) {
+ node.fireEvent("move", node, oldParent, me, refIndex, refNode);
+ }
+ }
+
+ return node;
+ },
+
+<span id='Ext-data.NodeInterface-method-insertChild'> /**
+</span> * 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
+ */
+ insertChild: function(index, node) {
+ var sibling = this.childNodes[index];
+ if (sibling) {
+ return this.insertBefore(node, sibling);
+ }
+ else {
+ return this.appendChild(node);
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-remove'> /**
+</span> * Removes this node from its parent
+ * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
+ * @return {Node} this
+ */
+ remove : function(destroy, suppressEvents) {
+ var parentNode = this.parentNode;
+
+ if (parentNode) {
+ parentNode.removeChild(this, destroy, suppressEvents, true);
+ }
+ return this;
+ },
+
+<span id='Ext-data.NodeInterface-method-removeAll'> /**
+</span> * Removes all child nodes from this node.
+ * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
+ * @return {Node} this
+ */
+ removeAll : function(destroy, suppressEvents) {
+ var cn = this.childNodes,
+ n;
+
+ while ((n = cn[0])) {
+ this.removeChild(n, destroy, suppressEvents);
+ }
+ return this;
+ },
+
+<span id='Ext-data.NodeInterface-method-getChildAt'> /**
+</span> * Returns the child node at the specified index.
+ * @param {Number} index
+ * @return {Node}
+ */
+ getChildAt : function(index) {
+ return this.childNodes[index];
+ },
+
+<span id='Ext-data.NodeInterface-method-replaceChild'> /**
+</span> * Replaces one child node in this node with another.
+ * @param {Node} newChild The replacement node
+ * @param {Node} oldChild The node to replace
+ * @return {Node} The replaced node
+ */
+ replaceChild : function(newChild, oldChild, suppressEvents) {
+ var s = oldChild ? oldChild.nextSibling : null;
+
+ this.removeChild(oldChild, suppressEvents);
+ this.insertBefore(newChild, s, suppressEvents);
+ return oldChild;
+ },
+
+<span id='Ext-data.NodeInterface-method-indexOf'> /**
+</span> * Returns the index of a child node
+ * @param {Node} node
+ * @return {Number} The index of the node or -1 if it was not found
+ */
+ indexOf : function(child) {
+ return Ext.Array.indexOf(this.childNodes, child);
+ },
+
+<span id='Ext-data.NodeInterface-method-getDepth'> /**
+</span> * Returns depth of this node (the root node has a depth of 0)
+ * @return {Number}
+ */
+ getDepth : function() {
+ return this.get('depth');
+ },
+
+<span id='Ext-data.NodeInterface-method-bubble'> /**
+</span> * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
+ * will be the args provided or the current node. If the function returns false at any point,
+ * the bubble is stopped.
+ * @param {Function} fn The function to call
+ * @param {Object} scope (optional) The scope (<code>this</code> 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)
+ */
+ bubble : function(fn, scope, args) {
+ var p = this;
+ while (p) {
+ if (fn.apply(scope || p, args || [p]) === false) {
+ break;
+ }
+ p = p.parentNode;
+ }
+ },
+
+ //<deprecated since=0.99>
+ cascade: function() {
+ if (Ext.isDefined(Ext.global.console)) {
+ Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
+ }
+ return this.cascadeBy.apply(this, arguments);
+ },
+ //</deprecated>
+
+<span id='Ext-data.NodeInterface-method-cascadeBy'> /**
+</span> * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
+ * 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 (<code>this</code> 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)
+ */
+ cascadeBy : function(fn, scope, args) {
+ if (fn.apply(scope || this, args || [this]) !== false) {
+ var childNodes = this.childNodes,
+ length = childNodes.length,
+ i;
+
+ for (i = 0; i < length; i++) {
+ childNodes[i].cascadeBy(fn, scope, args);
+ }
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-eachChild'> /**
+</span> * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
+ * 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 (<code>this</code> 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)
+ */
+ eachChild : function(fn, scope, args) {
+ var childNodes = this.childNodes,
+ length = childNodes.length,
+ i;
+
+ for (i = 0; i < length; i++) {
+ if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
+ break;
+ }
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-findChild'> /**
+</span> * 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
+ */
+ findChild : function(attribute, value, deep) {
+ return this.findChildBy(function() {
+ return this.get(attribute) == value;
+ }, null, deep);
+ },
+
+<span id='Ext-data.NodeInterface-method-findChildBy'> /**
+</span> * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
+ * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
+ * @param {Object} scope (optional) The scope (<code>this</code> 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
+ */
+ findChildBy : function(fn, scope, deep) {
+ var cs = this.childNodes,
+ len = cs.length,
+ i = 0, n, res;
+
+ for (; i < len; i++) {
+ n = cs[i];
+ if (fn.call(scope || n, n) === true) {
+ return n;
+ }
+ else if (deep) {
+ res = n.findChildBy(fn, scope, deep);
+ if (res !== null) {
+ return res;
+ }
+ }
+ }
+
+ return null;
+ },
+
+<span id='Ext-data.NodeInterface-method-contains'> /**
+</span> * Returns true if this node is an ancestor (at any point) of the passed node.
+ * @param {Node} node
+ * @return {Boolean}
+ */
+ contains : function(node) {
+ return node.isAncestor(this);
+ },
+
+<span id='Ext-data.NodeInterface-method-isAncestor'> /**
+</span> * Returns true if the passed node is an ancestor (at any point) of this node.
+ * @param {Node} node
+ * @return {Boolean}
+ */
+ isAncestor : function(node) {
+ var p = this.parentNode;
+ while (p) {
+ if (p == node) {
+ return true;
+ }
+ p = p.parentNode;
+ }
+ return false;
+ },
+
+<span id='Ext-data.NodeInterface-method-sort'> /**
+</span> * 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.
+ */
+ 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();
+ }
+ if (i == ln - 1) {
+ this.setLastChild(n);
+ n.updateInfo();
+ }
+ if (recursive && !n.isLeaf()) {
+ n.sort(sortFn, true, true);
+ }
+ }
+
+ if (suppressEvent !== true) {
+ this.fireEvent('sort', this, cs);
+ }
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-isExpanded'> /**
+</span> * Returns true if this node is expaned
+ * @return {Boolean}
+ */
+ isExpanded: function() {
+ return this.get('expanded');
+ },
+
+<span id='Ext-data.NodeInterface-method-isLoaded'> /**
+</span> * Returns true if this node is loaded
+ * @return {Boolean}
+ */
+ isLoaded: function() {
+ return this.get('loaded');
+ },
+
+<span id='Ext-data.NodeInterface-method-isLoading'> /**
+</span> * Returns true if this node is loading
+ * @return {Boolean}
+ */
+ isLoading: function() {
+ return this.get('loading');
+ },
+
+<span id='Ext-data.NodeInterface-method-isRoot'> /**
+</span> * Returns true if this node is the root node
+ * @return {Boolean}
+ */
+ isRoot: function() {
+ return !this.parentNode;
+ },
+
+<span id='Ext-data.NodeInterface-method-isVisible'> /**
+</span> * Returns true if this node is visible
+ * @return {Boolean}
+ */
+ isVisible: function() {
+ var parent = this.parentNode;
+ while (parent) {
+ if (!parent.isExpanded()) {
+ return false;
+ }
+ parent = parent.parentNode;
+ }
+ return true;
+ },
+
+<span id='Ext-data.NodeInterface-method-expand'> /**
+</span> * 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
+ */
+ expand: function(recursive, callback, scope) {
+ var me = this;
+
+ // all paths must call the callback (eventually) or things like
+ // selectPath fail
+
+ // 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(records) {
+ 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]);
+ }
+
+ // 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 {
+ Ext.callback(callback, scope || me); // leaf = no childNodes
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-expandChildren'> /**
+</span> * 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
+ */
+ expandChildren: function(recursive, callback, scope) {
+ var me = this,
+ i = 0,
+ nodes = me.childNodes,
+ ln = nodes.length,
+ node,
+ expanding = 0;
+
+ for (; i < ln; ++i) {
+ node = nodes[i];
+ if (!node.isLeaf() && !node.isExpanded()) {
+ expanding++;
+ nodes[i].expand(recursive, function () {
+ expanding--;
+ if (callback && !expanding) {
+ Ext.callback(callback, scope || me, me.childNodes);
+ }
+ });
+ }
+ }
+
+ if (!expanding && callback) {
+ Ext.callback(callback, scope || me, me.childNodes);
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-collapse'> /**
+</span> * 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
+ */
+ collapse: function(recursive, callback, scope) {
+ var me = this;
+
+ // First we start by checking if this node is a parent
+ if (!me.isLeaf()) {
+ // Now we check if this record is already collapsing or collapsed
+ if (!me.collapsing && me.isExpanded()) {
+ me.fireEvent('beforecollapse', me, function(records) {
+ me.set('expanded', false);
+ me.fireEvent('collapse', me, me.childNodes, false);
+
+ // 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]);
+ }
+ }, me);
+ }
+ // If it is is already collapsed but we want to recursively collapse then call collapseChildren
+ else if (recursive) {
+ me.collapseChildren(true, callback, scope);
+ }
+ }
+ // If it's not then we fire the callback right away
+ else {
+ Ext.callback(callback, scope || me, me.childNodes);
+ }
+ },
+
+<span id='Ext-data.NodeInterface-method-collapseChildren'> /**
+</span> * 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
+ */
+ collapseChildren: function(recursive, callback, scope) {
+ var me = this,
+ i = 0,
+ nodes = me.childNodes,
+ ln = nodes.length,
+ node,
+ collapsing = 0;
+
+ for (; i < ln; ++i) {
+ node = nodes[i];
+ if (!node.isLeaf() && node.isExpanded()) {
+ collapsing++;
+ nodes[i].collapse(recursive, function () {
+ collapsing--;
+ if (callback && !collapsing) {
+ Ext.callback(callback, scope || me, me.childNodes);
+ }
+ });
+ }
+ }
+
+ if (!collapsing && callback) {
+ Ext.callback(callback, scope || me, me.childNodes);
+ }
+ }
+ };
+ }
+ }
+});</pre></pre></body></html>
\ No newline at end of file