http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/classlist.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/classlist.js b/externs/GCL/externs/goog/dom/classlist.js new file mode 100644 index 0000000..dcbb7ed --- /dev/null +++ b/externs/GCL/externs/goog/dom/classlist.js @@ -0,0 +1,277 @@ +// Copyright 2012 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 Utilities for detecting, adding and removing classes. Prefer + * this over goog.dom.classes for new code since it attempts to use classList + * (DOMTokenList: http://dom.spec.whatwg.org/#domtokenlist) which is faster + * and requires less code. + * + * Note: these utilities are meant to operate on HTMLElements + * and may have unexpected behavior on elements with differing interfaces + * (such as SVGElements). + */ + + +goog.provide('goog.dom.classlist'); + +goog.require('goog.array'); + + +/** + * Override this define at build-time if you know your target supports it. + * @define {boolean} Whether to use the classList property (DOMTokenList). + */ +goog.define('goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST', false); + + +/** + * Gets an array-like object of class names on an element. + * @param {Element} element DOM node to get the classes of. + * @return {!goog.array.ArrayLike} Class names on {@code element}. + */ +goog.dom.classlist.get = function(element) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + return element.classList; + } + + var className = element.className; + // Some types of elements don't have a className in IE (e.g. iframes). + // Furthermore, in Firefox, className is not a string when the element is + // an SVG element. + return goog.isString(className) && className.match(/\S+/g) || []; +}; + + +/** + * Sets the entire class name of an element. + * @param {Element} element DOM node to set class of. + * @param {string} className Class name(s) to apply to element. + */ +goog.dom.classlist.set = function(element, className) { + element.className = className; +}; + + +/** + * Returns true if an element has a class. This method may throw a DOM + * exception for an invalid or empty class name if DOMTokenList is used. + * @param {Element} element DOM node to test. + * @param {string} className Class name to test for. + * @return {boolean} Whether element has the class. + */ +goog.dom.classlist.contains = function(element, className) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + return element.classList.contains(className); + } + return goog.array.contains(goog.dom.classlist.get(element), className); +}; + + +/** + * Adds a class to an element. Does not add multiples of class names. This + * method may throw a DOM exception for an invalid or empty class name if + * DOMTokenList is used. + * @param {Element} element DOM node to add class to. + * @param {string} className Class name to add. + */ +goog.dom.classlist.add = function(element, className) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + element.classList.add(className); + return; + } + + if (!goog.dom.classlist.contains(element, className)) { + // Ensure we add a space if this is not the first class name added. + element.className += element.className.length > 0 ? + (' ' + className) : className; + } +}; + + +/** + * Convenience method to add a number of class names at once. + * @param {Element} element The element to which to add classes. + * @param {goog.array.ArrayLike<string>} classesToAdd An array-like object + * containing a collection of class names to add to the element. + * This method may throw a DOM exception if classesToAdd contains invalid + * or empty class names. + */ +goog.dom.classlist.addAll = function(element, classesToAdd) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + goog.array.forEach(classesToAdd, function(className) { + goog.dom.classlist.add(element, className); + }); + return; + } + + var classMap = {}; + + // Get all current class names into a map. + goog.array.forEach(goog.dom.classlist.get(element), + function(className) { + classMap[className] = true; + }); + + // Add new class names to the map. + goog.array.forEach(classesToAdd, + function(className) { + classMap[className] = true; + }); + + // Flatten the keys of the map into the className. + element.className = ''; + for (var className in classMap) { + element.className += element.className.length > 0 ? + (' ' + className) : className; + } +}; + + +/** + * Removes a class from an element. This method may throw a DOM exception + * for an invalid or empty class name if DOMTokenList is used. + * @param {Element} element DOM node to remove class from. + * @param {string} className Class name to remove. + */ +goog.dom.classlist.remove = function(element, className) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + element.classList.remove(className); + return; + } + + if (goog.dom.classlist.contains(element, className)) { + // Filter out the class name. + element.className = goog.array.filter( + goog.dom.classlist.get(element), + function(c) { + return c != className; + }).join(' '); + } +}; + + +/** + * Removes a set of classes from an element. Prefer this call to + * repeatedly calling {@code goog.dom.classlist.remove} if you want to remove + * a large set of class names at once. + * @param {Element} element The element from which to remove classes. + * @param {goog.array.ArrayLike<string>} classesToRemove An array-like object + * containing a collection of class names to remove from the element. + * This method may throw a DOM exception if classesToRemove contains invalid + * or empty class names. + */ +goog.dom.classlist.removeAll = function(element, classesToRemove) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + goog.array.forEach(classesToRemove, function(className) { + goog.dom.classlist.remove(element, className); + }); + return; + } + // Filter out those classes in classesToRemove. + element.className = goog.array.filter( + goog.dom.classlist.get(element), + function(className) { + // If this class is not one we are trying to remove, + // add it to the array of new class names. + return !goog.array.contains(classesToRemove, className); + }).join(' '); +}; + + +/** + * Adds or removes a class depending on the enabled argument. This method + * may throw a DOM exception for an invalid or empty class name if DOMTokenList + * is used. + * @param {Element} element DOM node to add or remove the class on. + * @param {string} className Class name to add or remove. + * @param {boolean} enabled Whether to add or remove the class (true adds, + * false removes). + */ +goog.dom.classlist.enable = function(element, className, enabled) { + if (enabled) { + goog.dom.classlist.add(element, className); + } else { + goog.dom.classlist.remove(element, className); + } +}; + + +/** + * Adds or removes a set of classes depending on the enabled argument. This + * method may throw a DOM exception for an invalid or empty class name if + * DOMTokenList is used. + * @param {!Element} element DOM node to add or remove the class on. + * @param {goog.array.ArrayLike<string>} classesToEnable An array-like object + * containing a collection of class names to add or remove from the element. + * @param {boolean} enabled Whether to add or remove the classes (true adds, + * false removes). + */ +goog.dom.classlist.enableAll = function(element, classesToEnable, enabled) { + var f = enabled ? goog.dom.classlist.addAll : + goog.dom.classlist.removeAll; + f(element, classesToEnable); +}; + + +/** + * Switches a class on an element from one to another without disturbing other + * classes. If the fromClass isn't removed, the toClass won't be added. This + * method may throw a DOM exception if the class names are empty or invalid. + * @param {Element} element DOM node to swap classes on. + * @param {string} fromClass Class to remove. + * @param {string} toClass Class to add. + * @return {boolean} Whether classes were switched. + */ +goog.dom.classlist.swap = function(element, fromClass, toClass) { + if (goog.dom.classlist.contains(element, fromClass)) { + goog.dom.classlist.remove(element, fromClass); + goog.dom.classlist.add(element, toClass); + return true; + } + return false; +}; + + +/** + * Removes a class if an element has it, and adds it the element doesn't have + * it. Won't affect other classes on the node. This method may throw a DOM + * exception if the class name is empty or invalid. + * @param {Element} element DOM node to toggle class on. + * @param {string} className Class to toggle. + * @return {boolean} True if class was added, false if it was removed + * (in other words, whether element has the class after this function has + * been called). + */ +goog.dom.classlist.toggle = function(element, className) { + var add = !goog.dom.classlist.contains(element, className); + goog.dom.classlist.enable(element, className, add); + return add; +}; + + +/** + * Adds and removes a class of an element. Unlike + * {@link goog.dom.classlist.swap}, this method adds the classToAdd regardless + * of whether the classToRemove was present and had been removed. This method + * may throw a DOM exception if the class names are empty or invalid. + * + * @param {Element} element DOM node to swap classes on. + * @param {string} classToRemove Class to remove. + * @param {string} classToAdd Class to add. + */ +goog.dom.classlist.addRemove = function(element, classToRemove, classToAdd) { + goog.dom.classlist.remove(element, classToRemove); + goog.dom.classlist.add(element, classToAdd); +};
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/controlrange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/controlrange.js b/externs/GCL/externs/goog/dom/controlrange.js new file mode 100644 index 0000000..61df59a --- /dev/null +++ b/externs/GCL/externs/goog/dom/controlrange.js @@ -0,0 +1,494 @@ +// Copyright 2008 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 Utilities for working with IE control ranges. + * + * @author [email protected] (Robby Walker) + */ + + +goog.provide('goog.dom.ControlRange'); +goog.provide('goog.dom.ControlRangeIterator'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.AbstractMultiRange'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.RangeIterator'); +goog.require('goog.dom.RangeType'); +goog.require('goog.dom.SavedRange'); +goog.require('goog.dom.TagWalkType'); +goog.require('goog.dom.TextRange'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.userAgent'); + + + +/** + * Create a new control selection with no properties. Do not use this + * constructor: use one of the goog.dom.Range.createFrom* methods instead. + * @constructor + * @extends {goog.dom.AbstractMultiRange} + * @final + */ +goog.dom.ControlRange = function() { + /** + * The IE control range obejct. + * @private {Object} + */ + this.range_ = null; + + /** + * Cached list of elements. + * @private {Array<Element>} + */ + this.elements_ = null; + + /** + * Cached sorted list of elements. + * @private {Array<Element>} + */ + this.sortedElements_ = null; +}; +goog.inherits(goog.dom.ControlRange, goog.dom.AbstractMultiRange); + + +/** + * Create a new range wrapper from the given browser range object. Do not use + * this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Object} controlRange The browser range object. + * @return {!goog.dom.ControlRange} A range wrapper object. + */ +goog.dom.ControlRange.createFromBrowserRange = function(controlRange) { + var range = new goog.dom.ControlRange(); + range.range_ = controlRange; + return range; +}; + + +/** + * Create a new range wrapper that selects the given element. Do not use + * this method directly - please use goog.dom.Range.createFrom* instead. + * @param {...Element} var_args The element(s) to select. + * @return {!goog.dom.ControlRange} A range wrapper object. + */ +goog.dom.ControlRange.createFromElements = function(var_args) { + var range = goog.dom.getOwnerDocument(arguments[0]).body.createControlRange(); + for (var i = 0, len = arguments.length; i < len; i++) { + range.addElement(arguments[i]); + } + return goog.dom.ControlRange.createFromBrowserRange(range); +}; + + +// Method implementations + + +/** + * Clear cached values. + * @private + */ +goog.dom.ControlRange.prototype.clearCachedValues_ = function() { + this.elements_ = null; + this.sortedElements_ = null; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.clone = function() { + return goog.dom.ControlRange.createFromElements.apply(this, + this.getElements()); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getType = function() { + return goog.dom.RangeType.CONTROL; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getBrowserRangeObject = function() { + return this.range_ || document.body.createControlRange(); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.setBrowserRangeObject = function(nativeRange) { + if (!goog.dom.AbstractRange.isNativeControlRange(nativeRange)) { + return false; + } + this.range_ = nativeRange; + return true; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getTextRangeCount = function() { + return this.range_ ? this.range_.length : 0; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getTextRange = function(i) { + return goog.dom.TextRange.createFromNodeContents(this.range_.item(i)); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getContainer = function() { + return goog.dom.findCommonAncestor.apply(null, this.getElements()); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getStartNode = function() { + return this.getSortedElements()[0]; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getStartOffset = function() { + return 0; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getEndNode = function() { + var sorted = this.getSortedElements(); + var startsLast = /** @type {Node} */ (goog.array.peek(sorted)); + return /** @type {Node} */ (goog.array.find(sorted, function(el) { + return goog.dom.contains(el, startsLast); + })); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getEndOffset = function() { + return this.getEndNode().childNodes.length; +}; + + +// TODO(robbyw): Figure out how to unify getElements with TextRange API. +/** + * @return {!Array<Element>} Array of elements in the control range. + */ +goog.dom.ControlRange.prototype.getElements = function() { + if (!this.elements_) { + this.elements_ = []; + if (this.range_) { + for (var i = 0; i < this.range_.length; i++) { + this.elements_.push(this.range_.item(i)); + } + } + } + + return this.elements_; +}; + + +/** + * @return {!Array<Element>} Array of elements comprising the control range, + * sorted by document order. + */ +goog.dom.ControlRange.prototype.getSortedElements = function() { + if (!this.sortedElements_) { + this.sortedElements_ = this.getElements().concat(); + this.sortedElements_.sort(function(a, b) { + return a.sourceIndex - b.sourceIndex; + }); + } + + return this.sortedElements_; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.isRangeInDocument = function() { + var returnValue = false; + + try { + returnValue = goog.array.every(this.getElements(), function(element) { + // On IE, this throws an exception when the range is detached. + return goog.userAgent.IE ? + !!element.parentNode : + goog.dom.contains(element.ownerDocument.body, element); + }); + } catch (e) { + // IE sometimes throws Invalid Argument errors for detached elements. + // Note: trying to return a value from the above try block can cause IE + // to crash. It is necessary to use the local returnValue. + } + + return returnValue; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.isCollapsed = function() { + return !this.range_ || !this.range_.length; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getText = function() { + // TODO(robbyw): What about for table selections? Should those have text? + return ''; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getHtmlFragment = function() { + return goog.array.map(this.getSortedElements(), goog.dom.getOuterHtml). + join(''); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getValidHtml = function() { + return this.getHtmlFragment(); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getPastableHtml = + goog.dom.ControlRange.prototype.getValidHtml; + + +/** @override */ +goog.dom.ControlRange.prototype.__iterator__ = function(opt_keys) { + return new goog.dom.ControlRangeIterator(this); +}; + + +// RANGE ACTIONS + + +/** @override */ +goog.dom.ControlRange.prototype.select = function() { + if (this.range_) { + this.range_.select(); + } +}; + + +/** @override */ +goog.dom.ControlRange.prototype.removeContents = function() { + // TODO(robbyw): Test implementing with execCommand('Delete') + if (this.range_) { + var nodes = []; + for (var i = 0, len = this.range_.length; i < len; i++) { + nodes.push(this.range_.item(i)); + } + goog.array.forEach(nodes, goog.dom.removeNode); + + this.collapse(false); + } +}; + + +/** @override */ +goog.dom.ControlRange.prototype.replaceContentsWithNode = function(node) { + // Control selections have to have the node inserted before removing the + // selection contents because a collapsed control range doesn't have start or + // end nodes. + var result = this.insertNode(node, true); + + if (!this.isCollapsed()) { + this.removeContents(); + } + + return result; +}; + + +// SAVE/RESTORE + + +/** @override */ +goog.dom.ControlRange.prototype.saveUsingDom = function() { + return new goog.dom.DomSavedControlRange_(this); +}; + + +// RANGE MODIFICATION + + +/** @override */ +goog.dom.ControlRange.prototype.collapse = function(toAnchor) { + // TODO(robbyw): Should this return a text range? If so, API needs to change. + this.range_ = null; + this.clearCachedValues_(); +}; + + +// SAVED RANGE OBJECTS + + + +/** + * A SavedRange implementation using DOM endpoints. + * @param {goog.dom.ControlRange} range The range to save. + * @constructor + * @extends {goog.dom.SavedRange} + * @private + */ +goog.dom.DomSavedControlRange_ = function(range) { + /** + * The element list. + * @type {Array<Element>} + * @private + */ + this.elements_ = range.getElements(); +}; +goog.inherits(goog.dom.DomSavedControlRange_, goog.dom.SavedRange); + + +/** @override */ +goog.dom.DomSavedControlRange_.prototype.restoreInternal = function() { + var doc = this.elements_.length ? + goog.dom.getOwnerDocument(this.elements_[0]) : document; + var controlRange = doc.body.createControlRange(); + for (var i = 0, len = this.elements_.length; i < len; i++) { + controlRange.addElement(this.elements_[i]); + } + return goog.dom.ControlRange.createFromBrowserRange(controlRange); +}; + + +/** @override */ +goog.dom.DomSavedControlRange_.prototype.disposeInternal = function() { + goog.dom.DomSavedControlRange_.superClass_.disposeInternal.call(this); + delete this.elements_; +}; + + +// RANGE ITERATION + + + +/** + * Subclass of goog.dom.TagIterator that iterates over a DOM range. It + * adds functions to determine the portion of each text node that is selected. + * + * @param {goog.dom.ControlRange?} range The range to traverse. + * @constructor + * @extends {goog.dom.RangeIterator} + * @final + */ +goog.dom.ControlRangeIterator = function(range) { + /** + * The first node in the selection. + * @private {Node} + */ + this.startNode_ = null; + + /** + * The last node in the selection. + * @private {Node} + */ + this.endNode_ = null; + + /** + * The list of elements left to traverse. + * @private {Array<Element>?} + */ + this.elements_ = null; + + if (range) { + this.elements_ = range.getSortedElements(); + this.startNode_ = this.elements_.shift(); + this.endNode_ = /** @type {Node} */ (goog.array.peek(this.elements_)) || + this.startNode_; + } + + goog.dom.ControlRangeIterator.base( + this, 'constructor', this.startNode_, false); +}; +goog.inherits(goog.dom.ControlRangeIterator, goog.dom.RangeIterator); + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getStartTextOffset = function() { + return 0; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getEndTextOffset = function() { + return 0; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getStartNode = function() { + return this.startNode_; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getEndNode = function() { + return this.endNode_; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.isLast = function() { + return !this.depth && !this.elements_.length; +}; + + +/** + * Move to the next position in the selection. + * Throws {@code goog.iter.StopIteration} when it passes the end of the range. + * @return {Node} The node at the next position. + * @override + */ +goog.dom.ControlRangeIterator.prototype.next = function() { + // Iterate over each element in the range, and all of its children. + if (this.isLast()) { + throw goog.iter.StopIteration; + } else if (!this.depth) { + var el = this.elements_.shift(); + this.setPosition(el, + goog.dom.TagWalkType.START_TAG, + goog.dom.TagWalkType.START_TAG); + return el; + } + + // Call the super function. + return goog.dom.ControlRangeIterator.superClass_.next.call(this); +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.copyFrom = function(other) { + this.elements_ = other.elements_; + this.startNode_ = other.startNode_; + this.endNode_ = other.endNode_; + + goog.dom.ControlRangeIterator.superClass_.copyFrom.call(this, other); +}; + + +/** + * @return {!goog.dom.ControlRangeIterator} An identical iterator. + * @override + */ +goog.dom.ControlRangeIterator.prototype.clone = function() { + var copy = new goog.dom.ControlRangeIterator(null); + copy.copyFrom(this); + return copy; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/dataset.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/dataset.js b/externs/GCL/externs/goog/dom/dataset.js new file mode 100644 index 0000000..6f24587 --- /dev/null +++ b/externs/GCL/externs/goog/dom/dataset.js @@ -0,0 +1,154 @@ +// Copyright 2009 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 Utilities for adding, removing and setting values in + * an Element's dataset. + * See {@link http://www.w3.org/TR/html5/Overview.html#dom-dataset}. + * + * @author [email protected] (Alex Nicksay) + */ + +goog.provide('goog.dom.dataset'); + +goog.require('goog.string'); +goog.require('goog.userAgent.product'); + + +/** + * Whether using the dataset property is allowed. In IE (up to and including + * IE 11), setting element.dataset in JS does not propagate values to CSS, + * breaking expressions such as `content: attr(data-content)` that would + * otherwise work. + * See {@link https://github.com/google/closure-library/issues/396}. + * @const + * @private + */ +goog.dom.dataset.ALLOWED_ = !goog.userAgent.product.IE; + + +/** + * The DOM attribute name prefix that must be present for it to be considered + * for a dataset. + * @type {string} + * @const + * @private + */ +goog.dom.dataset.PREFIX_ = 'data-'; + + +/** + * Sets a custom data attribute on an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * @param {Element} element DOM node to set the custom data attribute on. + * @param {string} key Key for the custom data attribute. + * @param {string} value Value for the custom data attribute. + */ +goog.dom.dataset.set = function(element, key, value) { + if (goog.dom.dataset.ALLOWED_ && element.dataset) { + element.dataset[key] = value; + } else { + element.setAttribute( + goog.dom.dataset.PREFIX_ + goog.string.toSelectorCase(key), + value); + } +}; + + +/** + * Gets a custom data attribute from an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * @param {Element} element DOM node to get the custom data attribute from. + * @param {string} key Key for the custom data attribute. + * @return {?string} The attribute value, if it exists. + */ +goog.dom.dataset.get = function(element, key) { + if (goog.dom.dataset.ALLOWED_ && element.dataset) { + // Android browser (non-chrome) returns the empty string for + // element.dataset['doesNotExist']. + if (!(key in element.dataset)) { + return null; + } + return element.dataset[key]; + } else { + return element.getAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key)); + } +}; + + +/** + * Removes a custom data attribute from an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * @param {Element} element DOM node to get the custom data attribute from. + * @param {string} key Key for the custom data attribute. + */ +goog.dom.dataset.remove = function(element, key) { + if (goog.dom.dataset.ALLOWED_ && element.dataset) { + delete element.dataset[key]; + } else { + element.removeAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key)); + } +}; + + +/** + * Checks whether custom data attribute exists on an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * + * @param {Element} element DOM node to get the custom data attribute from. + * @param {string} key Key for the custom data attribute. + * @return {boolean} Whether the attribute exists. + */ +goog.dom.dataset.has = function(element, key) { + if (goog.dom.dataset.ALLOWED_ && element.dataset) { + return key in element.dataset; + } else if (element.hasAttribute) { + return element.hasAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key)); + } else { + return !!(element.getAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key))); + } +}; + + +/** + * Gets all custom data attributes as a string map. The attribute names will be + * camel cased (e.g., data-foo-bar -> dataset['fooBar']). This operation is not + * safe for attributes having camel-cased names clashing with already existing + * properties (e.g., data-to-string -> dataset['toString']). + * @param {!Element} element DOM node to get the data attributes from. + * @return {!Object} The string map containing data attributes and their + * respective values. + */ +goog.dom.dataset.getAll = function(element) { + if (goog.dom.dataset.ALLOWED_ && element.dataset) { + return element.dataset; + } else { + var dataset = {}; + var attributes = element.attributes; + for (var i = 0; i < attributes.length; ++i) { + var attribute = attributes[i]; + if (goog.string.startsWith(attribute.name, + goog.dom.dataset.PREFIX_)) { + // We use substr(5), since it's faster than replacing 'data-' with ''. + var key = goog.string.toCamelCase(attribute.name.substr(5)); + dataset[key] = attribute.value; + } + } + return dataset; + } +};
