Update wmts-hidpi, add nicer-api-docs

This commit is contained in:
Andreas Hocevar
2014-05-06 13:02:46 -05:00
parent b3ac1afd00
commit 1e25fc5585
2239 changed files with 3726515 additions and 37010 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,663 @@
// 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.TreeControl class, which
* provides a way to view a hierarchical set of data.
*
* @author arv@google.com (Erik Arvidsson)
* @author eae@google.com (Emil A Eklund)
* @author jonp@google.com (Jon Perlow)
* @author annams@google.com (Srinivas Annam)
*
* 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 ../../demos/tree/demo.html
*/
goog.provide('goog.ui.tree.TreeControl');
goog.require('goog.a11y.aria');
goog.require('goog.asserts');
goog.require('goog.dom.classes');
goog.require('goog.events.EventType');
goog.require('goog.events.FocusHandler');
goog.require('goog.events.KeyHandler');
goog.require('goog.log');
goog.require('goog.ui.tree.BaseNode');
goog.require('goog.ui.tree.TreeNode');
goog.require('goog.ui.tree.TypeAhead');
goog.require('goog.userAgent');
/**
* This creates a TreeControl object. A tree control provides a way to
* view a hierarchical set of data.
* @param {string} html The HTML content of the node label.
* @param {Object=} opt_config The configuration for the tree. See
* goog.ui.tree.TreeControl.defaultConfig. If not specified, a default config
* will be used.
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
* @constructor
* @extends {goog.ui.tree.BaseNode}
*/
goog.ui.tree.TreeControl = function(html, opt_config, opt_domHelper) {
goog.ui.tree.BaseNode.call(this, html, opt_config, opt_domHelper);
// The root is open and selected by default.
this.setExpandedInternal(true);
this.setSelectedInternal(true);
this.selectedItem_ = this;
/**
* Used for typeahead support.
* @type {!goog.ui.tree.TypeAhead}
* @private
*/
this.typeAhead_ = new goog.ui.tree.TypeAhead();
if (goog.userAgent.IE) {
/** @preserveTry */
try {
// works since IE6SP1
document.execCommand('BackgroundImageCache', false, true);
} catch (e) {
goog.log.warning(this.logger_, 'Failed to enable background image cache');
}
}
};
goog.inherits(goog.ui.tree.TreeControl, goog.ui.tree.BaseNode);
/**
* The object handling keyboard events.
* @type {goog.events.KeyHandler}
* @private
*/
goog.ui.tree.TreeControl.prototype.keyHandler_ = null;
/**
* The object handling focus events.
* @type {goog.events.FocusHandler}
* @private
*/
goog.ui.tree.TreeControl.prototype.focusHandler_ = null;
/**
* Logger
* @type {goog.log.Logger}
* @private
*/
goog.ui.tree.TreeControl.prototype.logger_ =
goog.log.getLogger('goog.ui.tree.TreeControl');
/**
* Whether the tree is focused.
* @type {boolean}
* @private
*/
goog.ui.tree.TreeControl.prototype.focused_ = false;
/**
* Child node that currently has focus.
* @type {goog.ui.tree.BaseNode}
* @private
*/
goog.ui.tree.TreeControl.prototype.focusedNode_ = null;
/**
* Whether to show lines.
* @type {boolean}
* @private
*/
goog.ui.tree.TreeControl.prototype.showLines_ = true;
/**
* Whether to show expanded lines.
* @type {boolean}
* @private
*/
goog.ui.tree.TreeControl.prototype.showExpandIcons_ = true;
/**
* Whether to show the root node.
* @type {boolean}
* @private
*/
goog.ui.tree.TreeControl.prototype.showRootNode_ = true;
/**
* Whether to show the root lines.
* @type {boolean}
* @private
*/
goog.ui.tree.TreeControl.prototype.showRootLines_ = true;
/** @override */
goog.ui.tree.TreeControl.prototype.getTree = function() {
return this;
};
/** @override */
goog.ui.tree.TreeControl.prototype.getDepth = function() {
return 0;
};
/**
* Expands the parent chain of this node so that it is visible.
* @override
*/
goog.ui.tree.TreeControl.prototype.reveal = function() {
// always expanded by default
// needs to be overriden so that we don't try to reveal our parent
// which is a generic component
};
/**
* Handles focus on the tree.
* @param {!goog.events.BrowserEvent} e The browser event.
* @private
*/
goog.ui.tree.TreeControl.prototype.handleFocus_ = function(e) {
this.focused_ = true;
goog.dom.classes.add(this.getElement(), 'focused');
if (this.selectedItem_) {
this.selectedItem_.select();
}
};
/**
* Handles blur on the tree.
* @param {!goog.events.BrowserEvent} e The browser event.
* @private
*/
goog.ui.tree.TreeControl.prototype.handleBlur_ = function(e) {
this.focused_ = false;
goog.dom.classes.remove(this.getElement(), 'focused');
};
/**
* @return {boolean} Whether the tree has keyboard focus.
*/
goog.ui.tree.TreeControl.prototype.hasFocus = function() {
return this.focused_;
};
/** @override */
goog.ui.tree.TreeControl.prototype.getExpanded = function() {
return !this.showRootNode_ ||
goog.ui.tree.TreeControl.superClass_.getExpanded.call(this);
};
/** @override */
goog.ui.tree.TreeControl.prototype.setExpanded = function(expanded) {
if (!this.showRootNode_) {
this.setExpandedInternal(expanded);
} else {
goog.ui.tree.TreeControl.superClass_.setExpanded.call(this, expanded);
}
};
/** @override */
goog.ui.tree.TreeControl.prototype.getExpandIconHtml = function() {
// no expand icon for root element
return '';
};
/** @override */
goog.ui.tree.TreeControl.prototype.getIconElement = function() {
var el = this.getRowElement();
return el ? /** @type {Element} */ (el.firstChild) : null;
};
/** @override */
goog.ui.tree.TreeControl.prototype.getExpandIconElement = function() {
// no expand icon for root element
return null;
};
/** @override */
goog.ui.tree.TreeControl.prototype.updateExpandIcon = function() {
// no expand icon
};
/** @override */
goog.ui.tree.TreeControl.prototype.getRowClassName = function() {
return goog.ui.tree.TreeControl.superClass_.getRowClassName.call(this) +
(this.showRootNode_ ? '' : ' ' + this.getConfig().cssHideRoot);
};
/**
* Returns the source for the icon.
* @return {string} Src for the icon.
* @override
*/
goog.ui.tree.TreeControl.prototype.getCalculatedIconClass = function() {
var expanded = this.getExpanded();
if (expanded && this.expandedIconClass_) {
return this.expandedIconClass_;
}
if (!expanded && this.iconClass_) {
return this.iconClass_;
}
// fall back on default icons
var config = this.getConfig();
if (expanded && config.cssExpandedRootIcon) {
return config.cssTreeIcon + ' ' + config.cssExpandedRootIcon;
} else if (!expanded && config.cssCollapsedRootIcon) {
return config.cssTreeIcon + ' ' + config.cssCollapsedRootIcon;
}
return '';
};
/**
* Sets the selected item.
* @param {goog.ui.tree.BaseNode} node The item to select.
*/
goog.ui.tree.TreeControl.prototype.setSelectedItem = function(node) {
if (this.selectedItem_ == node) {
return;
}
var hadFocus = false;
if (this.selectedItem_) {
hadFocus = this.selectedItem_ == this.focusedNode_;
this.selectedItem_.setSelectedInternal(false);
}
this.selectedItem_ = node;
if (node) {
node.setSelectedInternal(true);
if (hadFocus) {
node.select();
}
}
this.dispatchEvent(goog.events.EventType.CHANGE);
};
/**
* Returns the selected item.
* @return {goog.ui.tree.BaseNode} The currently selected item.
*/
goog.ui.tree.TreeControl.prototype.getSelectedItem = function() {
return this.selectedItem_;
};
/**
* Sets whether to show lines.
* @param {boolean} b Whether to show lines.
*/
goog.ui.tree.TreeControl.prototype.setShowLines = function(b) {
if (this.showLines_ != b) {
this.showLines_ = b;
if (this.isInDocument()) {
this.updateLinesAndExpandIcons_();
}
}
};
/**
* @return {boolean} Whether to show lines.
*/
goog.ui.tree.TreeControl.prototype.getShowLines = function() {
return this.showLines_;
};
/**
* Updates the lines after the tree has been drawn.
* @private
*/
goog.ui.tree.TreeControl.prototype.updateLinesAndExpandIcons_ = function() {
var tree = this;
var showLines = tree.getShowLines();
var showRootLines = tree.getShowRootLines();
// Recursively walk through all nodes and update the class names of the
// expand icon and the children element.
function updateShowLines(node) {
var childrenEl = node.getChildrenElement();
if (childrenEl) {
var hideLines = !showLines || tree == node.getParent() && !showRootLines;
var childClass = hideLines ? node.getConfig().cssChildrenNoLines :
node.getConfig().cssChildren;
childrenEl.className = childClass;
var expandIconEl = node.getExpandIconElement();
if (expandIconEl) {
expandIconEl.className = node.getExpandIconClass();
}
}
node.forEachChild(updateShowLines);
}
updateShowLines(this);
};
/**
* Sets whether to show root lines.
* @param {boolean} b Whether to show root lines.
*/
goog.ui.tree.TreeControl.prototype.setShowRootLines = function(b) {
if (this.showRootLines_ != b) {
this.showRootLines_ = b;
if (this.isInDocument()) {
this.updateLinesAndExpandIcons_();
}
}
};
/**
* @return {boolean} Whether to show root lines.
*/
goog.ui.tree.TreeControl.prototype.getShowRootLines = function() {
return this.showRootLines_;
};
/**
* Sets whether to show expand icons.
* @param {boolean} b Whether to show expand icons.
*/
goog.ui.tree.TreeControl.prototype.setShowExpandIcons = function(b) {
if (this.showExpandIcons_ != b) {
this.showExpandIcons_ = b;
if (this.isInDocument()) {
this.updateLinesAndExpandIcons_();
}
}
};
/**
* @return {boolean} Whether to show expand icons.
*/
goog.ui.tree.TreeControl.prototype.getShowExpandIcons = function() {
return this.showExpandIcons_;
};
/**
* Sets whether to show the root node.
* @param {boolean} b Whether to show the root node.
*/
goog.ui.tree.TreeControl.prototype.setShowRootNode = function(b) {
if (this.showRootNode_ != b) {
this.showRootNode_ = b;
if (this.isInDocument()) {
var el = this.getRowElement();
if (el) {
el.className = this.getRowClassName();
}
}
// Ensure that we do not hide the selected item.
if (!b && this.getSelectedItem() == this && this.getFirstChild()) {
this.setSelectedItem(this.getFirstChild());
}
}
};
/**
* @return {boolean} Whether to show the root node.
*/
goog.ui.tree.TreeControl.prototype.getShowRootNode = function() {
return this.showRootNode_;
};
/**
* Add roles and states.
* @protected
* @override
*/
goog.ui.tree.TreeControl.prototype.initAccessibility = function() {
goog.ui.tree.TreeControl.superClass_.initAccessibility.call(this);
var elt = this.getElement();
goog.asserts.assert(elt, 'The DOM element for the tree cannot be null.');
goog.a11y.aria.setRole(elt, 'tree');
goog.a11y.aria.setState(elt, 'labelledby', this.getLabelElement().id);
};
/** @override */
goog.ui.tree.TreeControl.prototype.enterDocument = function() {
goog.ui.tree.TreeControl.superClass_.enterDocument.call(this);
var el = this.getElement();
el.className = this.getConfig().cssRoot;
el.setAttribute('hideFocus', 'true');
this.attachEvents_();
this.initAccessibility();
};
/** @override */
goog.ui.tree.TreeControl.prototype.exitDocument = function() {
goog.ui.tree.TreeControl.superClass_.exitDocument.call(this);
this.detachEvents_();
};
/**
* Adds the event listeners to the tree.
* @private
*/
goog.ui.tree.TreeControl.prototype.attachEvents_ = function() {
var el = this.getElement();
el.tabIndex = 0;
var kh = this.keyHandler_ = new goog.events.KeyHandler(el);
var fh = this.focusHandler_ = new goog.events.FocusHandler(el);
this.getHandler().
listen(fh, goog.events.FocusHandler.EventType.FOCUSOUT, this.handleBlur_).
listen(fh, goog.events.FocusHandler.EventType.FOCUSIN, this.handleFocus_).
listen(kh, goog.events.KeyHandler.EventType.KEY, this.handleKeyEvent).
listen(el, goog.events.EventType.MOUSEDOWN, this.handleMouseEvent_).
listen(el, goog.events.EventType.CLICK, this.handleMouseEvent_).
listen(el, goog.events.EventType.DBLCLICK, this.handleMouseEvent_);
};
/**
* Removes the event listeners from the tree.
* @private
*/
goog.ui.tree.TreeControl.prototype.detachEvents_ = function() {
this.keyHandler_.dispose();
this.keyHandler_ = null;
this.focusHandler_.dispose();
this.focusHandler_ = null;
};
/**
* Handles mouse events.
* @param {!goog.events.BrowserEvent} e The browser event.
* @private
*/
goog.ui.tree.TreeControl.prototype.handleMouseEvent_ = function(e) {
goog.log.fine(this.logger_, 'Received event ' + e.type);
var node = this.getNodeFromEvent_(e);
if (node) {
switch (e.type) {
case goog.events.EventType.MOUSEDOWN:
node.onMouseDown(e);
break;
case goog.events.EventType.CLICK:
node.onClick_(e);
break;
case goog.events.EventType.DBLCLICK:
node.onDoubleClick_(e);
break;
}
}
};
/**
* Handles key down on the tree.
* @param {!goog.events.BrowserEvent} e The browser event.
* @return {boolean} The handled value.
*/
goog.ui.tree.TreeControl.prototype.handleKeyEvent = function(e) {
var handled = false;
// Handle typeahead and navigation keystrokes.
handled = this.typeAhead_.handleNavigation(e) ||
(this.selectedItem_ && this.selectedItem_.onKeyDown(e)) ||
this.typeAhead_.handleTypeAheadChar(e);
if (handled) {
e.preventDefault();
}
return handled;
};
/**
* Finds the containing node given an event.
* @param {!goog.events.BrowserEvent} e The browser event.
* @return {goog.ui.tree.BaseNode} The containing node or null if no node is
* found.
* @private
*/
goog.ui.tree.TreeControl.prototype.getNodeFromEvent_ = function(e) {
// find the right node
var node = null;
var target = e.target;
while (target != null) {
var id = target.id;
node = goog.ui.tree.BaseNode.allNodes[id];
if (node) {
return node;
}
if (target == this.getElement()) {
break;
}
target = target.parentNode;
}
return null;
};
/**
* Creates a new tree node using the same config as the root.
* @param {string} html The html content of the node label.
* @return {goog.ui.tree.TreeNode} The new item.
*/
goog.ui.tree.TreeControl.prototype.createNode = function(html) {
// Some projects call createNode without arguments which causes failure.
// See http://goto/misuse-createnode
// TODO(user): Fix them and remove the html || '' workaround.
return new goog.ui.tree.TreeNode(html || '', this.getConfig(),
this.getDomHelper());
};
/**
* Allows the caller to notify that the given node has been added or just had
* been updated in the tree.
* @param {goog.ui.tree.BaseNode} node New node being added or existing node
* that just had been updated.
*/
goog.ui.tree.TreeControl.prototype.setNode = function(node) {
this.typeAhead_.setNodeInMap(node);
};
/**
* Allows the caller to notify that the given node is being removed from the
* tree.
* @param {goog.ui.tree.BaseNode} node Node being removed.
*/
goog.ui.tree.TreeControl.prototype.removeNode = function(node) {
this.typeAhead_.removeNodeFromMap(node);
};
/**
* Clear the typeahead buffer.
*/
goog.ui.tree.TreeControl.prototype.clearTypeAhead = function() {
this.typeAhead_.clear();
};
/**
* A default configuration for the tree.
*/
goog.ui.tree.TreeControl.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')
};

