3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
\r
4 <title>The source code</title>
\r
5 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
\r
6 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
\r
8 <body onload="prettyPrint();">
\r
9 <pre class="prettyprint lang-js"><div id="cls-Ext.data.Tree"></div>/**
\r
10 * @class Ext.data.Tree
\r
11 * @extends Ext.util.Observable
\r
12 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
\r
13 * in the tree have most standard DOM functionality.
\r
15 * @param {Node} root (optional) The root node
\r
17 Ext.data.Tree = function(root){
\r
19 <div id="prop-Ext.data.Tree-root"></div>/**
\r
20 * The root node for this tree
\r
25 this.setRootNode(root);
\r
28 <div id="event-Ext.data.Tree-append"></div>/**
\r
30 * Fires when a new child node is appended to a node in this tree.
\r
31 * @param {Tree} tree The owner tree
\r
32 * @param {Node} parent The parent node
\r
33 * @param {Node} node The newly appended node
\r
34 * @param {Number} index The index of the newly appended node
\r
37 <div id="event-Ext.data.Tree-remove"></div>/**
\r
39 * Fires when a child node is removed from a node in this tree.
\r
40 * @param {Tree} tree The owner tree
\r
41 * @param {Node} parent The parent node
\r
42 * @param {Node} node The child node removed
\r
45 <div id="event-Ext.data.Tree-move"></div>/**
\r
47 * Fires when a node is moved to a new location in the tree
\r
48 * @param {Tree} tree The owner tree
\r
49 * @param {Node} node The node moved
\r
50 * @param {Node} oldParent The old parent of this node
\r
51 * @param {Node} newParent The new parent of this node
\r
52 * @param {Number} index The index it was moved to
\r
55 <div id="event-Ext.data.Tree-insert"></div>/**
\r
57 * Fires when a new child node is inserted in a node in this tree.
\r
58 * @param {Tree} tree The owner tree
\r
59 * @param {Node} parent The parent node
\r
60 * @param {Node} node The child node inserted
\r
61 * @param {Node} refNode The child node the node was inserted before
\r
64 <div id="event-Ext.data.Tree-beforeappend"></div>/**
\r
65 * @event beforeappend
\r
66 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
\r
67 * @param {Tree} tree The owner tree
\r
68 * @param {Node} parent The parent node
\r
69 * @param {Node} node The child node to be appended
\r
72 <div id="event-Ext.data.Tree-beforeremove"></div>/**
\r
73 * @event beforeremove
\r
74 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
\r
75 * @param {Tree} tree The owner tree
\r
76 * @param {Node} parent The parent node
\r
77 * @param {Node} node The child node to be removed
\r
80 <div id="event-Ext.data.Tree-beforemove"></div>/**
\r
82 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
\r
83 * @param {Tree} tree The owner tree
\r
84 * @param {Node} node The node being moved
\r
85 * @param {Node} oldParent The parent of the node
\r
86 * @param {Node} newParent The new parent the node is moving to
\r
87 * @param {Number} index The index it is being moved to
\r
90 <div id="event-Ext.data.Tree-beforeinsert"></div>/**
\r
91 * @event beforeinsert
\r
92 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
\r
93 * @param {Tree} tree The owner tree
\r
94 * @param {Node} parent The parent node
\r
95 * @param {Node} node The child node to be inserted
\r
96 * @param {Node} refNode The child node the node is being inserted before
\r
101 Ext.data.Tree.superclass.constructor.call(this);
\r
104 Ext.extend(Ext.data.Tree, Ext.util.Observable, {
\r
105 <div id="cfg-Ext.data.Tree-pathSeparator"></div>/**
\r
106 * @cfg {String} pathSeparator
\r
107 * The token used to separate paths in node ids (defaults to '/').
\r
109 pathSeparator: "/",
\r
112 proxyNodeEvent : function(){
\r
113 return this.fireEvent.apply(this, arguments);
\r
116 <div id="method-Ext.data.Tree-getRootNode"></div>/**
\r
117 * Returns the root node for this tree.
\r
120 getRootNode : function(){
\r
124 <div id="method-Ext.data.Tree-setRootNode"></div>/**
\r
125 * Sets the root node for this tree.
\r
126 * @param {Node} node
\r
129 setRootNode : function(node){
\r
131 node.ownerTree = this;
\r
132 node.isRoot = true;
\r
133 this.registerNode(node);
\r
137 <div id="method-Ext.data.Tree-getNodeById"></div>/**
\r
138 * Gets a node in this tree by its id.
\r
139 * @param {String} id
\r
142 getNodeById : function(id){
\r
143 return this.nodeHash[id];
\r
147 registerNode : function(node){
\r
148 this.nodeHash[node.id] = node;
\r
152 unregisterNode : function(node){
\r
153 delete this.nodeHash[node.id];
\r
156 toString : function(){
\r
157 return "[Tree"+(this.id?" "+this.id:"")+"]";
\r
161 <div id="cls-Ext.data.Node"></div>/**
\r
162 * @class Ext.data.Node
\r
163 * @extends Ext.util.Observable
\r
164 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
\r
165 * @cfg {String} id The id for this node. If one is not specified, one is generated.
\r
167 * @param {Object} attributes The attributes/config for the node
\r
169 Ext.data.Node = function(attributes){
\r
171 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
\r
174 this.attributes = attributes || {};
\r
175 this.leaf = this.attributes.leaf;
\r
177 * The node id. @type String
\r
179 this.id = this.attributes.id;
\r
181 this.id = Ext.id(null, "xnode-");
\r
182 this.attributes.id = this.id;
\r
185 * All child nodes of this node. @type Array
\r
187 this.childNodes = [];
\r
188 if(!this.childNodes.indexOf){ // indexOf is a must
\r
189 this.childNodes.indexOf = function(o){
\r
190 for(var i = 0, len = this.length; i < len; i++){
\r
199 * The parent node for this node. @type Node
\r
201 this.parentNode = null;
\r
203 * The first direct child node of this node, or null if this node has no child nodes. @type Node
\r
205 this.firstChild = null;
\r
207 * The last direct child node of this node, or null if this node has no child nodes. @type Node
\r
209 this.lastChild = null;
\r
211 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
\r
213 this.previousSibling = null;
\r
215 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
\r
217 this.nextSibling = null;
\r
222 * Fires when a new child node is appended
\r
223 * @param {Tree} tree The owner tree
\r
224 * @param {Node} this This node
\r
225 * @param {Node} node The newly appended node
\r
226 * @param {Number} index The index of the newly appended node
\r
231 * Fires when a child node is removed
\r
232 * @param {Tree} tree The owner tree
\r
233 * @param {Node} this This node
\r
234 * @param {Node} node The removed node
\r
239 * Fires when this node is moved to a new location in the tree
\r
240 * @param {Tree} tree The owner tree
\r
241 * @param {Node} this This node
\r
242 * @param {Node} oldParent The old parent of this node
\r
243 * @param {Node} newParent The new parent of this node
\r
244 * @param {Number} index The index it was moved to
\r
249 * Fires when a new child node is inserted.
\r
250 * @param {Tree} tree The owner tree
\r
251 * @param {Node} this This node
\r
252 * @param {Node} node The child node inserted
\r
253 * @param {Node} refNode The child node the node was inserted before
\r
257 * @event beforeappend
\r
258 * Fires before a new child is appended, return false to cancel the append.
\r
259 * @param {Tree} tree The owner tree
\r
260 * @param {Node} this This node
\r
261 * @param {Node} node The child node to be appended
\r
263 "beforeappend" : true,
\r
265 * @event beforeremove
\r
266 * Fires before a child is removed, return false to cancel the remove.
\r
267 * @param {Tree} tree The owner tree
\r
268 * @param {Node} this This node
\r
269 * @param {Node} node The child node to be removed
\r
271 "beforeremove" : true,
\r
273 * @event beforemove
\r
274 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
\r
275 * @param {Tree} tree The owner tree
\r
276 * @param {Node} this This node
\r
277 * @param {Node} oldParent The parent of this node
\r
278 * @param {Node} newParent The new parent this node is moving to
\r
279 * @param {Number} index The index it is being moved to
\r
281 "beforemove" : true,
\r
283 * @event beforeinsert
\r
284 * Fires before a new child is inserted, return false to cancel the insert.
\r
285 * @param {Tree} tree The owner tree
\r
286 * @param {Node} this This node
\r
287 * @param {Node} node The child node to be inserted
\r
288 * @param {Node} refNode The child node the node is being inserted before
\r
290 "beforeinsert" : true
\r
292 this.listeners = this.attributes.listeners;
\r
293 Ext.data.Node.superclass.constructor.call(this);
\r
296 Ext.extend(Ext.data.Node, Ext.util.Observable, {
\r
298 fireEvent : function(evtName){
\r
299 // first do standard event for this node
\r
300 if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
\r
303 // then bubble it up to the tree if the event wasn't cancelled
\r
304 var ot = this.getOwnerTree();
\r
306 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
\r
314 * Returns true if this node is a leaf
\r
315 * @return {Boolean}
\r
317 isLeaf : function(){
\r
318 return this.leaf === true;
\r
322 setFirstChild : function(node){
\r
323 this.firstChild = node;
\r
327 setLastChild : function(node){
\r
328 this.lastChild = node;
\r
333 * Returns true if this node is the last child of its parent
\r
334 * @return {Boolean}
\r
336 isLast : function(){
\r
337 return (!this.parentNode ? true : this.parentNode.lastChild == this);
\r
341 * Returns true if this node is the first child of its parent
\r
342 * @return {Boolean}
\r
344 isFirst : function(){
\r
345 return (!this.parentNode ? true : this.parentNode.firstChild == this);
\r
349 * Returns true if this node has one or more child nodes, else false.
\r
350 * @return {Boolean}
\r
352 hasChildNodes : function(){
\r
353 return !this.isLeaf() && this.childNodes.length > 0;
\r
357 * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
\r
358 * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
\r
359 * @return {Boolean}
\r
361 isExpandable : function(){
\r
362 return this.attributes.expandable || this.hasChildNodes();
\r
366 * Insert node(s) as the last child node of this node.
\r
367 * @param {Node/Array} node The node or Array of nodes to append
\r
368 * @return {Node} The appended node if single append, or null if an array was passed
\r
370 appendChild : function(node){
\r
372 if(Ext.isArray(node)){
\r
374 }else if(arguments.length > 1){
\r
377 // if passed an array or multiple args do them one by one
\r
379 for(var i = 0, len = multi.length; i < len; i++) {
\r
380 this.appendChild(multi[i]);
\r
383 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
\r
386 var index = this.childNodes.length;
\r
387 var oldParent = node.parentNode;
\r
388 // it's a move, make sure we move it cleanly
\r
390 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
\r
393 oldParent.removeChild(node);
\r
395 index = this.childNodes.length;
\r
397 this.setFirstChild(node);
\r
399 this.childNodes.push(node);
\r
400 node.parentNode = this;
\r
401 var ps = this.childNodes[index-1];
\r
403 node.previousSibling = ps;
\r
404 ps.nextSibling = node;
\r
406 node.previousSibling = null;
\r
408 node.nextSibling = null;
\r
409 this.setLastChild(node);
\r
410 node.setOwnerTree(this.getOwnerTree());
\r
411 this.fireEvent("append", this.ownerTree, this, node, index);
\r
413 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
\r
420 * Removes a child node from this node.
\r
421 * @param {Node} node The node to remove
\r
422 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
\r
423 * @return {Node} The removed node
\r
425 removeChild : function(node, destroy){
\r
426 var index = this.childNodes.indexOf(node);
\r
430 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
\r
434 // remove it from childNodes collection
\r
435 this.childNodes.splice(index, 1);
\r
438 if(node.previousSibling){
\r
439 node.previousSibling.nextSibling = node.nextSibling;
\r
441 if(node.nextSibling){
\r
442 node.nextSibling.previousSibling = node.previousSibling;
\r
445 // update child refs
\r
446 if(this.firstChild == node){
\r
447 this.setFirstChild(node.nextSibling);
\r
449 if(this.lastChild == node){
\r
450 this.setLastChild(node.previousSibling);
\r
454 this.fireEvent("remove", this.ownerTree, this, node);
\r
462 clear : function(destroy){
\r
463 // clear any references from the node
\r
464 this.setOwnerTree(null, destroy);
\r
465 this.parentNode = this.previousSibling = this.nextSibling = null
\r
467 this.firstChild = this.lastChild = null;
\r
472 * Destroys the node.
\r
474 destroy : function(){
\r
475 this.purgeListeners();
\r
477 Ext.each(this.childNodes, function(n){
\r
480 this.childNodes = null;
\r
484 * Inserts the first node before the second node in this nodes childNodes collection.
\r
485 * @param {Node} node The node to insert
\r
486 * @param {Node} refNode The node to insert before (if null the node is appended)
\r
487 * @return {Node} The inserted node
\r
489 insertBefore : function(node, refNode){
\r
490 if(!refNode){ // like standard Dom, refNode can be null for append
\r
491 return this.appendChild(node);
\r
494 if(node == refNode){
\r
498 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
\r
501 var index = this.childNodes.indexOf(refNode);
\r
502 var oldParent = node.parentNode;
\r
503 var refIndex = index;
\r
505 // when moving internally, indexes will change after remove
\r
506 if(oldParent == this && this.childNodes.indexOf(node) < index){
\r
510 // it's a move, make sure we move it cleanly
\r
512 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
\r
515 oldParent.removeChild(node);
\r
517 if(refIndex === 0){
\r
518 this.setFirstChild(node);
\r
520 this.childNodes.splice(refIndex, 0, node);
\r
521 node.parentNode = this;
\r
522 var ps = this.childNodes[refIndex-1];
\r
524 node.previousSibling = ps;
\r
525 ps.nextSibling = node;
\r
527 node.previousSibling = null;
\r
529 node.nextSibling = refNode;
\r
530 refNode.previousSibling = node;
\r
531 node.setOwnerTree(this.getOwnerTree());
\r
532 this.fireEvent("insert", this.ownerTree, this, node, refNode);
\r
534 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
\r
540 * Removes this node from its parent
\r
541 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
\r
542 * @return {Node} this
\r
544 remove : function(destroy){
\r
545 this.parentNode.removeChild(this, destroy);
\r
550 * Returns the child node at the specified index.
\r
551 * @param {Number} index
\r
554 item : function(index){
\r
555 return this.childNodes[index];
\r
559 * Replaces one child node in this node with another.
\r
560 * @param {Node} newChild The replacement node
\r
561 * @param {Node} oldChild The node to replace
\r
562 * @return {Node} The replaced node
\r
564 replaceChild : function(newChild, oldChild){
\r
565 var s = oldChild ? oldChild.nextSibling : null;
\r
566 this.removeChild(oldChild);
\r
567 this.insertBefore(newChild, s);
\r
572 * Returns the index of a child node
\r
573 * @param {Node} node
\r
574 * @return {Number} The index of the node or -1 if it was not found
\r
576 indexOf : function(child){
\r
577 return this.childNodes.indexOf(child);
\r
581 * Returns the tree this node is in.
\r
584 getOwnerTree : function(){
\r
585 // if it doesn't have one, look for one
\r
586 if(!this.ownerTree){
\r
590 this.ownerTree = p.ownerTree;
\r
596 return this.ownerTree;
\r
600 * Returns depth of this node (the root node has a depth of 0)
\r
603 getDepth : function(){
\r
606 while(p.parentNode){
\r
614 setOwnerTree : function(tree, destroy){
\r
615 // if it is a move, we need to update everyone
\r
616 if(tree != this.ownerTree){
\r
617 if(this.ownerTree){
\r
618 this.ownerTree.unregisterNode(this);
\r
620 this.ownerTree = tree;
\r
621 // If we're destroying, we don't need to recurse since it will be called on each child node
\r
622 if(destroy !== true){
\r
623 Ext.each(this.childNodes, function(n){
\r
624 n.setOwnerTree(tree);
\r
628 tree.registerNode(this);
\r
634 * Changes the id of this node.
\r
635 * @param {String} id The new id for the node.
\r
637 setId: function(id){
\r
638 if(id !== this.id){
\r
639 var t = this.ownerTree;
\r
641 t.unregisterNode(this);
\r
643 this.id = this.attributes.id = id;
\r
645 t.registerNode(this);
\r
647 this.onIdChange(id);
\r
652 onIdChange: Ext.emptyFn,
\r
655 * Returns the path for this node. The path can be used to expand or select this node programmatically.
\r
656 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
\r
657 * @return {String} The path
\r
659 getPath : function(attr){
\r
660 attr = attr || "id";
\r
661 var p = this.parentNode;
\r
662 var b = [this.attributes[attr]];
\r
664 b.unshift(p.attributes[attr]);
\r
667 var sep = this.getOwnerTree().pathSeparator;
\r
668 return sep + b.join(sep);
\r
672 * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
\r
673 * will be the args provided or the current node. If the function returns false at any point,
\r
674 * the bubble is stopped.
\r
675 * @param {Function} fn The function to call
\r
676 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
\r
677 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
\r
679 bubble : function(fn, scope, args){
\r
682 if(fn.apply(scope || p, args || [p]) === false){
\r
690 * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
\r
691 * will be the args provided or the current node. If the function returns false at any point,
\r
692 * the cascade is stopped on that branch.
\r
693 * @param {Function} fn The function to call
\r
694 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
\r
695 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
\r
697 cascade : function(fn, scope, args){
\r
698 if(fn.apply(scope || this, args || [this]) !== false){
\r
699 var cs = this.childNodes;
\r
700 for(var i = 0, len = cs.length; i < len; i++) {
\r
701 cs[i].cascade(fn, scope, args);
\r
707 * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
\r
708 * will be the args provided or the current node. If the function returns false at any point,
\r
709 * the iteration stops.
\r
710 * @param {Function} fn The function to call
\r
711 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
\r
712 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
\r
714 eachChild : function(fn, scope, args){
\r
715 var cs = this.childNodes;
\r
716 for(var i = 0, len = cs.length; i < len; i++) {
\r
717 if(fn.apply(scope || this, args || [cs[i]]) === false){
\r
724 * Finds the first child that has the attribute with the specified value.
\r
725 * @param {String} attribute The attribute name
\r
726 * @param {Mixed} value The value to search for
\r
727 * @return {Node} The found child or null if none was found
\r
729 findChild : function(attribute, value){
\r
730 var cs = this.childNodes;
\r
731 for(var i = 0, len = cs.length; i < len; i++) {
\r
732 if(cs[i].attributes[attribute] == value){
\r
740 * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
\r
741 * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
\r
742 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
\r
743 * @return {Node} The found child or null if none was found
\r
745 findChildBy : function(fn, scope){
\r
746 var cs = this.childNodes;
\r
747 for(var i = 0, len = cs.length; i < len; i++) {
\r
748 if(fn.call(scope||cs[i], cs[i]) === true){
\r
756 * Sorts this nodes children using the supplied sort function.
\r
757 * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
\r
758 * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
\r
760 sort : function(fn, scope){
\r
761 var cs = this.childNodes;
\r
762 var len = cs.length;
\r
764 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
\r
766 for(var i = 0; i < len; i++){
\r
768 n.previousSibling = cs[i-1];
\r
769 n.nextSibling = cs[i+1];
\r
771 this.setFirstChild(n);
\r
774 this.setLastChild(n);
\r
781 * Returns true if this node is an ancestor (at any point) of the passed node.
\r
782 * @param {Node} node
\r
783 * @return {Boolean}
\r
785 contains : function(node){
\r
786 return node.isAncestor(this);
\r
790 * Returns true if the passed node is an ancestor (at any point) of this node.
\r
791 * @param {Node} node
\r
792 * @return {Boolean}
\r
794 isAncestor : function(node){
\r
795 var p = this.parentNode;
\r
805 toString : function(){
\r
806 return "[Node"+(this.id?" "+this.id:"")+"]";
\r