3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
4 <title>The source code</title>
5 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <body onload="prettyPrint();">
9 <pre class="prettyprint lang-js">/*!
10 * Ext JS Library 3.2.0
11 * Copyright(c) 2006-2010 Ext JS, Inc.
13 * http://www.extjs.com/license
15 <div id="cls-Ext.data.Tree"></div>/**
16 * @class Ext.data.Tree
17 * @extends Ext.util.Observable
18 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
19 * in the tree have most standard DOM functionality.
21 * @param {Node} root (optional) The root node
23 Ext.data.Tree = function(root){
25 <div id="prop-Ext.data.Tree-root"></div>/**
26 * The root node for this tree
31 this.setRootNode(root);
34 <div id="event-Ext.data.Tree-append"></div>/**
36 * Fires when a new child node is appended to a node in this tree.
37 * @param {Tree} tree The owner tree
38 * @param {Node} parent The parent node
39 * @param {Node} node The newly appended node
40 * @param {Number} index The index of the newly appended node
43 <div id="event-Ext.data.Tree-remove"></div>/**
45 * Fires when a child node is removed from a node in this tree.
46 * @param {Tree} tree The owner tree
47 * @param {Node} parent The parent node
48 * @param {Node} node The child node removed
51 <div id="event-Ext.data.Tree-move"></div>/**
53 * Fires when a node is moved to a new location in the tree
54 * @param {Tree} tree The owner tree
55 * @param {Node} node The node moved
56 * @param {Node} oldParent The old parent of this node
57 * @param {Node} newParent The new parent of this node
58 * @param {Number} index The index it was moved to
61 <div id="event-Ext.data.Tree-insert"></div>/**
63 * Fires when a new child node is inserted in a node in this tree.
64 * @param {Tree} tree The owner tree
65 * @param {Node} parent The parent node
66 * @param {Node} node The child node inserted
67 * @param {Node} refNode The child node the node was inserted before
70 <div id="event-Ext.data.Tree-beforeappend"></div>/**
72 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
73 * @param {Tree} tree The owner tree
74 * @param {Node} parent The parent node
75 * @param {Node} node The child node to be appended
78 <div id="event-Ext.data.Tree-beforeremove"></div>/**
80 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
81 * @param {Tree} tree The owner tree
82 * @param {Node} parent The parent node
83 * @param {Node} node The child node to be removed
86 <div id="event-Ext.data.Tree-beforemove"></div>/**
88 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
89 * @param {Tree} tree The owner tree
90 * @param {Node} node The node being moved
91 * @param {Node} oldParent The parent of the node
92 * @param {Node} newParent The new parent the node is moving to
93 * @param {Number} index The index it is being moved to
96 <div id="event-Ext.data.Tree-beforeinsert"></div>/**
98 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
99 * @param {Tree} tree The owner tree
100 * @param {Node} parent The parent node
101 * @param {Node} node The child node to be inserted
102 * @param {Node} refNode The child node the node is being inserted before
107 Ext.data.Tree.superclass.constructor.call(this);
110 Ext.extend(Ext.data.Tree, Ext.util.Observable, {
111 <div id="cfg-Ext.data.Tree-pathSeparator"></div>/**
112 * @cfg {String} pathSeparator
113 * The token used to separate paths in node ids (defaults to '/').
118 proxyNodeEvent : function(){
119 return this.fireEvent.apply(this, arguments);
122 <div id="method-Ext.data.Tree-getRootNode"></div>/**
123 * Returns the root node for this tree.
126 getRootNode : function(){
130 <div id="method-Ext.data.Tree-setRootNode"></div>/**
131 * Sets the root node for this tree.
135 setRootNode : function(node){
137 node.ownerTree = this;
139 this.registerNode(node);
143 <div id="method-Ext.data.Tree-getNodeById"></div>/**
144 * Gets a node in this tree by its id.
148 getNodeById : function(id){
149 return this.nodeHash[id];
153 registerNode : function(node){
154 this.nodeHash[node.id] = node;
158 unregisterNode : function(node){
159 delete this.nodeHash[node.id];
162 toString : function(){
163 return "[Tree"+(this.id?" "+this.id:"")+"]";
167 <div id="cls-Ext.data.Node"></div>/**
168 * @class Ext.data.Node
169 * @extends Ext.util.Observable
170 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
171 * @cfg {String} id The id for this node. If one is not specified, one is generated.
173 * @param {Object} attributes The attributes/config for the node
175 Ext.data.Node = function(attributes){
177 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
180 this.attributes = attributes || {};
181 this.leaf = this.attributes.leaf;
183 * The node id. @type String
185 this.id = this.attributes.id;
187 this.id = Ext.id(null, "xnode-");
188 this.attributes.id = this.id;
191 * All child nodes of this node. @type Array
193 this.childNodes = [];
194 if(!this.childNodes.indexOf){ // indexOf is a must
195 this.childNodes.indexOf = function(o){
196 for(var i = 0, len = this.length; i < len; i++){
205 * The parent node for this node. @type Node
207 this.parentNode = null;
209 * The first direct child node of this node, or null if this node has no child nodes. @type Node
211 this.firstChild = null;
213 * The last direct child node of this node, or null if this node has no child nodes. @type Node
215 this.lastChild = null;
217 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
219 this.previousSibling = null;
221 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
223 this.nextSibling = null;
228 * Fires when a new child node is appended
229 * @param {Tree} tree The owner tree
230 * @param {Node} this This node
231 * @param {Node} node The newly appended node
232 * @param {Number} index The index of the newly appended node
237 * Fires when a child node is removed
238 * @param {Tree} tree The owner tree
239 * @param {Node} this This node
240 * @param {Node} node The removed node
245 * Fires when this node is moved to a new location in the tree
246 * @param {Tree} tree The owner tree
247 * @param {Node} this This node
248 * @param {Node} oldParent The old parent of this node
249 * @param {Node} newParent The new parent of this node
250 * @param {Number} index The index it was moved to
255 * Fires when a new child node is inserted.
256 * @param {Tree} tree The owner tree
257 * @param {Node} this This node
258 * @param {Node} node The child node inserted
259 * @param {Node} refNode The child node the node was inserted before
263 * @event beforeappend
264 * Fires before a new child is appended, return false to cancel the append.
265 * @param {Tree} tree The owner tree
266 * @param {Node} this This node
267 * @param {Node} node The child node to be appended
269 "beforeappend" : true,
271 * @event beforeremove
272 * Fires before a child is removed, return false to cancel the remove.
273 * @param {Tree} tree The owner tree
274 * @param {Node} this This node
275 * @param {Node} node The child node to be removed
277 "beforeremove" : true,
280 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
281 * @param {Tree} tree The owner tree
282 * @param {Node} this This node
283 * @param {Node} oldParent The parent of this node
284 * @param {Node} newParent The new parent this node is moving to
285 * @param {Number} index The index it is being moved to
289 * @event beforeinsert
290 * Fires before a new child is inserted, return false to cancel the insert.
291 * @param {Tree} tree The owner tree
292 * @param {Node} this This node
293 * @param {Node} node The child node to be inserted
294 * @param {Node} refNode The child node the node is being inserted before
296 "beforeinsert" : true
298 this.listeners = this.attributes.listeners;
299 Ext.data.Node.superclass.constructor.call(this);
302 Ext.extend(Ext.data.Node, Ext.util.Observable, {
304 fireEvent : function(evtName){
305 // first do standard event for this node
306 if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
309 // then bubble it up to the tree if the event wasn't cancelled
310 var ot = this.getOwnerTree();
312 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
320 * Returns true if this node is a leaf
324 return this.leaf === true;
328 setFirstChild : function(node){
329 this.firstChild = node;
333 setLastChild : function(node){
334 this.lastChild = node;
339 * Returns true if this node is the last child of its parent
343 return (!this.parentNode ? true : this.parentNode.lastChild == this);
347 * Returns true if this node is the first child of its parent
350 isFirst : function(){
351 return (!this.parentNode ? true : this.parentNode.firstChild == this);
355 * Returns true if this node has one or more child nodes, else false.
358 hasChildNodes : function(){
359 return !this.isLeaf() && this.childNodes.length > 0;
363 * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
364 * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
367 isExpandable : function(){
368 return this.attributes.expandable || this.hasChildNodes();
372 * Insert node(s) as the last child node of this node.
373 * @param {Node/Array} node The node or Array of nodes to append
374 * @return {Node} The appended node if single append, or null if an array was passed
376 appendChild : function(node){
378 if(Ext.isArray(node)){
380 }else if(arguments.length > 1){
383 // if passed an array or multiple args do them one by one
385 for(var i = 0, len = multi.length; i < len; i++) {
386 this.appendChild(multi[i]);
389 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
392 var index = this.childNodes.length;
393 var oldParent = node.parentNode;
394 // it's a move, make sure we move it cleanly
396 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
399 oldParent.removeChild(node);
401 index = this.childNodes.length;
403 this.setFirstChild(node);
405 this.childNodes.push(node);
406 node.parentNode = this;
407 var ps = this.childNodes[index-1];
409 node.previousSibling = ps;
410 ps.nextSibling = node;
412 node.previousSibling = null;
414 node.nextSibling = null;
415 this.setLastChild(node);
416 node.setOwnerTree(this.getOwnerTree());
417 this.fireEvent("append", this.ownerTree, this, node, index);
419 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
426 * Removes a child node from this node.
427 * @param {Node} node The node to remove
428 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
429 * @return {Node} The removed node
431 removeChild : function(node, destroy){
432 var index = this.childNodes.indexOf(node);
436 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
440 // remove it from childNodes collection
441 this.childNodes.splice(index, 1);
444 if(node.previousSibling){
445 node.previousSibling.nextSibling = node.nextSibling;
447 if(node.nextSibling){
448 node.nextSibling.previousSibling = node.previousSibling;
452 if(this.firstChild == node){
453 this.setFirstChild(node.nextSibling);
455 if(this.lastChild == node){
456 this.setLastChild(node.previousSibling);
459 this.fireEvent("remove", this.ownerTree, this, node);
469 clear : function(destroy){
470 // clear any references from the node
471 this.setOwnerTree(null, destroy);
472 this.parentNode = this.previousSibling = this.nextSibling = null;
474 this.firstChild = this.lastChild = null;
481 destroy : function(/* private */ silent){
483 * Silent is to be used in a number of cases
484 * 1) When setRootNode is called.
485 * 2) When destroy on the tree is called
486 * 3) For destroying child nodes on a node
489 this.purgeListeners();
491 Ext.each(this.childNodes, function(n){
494 this.childNodes = null;
501 * Inserts the first node before the second node in this nodes childNodes collection.
502 * @param {Node} node The node to insert
503 * @param {Node} refNode The node to insert before (if null the node is appended)
504 * @return {Node} The inserted node
506 insertBefore : function(node, refNode){
507 if(!refNode){ // like standard Dom, refNode can be null for append
508 return this.appendChild(node);
515 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
518 var index = this.childNodes.indexOf(refNode);
519 var oldParent = node.parentNode;
520 var refIndex = index;
522 // when moving internally, indexes will change after remove
523 if(oldParent == this && this.childNodes.indexOf(node) < index){
527 // it's a move, make sure we move it cleanly
529 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
532 oldParent.removeChild(node);
535 this.setFirstChild(node);
537 this.childNodes.splice(refIndex, 0, node);
538 node.parentNode = this;
539 var ps = this.childNodes[refIndex-1];
541 node.previousSibling = ps;
542 ps.nextSibling = node;
544 node.previousSibling = null;
546 node.nextSibling = refNode;
547 refNode.previousSibling = node;
548 node.setOwnerTree(this.getOwnerTree());
549 this.fireEvent("insert", this.ownerTree, this, node, refNode);
551 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
557 * Removes this node from its parent
558 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
559 * @return {Node} this
561 remove : function(destroy){
562 if (this.parentNode) {
563 this.parentNode.removeChild(this, destroy);
569 * Removes all child nodes from this node.
570 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
571 * @return {Node} this
573 removeAll : function(destroy){
574 var cn = this.childNodes,
577 this.removeChild(n, destroy);
583 * Returns the child node at the specified index.
584 * @param {Number} index
587 item : function(index){
588 return this.childNodes[index];
592 * Replaces one child node in this node with another.
593 * @param {Node} newChild The replacement node
594 * @param {Node} oldChild The node to replace
595 * @return {Node} The replaced node
597 replaceChild : function(newChild, oldChild){
598 var s = oldChild ? oldChild.nextSibling : null;
599 this.removeChild(oldChild);
600 this.insertBefore(newChild, s);
605 * Returns the index of a child node
607 * @return {Number} The index of the node or -1 if it was not found
609 indexOf : function(child){
610 return this.childNodes.indexOf(child);
614 * Returns the tree this node is in.
617 getOwnerTree : function(){
618 // if it doesn't have one, look for one
623 this.ownerTree = p.ownerTree;
629 return this.ownerTree;
633 * Returns depth of this node (the root node has a depth of 0)
636 getDepth : function(){
647 setOwnerTree : function(tree, destroy){
648 // if it is a move, we need to update everyone
649 if(tree != this.ownerTree){
651 this.ownerTree.unregisterNode(this);
653 this.ownerTree = tree;
654 // If we're destroying, we don't need to recurse since it will be called on each child node
655 if(destroy !== true){
656 Ext.each(this.childNodes, function(n){
657 n.setOwnerTree(tree);
661 tree.registerNode(this);
667 * Changes the id of this node.
668 * @param {String} id The new id for the node.
672 var t = this.ownerTree;
674 t.unregisterNode(this);
676 this.id = this.attributes.id = id;
678 t.registerNode(this);
685 onIdChange: Ext.emptyFn,
688 * Returns the path for this node. The path can be used to expand or select this node programmatically.
689 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
690 * @return {String} The path
692 getPath : function(attr){
694 var p = this.parentNode;
695 var b = [this.attributes[attr]];
697 b.unshift(p.attributes[attr]);
700 var sep = this.getOwnerTree().pathSeparator;
701 return sep + b.join(sep);
705 * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
706 * will be the args provided or the current node. If the function returns false at any point,
707 * the bubble is stopped.
708 * @param {Function} fn The function to call
709 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
710 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
712 bubble : function(fn, scope, args){
715 if(fn.apply(scope || p, args || [p]) === false){
723 * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
724 * will be the args provided or the current node. If the function returns false at any point,
725 * the cascade is stopped on that branch.
726 * @param {Function} fn The function to call
727 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
728 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
730 cascade : function(fn, scope, args){
731 if(fn.apply(scope || this, args || [this]) !== false){
732 var cs = this.childNodes;
733 for(var i = 0, len = cs.length; i < len; i++) {
734 cs[i].cascade(fn, scope, args);
740 * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
741 * will be the args provided or the current node. If the function returns false at any point,
742 * the iteration stops.
743 * @param {Function} fn The function to call
744 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
745 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
747 eachChild : function(fn, scope, args){
748 var cs = this.childNodes;
749 for(var i = 0, len = cs.length; i < len; i++) {
750 if(fn.apply(scope || this, args || [cs[i]]) === false){
757 * Finds the first child that has the attribute with the specified value.
758 * @param {String} attribute The attribute name
759 * @param {Mixed} value The value to search for
760 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
761 * @return {Node} The found child or null if none was found
763 findChild : function(attribute, value, deep){
764 return this.findChildBy(function(){
765 return this.attributes[attribute] == value;
770 * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
771 * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
772 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
773 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
774 * @return {Node} The found child or null if none was found
776 findChildBy : function(fn, scope, deep){
777 var cs = this.childNodes,
784 if(fn.call(scope || n, n) === true){
787 res = n.findChildBy(fn, scope, deep);
798 * Sorts this nodes children using the supplied sort function.
799 * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
800 * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
802 sort : function(fn, scope){
803 var cs = this.childNodes;
806 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
808 for(var i = 0; i < len; i++){
810 n.previousSibling = cs[i-1];
811 n.nextSibling = cs[i+1];
813 this.setFirstChild(n);
816 this.setLastChild(n);
823 * Returns true if this node is an ancestor (at any point) of the passed node.
827 contains : function(node){
828 return node.isAncestor(this);
832 * Returns true if the passed node is an ancestor (at any point) of this node.
836 isAncestor : function(node){
837 var p = this.parentNode;
847 toString : function(){
848 return "[Node"+(this.id?" "+this.id:"")+"]";