View File

@@ -0,0 +1,107 @@
// 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.TreeNode class.
*
* @author arv@google.com (Erik Arvidsson)
* @author eae@google.com (Emil A Eklund)
* @author jonp@google.com (Jon Perlow)
*
* This is a based on the webfx tree control. See file comment in
* treecontrol.js.
*/
goog.provide('goog.ui.tree.TreeNode');
goog.require('goog.ui.tree.BaseNode');
/**
* A single node in the tree.
* @param {string} html The html content of the node label.
* @param {Object=} opt_config The configuration for the tree. See
* goog.ui.tree.TreeControl.defaultConfig. If not specified, a default config
* will be used.
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
* @constructor
* @extends {goog.ui.tree.BaseNode}
*/
goog.ui.tree.TreeNode = function(html, opt_config, opt_domHelper) {
goog.ui.tree.BaseNode.call(this, html, opt_config, opt_domHelper);
};
goog.inherits(goog.ui.tree.TreeNode, goog.ui.tree.BaseNode);
/**
* The tree the item is in. Cached on demand from the parent.
* @type {goog.ui.tree.TreeControl?}
* @private
*/
goog.ui.tree.TreeNode.prototype.tree_ = null;
/**
* Returns the tree.
* @return {goog.ui.tree.TreeControl?} The tree.
* @override
*/
goog.ui.tree.TreeNode.prototype.getTree = function() {
if (this.tree_) {
return this.tree_;
}
var parent = this.getParent();
if (parent) {
var tree = parent.getTree();
if (tree) {
this.setTreeInternal(tree);
return tree;
}
}
return null;
};
/**
* Returns the source for the icon.
* @return {string} Src for the icon.
* @override
*/
goog.ui.tree.TreeNode.prototype.getCalculatedIconClass = function() {
var expanded = this.getExpanded();
if (expanded && this.expandedIconClass_) {
return this.expandedIconClass_;
}
if (!expanded && this.iconClass_) {
return this.iconClass_;
}
// fall back on default icons
var config = this.getConfig();
if (this.hasChildren()) {
if (expanded && config.cssExpandedFolderIcon) {
return config.cssTreeIcon + ' ' +
config.cssExpandedFolderIcon;
} else if (!expanded && config.cssCollapsedFolderIcon) {
return config.cssTreeIcon + ' ' +
config.cssCollapsedFolderIcon;
}
} else {
if (config.cssFileIcon) {
return config.cssTreeIcon + ' ' + config.cssFileIcon;
}
}
return '';
};

