home *** CD-ROM | disk | FTP | other *** search
/ Computer Active 2010 July / CA07.iso / Multimedija / QuickTimeInstaller.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_treeoutline.js < prev    next >
Encoding:
JavaScript  |  2010-03-15  |  27.2 KB  |  847 lines

  1. /*
  2.  * Copyright (C) 2007 Apple Inc.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1.  Redistributions of source code must retain the above copyright
  9.  *     notice, this list of conditions and the following disclaimer. 
  10.  * 2.  Redistributions in binary form must reproduce the above copyright
  11.  *     notice, this list of conditions and the following disclaimer in the
  12.  *     documentation and/or other materials provided with the distribution. 
  13.  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  14.  *     its contributors may be used to endorse or promote products derived
  15.  *     from this software without specific prior written permission. 
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  18.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20.  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  21.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. function TreeOutline(listNode)
  30. {
  31.     this.children = [];
  32.     this.selectedTreeElement = null;
  33.     this._childrenListNode = listNode;
  34.     this._childrenListNode.removeChildren();
  35.     this._knownTreeElements = [];
  36.     this._treeElementsExpandedState = [];
  37.     this.expandTreeElementsWhenArrowing = false;
  38.     this.root = true;
  39.     this.hasChildren = false;
  40.     this.expanded = true;
  41.     this.selected = false;
  42.     this.treeOutline = this;
  43. }
  44.  
  45. TreeOutline._knownTreeElementNextIdentifier = 1;
  46.  
  47. TreeOutline._appendChild = function(child)
  48. {
  49.     if (!child)
  50.         throw("child can't be undefined or null");
  51.  
  52.     var lastChild = this.children[this.children.length - 1];
  53.     if (lastChild) {
  54.         lastChild.nextSibling = child;
  55.         child.previousSibling = lastChild;
  56.     } else {
  57.         child.previousSibling = null;
  58.         child.nextSibling = null;
  59.     }
  60.  
  61.     this.children.push(child);
  62.     this.hasChildren = true;
  63.     child.parent = this;
  64.     child.treeOutline = this.treeOutline;
  65.     child.treeOutline._rememberTreeElement(child);
  66.  
  67.     var current = child.children[0];
  68.     while (current) {
  69.         current.treeOutline = this.treeOutline;
  70.         current.treeOutline._rememberTreeElement(current);
  71.         current = current.traverseNextTreeElement(false, child, true);
  72.     }
  73.  
  74.     if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
  75.         child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
  76.  
  77.     if (!this._childrenListNode) {
  78.         this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
  79.         this._childrenListNode.parentTreeElement = this;
  80.         this._childrenListNode.addStyleClass("children");
  81.         if (this.hidden)
  82.             this._childrenListNode.addStyleClass("hidden");
  83.     }
  84.  
  85.     child._attach();
  86. }
  87.  
  88. TreeOutline._insertChild = function(child, index)
  89. {
  90.     if (!child)
  91.         throw("child can't be undefined or null");
  92.  
  93.     var previousChild = (index > 0 ? this.children[index - 1] : null);
  94.     if (previousChild) {
  95.         previousChild.nextSibling = child;
  96.         child.previousSibling = previousChild;
  97.     } else {
  98.         child.previousSibling = null;
  99.     }
  100.  
  101.     var nextChild = this.children[index];
  102.     if (nextChild) {
  103.         nextChild.previousSibling = child;
  104.         child.nextSibling = nextChild;
  105.     } else {
  106.         child.nextSibling = null;
  107.     }
  108.  
  109.     this.children.splice(index, 0, child);
  110.     this.hasChildren = true;
  111.     child.parent = this;
  112.     child.treeOutline = this.treeOutline;
  113.     child.treeOutline._rememberTreeElement(child);
  114.  
  115.     var current = child.children[0];
  116.     while (current) {
  117.         current.treeOutline = this.treeOutline;
  118.         current.treeOutline._rememberTreeElement(current);
  119.         current = current.traverseNextTreeElement(false, child, true);
  120.     }
  121.  
  122.     if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined)
  123.         child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier];
  124.  
  125.     if (!this._childrenListNode) {
  126.         this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
  127.         this._childrenListNode.parentTreeElement = this;
  128.         this._childrenListNode.addStyleClass("children");
  129.         if (this.hidden)
  130.             this._childrenListNode.addStyleClass("hidden");
  131.     }
  132.  
  133.     child._attach();
  134. }
  135.  
  136. TreeOutline._removeChildAtIndex = function(childIndex)
  137. {
  138.     if (childIndex < 0 || childIndex >= this.children.length)
  139.         throw("childIndex out of range");
  140.  
  141.     var child = this.children[childIndex];
  142.     this.children.splice(childIndex, 1);
  143.  
  144.     child.deselect();
  145.  
  146.     if (child.previousSibling)
  147.         child.previousSibling.nextSibling = child.nextSibling;
  148.     if (child.nextSibling)
  149.         child.nextSibling.previousSibling = child.previousSibling;
  150.  
  151.     if (child.treeOutline) {
  152.         child.treeOutline._forgetTreeElement(child);
  153.         child.treeOutline._forgetChildrenRecursive(child);
  154.     }
  155.  
  156.     child._detach();
  157.     child.treeOutline = null;
  158.     child.parent = null;
  159.     child.nextSibling = null;
  160.     child.previousSibling = null;
  161. }
  162.  
  163. TreeOutline._removeChild = function(child)
  164. {
  165.     if (!child)
  166.         throw("child can't be undefined or null");
  167.  
  168.     var childIndex = this.children.indexOf(child);
  169.     if (childIndex === -1)
  170.         throw("child not found in this node's children");
  171.  
  172.     TreeOutline._removeChildAtIndex.call(this, childIndex);
  173. }
  174.  
  175. TreeOutline._removeChildren = function()
  176. {
  177.     for (var i = 0; i < this.children.length; ++i) {
  178.         var child = this.children[i];
  179.         child.deselect();
  180.  
  181.         if (child.treeOutline) {
  182.             child.treeOutline._forgetTreeElement(child);
  183.             child.treeOutline._forgetChildrenRecursive(child);
  184.         }
  185.  
  186.         child._detach();
  187.         child.treeOutline = null;
  188.         child.parent = null;
  189.         child.nextSibling = null;
  190.         child.previousSibling = null;
  191.     }
  192.  
  193.     this.children = [];
  194. }
  195.  
  196. TreeOutline._removeChildrenRecursive = function()
  197. {
  198.     var childrenToRemove = this.children;
  199.  
  200.     var child = this.children[0];
  201.     while (child) {
  202.         if (child.children.length)
  203.             childrenToRemove = childrenToRemove.concat(child.children);
  204.         child = child.traverseNextTreeElement(false, this, true);
  205.     }
  206.  
  207.     for (var i = 0; i < childrenToRemove.length; ++i) {
  208.         var child = childrenToRemove[i];
  209.         child.deselect();
  210.         if (child.treeOutline)
  211.             child.treeOutline._forgetTreeElement(child);
  212.         child._detach();
  213.         child.children = [];
  214.         child.treeOutline = null;
  215.         child.parent = null;
  216.         child.nextSibling = null;
  217.         child.previousSibling = null;
  218.     }
  219.  
  220.     this.children = [];
  221. }
  222.  
  223. TreeOutline.prototype._rememberTreeElement = function(element)
  224. {
  225.     if (!this._knownTreeElements[element.identifier])
  226.         this._knownTreeElements[element.identifier] = [];
  227.  
  228.     // check if the element is already known
  229.     var elements = this._knownTreeElements[element.identifier];
  230.     if (elements.indexOf(element) !== -1)
  231.         return;
  232.  
  233.     // add the element
  234.     elements.push(element);
  235. }
  236.  
  237. TreeOutline.prototype._forgetTreeElement = function(element)
  238. {
  239.     if (this._knownTreeElements[element.identifier])
  240.         this._knownTreeElements[element.identifier].remove(element, true);
  241. }
  242.  
  243. TreeOutline.prototype._forgetChildrenRecursive = function(parentElement)
  244. {
  245.     var child = parentElement.children[0];
  246.     while (child) {
  247.         this._forgetTreeElement(child);
  248.         child = child.traverseNextTreeElement(false, this, true);
  249.     }
  250. }
  251.  
  252. TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, getParent, equal)
  253. {
  254.     if (!representedObject)
  255.         return null;
  256.  
  257.     if (!equal)
  258.         equal = function(a, b) { return a === b };
  259.  
  260.     if ("__treeElementIdentifier" in representedObject) {
  261.         // If this representedObject has a tree element identifier, and it is a known TreeElement
  262.         // in our tree we can just return that tree element.
  263.         var elements = this._knownTreeElements[representedObject.__treeElementIdentifier];
  264.         if (elements) {
  265.             for (var i = 0; i < elements.length; ++i)
  266.                 if (equal(elements[i].representedObject, representedObject))
  267.                     return elements[i];
  268.         }
  269.     }
  270.  
  271.     if (!isAncestor || !(isAncestor instanceof Function) || !getParent || !(getParent instanceof Function))
  272.         return null;
  273.  
  274.     // The representedObject isn't know, so we start at the top of the tree and work down to find the first
  275.     // tree element that represents representedObject or one of its ancestors.
  276.     var item;
  277.     var found = false;
  278.     for (var i = 0; i < this.children.length; ++i) {
  279.         item = this.children[i];
  280.         if (equal(item.representedObject, representedObject) || isAncestor(item.representedObject, representedObject)) {
  281.             found = true;
  282.             break;
  283.         }
  284.     }
  285.  
  286.     if (!found)
  287.         return null;
  288.  
  289.     // Make sure the item that we found is connected to the root of the tree.
  290.     // Build up a list of representedObject's ancestors that aren't already in our tree.
  291.     var ancestors = [];
  292.     var currentObject = representedObject;
  293.     while (currentObject) {
  294.         ancestors.unshift(currentObject);
  295.         if (equal(currentObject, item.representedObject))
  296.             break;
  297.         currentObject = getParent(currentObject);
  298.     }
  299.  
  300.     // For each of those ancestors we populate them to fill in the tree.
  301.     for (var i = 0; i < ancestors.length; ++i) {
  302.         // Make sure we don't call findTreeElement with the same representedObject
  303.         // again, to prevent infinite recursion.
  304.         if (equal(ancestors[i], representedObject))
  305.             continue;
  306.         // FIXME: we could do something faster than findTreeElement since we will know the next
  307.         // ancestor exists in the tree.
  308.         item = this.findTreeElement(ancestors[i], isAncestor, getParent, equal);
  309.         if (item && item.onpopulate)
  310.             item.onpopulate(item);
  311.     }
  312.  
  313.     // Now that all the ancestors are populated, try to find the representedObject again. This time
  314.     // without the isAncestor and getParent functions to prevent an infinite recursion if it isn't found.
  315.     return this.findTreeElement(representedObject, null, null, equal);
  316. }
  317.  
  318. TreeOutline.prototype.treeElementFromPoint = function(x, y)
  319. {
  320.     var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y);
  321.     var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]);
  322.     if (listNode)
  323.         return listNode.parentTreeElement || listNode.treeElement;
  324.     return null;
  325. }
  326.  
  327. TreeOutline.prototype.handleKeyEvent = function(event)
  328. {
  329.     if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey)
  330.         return false;
  331.  
  332.     var handled = false;
  333.     var nextSelectedElement;
  334.     if (event.keyIdentifier === "Up" && !event.altKey) {
  335.         nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true);
  336.         while (nextSelectedElement && !nextSelectedElement.selectable)
  337.             nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing);
  338.         handled = nextSelectedElement ? true : false;
  339.     } else if (event.keyIdentifier === "Down" && !event.altKey) {
  340.         nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true);
  341.         while (nextSelectedElement && !nextSelectedElement.selectable)
  342.             nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing);
  343.         handled = nextSelectedElement ? true : false;
  344.     } else if (event.keyIdentifier === "Left") {
  345.         if (this.selectedTreeElement.expanded) {
  346.             if (event.altKey)
  347.                 this.selectedTreeElement.collapseRecursively();
  348.             else
  349.                 this.selectedTreeElement.collapse();
  350.             handled = true;
  351.         } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) {
  352.             handled = true;
  353.             if (this.selectedTreeElement.parent.selectable) {
  354.                 nextSelectedElement = this.selectedTreeElement.parent;
  355.                 handled = nextSelectedElement ? true : false;
  356.             } else if (this.selectedTreeElement.parent)
  357.                 this.selectedTreeElement.parent.collapse();
  358.         }
  359.     } else if (event.keyIdentifier === "Right") {
  360.         if (!this.selectedTreeElement.revealed()) {
  361.             this.selectedTreeElement.reveal();
  362.             handled = true;
  363.         } else if (this.selectedTreeElement.hasChildren) {
  364.             handled = true;
  365.             if (this.selectedTreeElement.expanded) {
  366.                 nextSelectedElement = this.selectedTreeElement.children[0];
  367.                 handled = nextSelectedElement ? true : false;
  368.             } else {
  369.                 if (event.altKey)
  370.                     this.selectedTreeElement.expandRecursively();
  371.                 else
  372.                     this.selectedTreeElement.expand();
  373.             }
  374.         }
  375.     }
  376.  
  377.     if (nextSelectedElement) {
  378.         nextSelectedElement.reveal();
  379.         nextSelectedElement.select();
  380.     }
  381.  
  382.     if (handled) {
  383.         event.preventDefault();
  384.         event.stopPropagation();
  385.     }
  386.  
  387.     return handled;
  388. }
  389.  
  390. TreeOutline.prototype.expand = function()
  391. {
  392.     // this is the root, do nothing
  393. }
  394.  
  395. TreeOutline.prototype.collapse = function()
  396. {
  397.     // this is the root, do nothing
  398. }
  399.  
  400. TreeOutline.prototype.revealed = function()
  401. {
  402.     return true;
  403. }
  404.  
  405. TreeOutline.prototype.reveal = function()
  406. {
  407.     // this is the root, do nothing
  408. }
  409.  
  410. TreeOutline.prototype.appendChild = TreeOutline._appendChild;
  411. TreeOutline.prototype.insertChild = TreeOutline._insertChild;
  412. TreeOutline.prototype.removeChild = TreeOutline._removeChild;
  413. TreeOutline.prototype.removeChildAtIndex = TreeOutline._removeChildAtIndex;
  414. TreeOutline.prototype.removeChildren = TreeOutline._removeChildren;
  415. TreeOutline.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive;
  416.  
  417. function TreeElement(title, representedObject, hasChildren)
  418. {
  419.     this._title = title;
  420.     this.representedObject = (representedObject || {});
  421.  
  422.     if (this.representedObject.__treeElementIdentifier)
  423.         this.identifier = this.representedObject.__treeElementIdentifier;
  424.     else {
  425.         this.identifier = TreeOutline._knownTreeElementNextIdentifier++;
  426.         this.representedObject.__treeElementIdentifier = this.identifier;
  427.     }
  428.  
  429.     this._hidden = false;
  430.     this.expanded = false;
  431.     this.selected = false;
  432.     this.hasChildren = hasChildren;
  433.     this.children = [];
  434.     this.treeOutline = null;
  435.     this.parent = null;
  436.     this.previousSibling = null;
  437.     this.nextSibling = null;
  438.     this._listItemNode = null;
  439. }
  440.  
  441. TreeElement.prototype = {
  442.     selectable: true,
  443.     arrowToggleWidth: 10,
  444.  
  445.     get listItemElement() {
  446.         return this._listItemNode;
  447.     },
  448.  
  449.     get childrenListElement() {
  450.         return this._childrenListNode;
  451.     },
  452.  
  453.     get title() {
  454.         return this._title;
  455.     },
  456.  
  457.     set title(x) {
  458.         this._title = x;
  459.         if (this._listItemNode)
  460.             this._listItemNode.innerHTML = x;
  461.     },
  462.  
  463.     get tooltip() {
  464.         return this._tooltip;
  465.     },
  466.  
  467.     set tooltip(x) {
  468.         this._tooltip = x;
  469.         if (this._listItemNode)
  470.             this._listItemNode.title = x ? x : "";
  471.     },
  472.  
  473.     get hasChildren() {
  474.         return this._hasChildren;
  475.     },
  476.  
  477.     set hasChildren(x) {
  478.         if (this._hasChildren === x)
  479.             return;
  480.  
  481.         this._hasChildren = x;
  482.  
  483.         if (!this._listItemNode)
  484.             return;
  485.  
  486.         if (x)
  487.             this._listItemNode.addStyleClass("parent");
  488.         else {
  489.             this._listItemNode.removeStyleClass("parent");
  490.             this.collapse();
  491.         }
  492.     },
  493.  
  494.     get hidden() {
  495.         return this._hidden;
  496.     },
  497.  
  498.     set hidden(x) {
  499.         if (this._hidden === x)
  500.             return;
  501.  
  502.         this._hidden = x;
  503.  
  504.         if (x) {
  505.             if (this._listItemNode)
  506.                 this._listItemNode.addStyleClass("hidden");
  507.             if (this._childrenListNode)
  508.                 this._childrenListNode.addStyleClass("hidden");
  509.         } else {
  510.             if (this._listItemNode)
  511.                 this._listItemNode.removeStyleClass("hidden");
  512.             if (this._childrenListNode)
  513.                 this._childrenListNode.removeStyleClass("hidden");
  514.         }
  515.     },
  516.  
  517.     get shouldRefreshChildren() {
  518.         return this._shouldRefreshChildren;
  519.     },
  520.  
  521.     set shouldRefreshChildren(x) {
  522.         this._shouldRefreshChildren = x;
  523.         if (x && this.expanded)
  524.             this.expand();
  525.     }
  526. }
  527.  
  528. TreeElement.prototype.appendChild = TreeOutline._appendChild;
  529. TreeElement.prototype.insertChild = TreeOutline._insertChild;
  530. TreeElement.prototype.removeChild = TreeOutline._removeChild;
  531. TreeElement.prototype.removeChildAtIndex = TreeOutline._removeChildAtIndex;
  532. TreeElement.prototype.removeChildren = TreeOutline._removeChildren;
  533. TreeElement.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive;
  534.  
  535. TreeElement.prototype._attach = function()
  536. {
  537.     if (!this._listItemNode || this.parent._shouldRefreshChildren) {
  538.         if (this._listItemNode && this._listItemNode.parentNode)
  539.             this._listItemNode.parentNode.removeChild(this._listItemNode);
  540.  
  541.         this._listItemNode = this.treeOutline._childrenListNode.ownerDocument.createElement("li");
  542.         this._listItemNode.treeElement = this;
  543.         this._listItemNode.innerHTML = this._title;
  544.         this._listItemNode.title = this._tooltip ? this._tooltip : "";
  545.  
  546.         if (this.hidden)
  547.             this._listItemNode.addStyleClass("hidden");
  548.         if (this.hasChildren)
  549.             this._listItemNode.addStyleClass("parent");
  550.         if (this.expanded)
  551.             this._listItemNode.addStyleClass("expanded");
  552.         if (this.selected)
  553.             this._listItemNode.addStyleClass("selected");
  554.  
  555.         this._listItemNode.addEventListener("mousedown", TreeElement.treeElementSelected, false);
  556.         this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false);
  557.         this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false);
  558.  
  559.         if (this.onattach)
  560.             this.onattach(this);
  561.     }
  562.  
  563.     var nextSibling = null;
  564.     if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
  565.         nextSibling = this.nextSibling._listItemNode;
  566.     this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
  567.     if (this._childrenListNode)
  568.         this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
  569.     if (this.selected)
  570.         this.select();
  571.     if (this.expanded)
  572.         this.expand();
  573. }
  574.  
  575. TreeElement.prototype._detach = function()
  576. {
  577.     if (this._listItemNode && this._listItemNode.parentNode)
  578.         this._listItemNode.parentNode.removeChild(this._listItemNode);
  579.     if (this._childrenListNode && this._childrenListNode.parentNode)
  580.         this._childrenListNode.parentNode.removeChild(this._childrenListNode);
  581. }
  582.  
  583. TreeElement.treeElementSelected = function(event)
  584. {
  585.     var element = event.currentTarget;
  586.     if (!element || !element.treeElement || !element.treeElement.selectable)
  587.         return;
  588.  
  589.     if (element.treeElement.isEventWithinDisclosureTriangle(event))
  590.         return;
  591.  
  592.     element.treeElement.select();
  593. }
  594.  
  595. TreeElement.treeElementToggled = function(event)
  596. {
  597.     var element = event.currentTarget;
  598.     if (!element || !element.treeElement)
  599.         return;
  600.  
  601.     if (!element.treeElement.isEventWithinDisclosureTriangle(event))
  602.         return;
  603.  
  604.     if (element.treeElement.expanded) {
  605.         if (event.altKey)
  606.             element.treeElement.collapseRecursively();
  607.         else
  608.             element.treeElement.collapse();
  609.     } else {
  610.         if (event.altKey)
  611.             element.treeElement.expandRecursively();
  612.         else
  613.             element.treeElement.expand();
  614.     }
  615. }
  616.  
  617. TreeElement.treeElementDoubleClicked = function(event)
  618. {
  619.     var element = event.currentTarget;
  620.     if (!element || !element.treeElement)
  621.         return;
  622.  
  623.     if (element.treeElement.ondblclick)
  624.         element.treeElement.ondblclick(element.treeElement, event);
  625.     else if (element.treeElement.hasChildren && !element.treeElement.expanded)
  626.         element.treeElement.expand();
  627. }
  628.  
  629. TreeElement.prototype.collapse = function()
  630. {
  631.     if (this._listItemNode)
  632.         this._listItemNode.removeStyleClass("expanded");
  633.     if (this._childrenListNode)
  634.         this._childrenListNode.removeStyleClass("expanded");
  635.  
  636.     this.expanded = false;
  637.     if (this.treeOutline)
  638.         this.treeOutline._treeElementsExpandedState[this.identifier] = true;
  639.  
  640.     if (this.oncollapse)
  641.         this.oncollapse(this);
  642. }
  643.  
  644. TreeElement.prototype.collapseRecursively = function()
  645. {
  646.     var item = this;
  647.     while (item) {
  648.         if (item.expanded)
  649.             item.collapse();
  650.         item = item.traverseNextTreeElement(false, this, true);
  651.     }
  652. }
  653.  
  654. TreeElement.prototype.expand = function()
  655. {
  656.     if (!this.hasChildren || (this.expanded && !this._shouldRefreshChildren && this._childrenListNode))
  657.         return;
  658.  
  659.     if (this.treeOutline && (!this._childrenListNode || this._shouldRefreshChildren)) {
  660.         if (this._childrenListNode && this._childrenListNode.parentNode)
  661.             this._childrenListNode.parentNode.removeChild(this._childrenListNode);
  662.  
  663.         this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol");
  664.         this._childrenListNode.parentTreeElement = this;
  665.         this._childrenListNode.addStyleClass("children");
  666.  
  667.         if (this.hidden)
  668.             this._childrenListNode.addStyleClass("hidden");
  669.  
  670.         if (this.onpopulate)
  671.             this.onpopulate(this);
  672.  
  673.         for (var i = 0; i < this.children.length; ++i)
  674.             this.children[i]._attach();
  675.  
  676.         delete this._shouldRefreshChildren;
  677.     }
  678.  
  679.     if (this._listItemNode) {
  680.         this._listItemNode.addStyleClass("expanded");
  681.         if (this._childrenListNode && this._childrenListNode.parentNode != this._listItemNode.parentNode)
  682.             this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
  683.     }
  684.  
  685.     if (this._childrenListNode)
  686.         this._childrenListNode.addStyleClass("expanded");
  687.  
  688.     this.expanded = true;
  689.     if (this.treeOutline)
  690.         this.treeOutline._treeElementsExpandedState[this.identifier] = true;
  691.  
  692.     if (this.onexpand)
  693.         this.onexpand(this);
  694. }
  695.  
  696. TreeElement.prototype.expandRecursively = function(maxDepth)
  697. {
  698.     var item = this;
  699.     var info = {};
  700.     var depth = 0;
  701.  
  702.     // The Inspector uses TreeOutlines to represents object properties, so recursive expansion
  703.     // in some case can be infinite, since JavaScript objects can hold circular references.
  704.     // So default to a recursion cap of 3 levels, since that gives fairly good results.
  705.     if (typeof maxDepth === "undefined" || typeof maxDepth === "null")
  706.         maxDepth = 3;
  707.  
  708.     while (item) {
  709.         if (depth < maxDepth)
  710.             item.expand();
  711.         item = item.traverseNextTreeElement(false, this, (depth >= maxDepth), info);
  712.         depth += info.depthChange;
  713.     }
  714. }
  715.  
  716. TreeElement.prototype.hasAncestor = function(ancestor) {
  717.     if (!ancestor)
  718.         return false;
  719.  
  720.     var currentNode = this.parent;
  721.     while (currentNode) {
  722.         if (ancestor === currentNode)
  723.             return true;
  724.         currentNode = currentNode.parent;
  725.     }
  726.  
  727.     return false;
  728. }
  729.  
  730. TreeElement.prototype.reveal = function()
  731. {
  732.     var currentAncestor = this.parent;
  733.     while (currentAncestor && !currentAncestor.root) {
  734.         if (!currentAncestor.expanded)
  735.             currentAncestor.expand();
  736.         currentAncestor = currentAncestor.parent;
  737.     }
  738.  
  739.     if (this.onreveal)
  740.         this.onreveal(this);
  741. }
  742.  
  743. TreeElement.prototype.revealed = function()
  744. {
  745.     var currentAncestor = this.parent;
  746.     while (currentAncestor && !currentAncestor.root) {
  747.         if (!currentAncestor.expanded)
  748.             return false;
  749.         currentAncestor = currentAncestor.parent;
  750.     }
  751.  
  752.     return true;
  753. }
  754.  
  755. TreeElement.prototype.select = function(supressOnSelect)
  756. {
  757.     if (!this.treeOutline || !this.selectable || this.selected)
  758.         return;
  759.  
  760.     if (this.treeOutline.selectedTreeElement)
  761.         this.treeOutline.selectedTreeElement.deselect();
  762.  
  763.     this.selected = true;
  764.     this.treeOutline.selectedTreeElement = this;
  765.     if (this._listItemNode)
  766.         this._listItemNode.addStyleClass("selected");
  767.  
  768.     if (this.onselect && !supressOnSelect)
  769.         this.onselect(this);
  770. }
  771.  
  772. TreeElement.prototype.deselect = function(supressOnDeselect)
  773. {
  774.     if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected)
  775.         return;
  776.  
  777.     this.selected = false;
  778.     this.treeOutline.selectedTreeElement = null;
  779.     if (this._listItemNode)
  780.         this._listItemNode.removeStyleClass("selected");
  781.  
  782.     if (this.ondeselect && !supressOnDeselect)
  783.         this.ondeselect(this);
  784. }
  785.  
  786. TreeElement.prototype.traverseNextTreeElement = function(skipHidden, stayWithin, dontPopulate, info)
  787. {
  788.     if (!dontPopulate && this.hasChildren && this.onpopulate)
  789.         this.onpopulate(this);
  790.  
  791.     if (info)
  792.         info.depthChange = 0;
  793.  
  794.     var element = skipHidden ? (this.revealed() ? this.children[0] : null) : this.children[0];
  795.     if (element && (!skipHidden || (skipHidden && this.expanded))) {
  796.         if (info)
  797.             info.depthChange = 1;
  798.         return element;
  799.     }
  800.  
  801.     if (this === stayWithin)
  802.         return null;
  803.  
  804.     element = skipHidden ? (this.revealed() ? this.nextSibling : null) : this.nextSibling;
  805.     if (element)
  806.         return element;
  807.  
  808.     element = this;
  809.     while (element && !element.root && !(skipHidden ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) {
  810.         if (info)
  811.             info.depthChange -= 1;
  812.         element = element.parent;
  813.     }
  814.  
  815.     if (!element)
  816.         return null;
  817.  
  818.     return (skipHidden ? (element.revealed() ? element.nextSibling : null) : element.nextSibling);
  819. }
  820.  
  821. TreeElement.prototype.traversePreviousTreeElement = function(skipHidden, dontPopulate)
  822. {
  823.     var element = skipHidden ? (this.revealed() ? this.previousSibling : null) : this.previousSibling;
  824.     if (!dontPopulate && element && element.hasChildren && element.onpopulate)
  825.         element.onpopulate(element);
  826.  
  827.     while (element && (skipHidden ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) {
  828.         if (!dontPopulate && element.hasChildren && element.onpopulate)
  829.             element.onpopulate(element);
  830.         element = (skipHidden ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1]);
  831.     }
  832.  
  833.     if (element)
  834.         return element;
  835.  
  836.     if (!this.parent || this.parent.root)
  837.         return null;
  838.  
  839.     return this.parent;
  840. }
  841.  
  842. TreeElement.prototype.isEventWithinDisclosureTriangle = function(event)
  843. {
  844.     var left = this._listItemNode.totalOffsetLeft;
  845.     return event.pageX >= left && event.pageX <= left + this.arrowToggleWidth && this.hasChildren;
  846. }
  847.