📄 treeoutline.js
字号:
/* * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */function TreeOutline(listNode){ this.children = []; this.selectedTreeElement = null; this._childrenListNode = listNode; this._childrenListNode.removeChildren(); this._knownTreeElements = []; this._treeElementsExpandedState = []; this.expandTreeElementsWhenArrowing = false; this.root = true; this.hasChildren = false; this.expanded = true; this.selected = false; this.treeOutline = this;}TreeOutline._knownTreeElementNextIdentifier = 1;TreeOutline._appendChild = function(child){ if (!child) throw("child can't be undefined or null"); var lastChild = this.children[this.children.length - 1]; if (lastChild) { lastChild.nextSibling = child; child.previousSibling = lastChild; } else { child.previousSibling = null; child.nextSibling = null; } this.children.push(child); this.hasChildren = true; child.parent = this; child.treeOutline = this.treeOutline; child.treeOutline._rememberTreeElement(child); var current = child.children[0]; while (current) { current.treeOutline = this.treeOutline; current.treeOutline._rememberTreeElement(current); current = current.traverseNextTreeElement(false, child, true); } if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined) child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier]; if (!this._childrenListNode) { this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol"); this._childrenListNode.parentTreeElement = this; this._childrenListNode.addStyleClass("children"); if (this.hidden) this._childrenListNode.addStyleClass("hidden"); } child._attach();}TreeOutline._insertChild = function(child, index){ if (!child) throw("child can't be undefined or null"); var previousChild = (index > 0 ? this.children[index - 1] : null); if (previousChild) { previousChild.nextSibling = child; child.previousSibling = previousChild; } else { child.previousSibling = null; } var nextChild = this.children[index]; if (nextChild) { nextChild.previousSibling = child; child.nextSibling = nextChild; } else { child.nextSibling = null; } this.children.splice(index, 0, child); this.hasChildren = true; child.parent = this; child.treeOutline = this.treeOutline; child.treeOutline._rememberTreeElement(child); var current = child.children[0]; while (current) { current.treeOutline = this.treeOutline; current.treeOutline._rememberTreeElement(current); current = current.traverseNextTreeElement(false, child, true); } if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined) child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier]; if (!this._childrenListNode) { this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol"); this._childrenListNode.parentTreeElement = this; this._childrenListNode.addStyleClass("children"); if (this.hidden) this._childrenListNode.addStyleClass("hidden"); } child._attach();}TreeOutline._removeChildAtIndex = function(childIndex){ if (childIndex < 0 || childIndex >= this.children.length) throw("childIndex out of range"); var child = this.children[childIndex]; this.children.splice(childIndex, 1); child.deselect(); if (child.previousSibling) child.previousSibling.nextSibling = child.nextSibling; if (child.nextSibling) child.nextSibling.previousSibling = child.previousSibling; if (child.treeOutline) { child.treeOutline._forgetTreeElement(child); child.treeOutline._forgetChildrenRecursive(child); } child._detach(); child.treeOutline = null; child.parent = null; child.nextSibling = null; child.previousSibling = null;}TreeOutline._removeChild = function(child){ if (!child) throw("child can't be undefined or null"); var childIndex = this.children.indexOf(child); if (childIndex === -1) throw("child not found in this node's children"); TreeOutline._removeChildAtIndex.call(this, childIndex);}TreeOutline._removeChildren = function(){ for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; child.deselect(); if (child.treeOutline) { child.treeOutline._forgetTreeElement(child); child.treeOutline._forgetChildrenRecursive(child); } child._detach(); child.treeOutline = null; child.parent = null; child.nextSibling = null; child.previousSibling = null; } this.children = [];}TreeOutline._removeChildrenRecursive = function(){ var childrenToRemove = this.children; var child = this.children[0]; while (child) { if (child.children.length) childrenToRemove = childrenToRemove.concat(child.children); child = child.traverseNextTreeElement(false, this, true); } for (var i = 0; i < childrenToRemove.length; ++i) { var child = childrenToRemove[i]; child.deselect(); if (child.treeOutline) child.treeOutline._forgetTreeElement(child); child._detach(); child.children = []; child.treeOutline = null; child.parent = null; child.nextSibling = null; child.previousSibling = null; } this.children = [];}TreeOutline.prototype._rememberTreeElement = function(element){ if (!this._knownTreeElements[element.identifier]) this._knownTreeElements[element.identifier] = []; // check if the element is already known var elements = this._knownTreeElements[element.identifier]; if (elements.indexOf(element) !== -1) return; // add the element elements.push(element);}TreeOutline.prototype._forgetTreeElement = function(element){ if (this._knownTreeElements[element.identifier]) this._knownTreeElements[element.identifier].remove(element, true);}TreeOutline.prototype._forgetChildrenRecursive = function(parentElement){ var child = parentElement.children[0]; while (child) { this._forgetTreeElement(child); child = child.traverseNextTreeElement(false, this, true); }}TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, getParent, equal){ if (!representedObject) return null; if (!equal) equal = function(a, b) { return a === b }; if ("__treeElementIdentifier" in representedObject) { // If this representedObject has a tree element identifier, and it is a known TreeElement // in our tree we can just return that tree element. var elements = this._knownTreeElements[representedObject.__treeElementIdentifier]; if (elements) { for (var i = 0; i < elements.length; ++i) if (equal(elements[i].representedObject, representedObject)) return elements[i]; } } if (!isAncestor || !(isAncestor instanceof Function) || !getParent || !(getParent instanceof Function)) return null; // The representedObject isn't know, so we start at the top of the tree and work down to find the first // tree element that represents representedObject or one of its ancestors. var item; var found = false; for (var i = 0; i < this.children.length; ++i) { item = this.children[i]; if (equal(item.representedObject, representedObject) || isAncestor(item.representedObject, representedObject)) { found = true; break; } } if (!found) return null; // Make sure the item that we found is connected to the root of the tree. // Build up a list of representedObject's ancestors that aren't already in our tree. var ancestors = []; var currentObject = representedObject; while (currentObject) { ancestors.unshift(currentObject); if (equal(currentObject, item.representedObject)) break; currentObject = getParent(currentObject); } // For each of those ancestors we populate them to fill in the tree. for (var i = 0; i < ancestors.length; ++i) { // Make sure we don't call findTreeElement with the same representedObject // again, to prevent infinite recursion. if (equal(ancestors[i], representedObject)) continue; // FIXME: we could do something faster than findTreeElement since we will know the next // ancestor exists in the tree. item = this.findTreeElement(ancestors[i], isAncestor, getParent, equal); if (item && item.onpopulate) item.onpopulate(item); } // Now that all the ancestors are populated, try to find the representedObject again. This time // without the isAncestor and getParent functions to prevent an infinite recursion if it isn't found. return this.findTreeElement(representedObject, null, null, equal);}TreeOutline.prototype.treeElementFromPoint = function(x, y){ var node = this._childrenListNode.ownerDocument.elementFromPoint(x, y); var listNode = node.enclosingNodeOrSelfWithNodeNameInArray(["ol", "li"]); if (listNode) return listNode.parentTreeElement || listNode.treeElement; return null;}TreeOutline.prototype.handleKeyEvent = function(event){ if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey) return false; var handled = false; var nextSelectedElement; if (event.keyIdentifier === "Up" && !event.altKey) { nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true); while (nextSelectedElement && !nextSelectedElement.selectable) nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing); handled = nextSelectedElement ? true : false; } else if (event.keyIdentifier === "Down" && !event.altKey) { nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true); while (nextSelectedElement && !nextSelectedElement.selectable) nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing); handled = nextSelectedElement ? true : false; } else if (event.keyIdentifier === "Left") { if (this.selectedTreeElement.expanded) { if (event.altKey) this.selectedTreeElement.collapseRecursively(); else this.selectedTreeElement.collapse(); handled = true; } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) { handled = true; if (this.selectedTreeElement.parent.selectable) { nextSelectedElement = this.selectedTreeElement.parent; handled = nextSelectedElement ? true : false; } else if (this.selectedTreeElement.parent) this.selectedTreeElement.parent.collapse(); } } else if (event.keyIdentifier === "Right") { if (!this.selectedTreeElement.revealed()) { this.selectedTreeElement.reveal(); handled = true; } else if (this.selectedTreeElement.hasChildren) { handled = true; if (this.selectedTreeElement.expanded) { nextSelectedElement = this.selectedTreeElement.children[0]; handled = nextSelectedElement ? true : false; } else { if (event.altKey) this.selectedTreeElement.expandRecursively(); else this.selectedTreeElement.expand(); } } } if (nextSelectedElement) { nextSelectedElement.reveal(); nextSelectedElement.select(); } if (handled) { event.preventDefault(); event.stopPropagation(); } return handled;}TreeOutline.prototype.expand = function(){ // this is the root, do nothing}TreeOutline.prototype.collapse = function(){ // this is the root, do nothing}TreeOutline.prototype.revealed = function(){ return true;}TreeOutline.prototype.reveal = function(){ // this is the root, do nothing}TreeOutline.prototype.appendChild = TreeOutline._appendChild;TreeOutline.prototype.insertChild = TreeOutline._insertChild;TreeOutline.prototype.removeChild = TreeOutline._removeChild;TreeOutline.prototype.removeChildAtIndex = TreeOutline._removeChildAtIndex;TreeOutline.prototype.removeChildren = TreeOutline._removeChildren;TreeOutline.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive;function TreeElement(title, representedObject, hasChildren){ this._title = title; this.representedObject = (representedObject || {}); if (this.representedObject.__treeElementIdentifier) this.identifier = this.representedObject.__treeElementIdentifier; else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -