Tree.js
上传用户:shuoshiled
上传日期:2018-01-28
资源大小:10124k
文件大小:25k
源码类别:

中间件编程

开发平台:

JavaScript

  1. /*!  * Ext JS Library 3.0.0  * Copyright(c) 2006-2009 Ext JS, LLC  * licensing@extjs.com  * http://www.extjs.com/license  */ /**
  2.  * @class Ext.data.Tree
  3.  * @extends Ext.util.Observable
  4.  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
  5.  * in the tree have most standard DOM functionality.
  6.  * @constructor
  7.  * @param {Node} root (optional) The root node
  8.  */
  9. Ext.data.Tree = function(root){
  10.    this.nodeHash = {};
  11.    /**
  12.     * The root node for this tree
  13.     * @type Node
  14.     */
  15.    this.root = null;
  16.    if(root){
  17.        this.setRootNode(root);
  18.    }
  19.    this.addEvents(
  20.        /**
  21.         * @event append
  22.         * Fires when a new child node is appended to a node in this tree.
  23.         * @param {Tree} tree The owner tree
  24.         * @param {Node} parent The parent node
  25.         * @param {Node} node The newly appended node
  26.         * @param {Number} index The index of the newly appended node
  27.         */
  28.        "append",
  29.        /**
  30.         * @event remove
  31.         * Fires when a child node is removed from a node in this tree.
  32.         * @param {Tree} tree The owner tree
  33.         * @param {Node} parent The parent node
  34.         * @param {Node} node The child node removed
  35.         */
  36.        "remove",
  37.        /**
  38.         * @event move
  39.         * Fires when a node is moved to a new location in the tree
  40.         * @param {Tree} tree The owner tree
  41.         * @param {Node} node The node moved
  42.         * @param {Node} oldParent The old parent of this node
  43.         * @param {Node} newParent The new parent of this node
  44.         * @param {Number} index The index it was moved to
  45.         */
  46.        "move",
  47.        /**
  48.         * @event insert
  49.         * Fires when a new child node is inserted in a node in this tree.
  50.         * @param {Tree} tree The owner tree
  51.         * @param {Node} parent The parent node
  52.         * @param {Node} node The child node inserted
  53.         * @param {Node} refNode The child node the node was inserted before
  54.         */
  55.        "insert",
  56.        /**
  57.         * @event beforeappend
  58.         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
  59.         * @param {Tree} tree The owner tree
  60.         * @param {Node} parent The parent node
  61.         * @param {Node} node The child node to be appended
  62.         */
  63.        "beforeappend",
  64.        /**
  65.         * @event beforeremove
  66.         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
  67.         * @param {Tree} tree The owner tree
  68.         * @param {Node} parent The parent node
  69.         * @param {Node} node The child node to be removed
  70.         */
  71.        "beforeremove",
  72.        /**
  73.         * @event beforemove
  74.         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
  75.         * @param {Tree} tree The owner tree
  76.         * @param {Node} node The node being moved
  77.         * @param {Node} oldParent The parent of the node
  78.         * @param {Node} newParent The new parent the node is moving to
  79.         * @param {Number} index The index it is being moved to
  80.         */
  81.        "beforemove",
  82.        /**
  83.         * @event beforeinsert
  84.         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
  85.         * @param {Tree} tree The owner tree
  86.         * @param {Node} parent The parent node
  87.         * @param {Node} node The child node to be inserted
  88.         * @param {Node} refNode The child node the node is being inserted before
  89.         */
  90.        "beforeinsert"
  91.    );
  92.     Ext.data.Tree.superclass.constructor.call(this);
  93. };
  94. Ext.extend(Ext.data.Tree, Ext.util.Observable, {
  95.     /**
  96.      * @cfg {String} pathSeparator
  97.      * The token used to separate paths in node ids (defaults to '/').
  98.      */
  99.     pathSeparator: "/",
  100.     // private
  101.     proxyNodeEvent : function(){
  102.         return this.fireEvent.apply(this, arguments);
  103.     },
  104.     /**
  105.      * Returns the root node for this tree.
  106.      * @return {Node}
  107.      */
  108.     getRootNode : function(){
  109.         return this.root;
  110.     },
  111.     /**
  112.      * Sets the root node for this tree.
  113.      * @param {Node} node
  114.      * @return {Node}
  115.      */
  116.     setRootNode : function(node){
  117.         this.root = node;
  118.         node.ownerTree = this;
  119.         node.isRoot = true;
  120.         this.registerNode(node);
  121.         return node;
  122.     },
  123.     /**
  124.      * Gets a node in this tree by its id.
  125.      * @param {String} id
  126.      * @return {Node}
  127.      */
  128.     getNodeById : function(id){
  129.         return this.nodeHash[id];
  130.     },
  131.     // private
  132.     registerNode : function(node){
  133.         this.nodeHash[node.id] = node;
  134.     },
  135.     // private
  136.     unregisterNode : function(node){
  137.         delete this.nodeHash[node.id];
  138.     },
  139.     toString : function(){
  140.         return "[Tree"+(this.id?" "+this.id:"")+"]";
  141.     }
  142. });
  143. /**
  144.  * @class Ext.data.Node
  145.  * @extends Ext.util.Observable
  146.  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
  147.  * @cfg {String} id The id for this node. If one is not specified, one is generated.
  148.  * @constructor
  149.  * @param {Object} attributes The attributes/config for the node
  150.  */
  151. Ext.data.Node = function(attributes){
  152.     /**
  153.      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
  154.      * @type {Object}
  155.      */
  156.     this.attributes = attributes || {};
  157.     this.leaf = this.attributes.leaf;
  158.     /**
  159.      * The node id. @type String
  160.      */
  161.     this.id = this.attributes.id;
  162.     if(!this.id){
  163.         this.id = Ext.id(null, "xnode-");
  164.         this.attributes.id = this.id;
  165.     }
  166.     /**
  167.      * All child nodes of this node. @type Array
  168.      */
  169.     this.childNodes = [];
  170.     if(!this.childNodes.indexOf){ // indexOf is a must
  171.         this.childNodes.indexOf = function(o){
  172.             for(var i = 0, len = this.length; i < len; i++){
  173.                 if(this[i] == o){
  174.                     return i;
  175.                 }
  176.             }
  177.             return -1;
  178.         };
  179.     }
  180.     /**
  181.      * The parent node for this node. @type Node
  182.      */
  183.     this.parentNode = null;
  184.     /**
  185.      * The first direct child node of this node, or null if this node has no child nodes. @type Node
  186.      */
  187.     this.firstChild = null;
  188.     /**
  189.      * The last direct child node of this node, or null if this node has no child nodes. @type Node
  190.      */
  191.     this.lastChild = null;
  192.     /**
  193.      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
  194.      */
  195.     this.previousSibling = null;
  196.     /**
  197.      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
  198.      */
  199.     this.nextSibling = null;
  200.     this.addEvents({
  201.        /**
  202.         * @event append
  203.         * Fires when a new child node is appended
  204.         * @param {Tree} tree The owner tree
  205.         * @param {Node} this This node
  206.         * @param {Node} node The newly appended node
  207.         * @param {Number} index The index of the newly appended node
  208.         */
  209.        "append" : true,
  210.        /**
  211.         * @event remove
  212.         * Fires when a child node is removed
  213.         * @param {Tree} tree The owner tree
  214.         * @param {Node} this This node
  215.         * @param {Node} node The removed node
  216.         */
  217.        "remove" : true,
  218.        /**
  219.         * @event move
  220.         * Fires when this node is moved to a new location in the tree
  221.         * @param {Tree} tree The owner tree
  222.         * @param {Node} this This node
  223.         * @param {Node} oldParent The old parent of this node
  224.         * @param {Node} newParent The new parent of this node
  225.         * @param {Number} index The index it was moved to
  226.         */
  227.        "move" : true,
  228.        /**
  229.         * @event insert
  230.         * Fires when a new child node is inserted.
  231.         * @param {Tree} tree The owner tree
  232.         * @param {Node} this This node
  233.         * @param {Node} node The child node inserted
  234.         * @param {Node} refNode The child node the node was inserted before
  235.         */
  236.        "insert" : true,
  237.        /**
  238.         * @event beforeappend
  239.         * Fires before a new child is appended, return false to cancel the append.
  240.         * @param {Tree} tree The owner tree
  241.         * @param {Node} this This node
  242.         * @param {Node} node The child node to be appended
  243.         */
  244.        "beforeappend" : true,
  245.        /**
  246.         * @event beforeremove
  247.         * Fires before a child is removed, return false to cancel the remove.
  248.         * @param {Tree} tree The owner tree
  249.         * @param {Node} this This node
  250.         * @param {Node} node The child node to be removed
  251.         */
  252.        "beforeremove" : true,
  253.        /**
  254.         * @event beforemove
  255.         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
  256.         * @param {Tree} tree The owner tree
  257.         * @param {Node} this This node
  258.         * @param {Node} oldParent The parent of this node
  259.         * @param {Node} newParent The new parent this node is moving to
  260.         * @param {Number} index The index it is being moved to
  261.         */
  262.        "beforemove" : true,
  263.        /**
  264.         * @event beforeinsert
  265.         * Fires before a new child is inserted, return false to cancel the insert.
  266.         * @param {Tree} tree The owner tree
  267.         * @param {Node} this This node
  268.         * @param {Node} node The child node to be inserted
  269.         * @param {Node} refNode The child node the node is being inserted before
  270.         */
  271.        "beforeinsert" : true
  272.    });
  273.     this.listeners = this.attributes.listeners;
  274.     Ext.data.Node.superclass.constructor.call(this);
  275. };
  276. Ext.extend(Ext.data.Node, Ext.util.Observable, {
  277.     // private
  278.     fireEvent : function(evtName){
  279.         // first do standard event for this node
  280.         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
  281.             return false;
  282.         }
  283.         // then bubble it up to the tree if the event wasn't cancelled
  284.         var ot = this.getOwnerTree();
  285.         if(ot){
  286.             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
  287.                 return false;
  288.             }
  289.         }
  290.         return true;
  291.     },
  292.     /**
  293.      * Returns true if this node is a leaf
  294.      * @return {Boolean}
  295.      */
  296.     isLeaf : function(){
  297.         return this.leaf === true;
  298.     },
  299.     // private
  300.     setFirstChild : function(node){
  301.         this.firstChild = node;
  302.     },
  303.     //private
  304.     setLastChild : function(node){
  305.         this.lastChild = node;
  306.     },
  307.     /**
  308.      * Returns true if this node is the last child of its parent
  309.      * @return {Boolean}
  310.      */
  311.     isLast : function(){
  312.        return (!this.parentNode ? true : this.parentNode.lastChild == this);
  313.     },
  314.     /**
  315.      * Returns true if this node is the first child of its parent
  316.      * @return {Boolean}
  317.      */
  318.     isFirst : function(){
  319.        return (!this.parentNode ? true : this.parentNode.firstChild == this);
  320.     },
  321.     /**
  322.      * Returns true if this node has one or more child nodes, else false.
  323.      * @return {Boolean}
  324.      */
  325.     hasChildNodes : function(){
  326.         return !this.isLeaf() && this.childNodes.length > 0;
  327.     },
  328.     
  329.     /**
  330.      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
  331.      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
  332.      * @return {Boolean}
  333.      */
  334.     isExpandable : function(){
  335.         return this.attributes.expandable || this.hasChildNodes();
  336.     },
  337.     /**
  338.      * Insert node(s) as the last child node of this node.
  339.      * @param {Node/Array} node The node or Array of nodes to append
  340.      * @return {Node} The appended node if single append, or null if an array was passed
  341.      */
  342.     appendChild : function(node){
  343.         var multi = false;
  344.         if(Ext.isArray(node)){
  345.             multi = node;
  346.         }else if(arguments.length > 1){
  347.             multi = arguments;
  348.         }
  349.         // if passed an array or multiple args do them one by one
  350.         if(multi){
  351.             for(var i = 0, len = multi.length; i < len; i++) {
  352.              this.appendChild(multi[i]);
  353.             }
  354.         }else{
  355.             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
  356.                 return false;
  357.             }
  358.             var index = this.childNodes.length;
  359.             var oldParent = node.parentNode;
  360.             // it's a move, make sure we move it cleanly
  361.             if(oldParent){
  362.                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
  363.                     return false;
  364.                 }
  365.                 oldParent.removeChild(node);
  366.             }
  367.             index = this.childNodes.length;
  368.             if(index === 0){
  369.                 this.setFirstChild(node);
  370.             }
  371.             this.childNodes.push(node);
  372.             node.parentNode = this;
  373.             var ps = this.childNodes[index-1];
  374.             if(ps){
  375.                 node.previousSibling = ps;
  376.                 ps.nextSibling = node;
  377.             }else{
  378.                 node.previousSibling = null;
  379.             }
  380.             node.nextSibling = null;
  381.             this.setLastChild(node);
  382.             node.setOwnerTree(this.getOwnerTree());
  383.             this.fireEvent("append", this.ownerTree, this, node, index);
  384.             if(oldParent){
  385.                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
  386.             }
  387.             return node;
  388.         }
  389.     },
  390.     /**
  391.      * Removes a child node from this node.
  392.      * @param {Node} node The node to remove
  393.      * @return {Node} The removed node
  394.      */
  395.     removeChild : function(node){
  396.         var index = this.childNodes.indexOf(node);
  397.         if(index == -1){
  398.             return false;
  399.         }
  400.         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
  401.             return false;
  402.         }
  403.         // remove it from childNodes collection
  404.         this.childNodes.splice(index, 1);
  405.         // update siblings
  406.         if(node.previousSibling){
  407.             node.previousSibling.nextSibling = node.nextSibling;
  408.         }
  409.         if(node.nextSibling){
  410.             node.nextSibling.previousSibling = node.previousSibling;
  411.         }
  412.         // update child refs
  413.         if(this.firstChild == node){
  414.             this.setFirstChild(node.nextSibling);
  415.         }
  416.         if(this.lastChild == node){
  417.             this.setLastChild(node.previousSibling);
  418.         }
  419.         node.setOwnerTree(null);
  420.         // clear any references from the node
  421.         node.parentNode = null;
  422.         node.previousSibling = null;
  423.         node.nextSibling = null;
  424.         this.fireEvent("remove", this.ownerTree, this, node);
  425.         return node;
  426.     },
  427.     /**
  428.      * Inserts the first node before the second node in this nodes childNodes collection.
  429.      * @param {Node} node The node to insert
  430.      * @param {Node} refNode The node to insert before (if null the node is appended)
  431.      * @return {Node} The inserted node
  432.      */
  433.     insertBefore : function(node, refNode){
  434.         if(!refNode){ // like standard Dom, refNode can be null for append
  435.             return this.appendChild(node);
  436.         }
  437.         // nothing to do
  438.         if(node == refNode){
  439.             return false;
  440.         }
  441.         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
  442.             return false;
  443.         }
  444.         var index = this.childNodes.indexOf(refNode);
  445.         var oldParent = node.parentNode;
  446.         var refIndex = index;
  447.         // when moving internally, indexes will change after remove
  448.         if(oldParent == this && this.childNodes.indexOf(node) < index){
  449.             refIndex--;
  450.         }
  451.         // it's a move, make sure we move it cleanly
  452.         if(oldParent){
  453.             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
  454.                 return false;
  455.             }
  456.             oldParent.removeChild(node);
  457.         }
  458.         if(refIndex === 0){
  459.             this.setFirstChild(node);
  460.         }
  461.         this.childNodes.splice(refIndex, 0, node);
  462.         node.parentNode = this;
  463.         var ps = this.childNodes[refIndex-1];
  464.         if(ps){
  465.             node.previousSibling = ps;
  466.             ps.nextSibling = node;
  467.         }else{
  468.             node.previousSibling = null;
  469.         }
  470.         node.nextSibling = refNode;
  471.         refNode.previousSibling = node;
  472.         node.setOwnerTree(this.getOwnerTree());
  473.         this.fireEvent("insert", this.ownerTree, this, node, refNode);
  474.         if(oldParent){
  475.             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
  476.         }
  477.         return node;
  478.     },
  479.     /**
  480.      * Removes this node from its parent
  481.      * @return {Node} this
  482.      */
  483.     remove : function(){
  484.         this.parentNode.removeChild(this);
  485.         return this;
  486.     },
  487.     /**
  488.      * Returns the child node at the specified index.
  489.      * @param {Number} index
  490.      * @return {Node}
  491.      */
  492.     item : function(index){
  493.         return this.childNodes[index];
  494.     },
  495.     /**
  496.      * Replaces one child node in this node with another.
  497.      * @param {Node} newChild The replacement node
  498.      * @param {Node} oldChild The node to replace
  499.      * @return {Node} The replaced node
  500.      */
  501.     replaceChild : function(newChild, oldChild){
  502.         var s = oldChild ? oldChild.nextSibling : null;
  503.         this.removeChild(oldChild);
  504.         this.insertBefore(newChild, s);
  505.         return oldChild;
  506.     },
  507.     /**
  508.      * Returns the index of a child node
  509.      * @param {Node} node
  510.      * @return {Number} The index of the node or -1 if it was not found
  511.      */
  512.     indexOf : function(child){
  513.         return this.childNodes.indexOf(child);
  514.     },
  515.     /**
  516.      * Returns the tree this node is in.
  517.      * @return {Tree}
  518.      */
  519.     getOwnerTree : function(){
  520.         // if it doesn't have one, look for one
  521.         if(!this.ownerTree){
  522.             var p = this;
  523.             while(p){
  524.                 if(p.ownerTree){
  525.                     this.ownerTree = p.ownerTree;
  526.                     break;
  527.                 }
  528.                 p = p.parentNode;
  529.             }
  530.         }
  531.         return this.ownerTree;
  532.     },
  533.     /**
  534.      * Returns depth of this node (the root node has a depth of 0)
  535.      * @return {Number}
  536.      */
  537.     getDepth : function(){
  538.         var depth = 0;
  539.         var p = this;
  540.         while(p.parentNode){
  541.             ++depth;
  542.             p = p.parentNode;
  543.         }
  544.         return depth;
  545.     },
  546.     // private
  547.     setOwnerTree : function(tree){
  548.         // if it is a move, we need to update everyone
  549.         if(tree != this.ownerTree){
  550.             if(this.ownerTree){
  551.                 this.ownerTree.unregisterNode(this);
  552.             }
  553.             this.ownerTree = tree;
  554.             var cs = this.childNodes;
  555.             for(var i = 0, len = cs.length; i < len; i++) {
  556.              cs[i].setOwnerTree(tree);
  557.             }
  558.             if(tree){
  559.                 tree.registerNode(this);
  560.             }
  561.         }
  562.     },
  563.     
  564.     /**
  565.      * Changes the id of this node.
  566.      * @param {String} id The new id for the node.
  567.      */
  568.     setId: function(id){
  569.         if(id !== this.id){
  570.             var t = this.ownerTree;
  571.             if(t){
  572.                 t.unregisterNode(this);
  573.             }
  574.             this.id = id;
  575.             if(t){
  576.                 t.registerNode(this);
  577.             }
  578.             this.onIdChange(id);
  579.         }
  580.     },
  581.     
  582.     // private
  583.     onIdChange: Ext.emptyFn,
  584.     /**
  585.      * Returns the path for this node. The path can be used to expand or select this node programmatically.
  586.      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
  587.      * @return {String} The path
  588.      */
  589.     getPath : function(attr){
  590.         attr = attr || "id";
  591.         var p = this.parentNode;
  592.         var b = [this.attributes[attr]];
  593.         while(p){
  594.             b.unshift(p.attributes[attr]);
  595.             p = p.parentNode;
  596.         }
  597.         var sep = this.getOwnerTree().pathSeparator;
  598.         return sep + b.join(sep);
  599.     },
  600.     /**
  601.      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
  602.      * function call will be the scope provided or the current node. The arguments to the function
  603.      * will be the args provided or the current node. If the function returns false at any point,
  604.      * the bubble is stopped.
  605.      * @param {Function} fn The function to call
  606.      * @param {Object} scope (optional) The scope of the function (defaults to current node)
  607.      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
  608.      */
  609.     bubble : function(fn, scope, args){
  610.         var p = this;
  611.         while(p){
  612.             if(fn.apply(scope || p, args || [p]) === false){
  613.                 break;
  614.             }
  615.             p = p.parentNode;
  616.         }
  617.     },
  618.     /**
  619.      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
  620.      * function call will be the scope provided or the current node. The arguments to the function
  621.      * will be the args provided or the current node. If the function returns false at any point,
  622.      * the cascade is stopped on that branch.
  623.      * @param {Function} fn The function to call
  624.      * @param {Object} scope (optional) The scope of the function (defaults to current node)
  625.      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
  626.      */
  627.     cascade : function(fn, scope, args){
  628.         if(fn.apply(scope || this, args || [this]) !== false){
  629.             var cs = this.childNodes;
  630.             for(var i = 0, len = cs.length; i < len; i++) {
  631.              cs[i].cascade(fn, scope, args);
  632.             }
  633.         }
  634.     },
  635.     /**
  636.      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
  637.      * function call will be the scope provided or the current node. The arguments to the function
  638.      * will be the args provided or the current node. If the function returns false at any point,
  639.      * the iteration stops.
  640.      * @param {Function} fn The function to call
  641.      * @param {Object} scope (optional) The scope of the function (defaults to current node)
  642.      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
  643.      */
  644.     eachChild : function(fn, scope, args){
  645.         var cs = this.childNodes;
  646.         for(var i = 0, len = cs.length; i < len; i++) {
  647.          if(fn.apply(scope || this, args || [cs[i]]) === false){
  648.              break;
  649.          }
  650.         }
  651.     },
  652.     /**
  653.      * Finds the first child that has the attribute with the specified value.
  654.      * @param {String} attribute The attribute name
  655.      * @param {Mixed} value The value to search for
  656.      * @return {Node} The found child or null if none was found
  657.      */
  658.     findChild : function(attribute, value){
  659.         var cs = this.childNodes;
  660.         for(var i = 0, len = cs.length; i < len; i++) {
  661.          if(cs[i].attributes[attribute] == value){
  662.              return cs[i];
  663.          }
  664.         }
  665.         return null;
  666.     },
  667.     /**
  668.      * Finds the first child by a custom function. The child matches if the function passed
  669.      * returns true.
  670.      * @param {Function} fn
  671.      * @param {Object} scope (optional)
  672.      * @return {Node} The found child or null if none was found
  673.      */
  674.     findChildBy : function(fn, scope){
  675.         var cs = this.childNodes;
  676.         for(var i = 0, len = cs.length; i < len; i++) {
  677.          if(fn.call(scope||cs[i], cs[i]) === true){
  678.              return cs[i];
  679.          }
  680.         }
  681.         return null;
  682.     },
  683.     /**
  684.      * Sorts this nodes children using the supplied sort function
  685.      * @param {Function} fn
  686.      * @param {Object} scope (optional)
  687.      */
  688.     sort : function(fn, scope){
  689.         var cs = this.childNodes;
  690.         var len = cs.length;
  691.         if(len > 0){
  692.             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
  693.             cs.sort(sortFn);
  694.             for(var i = 0; i < len; i++){
  695.                 var n = cs[i];
  696.                 n.previousSibling = cs[i-1];
  697.                 n.nextSibling = cs[i+1];
  698.                 if(i === 0){
  699.                     this.setFirstChild(n);
  700.                 }
  701.                 if(i == len-1){
  702.                     this.setLastChild(n);
  703.                 }
  704.             }
  705.         }
  706.     },
  707.     /**
  708.      * Returns true if this node is an ancestor (at any point) of the passed node.
  709.      * @param {Node} node
  710.      * @return {Boolean}
  711.      */
  712.     contains : function(node){
  713.         return node.isAncestor(this);
  714.     },
  715.     /**
  716.      * Returns true if the passed node is an ancestor (at any point) of this node.
  717.      * @param {Node} node
  718.      * @return {Boolean}
  719.      */
  720.     isAncestor : function(node){
  721.         var p = this.parentNode;
  722.         while(p){
  723.             if(p == node){
  724.                 return true;
  725.             }
  726.             p = p.parentNode;
  727.         }
  728.         return false;
  729.     },
  730.     toString : function(){
  731.         return "[Node"+(this.id?" "+this.id:"")+"]";
  732.     }
  733. });