12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526 |
- // 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.a11y.aria.State');
- 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.string');
- goog.require('goog.string.StringBuffer');
- goog.require('goog.style');
- goog.require('goog.ui.Component');
- goog.forwardDeclare('goog.ui.tree.TreeControl'); // circular
- /**
- * An abstract base class for a node in the tree.
- *
- * @param {string|!goog.html.SafeHtml} content The content of the node label.
- * Strings are treated as plain-text and will be HTML escaped.
- * @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(content, 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_ = goog.html.SafeHtml.htmlEscapePreservingNewlines(content);
- /** @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_;
- /**
- * Whether the tree item is selected.
- * @private {boolean}
- */
- this.selected_ = false;
- /**
- * Whether the tree node is expanded.
- * @private {boolean}
- */
- this.expanded_ = false;
- /**
- * Tooltip for the tree item
- * @private {?string}
- */
- this.toolTip_ = null;
- /**
- * HTML that can appear after the label (so not inside the anchor).
- * @private {!goog.html.SafeHtml}
- */
- this.afterLabelHtml_ = goog.html.SafeHtml.EMPTY;
- /**
- * Whether to allow user to collapse this node.
- * @private {boolean}
- */
- this.isUserCollapsible_ = true;
- /**
- * Nesting depth of this node; cached result of computeDepth_.
- * -1 if value has not been cached.
- * @private {number}
- */
- this.depth_ = -1;
- };
- 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 = {};
- /** @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, '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()) {
- // Only set aria-expanded if the node has children (can be expanded).
- goog.a11y.aria.setState(el, goog.a11y.aria.State.EXPANDED, false);
- // 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);
- var el = this.getElement();
- if (el) {
- this.updateExpandIcon();
- goog.a11y.aria.setState(
- el, goog.a11y.aria.State.EXPANDED, this.getExpanded());
- if (this.getExpanded()) {
- var childrenEl = this.getChildrenElement();
- if (!child.getElement()) {
- child.createDom();
- }
- var childElement = child.getElement();
- var nextElement = nextNode && nextNode.getElement();
- childrenEl.insertBefore(childElement, nextElement);
- if (this.isInDocument()) {
- child.enterDocument();
- }
- if (!nextNode) {
- if (prevNode) {
- prevNode.updateExpandIcon();
- } else {
- goog.style.setElementShown(childrenEl, 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 the child node is now removed.
- tree.removeNode(child);
- if (this.isInDocument()) {
- var childrenEl = this.getChildrenElement();
- if (child.isInDocument()) {
- var childEl = child.getElement();
- childrenEl.removeChild(childEl);
- child.exitDocument();
- }
- if (wasLast) {
- var newLast = this.getLastChild();
- if (newLast) {
- newLast.updateExpandIcon();
- }
- }
- if (!this.hasChildren()) {
- childrenEl.style.display = 'none';
- this.updateExpandIcon();
- this.updateIcon_();
- var el = this.getElement();
- if (el) {
- goog.a11y.aria.removeState(el, goog.a11y.aria.State.EXPANDED);
- }
- }
- }
- }
- 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.
- * @return {?goog.ui.tree.TreeControl}
- */
- 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);
- goog.a11y.aria.setState(el, goog.a11y.aria.State.EXPANDED, 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_();
- }
- 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_;
- };
- /**
- * 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_));
- };
- /**
- * 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;
- };
- /**
- * @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')
- };
|