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