1582 lines
39 KiB
JavaScript
1582 lines
39 KiB
JavaScript
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Definition of the goog.ui.tree.BaseNode class.
|
|
*
|
|
* @author arv@google.com (Erik Arvidsson)
|
|
* @author eae@google.com (Emil A Eklund)
|
|
*
|
|
* This is a based on the webfx tree control. It since been updated to add
|
|
* typeahead support, as well as accessibility support using ARIA framework.
|
|
* See file comment in treecontrol.js.
|
|
*/
|
|
|
|
goog.provide('goog.ui.tree.BaseNode');
|
|
goog.provide('goog.ui.tree.BaseNode.EventType');
|
|
|
|
goog.require('goog.Timer');
|
|
goog.require('goog.a11y.aria');
|
|
goog.require('goog.asserts');
|
|
goog.require('goog.dom.safe');
|
|
goog.require('goog.events.Event');
|
|
goog.require('goog.events.KeyCodes');
|
|
goog.require('goog.html.SafeHtml');
|
|
goog.require('goog.html.SafeStyle');
|
|
goog.require('goog.html.legacyconversions');
|
|
goog.require('goog.string');
|
|
goog.require('goog.string.StringBuffer');
|
|
goog.require('goog.style');
|
|
goog.require('goog.ui.Component');
|
|
|
|
|
|
|
|
/**
|
|
* An abstract base class for a node in the tree.
|
|
*
|
|
* @param {string|!goog.html.SafeHtml} html The html content of the node label.
|
|
* @param {Object=} opt_config The configuration for the tree. See
|
|
* {@link goog.ui.tree.BaseNode.defaultConfig}. If not specified the
|
|
* default config will be used.
|
|
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
|
* @constructor
|
|
* @extends {goog.ui.Component}
|
|
*/
|
|
goog.ui.tree.BaseNode = function(html, opt_config, opt_domHelper) {
|
|
goog.ui.Component.call(this, opt_domHelper);
|
|
|
|
/**
|
|
* The configuration for the tree.
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this.config_ = opt_config || goog.ui.tree.BaseNode.defaultConfig;
|
|
|
|
/**
|
|
* HTML content of the node label.
|
|
* @type {!goog.html.SafeHtml}
|
|
* @private
|
|
*/
|
|
this.html_ = (html instanceof goog.html.SafeHtml ? html :
|
|
goog.html.legacyconversions.safeHtmlFromString(html));
|
|
|
|
/** @private {string} */
|
|
this.iconClass_;
|
|
|
|
/** @private {string} */
|
|
this.expandedIconClass_;
|
|
|
|
/** @protected {goog.ui.tree.TreeControl} */
|
|
this.tree;
|
|
|
|
/** @private {goog.ui.tree.BaseNode} */
|
|
this.previousSibling_;
|
|
|
|
/** @private {goog.ui.tree.BaseNode} */
|
|
this.nextSibling_;
|
|
|
|
/** @private {goog.ui.tree.BaseNode} */
|
|
this.firstChild_;
|
|
|
|
/** @private {goog.ui.tree.BaseNode} */
|
|
this.lastChild_;
|
|
};
|
|
goog.inherits(goog.ui.tree.BaseNode, goog.ui.Component);
|
|
|
|
|
|
/**
|
|
* The event types dispatched by this class.
|
|
* @enum {string}
|
|
*/
|
|
goog.ui.tree.BaseNode.EventType = {
|
|
BEFORE_EXPAND: 'beforeexpand',
|
|
EXPAND: 'expand',
|
|
BEFORE_COLLAPSE: 'beforecollapse',
|
|
COLLAPSE: 'collapse'
|
|
};
|
|
|
|
|
|
/**
|
|
* Map of nodes in existence. Needed to route events to the appropriate nodes.
|
|
* Nodes are added to the map at {@link #enterDocument} time and removed at
|
|
* {@link #exitDocument} time.
|
|
* @type {Object}
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.allNodes = {};
|
|
|
|
|
|
/**
|
|
* Whether the tree item is selected.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.selected_ = false;
|
|
|
|
|
|
/**
|
|
* Whether the tree node is expanded.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.expanded_ = false;
|
|
|
|
|
|
/**
|
|
* Tooltip for the tree item
|
|
* @type {?string}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.toolTip_ = null;
|
|
|
|
|
|
/**
|
|
* HTML that can appear after the label (so not inside the anchor).
|
|
* @type {!goog.html.SafeHtml}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.afterLabelHtml_ = goog.html.SafeHtml.EMPTY;
|
|
|
|
|
|
/**
|
|
* Whether to allow user to collapse this node.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.isUserCollapsible_ = true;
|
|
|
|
|
|
/**
|
|
* Nesting depth of this node; cached result of computeDepth_.
|
|
* -1 if value has not been cached.
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.depth_ = -1;
|
|
|
|
|
|
/** @override */
|
|
goog.ui.tree.BaseNode.prototype.disposeInternal = function() {
|
|
goog.ui.tree.BaseNode.superClass_.disposeInternal.call(this);
|
|
if (this.tree) {
|
|
this.tree.removeNode(this);
|
|
this.tree = null;
|
|
}
|
|
this.setElementInternal(null);
|
|
};
|
|
|
|
|
|
/**
|
|
* Adds roles and states.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.initAccessibility = function() {
|
|
var el = this.getElement();
|
|
if (el) {
|
|
// Set an id for the label
|
|
var label = this.getLabelElement();
|
|
if (label && !label.id) {
|
|
label.id = this.getId() + '.label';
|
|
}
|
|
|
|
goog.a11y.aria.setRole(el, 'treeitem');
|
|
goog.a11y.aria.setState(el, 'selected', false);
|
|
goog.a11y.aria.setState(el, 'expanded', false);
|
|
goog.a11y.aria.setState(el, 'level', this.getDepth());
|
|
if (label) {
|
|
goog.a11y.aria.setState(el, 'labelledby', label.id);
|
|
}
|
|
|
|
var img = this.getIconElement();
|
|
if (img) {
|
|
goog.a11y.aria.setRole(img, 'presentation');
|
|
}
|
|
var ei = this.getExpandIconElement();
|
|
if (ei) {
|
|
goog.a11y.aria.setRole(ei, 'presentation');
|
|
}
|
|
|
|
var ce = this.getChildrenElement();
|
|
if (ce) {
|
|
goog.a11y.aria.setRole(ce, 'group');
|
|
|
|
// In case the children will be created lazily.
|
|
if (ce.hasChildNodes()) {
|
|
// do setsize for each child
|
|
var count = this.getChildCount();
|
|
for (var i = 1; i <= count; i++) {
|
|
var child = this.getChildAt(i - 1).getElement();
|
|
goog.asserts.assert(child, 'The child element cannot be null');
|
|
goog.a11y.aria.setState(child, 'setsize', count);
|
|
goog.a11y.aria.setState(child, 'posinset', i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.ui.tree.BaseNode.prototype.createDom = function() {
|
|
var element = this.getDomHelper().safeHtmlToNode(this.toSafeHtml());
|
|
this.setElementInternal(/** @type {!Element} */ (element));
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.ui.tree.BaseNode.prototype.enterDocument = function() {
|
|
goog.ui.tree.BaseNode.superClass_.enterDocument.call(this);
|
|
goog.ui.tree.BaseNode.allNodes[this.getId()] = this;
|
|
this.initAccessibility();
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.ui.tree.BaseNode.prototype.exitDocument = function() {
|
|
goog.ui.tree.BaseNode.superClass_.exitDocument.call(this);
|
|
delete goog.ui.tree.BaseNode.allNodes[this.getId()];
|
|
};
|
|
|
|
|
|
/**
|
|
* The method assumes that the child doesn't have parent node yet.
|
|
* The {@code opt_render} argument is not used. If the parent node is expanded,
|
|
* the child node's state will be the same as the parent's. Otherwise the
|
|
* child's DOM tree won't be created.
|
|
* @override
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.addChildAt = function(child, index,
|
|
opt_render) {
|
|
goog.asserts.assert(!child.getParent());
|
|
goog.asserts.assertInstanceof(child, goog.ui.tree.BaseNode);
|
|
var prevNode = this.getChildAt(index - 1);
|
|
var nextNode = this.getChildAt(index);
|
|
|
|
goog.ui.tree.BaseNode.superClass_.addChildAt.call(this, child, index);
|
|
|
|
child.previousSibling_ = prevNode;
|
|
child.nextSibling_ = nextNode;
|
|
|
|
if (prevNode) {
|
|
prevNode.nextSibling_ = child;
|
|
} else {
|
|
this.firstChild_ = child;
|
|
}
|
|
if (nextNode) {
|
|
nextNode.previousSibling_ = child;
|
|
} else {
|
|
this.lastChild_ = child;
|
|
}
|
|
|
|
var tree = this.getTree();
|
|
if (tree) {
|
|
child.setTreeInternal(tree);
|
|
}
|
|
|
|
child.setDepth_(this.getDepth() + 1);
|
|
|
|
if (this.getElement()) {
|
|
this.updateExpandIcon();
|
|
if (this.getExpanded()) {
|
|
var el = this.getChildrenElement();
|
|
if (!child.getElement()) {
|
|
child.createDom();
|
|
}
|
|
var childElement = child.getElement();
|
|
var nextElement = nextNode && nextNode.getElement();
|
|
el.insertBefore(childElement, nextElement);
|
|
|
|
if (this.isInDocument()) {
|
|
child.enterDocument();
|
|
}
|
|
|
|
if (!nextNode) {
|
|
if (prevNode) {
|
|
prevNode.updateExpandIcon();
|
|
} else {
|
|
goog.style.setElementShown(el, true);
|
|
this.setExpanded(this.getExpanded());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Adds a node as a child to the current node.
|
|
* @param {goog.ui.tree.BaseNode} child The child to add.
|
|
* @param {goog.ui.tree.BaseNode=} opt_before If specified, the new child is
|
|
* added as a child before this one. If not specified, it's appended to the
|
|
* end.
|
|
* @return {!goog.ui.tree.BaseNode} The added child.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.add = function(child, opt_before) {
|
|
goog.asserts.assert(!opt_before || opt_before.getParent() == this,
|
|
'Can only add nodes before siblings');
|
|
if (child.getParent()) {
|
|
child.getParent().removeChild(child);
|
|
}
|
|
this.addChildAt(child,
|
|
opt_before ? this.indexOfChild(opt_before) : this.getChildCount());
|
|
return child;
|
|
};
|
|
|
|
|
|
/**
|
|
* Removes a child. The caller is responsible for disposing the node.
|
|
* @param {goog.ui.Component|string} childNode The child to remove. Must be a
|
|
* {@link goog.ui.tree.BaseNode}.
|
|
* @param {boolean=} opt_unrender Unused. The child will always be unrendered.
|
|
* @return {!goog.ui.tree.BaseNode} The child that was removed.
|
|
* @override
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.removeChild =
|
|
function(childNode, opt_unrender) {
|
|
// In reality, this only accepts BaseNodes.
|
|
var child = /** @type {goog.ui.tree.BaseNode} */ (childNode);
|
|
|
|
// if we remove selected or tree with the selected we should select this
|
|
var tree = this.getTree();
|
|
var selectedNode = tree ? tree.getSelectedItem() : null;
|
|
if (selectedNode == child || child.contains(selectedNode)) {
|
|
if (tree.hasFocus()) {
|
|
this.select();
|
|
goog.Timer.callOnce(this.onTimeoutSelect_, 10, this);
|
|
} else {
|
|
this.select();
|
|
}
|
|
}
|
|
|
|
goog.ui.tree.BaseNode.superClass_.removeChild.call(this, child);
|
|
|
|
if (this.lastChild_ == child) {
|
|
this.lastChild_ = child.previousSibling_;
|
|
}
|
|
if (this.firstChild_ == child) {
|
|
this.firstChild_ = child.nextSibling_;
|
|
}
|
|
if (child.previousSibling_) {
|
|
child.previousSibling_.nextSibling_ = child.nextSibling_;
|
|
}
|
|
if (child.nextSibling_) {
|
|
child.nextSibling_.previousSibling_ = child.previousSibling_;
|
|
}
|
|
|
|
var wasLast = child.isLastSibling();
|
|
|
|
child.tree = null;
|
|
child.depth_ = -1;
|
|
|
|
if (tree) {
|
|
// Tell the tree control that this node is now removed.
|
|
tree.removeNode(this);
|
|
|
|
if (this.isInDocument()) {
|
|
var el = this.getChildrenElement();
|
|
|
|
if (child.isInDocument()) {
|
|
var childEl = child.getElement();
|
|
el.removeChild(childEl);
|
|
|
|
child.exitDocument();
|
|
}
|
|
|
|
if (wasLast) {
|
|
var newLast = this.getLastChild();
|
|
if (newLast) {
|
|
newLast.updateExpandIcon();
|
|
}
|
|
}
|
|
if (!this.hasChildren()) {
|
|
el.style.display = 'none';
|
|
this.updateExpandIcon();
|
|
this.updateIcon_();
|
|
}
|
|
}
|
|
}
|
|
|
|
return child;
|
|
};
|
|
|
|
|
|
/**
|
|
* @deprecated Use {@link #removeChild}.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.remove =
|
|
goog.ui.tree.BaseNode.prototype.removeChild;
|
|
|
|
|
|
/**
|
|
* Handler for setting focus asynchronously.
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.onTimeoutSelect_ = function() {
|
|
this.select();
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the tree.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getTree = goog.abstractMethod;
|
|
|
|
|
|
/**
|
|
* Returns the depth of the node in the tree. Should not be overridden.
|
|
* @return {number} The non-negative depth of this node (the root is zero).
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getDepth = function() {
|
|
var depth = this.depth_;
|
|
if (depth < 0) {
|
|
depth = this.computeDepth_();
|
|
this.setDepth_(depth);
|
|
}
|
|
return depth;
|
|
};
|
|
|
|
|
|
/**
|
|
* Computes the depth of the node in the tree.
|
|
* Called only by getDepth, when the depth hasn't already been cached.
|
|
* @return {number} The non-negative depth of this node (the root is zero).
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.computeDepth_ = function() {
|
|
var parent = this.getParent();
|
|
if (parent) {
|
|
return parent.getDepth() + 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Changes the depth of a node (and all its descendants).
|
|
* @param {number} depth The new nesting depth; must be non-negative.
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setDepth_ = function(depth) {
|
|
if (depth != this.depth_) {
|
|
this.depth_ = depth;
|
|
var row = this.getRowElement();
|
|
if (row) {
|
|
var indent = this.getPixelIndent_() + 'px';
|
|
if (this.isRightToLeft()) {
|
|
row.style.paddingRight = indent;
|
|
} else {
|
|
row.style.paddingLeft = indent;
|
|
}
|
|
}
|
|
this.forEachChild(function(child) {
|
|
child.setDepth_(depth + 1);
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns true if the node is a descendant of this node
|
|
* @param {goog.ui.tree.BaseNode} node The node to check.
|
|
* @return {boolean} True if the node is a descendant of this node, false
|
|
* otherwise.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.contains = function(node) {
|
|
var current = node;
|
|
while (current) {
|
|
if (current == this) {
|
|
return true;
|
|
}
|
|
current = current.getParent();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
|
|
/**
|
|
* An array of empty children to return for nodes that have no children.
|
|
* @type {!Array<!goog.ui.tree.BaseNode>}
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.EMPTY_CHILDREN_ = [];
|
|
|
|
|
|
/**
|
|
* @param {number} index 0-based index.
|
|
* @return {goog.ui.tree.BaseNode} The child at the given index; null if none.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getChildAt;
|
|
|
|
|
|
/**
|
|
* Returns the children of this node.
|
|
* @return {!Array<!goog.ui.tree.BaseNode>} The children.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getChildren = function() {
|
|
var children = [];
|
|
this.forEachChild(function(child) {
|
|
children.push(child);
|
|
});
|
|
return children;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The first child of this node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getFirstChild = function() {
|
|
return this.getChildAt(0);
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The last child of this node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getLastChild = function() {
|
|
return this.getChildAt(this.getChildCount() - 1);
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The previous sibling of this node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getPreviousSibling = function() {
|
|
return this.previousSibling_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The next sibling of this node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getNextSibling = function() {
|
|
return this.nextSibling_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {boolean} Whether the node is the last sibling.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.isLastSibling = function() {
|
|
return !this.nextSibling_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {boolean} Whether the node is selected.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.isSelected = function() {
|
|
return this.selected_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Selects the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.select = function() {
|
|
var tree = this.getTree();
|
|
if (tree) {
|
|
tree.setSelectedItem(this);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Originally it was intended to deselect the node but never worked.
|
|
* @deprecated Use {@code tree.setSelectedItem(null)}.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.deselect = goog.nullFunction;
|
|
|
|
|
|
/**
|
|
* Called from the tree to instruct the node change its selection state.
|
|
* @param {boolean} selected The new selection state.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setSelectedInternal = function(selected) {
|
|
if (this.selected_ == selected) {
|
|
return;
|
|
}
|
|
this.selected_ = selected;
|
|
|
|
this.updateRow();
|
|
|
|
var el = this.getElement();
|
|
if (el) {
|
|
goog.a11y.aria.setState(el, 'selected', selected);
|
|
if (selected) {
|
|
var treeElement = this.getTree().getElement();
|
|
goog.asserts.assert(treeElement,
|
|
'The DOM element for the tree cannot be null');
|
|
goog.a11y.aria.setState(treeElement,
|
|
'activedescendant',
|
|
this.getId());
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {boolean} Whether the node is expanded.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getExpanded = function() {
|
|
return this.expanded_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the node to be expanded internally, without state change events.
|
|
* @param {boolean} expanded Whether to expand or close the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setExpandedInternal = function(expanded) {
|
|
this.expanded_ = expanded;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the node to be expanded.
|
|
* @param {boolean} expanded Whether to expand or close the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setExpanded = function(expanded) {
|
|
var isStateChange = expanded != this.expanded_;
|
|
if (isStateChange) {
|
|
// Only fire events if the expanded state has actually changed.
|
|
var prevented = !this.dispatchEvent(
|
|
expanded ? goog.ui.tree.BaseNode.EventType.BEFORE_EXPAND :
|
|
goog.ui.tree.BaseNode.EventType.BEFORE_COLLAPSE);
|
|
if (prevented) return;
|
|
}
|
|
var ce;
|
|
this.expanded_ = expanded;
|
|
var tree = this.getTree();
|
|
var el = this.getElement();
|
|
|
|
if (this.hasChildren()) {
|
|
if (!expanded && tree && this.contains(tree.getSelectedItem())) {
|
|
this.select();
|
|
}
|
|
|
|
if (el) {
|
|
ce = this.getChildrenElement();
|
|
if (ce) {
|
|
goog.style.setElementShown(ce, expanded);
|
|
|
|
// Make sure we have the HTML for the children here.
|
|
if (expanded && this.isInDocument() && !ce.hasChildNodes()) {
|
|
var children = [];
|
|
this.forEachChild(function(child) {
|
|
children.push(child.toSafeHtml());
|
|
});
|
|
goog.dom.safe.setInnerHtml(ce, goog.html.SafeHtml.concat(children));
|
|
this.forEachChild(function(child) {
|
|
child.enterDocument();
|
|
});
|
|
}
|
|
}
|
|
this.updateExpandIcon();
|
|
}
|
|
} else {
|
|
ce = this.getChildrenElement();
|
|
if (ce) {
|
|
goog.style.setElementShown(ce, false);
|
|
}
|
|
}
|
|
if (el) {
|
|
this.updateIcon_();
|
|
goog.a11y.aria.setState(el, 'expanded', expanded);
|
|
}
|
|
|
|
if (isStateChange) {
|
|
this.dispatchEvent(expanded ? goog.ui.tree.BaseNode.EventType.EXPAND :
|
|
goog.ui.tree.BaseNode.EventType.COLLAPSE);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Toggles the expanded state of the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.toggle = function() {
|
|
this.setExpanded(!this.getExpanded());
|
|
};
|
|
|
|
|
|
/**
|
|
* Expands the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.expand = function() {
|
|
this.setExpanded(true);
|
|
};
|
|
|
|
|
|
/**
|
|
* Collapses the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.collapse = function() {
|
|
this.setExpanded(false);
|
|
};
|
|
|
|
|
|
/**
|
|
* Collapses the children of the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.collapseChildren = function() {
|
|
this.forEachChild(function(child) {
|
|
child.collapseAll();
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Collapses the children and the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.collapseAll = function() {
|
|
this.collapseChildren();
|
|
this.collapse();
|
|
};
|
|
|
|
|
|
/**
|
|
* Expands the children of the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.expandChildren = function() {
|
|
this.forEachChild(function(child) {
|
|
child.expandAll();
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Expands the children and the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.expandAll = function() {
|
|
this.expandChildren();
|
|
this.expand();
|
|
};
|
|
|
|
|
|
/**
|
|
* Expands the parent chain of this node so that it is visible.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.reveal = function() {
|
|
var parent = this.getParent();
|
|
if (parent) {
|
|
parent.setExpanded(true);
|
|
parent.reveal();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets whether the node will allow the user to collapse it.
|
|
* @param {boolean} isCollapsible Whether to allow node collapse.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setIsUserCollapsible = function(isCollapsible) {
|
|
this.isUserCollapsible_ = isCollapsible;
|
|
if (!this.isUserCollapsible_) {
|
|
this.expand();
|
|
}
|
|
if (this.getElement()) {
|
|
this.updateExpandIcon();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {boolean} Whether the node is collapsible by user actions.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.isUserCollapsible = function() {
|
|
return this.isUserCollapsible_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Creates HTML for the node.
|
|
* @return {!goog.html.SafeHtml}
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.toSafeHtml = function() {
|
|
var tree = this.getTree();
|
|
var hideLines = !tree.getShowLines() ||
|
|
tree == this.getParent() && !tree.getShowRootLines();
|
|
|
|
var childClass = hideLines ? this.config_.cssChildrenNoLines :
|
|
this.config_.cssChildren;
|
|
|
|
var nonEmptyAndExpanded = this.getExpanded() && this.hasChildren();
|
|
|
|
var attributes = {
|
|
'class': childClass,
|
|
'style': this.getLineStyle()
|
|
};
|
|
|
|
var content = [];
|
|
if (nonEmptyAndExpanded) {
|
|
// children
|
|
this.forEachChild(function(child) {
|
|
content.push(child.toSafeHtml());
|
|
});
|
|
}
|
|
|
|
var children = goog.html.SafeHtml.create('div', attributes, content);
|
|
|
|
return goog.html.SafeHtml.create('div',
|
|
{'class': this.config_.cssItem, 'id': this.getId()},
|
|
[this.getRowSafeHtml(), children]);
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {number} The pixel indent of the row.
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getPixelIndent_ = function() {
|
|
return Math.max(0, (this.getDepth() - 1) * this.config_.indentWidth);
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {!goog.html.SafeHtml} The html for the row.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getRowSafeHtml = function() {
|
|
var style = {};
|
|
style['padding-' + (this.isRightToLeft() ? 'right' : 'left')] =
|
|
this.getPixelIndent_() + 'px';
|
|
var attributes = {
|
|
'class': this.getRowClassName(),
|
|
'style': style
|
|
};
|
|
var content = [
|
|
this.getExpandIconSafeHtml(),
|
|
this.getIconSafeHtml(),
|
|
this.getLabelSafeHtml()
|
|
];
|
|
return goog.html.SafeHtml.create('div', attributes, content);
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {string} The class name for the row.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getRowClassName = function() {
|
|
var selectedClass;
|
|
if (this.isSelected()) {
|
|
selectedClass = ' ' + this.config_.cssSelectedRow;
|
|
} else {
|
|
selectedClass = '';
|
|
}
|
|
return this.config_.cssTreeRow + selectedClass;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {!goog.html.SafeHtml} The html for the label.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getLabelSafeHtml = function() {
|
|
var html = goog.html.SafeHtml.create('span',
|
|
{
|
|
'class': this.config_.cssItemLabel,
|
|
'title': this.getToolTip() || null
|
|
},
|
|
this.getSafeHtml());
|
|
return goog.html.SafeHtml.concat(html,
|
|
goog.html.SafeHtml.create('span', {}, this.getAfterLabelSafeHtml()));
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the html that appears after the label. This is useful if you want to
|
|
* put extra UI on the row of the label but not inside the anchor tag.
|
|
* @return {string} The html.
|
|
* @final
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getAfterLabelHtml = function() {
|
|
return goog.html.SafeHtml.unwrap(this.getAfterLabelSafeHtml());
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the html that appears after the label. This is useful if you want to
|
|
* put extra UI on the row of the label but not inside the anchor tag.
|
|
* @return {!goog.html.SafeHtml} The html.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getAfterLabelSafeHtml = function() {
|
|
return this.afterLabelHtml_;
|
|
};
|
|
|
|
|
|
// TODO(jakubvrana): Deprecate in favor of setSafeHtml, once developer docs on
|
|
// using goog.html.SafeHtml are in place.
|
|
/**
|
|
* Sets the html that appears after the label. This is useful if you want to
|
|
* put extra UI on the row of the label but not inside the anchor tag.
|
|
* @param {string} html The html.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setAfterLabelHtml = function(html) {
|
|
this.setAfterLabelSafeHtml(goog.html.legacyconversions.safeHtmlFromString(
|
|
html));
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the html that appears after the label. This is useful if you want to
|
|
* put extra UI on the row of the label but not inside the anchor tag.
|
|
* @param {!goog.html.SafeHtml} html The html.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setAfterLabelSafeHtml = function(html) {
|
|
this.afterLabelHtml_ = html;
|
|
var el = this.getAfterLabelElement();
|
|
if (el) {
|
|
goog.dom.safe.setInnerHtml(el, html);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {!goog.html.SafeHtml} The html for the icon.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getIconSafeHtml = function() {
|
|
return goog.html.SafeHtml.create('span', {
|
|
'style': {'display': 'inline-block'},
|
|
'class': this.getCalculatedIconClass()
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the calculated icon class.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getCalculatedIconClass = goog.abstractMethod;
|
|
|
|
|
|
/**
|
|
* @return {!goog.html.SafeHtml} The source for the icon.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getExpandIconSafeHtml = function() {
|
|
return goog.html.SafeHtml.create('span', {
|
|
'type': 'expand',
|
|
'style': {'display': 'inline-block'},
|
|
'class': this.getExpandIconClass()
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {string} The class names of the icon used for expanding the node.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getExpandIconClass = function() {
|
|
var tree = this.getTree();
|
|
var hideLines = !tree.getShowLines() ||
|
|
tree == this.getParent() && !tree.getShowRootLines();
|
|
|
|
var config = this.config_;
|
|
var sb = new goog.string.StringBuffer();
|
|
sb.append(config.cssTreeIcon, ' ', config.cssExpandTreeIcon, ' ');
|
|
|
|
if (this.hasChildren()) {
|
|
var bits = 0;
|
|
/*
|
|
Bitmap used to determine which icon to use
|
|
1 Plus
|
|
2 Minus
|
|
4 T Line
|
|
8 L Line
|
|
*/
|
|
|
|
if (tree.getShowExpandIcons() && this.isUserCollapsible_) {
|
|
if (this.getExpanded()) {
|
|
bits = 2;
|
|
} else {
|
|
bits = 1;
|
|
}
|
|
}
|
|
|
|
if (!hideLines) {
|
|
if (this.isLastSibling()) {
|
|
bits += 4;
|
|
} else {
|
|
bits += 8;
|
|
}
|
|
}
|
|
|
|
switch (bits) {
|
|
case 1:
|
|
sb.append(config.cssExpandTreeIconPlus);
|
|
break;
|
|
case 2:
|
|
sb.append(config.cssExpandTreeIconMinus);
|
|
break;
|
|
case 4:
|
|
sb.append(config.cssExpandTreeIconL);
|
|
break;
|
|
case 5:
|
|
sb.append(config.cssExpandTreeIconLPlus);
|
|
break;
|
|
case 6:
|
|
sb.append(config.cssExpandTreeIconLMinus);
|
|
break;
|
|
case 8:
|
|
sb.append(config.cssExpandTreeIconT);
|
|
break;
|
|
case 9:
|
|
sb.append(config.cssExpandTreeIconTPlus);
|
|
break;
|
|
case 10:
|
|
sb.append(config.cssExpandTreeIconTMinus);
|
|
break;
|
|
default: // 0
|
|
sb.append(config.cssExpandTreeIconBlank);
|
|
}
|
|
} else {
|
|
if (hideLines) {
|
|
sb.append(config.cssExpandTreeIconBlank);
|
|
} else if (this.isLastSibling()) {
|
|
sb.append(config.cssExpandTreeIconL);
|
|
} else {
|
|
sb.append(config.cssExpandTreeIconT);
|
|
}
|
|
}
|
|
return sb.toString();
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {!goog.html.SafeStyle} The line style.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getLineStyle = function() {
|
|
var nonEmptyAndExpanded = this.getExpanded() && this.hasChildren();
|
|
return goog.html.SafeStyle.create({
|
|
'background-position': this.getBackgroundPosition(),
|
|
'display': nonEmptyAndExpanded ? null : 'none'
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {string} The background position style value.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getBackgroundPosition = function() {
|
|
return (this.isLastSibling() ? '-100' :
|
|
(this.getDepth() - 1) * this.config_.indentWidth) + 'px 0';
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The element for the tree node.
|
|
* @override
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getElement = function() {
|
|
var el = goog.ui.tree.BaseNode.superClass_.getElement.call(this);
|
|
if (!el) {
|
|
el = this.getDomHelper().getElement(this.getId());
|
|
this.setElementInternal(el);
|
|
}
|
|
return el;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The row is the div that is used to draw the node without
|
|
* the children.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getRowElement = function() {
|
|
var el = this.getElement();
|
|
return el ? /** @type {Element} */ (el.firstChild) : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The expanded icon element.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getExpandIconElement = function() {
|
|
var el = this.getRowElement();
|
|
return el ? /** @type {Element} */ (el.firstChild) : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The icon element.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getIconElement = function() {
|
|
var el = this.getRowElement();
|
|
return el ? /** @type {Element} */ (el.childNodes[1]) : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The label element.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getLabelElement = function() {
|
|
var el = this.getRowElement();
|
|
// TODO: find/fix race condition that requires us to add
|
|
// the lastChild check
|
|
return el && el.lastChild ?
|
|
/** @type {Element} */ (el.lastChild.previousSibling) : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The element after the label.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getAfterLabelElement = function() {
|
|
var el = this.getRowElement();
|
|
return el ? /** @type {Element} */ (el.lastChild) : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {Element} The div containing the children.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getChildrenElement = function() {
|
|
var el = this.getElement();
|
|
return el ? /** @type {Element} */ (el.lastChild) : null;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the icon class for the node.
|
|
* @param {string} s The icon class.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setIconClass = function(s) {
|
|
this.iconClass_ = s;
|
|
if (this.isInDocument()) {
|
|
this.updateIcon_();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the icon class for the node.
|
|
* @return {string} s The icon source.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getIconClass = function() {
|
|
return this.iconClass_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the icon class for when the node is expanded.
|
|
* @param {string} s The expanded icon class.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setExpandedIconClass = function(s) {
|
|
this.expandedIconClass_ = s;
|
|
if (this.isInDocument()) {
|
|
this.updateIcon_();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the icon class for when the node is expanded.
|
|
* @return {string} The class.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getExpandedIconClass = function() {
|
|
return this.expandedIconClass_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the text of the label.
|
|
* @param {string} s The plain text of the label.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setText = function(s) {
|
|
this.setSafeHtml(goog.html.SafeHtml.htmlEscape(s));
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the text of the label. If the text was originally set as HTML, the
|
|
* return value is unspecified.
|
|
* @return {string} The plain text of the label.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getText = function() {
|
|
return goog.string.unescapeEntities(goog.html.SafeHtml.unwrap(this.html_));
|
|
};
|
|
|
|
|
|
// TODO(jakubvrana): Deprecate in favor of setSafeHtml, once developer docs on
|
|
// using goog.html.SafeHtml are in place.
|
|
/**
|
|
* Sets the html of the label.
|
|
* @param {string} s The html string for the label.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setHtml = function(s) {
|
|
this.setSafeHtml(goog.html.legacyconversions.safeHtmlFromString(s));
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the HTML of the label.
|
|
* @param {!goog.html.SafeHtml} html The HTML object for the label.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setSafeHtml = function(html) {
|
|
this.html_ = html;
|
|
var el = this.getLabelElement();
|
|
if (el) {
|
|
goog.dom.safe.setInnerHtml(el, html);
|
|
}
|
|
var tree = this.getTree();
|
|
if (tree) {
|
|
// Tell the tree control about the updated label text.
|
|
tree.setNode(this);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the html of the label.
|
|
* @return {string} The html string of the label.
|
|
* @final
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getHtml = function() {
|
|
return goog.html.SafeHtml.unwrap(this.getSafeHtml());
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the html of the label.
|
|
* @return {!goog.html.SafeHtml} The html string of the label.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getSafeHtml = function() {
|
|
return this.html_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the text of the tooltip.
|
|
* @param {string} s The tooltip text to set.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setToolTip = function(s) {
|
|
this.toolTip_ = s;
|
|
var el = this.getLabelElement();
|
|
if (el) {
|
|
el.title = s;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the text of the tooltip.
|
|
* @return {?string} The tooltip text.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getToolTip = function() {
|
|
return this.toolTip_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Updates the row styles.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.updateRow = function() {
|
|
var rowEl = this.getRowElement();
|
|
if (rowEl) {
|
|
rowEl.className = this.getRowClassName();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Updates the expand icon of the node.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.updateExpandIcon = function() {
|
|
var img = this.getExpandIconElement();
|
|
if (img) {
|
|
img.className = this.getExpandIconClass();
|
|
}
|
|
var cel = this.getChildrenElement();
|
|
if (cel) {
|
|
cel.style.backgroundPosition = this.getBackgroundPosition();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Updates the icon of the node. Assumes that this.getElement() is created.
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.updateIcon_ = function() {
|
|
this.getIconElement().className = this.getCalculatedIconClass();
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles mouse down event.
|
|
* @param {!goog.events.BrowserEvent} e The browser event.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.onMouseDown = function(e) {
|
|
var el = e.target;
|
|
// expand icon
|
|
var type = el.getAttribute('type');
|
|
if (type == 'expand' && this.hasChildren()) {
|
|
if (this.isUserCollapsible_) {
|
|
this.toggle();
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.select();
|
|
this.updateRow();
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a click event.
|
|
* @param {!goog.events.BrowserEvent} e The browser event.
|
|
* @protected
|
|
* @suppress {underscore|visibility}
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.onClick_ = goog.events.Event.preventDefault;
|
|
|
|
|
|
/**
|
|
* Handles a double click event.
|
|
* @param {!goog.events.BrowserEvent} e The browser event.
|
|
* @protected
|
|
* @suppress {underscore|visibility}
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.onDoubleClick_ = function(e) {
|
|
var el = e.target;
|
|
// expand icon
|
|
var type = el.getAttribute('type');
|
|
if (type == 'expand' && this.hasChildren()) {
|
|
return;
|
|
}
|
|
|
|
if (this.isUserCollapsible_) {
|
|
this.toggle();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a key down event.
|
|
* @param {!goog.events.BrowserEvent} e The browser event.
|
|
* @return {boolean} The handled value.
|
|
* @protected
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.onKeyDown = function(e) {
|
|
var handled = true;
|
|
switch (e.keyCode) {
|
|
case goog.events.KeyCodes.RIGHT:
|
|
if (e.altKey) {
|
|
break;
|
|
}
|
|
if (this.hasChildren()) {
|
|
if (!this.getExpanded()) {
|
|
this.setExpanded(true);
|
|
} else {
|
|
this.getFirstChild().select();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case goog.events.KeyCodes.LEFT:
|
|
if (e.altKey) {
|
|
break;
|
|
}
|
|
if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) {
|
|
this.setExpanded(false);
|
|
} else {
|
|
var parent = this.getParent();
|
|
var tree = this.getTree();
|
|
// don't go to root if hidden
|
|
if (parent && (tree.getShowRootNode() || parent != tree)) {
|
|
parent.select();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case goog.events.KeyCodes.DOWN:
|
|
var nextNode = this.getNextShownNode();
|
|
if (nextNode) {
|
|
nextNode.select();
|
|
}
|
|
break;
|
|
|
|
case goog.events.KeyCodes.UP:
|
|
var previousNode = this.getPreviousShownNode();
|
|
if (previousNode) {
|
|
previousNode.select();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
handled = false;
|
|
}
|
|
|
|
if (handled) {
|
|
e.preventDefault();
|
|
var tree = this.getTree();
|
|
if (tree) {
|
|
// clear type ahead buffer as user navigates with arrow keys
|
|
tree.clearTypeAhead();
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a key down event.
|
|
* @param {!goog.events.BrowserEvent} e The browser event.
|
|
* @private
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.onKeyPress_ = function(e) {
|
|
if (!e.altKey && e.keyCode >= goog.events.KeyCodes.LEFT &&
|
|
e.keyCode <= goog.events.KeyCodes.DOWN) {
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The last shown descendant.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getLastShownDescendant = function() {
|
|
if (!this.getExpanded() || !this.hasChildren()) {
|
|
return this;
|
|
}
|
|
// we know there is at least 1 child
|
|
return this.getLastChild().getLastShownDescendant();
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The next node to show or null if there isn't
|
|
* a next node to show.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getNextShownNode = function() {
|
|
if (this.hasChildren() && this.getExpanded()) {
|
|
return this.getFirstChild();
|
|
} else {
|
|
var parent = this;
|
|
var next;
|
|
while (parent != this.getTree()) {
|
|
next = parent.getNextSibling();
|
|
if (next != null) {
|
|
return next;
|
|
}
|
|
parent = parent.getParent();
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {goog.ui.tree.BaseNode} The previous node to show.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getPreviousShownNode = function() {
|
|
var ps = this.getPreviousSibling();
|
|
if (ps != null) {
|
|
return ps.getLastShownDescendant();
|
|
}
|
|
var parent = this.getParent();
|
|
var tree = this.getTree();
|
|
if (!tree.getShowRootNode() && parent == tree) {
|
|
return null;
|
|
}
|
|
// The root is the first node.
|
|
if (this == tree) {
|
|
return null;
|
|
}
|
|
return /** @type {goog.ui.tree.BaseNode} */ (parent);
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {*} Data set by the client.
|
|
* @deprecated Use {@link #getModel} instead.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getClientData =
|
|
goog.ui.tree.BaseNode.prototype.getModel;
|
|
|
|
|
|
/**
|
|
* Sets client data to associate with the node.
|
|
* @param {*} data The client data to associate with the node.
|
|
* @deprecated Use {@link #setModel} instead.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setClientData =
|
|
goog.ui.tree.BaseNode.prototype.setModel;
|
|
|
|
|
|
/**
|
|
* @return {Object} The configuration for the tree.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.getConfig = function() {
|
|
return this.config_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Internal method that is used to set the tree control on the node.
|
|
* @param {goog.ui.tree.TreeControl} tree The tree control.
|
|
*/
|
|
goog.ui.tree.BaseNode.prototype.setTreeInternal = function(tree) {
|
|
if (this.tree != tree) {
|
|
this.tree = tree;
|
|
// Add new node to the type ahead node map.
|
|
tree.setNode(this);
|
|
this.forEachChild(function(child) {
|
|
child.setTreeInternal(tree);
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* A default configuration for the tree.
|
|
*/
|
|
goog.ui.tree.BaseNode.defaultConfig = {
|
|
indentWidth: 19,
|
|
cssRoot: goog.getCssName('goog-tree-root') + ' ' +
|
|
goog.getCssName('goog-tree-item'),
|
|
cssHideRoot: goog.getCssName('goog-tree-hide-root'),
|
|
cssItem: goog.getCssName('goog-tree-item'),
|
|
cssChildren: goog.getCssName('goog-tree-children'),
|
|
cssChildrenNoLines: goog.getCssName('goog-tree-children-nolines'),
|
|
cssTreeRow: goog.getCssName('goog-tree-row'),
|
|
cssItemLabel: goog.getCssName('goog-tree-item-label'),
|
|
cssTreeIcon: goog.getCssName('goog-tree-icon'),
|
|
cssExpandTreeIcon: goog.getCssName('goog-tree-expand-icon'),
|
|
cssExpandTreeIconPlus: goog.getCssName('goog-tree-expand-icon-plus'),
|
|
cssExpandTreeIconMinus: goog.getCssName('goog-tree-expand-icon-minus'),
|
|
cssExpandTreeIconTPlus: goog.getCssName('goog-tree-expand-icon-tplus'),
|
|
cssExpandTreeIconTMinus: goog.getCssName('goog-tree-expand-icon-tminus'),
|
|
cssExpandTreeIconLPlus: goog.getCssName('goog-tree-expand-icon-lplus'),
|
|
cssExpandTreeIconLMinus: goog.getCssName('goog-tree-expand-icon-lminus'),
|
|
cssExpandTreeIconT: goog.getCssName('goog-tree-expand-icon-t'),
|
|
cssExpandTreeIconL: goog.getCssName('goog-tree-expand-icon-l'),
|
|
cssExpandTreeIconBlank: goog.getCssName('goog-tree-expand-icon-blank'),
|
|
cssExpandedFolderIcon: goog.getCssName('goog-tree-expanded-folder-icon'),
|
|
cssCollapsedFolderIcon: goog.getCssName('goog-tree-collapsed-folder-icon'),
|
|
cssFileIcon: goog.getCssName('goog-tree-file-icon'),
|
|
cssExpandedRootIcon: goog.getCssName('goog-tree-expanded-folder-icon'),
|
|
cssCollapsedRootIcon: goog.getCssName('goog-tree-collapsed-folder-icon'),
|
|
cssSelectedRow: goog.getCssName('selected')
|
|
};
|