View File

@@ -0,0 +1,332 @@
// 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 Provides the typeahead functionality for the tree class.
*
*/
goog.provide('goog.ui.tree.TypeAhead');
goog.provide('goog.ui.tree.TypeAhead.Offset');
goog.require('goog.array');
goog.require('goog.events.KeyCodes');
goog.require('goog.string');
goog.require('goog.structs.Trie');
/**
* Constructs a TypeAhead object.
* @constructor
*/
goog.ui.tree.TypeAhead = function() {
this.nodeMap_ = new goog.structs.Trie();
};
/**
* Map of tree nodes to allow for quick access by characters in the label text.
* @type {goog.structs.Trie}
* @private
*/
goog.ui.tree.TypeAhead.prototype.nodeMap_;
/**
* Buffer for storing typeahead characters.
* @type {string}
* @private
*/
goog.ui.tree.TypeAhead.prototype.buffer_ = '';
/**
* Matching labels from the latest typeahead search.
* @type {Array.<string>?}
* @private
*/
goog.ui.tree.TypeAhead.prototype.matchingLabels_ = null;
/**
* Matching nodes from the latest typeahead search. Used when more than
* one node is present with the same label text.
* @type {Array.<goog.ui.tree.BaseNode>?}
* @private
*/
goog.ui.tree.TypeAhead.prototype.matchingNodes_ = null;
/**
* Specifies the current index of the label from the latest typeahead search.
* @type {number}
* @private
*/
goog.ui.tree.TypeAhead.prototype.matchingLabelIndex_ = 0;
/**
* Specifies the index into matching nodes when more than one node is found
* with the same label.
* @type {number}
* @private
*/
goog.ui.tree.TypeAhead.prototype.matchingNodeIndex_ = 0;
/**
* Enum for offset values that are used for ctrl-key navigation among the
* multiple matches of a given typeahead buffer.
*
* @enum {number}
*/
goog.ui.tree.TypeAhead.Offset = {
DOWN: 1,
UP: -1
};
/**
* Handles navigation keys.
* @param {goog.events.BrowserEvent} e The browser event.
* @return {boolean} The handled value.
*/
goog.ui.tree.TypeAhead.prototype.handleNavigation = function(e) {
var handled = false;
switch (e.keyCode) {
// Handle ctrl+down, ctrl+up to navigate within typeahead results.
case goog.events.KeyCodes.DOWN:
case goog.events.KeyCodes.UP:
if (e.ctrlKey) {
this.jumpTo_(e.keyCode == goog.events.KeyCodes.DOWN ?
goog.ui.tree.TypeAhead.Offset.DOWN :
goog.ui.tree.TypeAhead.Offset.UP);
handled = true;
}
break;
// Remove the last typeahead char.
case goog.events.KeyCodes.BACKSPACE:
var length = this.buffer_.length - 1;
handled = true;
if (length > 0) {
this.buffer_ = this.buffer_.substring(0, length);
this.jumpToLabel_(this.buffer_);
} else if (length == 0) {
// Clear the last character in typeahead.
this.buffer_ = '';
} else {
handled = false;
}
break;
// Clear typeahead buffer.
case goog.events.KeyCodes.ESC:
this.buffer_ = '';
handled = true;
break;
}
return handled;
};
/**
* Handles the character presses.
* @param {goog.events.BrowserEvent} e The browser event.
* Expected event type is goog.events.KeyHandler.EventType.KEY.
* @return {boolean} The handled value.
*/
goog.ui.tree.TypeAhead.prototype.handleTypeAheadChar = function(e) {
var handled = false;
if (!e.ctrlKey && !e.altKey) {
// Since goog.structs.Trie.getKeys compares characters during
// lookup, we should use charCode instead of keyCode where possible.
// Convert to lowercase, typeahead is case insensitive.
var ch = String.fromCharCode(e.charCode || e.keyCode).toLowerCase();
if (goog.string.isUnicodeChar(ch) && (ch != ' ' || this.buffer_)) {
this.buffer_ += ch;
handled = this.jumpToLabel_(this.buffer_);
}
}
return handled;
};
/**
* Adds or updates the given node in the nodemap. The label text is used as a
* key and the node id is used as a value. In the case that the key already
* exists, such as when more than one node exists with the same label, then this
* function creates an array to hold the multiple nodes.
* @param {goog.ui.tree.BaseNode} node Node to be added or updated.
*/
goog.ui.tree.TypeAhead.prototype.setNodeInMap = function(node) {
var labelText = node.getText();
if (labelText && !goog.string.isEmptySafe(labelText)) {
// Typeahead is case insensitive, convert to lowercase.
labelText = labelText.toLowerCase();
var previousValue = this.nodeMap_.get(labelText);
if (previousValue) {
// Found a previously created array, add the given node.
previousValue.push(node);
} else {
// Create a new array and set the array as value.
var nodeList = [node];
this.nodeMap_.set(labelText, nodeList);
}
}
};
/**
* Removes the given node from the nodemap.
* @param {goog.ui.tree.BaseNode} node Node to be removed.
*/
goog.ui.tree.TypeAhead.prototype.removeNodeFromMap = function(node) {
var labelText = node.getText();
if (labelText && !goog.string.isEmptySafe(labelText)) {
labelText = labelText.toLowerCase();
var nodeList = /** @type {Array} */ (this.nodeMap_.get(labelText));
if (nodeList) {
// Remove the node from the array.
goog.array.remove(nodeList, node);
if (!!nodeList.length) {
this.nodeMap_.remove(labelText);
}
}
}
};
/**
* Select the first matching node for the given typeahead.
* @param {string} typeAhead Typeahead characters to match.
* @return {boolean} True iff a node is found.
* @private
*/
goog.ui.tree.TypeAhead.prototype.jumpToLabel_ = function(typeAhead) {
var handled = false;
var labels = this.nodeMap_.getKeys(typeAhead);
// Make sure we have at least one matching label.
if (labels && labels.length) {
this.matchingNodeIndex_ = 0;
this.matchingLabelIndex_ = 0;
var nodes = /** @type {Array} */ (this.nodeMap_.get(labels[0]));
if ((handled = this.selectMatchingNode_(nodes))) {
this.matchingLabels_ = labels;
}
}
// TODO(user): beep when no node is found
return handled;
};
/**
* Select the next or previous node based on the offset.
* @param {goog.ui.tree.TypeAhead.Offset} offset DOWN or UP.
* @return {boolean} Whether a node is found.
* @private
*/
goog.ui.tree.TypeAhead.prototype.jumpTo_ = function(offset) {
var handled = false;
var labels = this.matchingLabels_;
if (labels) {
var nodes = null;
var nodeIndexOutOfRange = false;
// Navigate within the nodes array.
if (this.matchingNodes_) {
var newNodeIndex = this.matchingNodeIndex_ + offset;
if (newNodeIndex >= 0 && newNodeIndex < this.matchingNodes_.length) {
this.matchingNodeIndex_ = newNodeIndex;
nodes = this.matchingNodes_;
} else {
nodeIndexOutOfRange = true;
}
}
// Navigate to the next or previous label.
if (!nodes) {
var newLabelIndex = this.matchingLabelIndex_ + offset;
if (newLabelIndex >= 0 && newLabelIndex < labels.length) {
this.matchingLabelIndex_ = newLabelIndex;
}
if (labels.length > this.matchingLabelIndex_) {
nodes = /** @type {Array} */ (this.nodeMap_.get(
labels[this.matchingLabelIndex_]));
}
// Handle the case where we are moving beyond the available nodes,
// while going UP select the last item of multiple nodes with same label
// and while going DOWN select the first item of next set of nodes
if (nodes && nodes.length && nodeIndexOutOfRange) {
this.matchingNodeIndex_ = (offset == goog.ui.tree.TypeAhead.Offset.UP) ?
nodes.length - 1 : 0;
}
}
if ((handled = this.selectMatchingNode_(nodes))) {
this.matchingLabels_ = labels;
}
}
// TODO(user): beep when no node is found
return handled;
};
/**
* Given a nodes array reveals and selects the node while using node index.
* @param {Array.<goog.ui.tree.BaseNode>?} nodes Nodes array to select the
* node from.
* @return {boolean} Whether a matching node was found.
* @private
*/
goog.ui.tree.TypeAhead.prototype.selectMatchingNode_ = function(nodes) {
var node;
if (nodes) {
// Find the matching node.
if (this.matchingNodeIndex_ < nodes.length) {
node = nodes[this.matchingNodeIndex_];
this.matchingNodes_ = nodes;
}
if (node) {
node.reveal();
node.select();
}
}
return !!node;
};
/**
* Clears the typeahead buffer.
*/
goog.ui.tree.TypeAhead.prototype.clear = function() {
this.buffer_ = '';
};