http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/browserrange/browserrange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/browserrange/browserrange.js b/externs/GCL/externs/goog/dom/browserrange/browserrange.js new file mode 100644 index 0000000..0cd70e7 --- /dev/null +++ b/externs/GCL/externs/goog/dom/browserrange/browserrange.js @@ -0,0 +1,149 @@ +// 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 browser range namespace and interface, as + * well as several useful utility functions. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author [email protected] (Robby Walker) + * + * @supported IE6, IE7, FF1.5+, Safari. + */ + + +goog.provide('goog.dom.browserrange'); +goog.provide('goog.dom.browserrange.Error'); + +goog.require('goog.dom'); +goog.require('goog.dom.BrowserFeature'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.browserrange.GeckoRange'); +goog.require('goog.dom.browserrange.IeRange'); +goog.require('goog.dom.browserrange.OperaRange'); +goog.require('goog.dom.browserrange.W3cRange'); +goog.require('goog.dom.browserrange.WebKitRange'); +goog.require('goog.userAgent'); + + +/** + * Common error constants. + * @enum {string} + */ +goog.dom.browserrange.Error = { + NOT_IMPLEMENTED: 'Not Implemented' +}; + + +// NOTE(robbyw): While it would be nice to eliminate the duplicate switches +// below, doing so uncovers bugs in the JsCompiler in which +// necessary code is stripped out. + + +/** + * Static method that returns the proper type of browser range. + * @param {Range|TextRange} range A browser range object. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.createRange = function(range) { + if (goog.dom.BrowserFeature.LEGACY_IE_RANGES) { + return new goog.dom.browserrange.IeRange( + /** @type {TextRange} */ (range), + goog.dom.getOwnerDocument(range.parentElement())); + } else if (goog.userAgent.WEBKIT) { + return new goog.dom.browserrange.WebKitRange( + /** @type {Range} */ (range)); + } else if (goog.userAgent.GECKO) { + return new goog.dom.browserrange.GeckoRange( + /** @type {Range} */ (range)); + } else if (goog.userAgent.OPERA) { + return new goog.dom.browserrange.OperaRange( + /** @type {Range} */ (range)); + } else { + // Default other browsers, including Opera, to W3c ranges. + return new goog.dom.browserrange.W3cRange( + /** @type {Range} */ (range)); + } +}; + + +/** + * Static method that returns the proper type of browser range. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.createRangeFromNodeContents = function(node) { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + return goog.dom.browserrange.IeRange.createFromNodeContents(node); + } else if (goog.userAgent.WEBKIT) { + return goog.dom.browserrange.WebKitRange.createFromNodeContents(node); + } else if (goog.userAgent.GECKO) { + return goog.dom.browserrange.GeckoRange.createFromNodeContents(node); + } else if (goog.userAgent.OPERA) { + return goog.dom.browserrange.OperaRange.createFromNodeContents(node); + } else { + // Default other browsers to W3c ranges. + return goog.dom.browserrange.W3cRange.createFromNodeContents(node); + } +}; + + +/** + * Static method that returns the proper type of browser range. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. This is + * either the index into the childNodes array for element startNodes or + * the index into the character array for text startNodes. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. This is + * either the index into the childNodes array for element endNodes or + * the index into the character array for text endNodes. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.createRangeFromNodes = function(startNode, startOffset, + endNode, endOffset) { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + return goog.dom.browserrange.IeRange.createFromNodes(startNode, startOffset, + endNode, endOffset); + } else if (goog.userAgent.WEBKIT) { + return goog.dom.browserrange.WebKitRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } else if (goog.userAgent.GECKO) { + return goog.dom.browserrange.GeckoRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } else if (goog.userAgent.OPERA) { + return goog.dom.browserrange.OperaRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } else { + // Default other browsers to W3c ranges. + return goog.dom.browserrange.W3cRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } +}; + + +/** + * Tests whether the given node can contain a range end point. + * @param {Node} node The node to check. + * @return {boolean} Whether the given node can contain a range end point. + */ +goog.dom.browserrange.canContainRangeEndpoint = function(node) { + // NOTE(user, bloom): This is not complete, as divs with style - + // 'display:inline-block' or 'position:absolute' can also not contain range + // endpoints. A more complete check is to see if that element can be partially + // selected (can be container) or not. + return goog.dom.canHaveChildren(node) || + node.nodeType == goog.dom.NodeType.TEXT; +};
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/browserrange/geckorange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/browserrange/geckorange.js b/externs/GCL/externs/goog/dom/browserrange/geckorange.js new file mode 100644 index 0000000..b01f2dd --- /dev/null +++ b/externs/GCL/externs/goog/dom/browserrange/geckorange.js @@ -0,0 +1,88 @@ +// 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 Gecko specific range wrapper. Inherits most + * functionality from W3CRange, but adds exceptions as necessary. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author [email protected] (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.GeckoRange'); + +goog.require('goog.dom.browserrange.W3cRange'); + + + +/** + * The constructor for Gecko specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.W3cRange} + * @final + */ +goog.dom.browserrange.GeckoRange = function(range) { + goog.dom.browserrange.W3cRange.call(this, range); +}; +goog.inherits(goog.dom.browserrange.GeckoRange, goog.dom.browserrange.W3cRange); + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.GeckoRange} A Gecko range wrapper object. + */ +goog.dom.browserrange.GeckoRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.GeckoRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. + * @return {!goog.dom.browserrange.GeckoRange} A wrapper object. + */ +goog.dom.browserrange.GeckoRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.GeckoRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** @override */ +goog.dom.browserrange.GeckoRange.prototype.selectInternal = function( + selection, reversed) { + if (!reversed || this.isCollapsed()) { + // The base implementation for select() is more robust, and works fine for + // collapsed and forward ranges. This works around + // https://bugzilla.mozilla.org/show_bug.cgi?id=773137, and is tested by + // range_test.html's testFocusedElementDisappears. + goog.dom.browserrange.GeckoRange.base( + this, 'selectInternal', selection, reversed); + } else { + // Reversed selection -- start with a caret on the end node, and extend it + // back to the start. Unfortunately, collapse() fails when focus is + // invalid. + selection.collapse(this.getEndNode(), this.getEndOffset()); + selection.extend(this.getStartNode(), this.getStartOffset()); + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/browserrange/ierange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/browserrange/ierange.js b/externs/GCL/externs/goog/dom/browserrange/ierange.js new file mode 100644 index 0000000..a2add21 --- /dev/null +++ b/externs/GCL/externs/goog/dom/browserrange/ierange.js @@ -0,0 +1,935 @@ +// 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 IE browser specific range wrapper. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author [email protected] (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.IeRange'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.browserrange.AbstractRange'); +goog.require('goog.log'); +goog.require('goog.string'); + + + +/** + * The constructor for IE specific browser ranges. + * @param {TextRange} range The range object. + * @param {Document} doc The document the range exists in. + * @constructor + * @extends {goog.dom.browserrange.AbstractRange} + * @final + */ +goog.dom.browserrange.IeRange = function(range, doc) { + /** + * Lazy cache of the node containing the entire selection. + * @private {Node} + */ + this.parentNode_ = null; + + /** + * Lazy cache of the node containing the start of the selection. + * @private {Node} + */ + this.startNode_ = null; + + /** + * Lazy cache of the node containing the end of the selection. + * @private {Node} + */ + this.endNode_ = null; + + /** + * Lazy cache of the offset in startNode_ where this range starts. + * @private {number} + */ + this.startOffset_ = -1; + + /** + * Lazy cache of the offset in endNode_ where this range ends. + * @private {number} + */ + this.endOffset_ = -1; + + /** + * The browser range object this class wraps. + * @private {TextRange} + */ + this.range_ = range; + + /** + * The document the range exists in. + * @private {Document} + */ + this.doc_ = doc; +}; +goog.inherits(goog.dom.browserrange.IeRange, + goog.dom.browserrange.AbstractRange); + + +/** + * Logging object. + * @type {goog.log.Logger} + * @private + */ +goog.dom.browserrange.IeRange.logger_ = + goog.log.getLogger('goog.dom.browserrange.IeRange'); + + +/** + * Returns a browser range spanning the given node's contents. + * @param {Node} node The node to select. + * @return {!TextRange} A browser range spanning the node's contents. + * @private + */ +goog.dom.browserrange.IeRange.getBrowserRangeForNode_ = function(node) { + var nodeRange = goog.dom.getOwnerDocument(node).body.createTextRange(); + if (node.nodeType == goog.dom.NodeType.ELEMENT) { + // Elements are easy. + nodeRange.moveToElementText(node); + // Note(user) : If there are no child nodes of the element, the + // range.htmlText includes the element's outerHTML. The range created above + // is not collapsed, and should be collapsed explicitly. + // Example : node = <div></div> + // But if the node is sth like <br>, it shouldnt be collapsed. + if (goog.dom.browserrange.canContainRangeEndpoint(node) && + !node.childNodes.length) { + nodeRange.collapse(false); + } + } else { + // Text nodes are hard. + // Compute the offset from the nearest element related position. + var offset = 0; + var sibling = node; + while (sibling = sibling.previousSibling) { + var nodeType = sibling.nodeType; + if (nodeType == goog.dom.NodeType.TEXT) { + offset += sibling.length; + } else if (nodeType == goog.dom.NodeType.ELEMENT) { + // Move to the space after this element. + nodeRange.moveToElementText(sibling); + break; + } + } + + if (!sibling) { + nodeRange.moveToElementText(node.parentNode); + } + + nodeRange.collapse(!sibling); + + if (offset) { + nodeRange.move('character', offset); + } + + nodeRange.moveEnd('character', node.length); + } + + return nodeRange; +}; + + +/** + * Returns a browser range spanning the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!TextRange} A browser range spanning the node's contents. + * @private + */ +goog.dom.browserrange.IeRange.getBrowserRangeForNodes_ = function(startNode, + startOffset, endNode, endOffset) { + // Create a range starting at the correct start position. + var child, collapse = false; + if (startNode.nodeType == goog.dom.NodeType.ELEMENT) { + if (startOffset > startNode.childNodes.length) { + goog.log.error(goog.dom.browserrange.IeRange.logger_, + 'Cannot have startOffset > startNode child count'); + } + child = startNode.childNodes[startOffset]; + collapse = !child; + startNode = child || startNode.lastChild || startNode; + startOffset = 0; + } + var leftRange = goog.dom.browserrange.IeRange. + getBrowserRangeForNode_(startNode); + + // This happens only when startNode is a text node. + if (startOffset) { + leftRange.move('character', startOffset); + } + + + // The range movements in IE are still an approximation to the standard W3C + // behavior, and IE has its trickery when it comes to htmlText and text + // properties of the range. So we short-circuit computation whenever we can. + if (startNode == endNode && startOffset == endOffset) { + leftRange.collapse(true); + return leftRange; + } + + // This can happen only when the startNode is an element, and there is no node + // at the given offset. We start at the last point inside the startNode in + // that case. + if (collapse) { + leftRange.collapse(false); + } + + // Create a range that ends at the right position. + collapse = false; + if (endNode.nodeType == goog.dom.NodeType.ELEMENT) { + if (endOffset > endNode.childNodes.length) { + goog.log.error(goog.dom.browserrange.IeRange.logger_, + 'Cannot have endOffset > endNode child count'); + } + child = endNode.childNodes[endOffset]; + endNode = child || endNode.lastChild || endNode; + endOffset = 0; + collapse = !child; + } + var rightRange = goog.dom.browserrange.IeRange. + getBrowserRangeForNode_(endNode); + rightRange.collapse(!collapse); + if (endOffset) { + rightRange.moveEnd('character', endOffset); + } + + // Merge and return. + leftRange.setEndPoint('EndToEnd', rightRange); + return leftRange; +}; + + +/** + * Create a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.IeRange} An IE range wrapper object. + */ +goog.dom.browserrange.IeRange.createFromNodeContents = function(node) { + var range = new goog.dom.browserrange.IeRange( + goog.dom.browserrange.IeRange.getBrowserRangeForNode_(node), + goog.dom.getOwnerDocument(node)); + + if (!goog.dom.browserrange.canContainRangeEndpoint(node)) { + range.startNode_ = range.endNode_ = range.parentNode_ = node.parentNode; + range.startOffset_ = goog.array.indexOf(range.parentNode_.childNodes, node); + range.endOffset_ = range.startOffset_ + 1; + } else { + // Note(user) : Emulate the behavior of W3CRange - Go to deepest possible + // range containers on both edges. It seems W3CRange did this to match the + // IE behavior, and now it is a circle. Changing W3CRange may break clients + // in all sorts of ways. + var tempNode, leaf = node; + while ((tempNode = leaf.firstChild) && + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + range.startNode_ = leaf; + range.startOffset_ = 0; + + leaf = node; + while ((tempNode = leaf.lastChild) && + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + range.endNode_ = leaf; + range.endOffset_ = leaf.nodeType == goog.dom.NodeType.ELEMENT ? + leaf.childNodes.length : leaf.length; + range.parentNode_ = node; + } + return range; +}; + + +/** + * Static method that returns the proper type of browser range. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.IeRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + var range = new goog.dom.browserrange.IeRange( + goog.dom.browserrange.IeRange.getBrowserRangeForNodes_(startNode, + startOffset, endNode, endOffset), + goog.dom.getOwnerDocument(startNode)); + range.startNode_ = startNode; + range.startOffset_ = startOffset; + range.endNode_ = endNode; + range.endOffset_ = endOffset; + return range; +}; + + +/** + * @return {!goog.dom.browserrange.IeRange} A clone of this range. + * @override + */ +goog.dom.browserrange.IeRange.prototype.clone = function() { + var range = new goog.dom.browserrange.IeRange( + this.range_.duplicate(), this.doc_); + range.parentNode_ = this.parentNode_; + range.startNode_ = this.startNode_; + range.endNode_ = this.endNode_; + return range; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getBrowserRange = function() { + return this.range_; +}; + + +/** + * Clears the cached values for containers. + * @private + */ +goog.dom.browserrange.IeRange.prototype.clearCachedValues_ = function() { + this.parentNode_ = this.startNode_ = this.endNode_ = null; + this.startOffset_ = this.endOffset_ = -1; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getContainer = function() { + if (!this.parentNode_) { + var selectText = this.range_.text; + + // If the selection ends with spaces, we need to remove these to get the + // parent container of only the real contents. This is to get around IE's + // inconsistency where it selects the spaces after a word when you double + // click, but leaves out the spaces during execCommands. + var range = this.range_.duplicate(); + // We can't use goog.string.trimRight, as that will remove other whitespace + // too. + var rightTrimmedSelectText = selectText.replace(/ +$/, ''); + var numSpacesAtEnd = selectText.length - rightTrimmedSelectText.length; + if (numSpacesAtEnd) { + range.moveEnd('character', -numSpacesAtEnd); + } + + // Get the parent node. This should be the end, but alas, it is not. + var parent = range.parentElement(); + + var htmlText = range.htmlText; + var htmlTextLen = goog.string.stripNewlines(htmlText).length; + if (this.isCollapsed() && htmlTextLen > 0) { + return (this.parentNode_ = parent); + } + + // Deal with selection bug where IE thinks one of the selection's children + // is actually the selection's parent. Relies on the assumption that the + // HTML text of the parent container is longer than the length of the + // selection's HTML text. + + // Also note IE will sometimes insert \r and \n whitespace, which should be + // disregarded. Otherwise the loop may run too long and return wrong parent + while (htmlTextLen > goog.string.stripNewlines(parent.outerHTML).length) { + parent = parent.parentNode; + } + + // Deal with IE's selecting the outer tags when you double click + // If the innerText is the same, then we just want the inner node + while (parent.childNodes.length == 1 && + parent.innerText == goog.dom.browserrange.IeRange.getNodeText_( + parent.firstChild)) { + // A container should be an element which can have children or a text + // node. Elements like IMG, BR, etc. can not be containers. + if (!goog.dom.browserrange.canContainRangeEndpoint(parent.firstChild)) { + break; + } + parent = parent.firstChild; + } + + // If the selection is empty, we may need to do extra work to position it + // properly. + if (selectText.length == 0) { + parent = this.findDeepestContainer_(parent); + } + + this.parentNode_ = parent; + } + + return this.parentNode_; +}; + + +/** + * Helper method to find the deepest parent for this range, starting + * the search from {@code node}, which must contain the range. + * @param {Node} node The node to start the search from. + * @return {Node} The deepest parent for this range. + * @private + */ +goog.dom.browserrange.IeRange.prototype.findDeepestContainer_ = function(node) { + var childNodes = node.childNodes; + for (var i = 0, len = childNodes.length; i < len; i++) { + var child = childNodes[i]; + + if (goog.dom.browserrange.canContainRangeEndpoint(child)) { + var childRange = + goog.dom.browserrange.IeRange.getBrowserRangeForNode_(child); + var start = goog.dom.RangeEndpoint.START; + var end = goog.dom.RangeEndpoint.END; + + // There are two types of erratic nodes where the range over node has + // different htmlText than the node's outerHTML. + // Case 1 - A node with magic child. In this case : + // nodeRange.htmlText shows ('<p> </p>), while + // node.outerHTML doesn't show the magic node (<p></p>). + // Case 2 - Empty span. In this case : + // node.outerHTML shows '<span></span>' + // node.htmlText is just empty string ''. + var isChildRangeErratic = (childRange.htmlText != child.outerHTML); + + // Moreover the inRange comparison fails only when the + var isNativeInRangeErratic = this.isCollapsed() && isChildRangeErratic; + + // In case 2 mentioned above, childRange is also collapsed. So we need to + // compare start of this range with both start and end of child range. + var inChildRange = isNativeInRangeErratic ? + (this.compareBrowserRangeEndpoints(childRange, start, start) >= 0 && + this.compareBrowserRangeEndpoints(childRange, start, end) <= 0) : + this.range_.inRange(childRange); + if (inChildRange) { + return this.findDeepestContainer_(child); + } + } + } + + return node; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getStartNode = function() { + if (!this.startNode_) { + this.startNode_ = this.getEndpointNode_(goog.dom.RangeEndpoint.START); + if (this.isCollapsed()) { + this.endNode_ = this.startNode_; + } + } + return this.startNode_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getStartOffset = function() { + if (this.startOffset_ < 0) { + this.startOffset_ = this.getOffset_(goog.dom.RangeEndpoint.START); + if (this.isCollapsed()) { + this.endOffset_ = this.startOffset_; + } + } + return this.startOffset_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getEndNode = function() { + if (this.isCollapsed()) { + return this.getStartNode(); + } + if (!this.endNode_) { + this.endNode_ = this.getEndpointNode_(goog.dom.RangeEndpoint.END); + } + return this.endNode_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getEndOffset = function() { + if (this.isCollapsed()) { + return this.getStartOffset(); + } + if (this.endOffset_ < 0) { + this.endOffset_ = this.getOffset_(goog.dom.RangeEndpoint.END); + if (this.isCollapsed()) { + this.startOffset_ = this.endOffset_; + } + } + return this.endOffset_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.compareBrowserRangeEndpoints = function( + range, thisEndpoint, otherEndpoint) { + return this.range_.compareEndPoints( + (thisEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End') + + 'To' + + (otherEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End'), + range); +}; + + +/** + * Recurses to find the correct node for the given endpoint. + * @param {goog.dom.RangeEndpoint} endpoint The endpoint to get the node for. + * @param {Node=} opt_node Optional node to start the search from. + * @return {Node} The deepest node containing the endpoint. + * @private + */ +goog.dom.browserrange.IeRange.prototype.getEndpointNode_ = function(endpoint, + opt_node) { + + /** @type {Node} */ + var node = opt_node || this.getContainer(); + + // If we're at a leaf in the DOM, we're done. + if (!node || !node.firstChild) { + return node; + } + + var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END; + var isStartEndpoint = endpoint == start; + + // Find the first/last child that overlaps the selection. + // NOTE(user) : One of the children can be the magic node. This + // node will have only nodeType property as valid and accessible. All other + // dom related properties like ownerDocument, parentNode, nextSibling etc + // cause error when accessed. Therefore use the for-loop on childNodes to + // iterate. + for (var j = 0, length = node.childNodes.length; j < length; j++) { + var i = isStartEndpoint ? j : length - j - 1; + var child = node.childNodes[i]; + var childRange; + try { + childRange = goog.dom.browserrange.createRangeFromNodeContents(child); + } catch (e) { + // If the child is the magic node, then the above will throw + // error. The magic node exists only when editing using keyboard, so can + // not add any unit test. + continue; + } + var ieRange = childRange.getBrowserRange(); + + // Case 1 : Finding end points when this range is collapsed. + // Note that in case of collapsed range, getEnd{Node,Offset} call + // getStart{Node,Offset}. + if (this.isCollapsed()) { + // Handle situations where caret is not in a text node. In such cases, + // the adjacent child won't be a valid range endpoint container. + if (!goog.dom.browserrange.canContainRangeEndpoint(child)) { + // The following handles a scenario like <div><BR>[caret]<BR></div>, + // where point should be (div, 1). + if (this.compareBrowserRangeEndpoints(ieRange, start, start) == 0) { + this.startOffset_ = this.endOffset_ = i; + return node; + } + } else if (childRange.containsRange(this)) { + // For collapsed range, we should invert the containsRange check with + // childRange. + return this.getEndpointNode_(endpoint, child); + } + + // Case 2 - The first child encountered to have overlap this range is + // contained entirely in this range. + } else if (this.containsRange(childRange)) { + // If it is an element which can not be a range endpoint container, the + // current child offset can be used to deduce the endpoint offset. + if (!goog.dom.browserrange.canContainRangeEndpoint(child)) { + + // Container can't be any deeper, so current node is the container. + if (isStartEndpoint) { + this.startOffset_ = i; + } else { + this.endOffset_ = i + 1; + } + return node; + } + + // If child can contain range endpoints, recurse inside this child. + return this.getEndpointNode_(endpoint, child); + + // Case 3 - Partial non-adjacency overlap. + } else if (this.compareBrowserRangeEndpoints(ieRange, start, end) < 0 && + this.compareBrowserRangeEndpoints(ieRange, end, start) > 0) { + // If this child overlaps the selection partially, recurse down to find + // the first/last child the next level down that overlaps the selection + // completely. We do not consider edge-adjacency (== 0) as overlap. + return this.getEndpointNode_(endpoint, child); + } + + } + + // None of the children of this node overlapped the selection, that means + // the selection starts/ends in this node directly. + return node; +}; + + +/** + * Compares one endpoint of this range with the endpoint of a node. + * For internal methods, we should prefer this method to containsNode. + * containsNode has a lot of false negatives when we're dealing with + * {@code <br>} tags. + * + * @param {Node} node The node to compare against. + * @param {goog.dom.RangeEndpoint} thisEndpoint The endpoint of this range + * to compare with. + * @param {goog.dom.RangeEndpoint} otherEndpoint The endpoint of the node + * to compare with. + * @return {number} 0 if the endpoints are equal, negative if this range + * endpoint comes before the other node endpoint, and positive otherwise. + * @private + */ +goog.dom.browserrange.IeRange.prototype.compareNodeEndpoints_ = + function(node, thisEndpoint, otherEndpoint) { + return this.range_.compareEndPoints( + (thisEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End') + + 'To' + + (otherEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End'), + goog.dom.browserrange.createRangeFromNodeContents(node). + getBrowserRange()); +}; + + +/** + * Returns the offset into the start/end container. + * @param {goog.dom.RangeEndpoint} endpoint The endpoint to get the offset for. + * @param {Node=} opt_container The container to get the offset relative to. + * Defaults to the value returned by getStartNode/getEndNode. + * @return {number} The offset. + * @private + */ +goog.dom.browserrange.IeRange.prototype.getOffset_ = function(endpoint, + opt_container) { + var isStartEndpoint = endpoint == goog.dom.RangeEndpoint.START; + var container = opt_container || + (isStartEndpoint ? this.getStartNode() : this.getEndNode()); + + if (container.nodeType == goog.dom.NodeType.ELEMENT) { + // Find the first/last child that overlaps the selection + var children = container.childNodes; + var len = children.length; + var edge = isStartEndpoint ? 0 : len - 1; + var sign = isStartEndpoint ? 1 : - 1; + + // We find the index in the child array of the endpoint of the selection. + for (var i = edge; i >= 0 && i < len; i += sign) { + var child = children[i]; + // Ignore the child nodes, which could be end point containers. + if (goog.dom.browserrange.canContainRangeEndpoint(child)) { + continue; + } + // Stop looping when we reach the edge of the selection. + var endPointCompare = + this.compareNodeEndpoints_(child, endpoint, endpoint); + if (endPointCompare == 0) { + return isStartEndpoint ? i : i + 1; + } + } + + // When starting from the end in an empty container, we erroneously return + // -1: fix this to return 0. + return i == -1 ? 0 : i; + } else { + // Get a temporary range object. + var range = this.range_.duplicate(); + + // Create a range that selects the entire container. + var nodeRange = goog.dom.browserrange.IeRange.getBrowserRangeForNode_( + container); + + // Now, intersect our range with the container range - this should give us + // the part of our selection that is in the container. + range.setEndPoint(isStartEndpoint ? 'EndToEnd' : 'StartToStart', nodeRange); + + var rangeLength = range.text.length; + return isStartEndpoint ? container.length - rangeLength : rangeLength; + } +}; + + +/** + * Returns the text of the given node. Uses IE specific properties. + * @param {Node} node The node to retrieve the text of. + * @return {string} The node's text. + * @private + */ +goog.dom.browserrange.IeRange.getNodeText_ = function(node) { + return node.nodeType == goog.dom.NodeType.TEXT ? + node.nodeValue : node.innerText; +}; + + +/** + * Tests whether this range is valid (i.e. whether its endpoints are still in + * the document). A range becomes invalid when, after this object was created, + * either one or both of its endpoints are removed from the document. Use of + * an invalid range can lead to runtime errors, particularly in IE. + * @return {boolean} Whether the range is valid. + */ +goog.dom.browserrange.IeRange.prototype.isRangeInDocument = function() { + var range = this.doc_.body.createTextRange(); + range.moveToElementText(this.doc_.body); + + return this.containsRange( + new goog.dom.browserrange.IeRange(range, this.doc_), true); +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.isCollapsed = function() { + // Note(user) : The earlier implementation used (range.text == ''), but this + // fails when (range.htmlText == '<br>') + // Alternative: this.range_.htmlText == ''; + return this.range_.compareEndPoints('StartToEnd', this.range_) == 0; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getText = function() { + return this.range_.text; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getValidHtml = function() { + return this.range_.htmlText; +}; + + +// SELECTION MODIFICATION + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.select = function(opt_reverse) { + // IE doesn't support programmatic reversed selections. + this.range_.select(); +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.removeContents = function() { + // NOTE: Sometimes htmlText is non-empty, but the range is actually empty. + // TODO(gboyer): The htmlText check is probably unnecessary, but I left it in + // for paranoia. + if (!this.isCollapsed() && this.range_.htmlText) { + // Store some before-removal state. + var startNode = this.getStartNode(); + var endNode = this.getEndNode(); + var oldText = this.range_.text; + + // IE sometimes deletes nodes unrelated to the selection. This trick fixes + // that problem most of the time. Even though it looks like a no-op, it is + // somehow changing IE's internal state such that empty unrelated nodes are + // no longer deleted. + var clone = this.range_.duplicate(); + clone.moveStart('character', 1); + clone.moveStart('character', -1); + + // However, sometimes moving the start back and forth ends up changing the + // range. + // TODO(gboyer): This condition used to happen for empty ranges, but (1) + // never worked, and (2) the isCollapsed call should protect against empty + // ranges better than before. However, this is left for paranoia. + if (clone.text == oldText) { + this.range_ = clone; + } + + // Use the browser's native deletion code. + this.range_.text = ''; + this.clearCachedValues_(); + + // Unfortunately, when deleting a portion of a single text node, IE creates + // an extra text node unlike other browsers which just change the text in + // the node. We normalize for that behavior here, making IE behave like all + // the other browsers. + var newStartNode = this.getStartNode(); + var newStartOffset = this.getStartOffset(); + /** @preserveTry */ + try { + var sibling = startNode.nextSibling; + if (startNode == endNode && startNode.parentNode && + startNode.nodeType == goog.dom.NodeType.TEXT && + sibling && sibling.nodeType == goog.dom.NodeType.TEXT) { + startNode.nodeValue += sibling.nodeValue; + goog.dom.removeNode(sibling); + + // Make sure to reselect the appropriate position. + this.range_ = goog.dom.browserrange.IeRange.getBrowserRangeForNode_( + newStartNode); + this.range_.move('character', newStartOffset); + this.clearCachedValues_(); + } + } catch (e) { + // IE throws errors on orphaned nodes. + } + } +}; + + +/** + * @param {TextRange} range The range to get a dom helper for. + * @return {!goog.dom.DomHelper} A dom helper for the document the range + * resides in. + * @private + */ +goog.dom.browserrange.IeRange.getDomHelper_ = function(range) { + return goog.dom.getDomHelper(range.parentElement()); +}; + + +/** + * Pastes the given element into the given range, returning the resulting + * element. + * @param {TextRange} range The range to paste into. + * @param {Element} element The node to insert a copy of. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper object for the document + * the range resides in. + * @return {Element} The resulting copy of element. + * @private + */ +goog.dom.browserrange.IeRange.pasteElement_ = function(range, element, + opt_domHelper) { + opt_domHelper = opt_domHelper || goog.dom.browserrange.IeRange.getDomHelper_( + range); + + // Make sure the node has a unique id. + var id; + var originalId = id = element.id; + if (!id) { + id = element.id = goog.string.createUniqueString(); + } + + // Insert (a clone of) the node. + range.pasteHTML(element.outerHTML); + + // Pasting the outerHTML of the modified element into the document creates + // a clone of the element argument. We want to return a reference to the + // clone, not the original. However we need to remove the temporary ID + // first. + element = opt_domHelper.getElement(id); + + // If element is null here, we failed. + if (element) { + if (!originalId) { + element.removeAttribute('id'); + } + } + + return element; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.surroundContents = function(element) { + // Make sure the element is detached from the document. + goog.dom.removeNode(element); + + // IE more or less guarantees that range.htmlText is well-formed & valid. + element.innerHTML = this.range_.htmlText; + element = goog.dom.browserrange.IeRange.pasteElement_(this.range_, element); + + // If element is null here, we failed. + if (element) { + this.range_.moveToElementText(element); + } + + this.clearCachedValues_(); + + return element; +}; + + +/** + * Internal handler for inserting a node. + * @param {TextRange} clone A clone of this range's browser range object. + * @param {Node} node The node to insert. + * @param {boolean} before Whether to insert the node before or after the range. + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use. + * @return {Node} The resulting copy of node. + * @private + */ +goog.dom.browserrange.IeRange.insertNode_ = function(clone, node, + before, opt_domHelper) { + // Get a DOM helper. + opt_domHelper = opt_domHelper || goog.dom.browserrange.IeRange.getDomHelper_( + clone); + + // If it's not an element, wrap it in one. + var isNonElement; + if (node.nodeType != goog.dom.NodeType.ELEMENT) { + isNonElement = true; + node = opt_domHelper.createDom(goog.dom.TagName.DIV, null, node); + } + + clone.collapse(before); + node = goog.dom.browserrange.IeRange.pasteElement_(clone, + /** @type {!Element} */ (node), opt_domHelper); + + // If we didn't want an element, unwrap the element and return the node. + if (isNonElement) { + // pasteElement_() may have returned a copy of the wrapper div, and the + // node it wraps could also be a new copy. So we must extract that new + // node from the new wrapper. + var newNonElement = node.firstChild; + opt_domHelper.flattenElement(node); + node = newNonElement; + } + + return node; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.insertNode = function(node, before) { + var output = goog.dom.browserrange.IeRange.insertNode_( + this.range_.duplicate(), node, before); + this.clearCachedValues_(); + return output; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.surroundWithNodes = function( + startNode, endNode) { + var clone1 = this.range_.duplicate(); + var clone2 = this.range_.duplicate(); + goog.dom.browserrange.IeRange.insertNode_(clone1, startNode, true); + goog.dom.browserrange.IeRange.insertNode_(clone2, endNode, false); + + this.clearCachedValues_(); +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.collapse = function(toStart) { + this.range_.collapse(toStart); + + if (toStart) { + this.endNode_ = this.startNode_; + this.endOffset_ = this.startOffset_; + } else { + this.startNode_ = this.endNode_; + this.startOffset_ = this.endOffset_; + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/browserrange/operarange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/browserrange/operarange.js b/externs/GCL/externs/goog/dom/browserrange/operarange.js new file mode 100644 index 0000000..f277dc1 --- /dev/null +++ b/externs/GCL/externs/goog/dom/browserrange/operarange.js @@ -0,0 +1,84 @@ +// 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 Definition of the Opera specific range wrapper. Inherits most + * functionality from W3CRange, but adds exceptions as necessary. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + */ + + +goog.provide('goog.dom.browserrange.OperaRange'); + +goog.require('goog.dom.browserrange.W3cRange'); + + + +/** + * The constructor for Opera specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.W3cRange} + * @final + */ +goog.dom.browserrange.OperaRange = function(range) { + goog.dom.browserrange.W3cRange.call(this, range); +}; +goog.inherits(goog.dom.browserrange.OperaRange, goog.dom.browserrange.W3cRange); + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.OperaRange} A Opera range wrapper object. + */ +goog.dom.browserrange.OperaRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.OperaRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. + * @return {!goog.dom.browserrange.OperaRange} A wrapper object. + */ +goog.dom.browserrange.OperaRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.OperaRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** @override */ +goog.dom.browserrange.OperaRange.prototype.selectInternal = function( + selection, reversed) { + // Avoid using addRange as we have to removeAllRanges first, which + // blurs editable fields in Opera. + selection.collapse(this.getStartNode(), this.getStartOffset()); + if (this.getEndNode() != this.getStartNode() || + this.getEndOffset() != this.getStartOffset()) { + selection.extend(this.getEndNode(), this.getEndOffset()); + } + // This can happen if the range isn't in an editable field. + if (selection.rangeCount == 0) { + selection.addRange(this.range_); + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/browserrange/w3crange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/browserrange/w3crange.js b/externs/GCL/externs/goog/dom/browserrange/w3crange.js new file mode 100644 index 0000000..994f19f --- /dev/null +++ b/externs/GCL/externs/goog/dom/browserrange/w3crange.js @@ -0,0 +1,396 @@ +// 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 W3C spec following range wrapper. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author [email protected] (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.W3cRange'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.browserrange.AbstractRange'); +goog.require('goog.string'); +goog.require('goog.userAgent'); + + + +/** + * The constructor for W3C specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.AbstractRange} + */ +goog.dom.browserrange.W3cRange = function(range) { + this.range_ = range; +}; +goog.inherits(goog.dom.browserrange.W3cRange, + goog.dom.browserrange.AbstractRange); + + +/** + * Returns a browser range spanning the given node's contents. + * @param {Node} node The node to select. + * @return {!Range} A browser range spanning the node's contents. + * @protected + */ +goog.dom.browserrange.W3cRange.getBrowserRangeForNode = function(node) { + var nodeRange = goog.dom.getOwnerDocument(node).createRange(); + + if (node.nodeType == goog.dom.NodeType.TEXT) { + nodeRange.setStart(node, 0); + nodeRange.setEnd(node, node.length); + } else { + /** @suppress {missingRequire} */ + if (!goog.dom.browserrange.canContainRangeEndpoint(node)) { + var rangeParent = node.parentNode; + var rangeStartOffset = goog.array.indexOf(rangeParent.childNodes, node); + nodeRange.setStart(rangeParent, rangeStartOffset); + nodeRange.setEnd(rangeParent, rangeStartOffset + 1); + } else { + var tempNode, leaf = node; + while ((tempNode = leaf.firstChild) && + /** @suppress {missingRequire} */ + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + nodeRange.setStart(leaf, 0); + + leaf = node; + while ((tempNode = leaf.lastChild) && + /** @suppress {missingRequire} */ + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + nodeRange.setEnd(leaf, leaf.nodeType == goog.dom.NodeType.ELEMENT ? + leaf.childNodes.length : leaf.length); + } + } + + return nodeRange; +}; + + +/** + * Returns a browser range spanning the given nodes. + * @param {Node} startNode The node to start with - should not be a BR. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with - should not be a BR. + * @param {number} endOffset The offset within the end node. + * @return {!Range} A browser range spanning the node's contents. + * @protected + */ +goog.dom.browserrange.W3cRange.getBrowserRangeForNodes = function(startNode, + startOffset, endNode, endOffset) { + // Create and return the range. + var nodeRange = goog.dom.getOwnerDocument(startNode).createRange(); + nodeRange.setStart(startNode, startOffset); + nodeRange.setEnd(endNode, endOffset); + return nodeRange; +}; + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.W3cRange} A Gecko range wrapper object. + */ +goog.dom.browserrange.W3cRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.W3cRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!goog.dom.browserrange.W3cRange} A wrapper object. + */ +goog.dom.browserrange.W3cRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.W3cRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** + * @return {!goog.dom.browserrange.W3cRange} A clone of this range. + * @override + */ +goog.dom.browserrange.W3cRange.prototype.clone = function() { + return new this.constructor(this.range_.cloneRange()); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getBrowserRange = function() { + return this.range_; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getContainer = function() { + return this.range_.commonAncestorContainer; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getStartNode = function() { + return this.range_.startContainer; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getStartOffset = function() { + return this.range_.startOffset; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getEndNode = function() { + return this.range_.endContainer; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getEndOffset = function() { + return this.range_.endOffset; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.compareBrowserRangeEndpoints = + function(range, thisEndpoint, otherEndpoint) { + return this.range_.compareBoundaryPoints( + otherEndpoint == goog.dom.RangeEndpoint.START ? + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].START_TO_START : + goog.global['Range'].START_TO_END) : + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].END_TO_START : + goog.global['Range'].END_TO_END), + /** @type {Range} */ (range)); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.isCollapsed = function() { + return this.range_.collapsed; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getText = function() { + return this.range_.toString(); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getValidHtml = function() { + var div = goog.dom.getDomHelper(this.range_.startContainer).createDom( + goog.dom.TagName.DIV); + div.appendChild(this.range_.cloneContents()); + var result = div.innerHTML; + + if (goog.string.startsWith(result, '<') || + !this.isCollapsed() && !goog.string.contains(result, '<')) { + // We attempt to mimic IE, which returns no containing element when a + // only text nodes are selected, does return the containing element when + // the selection is empty, and does return the element when multiple nodes + // are selected. + return result; + } + + var container = this.getContainer(); + container = container.nodeType == goog.dom.NodeType.ELEMENT ? container : + container.parentNode; + + var html = goog.dom.getOuterHtml( + /** @type {!Element} */ (container.cloneNode(false))); + return html.replace('>', '>' + result); +}; + + +// SELECTION MODIFICATION + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.select = function(reverse) { + var win = goog.dom.getWindow(goog.dom.getOwnerDocument(this.getStartNode())); + this.selectInternal(win.getSelection(), reverse); +}; + + +/** + * Select this range. + * @param {Selection} selection Browser selection object. + * @param {*} reverse Whether to select this range in reverse. + * @protected + */ +goog.dom.browserrange.W3cRange.prototype.selectInternal = function(selection, + reverse) { + // Browser-specific tricks are needed to create reversed selections + // programatically. For this generic W3C codepath, ignore the reverse + // parameter. + selection.removeAllRanges(); + selection.addRange(this.range_); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.removeContents = function() { + var range = this.range_; + range.extractContents(); + + if (range.startContainer.hasChildNodes()) { + // Remove any now empty nodes surrounding the extracted contents. + var rangeStartContainer = + range.startContainer.childNodes[range.startOffset]; + if (rangeStartContainer) { + var rangePrevious = rangeStartContainer.previousSibling; + + if (goog.dom.getRawTextContent(rangeStartContainer) == '') { + goog.dom.removeNode(rangeStartContainer); + } + + if (rangePrevious && goog.dom.getRawTextContent(rangePrevious) == '') { + goog.dom.removeNode(rangePrevious); + } + } + } + + if (goog.userAgent.IE) { + // Unfortunately, when deleting a portion of a single text node, IE creates + // an extra text node instead of modifying the nodeValue of the start node. + // We normalize for that behavior here, similar to code in + // goog.dom.browserrange.IeRange#removeContents + // See https://connect.microsoft.com/IE/feedback/details/746591 + var startNode = this.getStartNode(); + var startOffset = this.getStartOffset(); + var endNode = this.getEndNode(); + var endOffset = this.getEndOffset(); + var sibling = startNode.nextSibling; + if (startNode == endNode && startNode.parentNode && + startNode.nodeType == goog.dom.NodeType.TEXT && + sibling && sibling.nodeType == goog.dom.NodeType.TEXT) { + startNode.nodeValue += sibling.nodeValue; + goog.dom.removeNode(sibling); + + // Modifying the node value clears the range offsets. Reselect the + // position in the modified start node. + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + } + } +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.surroundContents = function(element) { + this.range_.surroundContents(element); + return element; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.insertNode = function(node, before) { + var range = this.range_.cloneRange(); + range.collapse(before); + range.insertNode(node); + range.detach(); + + return node; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.surroundWithNodes = function( + startNode, endNode) { + var win = goog.dom.getWindow( + goog.dom.getOwnerDocument(this.getStartNode())); + /** @suppress {missingRequire} */ + var selectionRange = goog.dom.Range.createFromWindow(win); + if (selectionRange) { + var sNode = selectionRange.getStartNode(); + var eNode = selectionRange.getEndNode(); + var sOffset = selectionRange.getStartOffset(); + var eOffset = selectionRange.getEndOffset(); + } + + var clone1 = this.range_.cloneRange(); + var clone2 = this.range_.cloneRange(); + + clone1.collapse(false); + clone2.collapse(true); + + clone1.insertNode(endNode); + clone2.insertNode(startNode); + + clone1.detach(); + clone2.detach(); + + if (selectionRange) { + // There are 4 ways that surroundWithNodes can wreck the saved + // selection object. All of them happen when an inserted node splits + // a text node, and one of the end points of the selection was in the + // latter half of that text node. + // + // Clients of this library should use saveUsingCarets to avoid this + // problem. Unfortunately, saveUsingCarets uses this method, so that's + // not really an option for us. :( We just recompute the offsets. + var isInsertedNode = function(n) { + return n == startNode || n == endNode; + }; + if (sNode.nodeType == goog.dom.NodeType.TEXT) { + while (sOffset > sNode.length) { + sOffset -= sNode.length; + do { + sNode = sNode.nextSibling; + } while (isInsertedNode(sNode)); + } + } + + if (eNode.nodeType == goog.dom.NodeType.TEXT) { + while (eOffset > eNode.length) { + eOffset -= eNode.length; + do { + eNode = eNode.nextSibling; + } while (isInsertedNode(eNode)); + } + } + + /** @suppress {missingRequire} */ + goog.dom.Range.createFromNodes( + sNode, /** @type {number} */ (sOffset), + eNode, /** @type {number} */ (eOffset)).select(); + } +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.collapse = function(toStart) { + this.range_.collapse(toStart); +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/browserrange/webkitrange.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/browserrange/webkitrange.js b/externs/GCL/externs/goog/dom/browserrange/webkitrange.js new file mode 100644 index 0000000..dd428bd --- /dev/null +++ b/externs/GCL/externs/goog/dom/browserrange/webkitrange.js @@ -0,0 +1,108 @@ +// 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 WebKit specific range wrapper. Inherits most + * functionality from W3CRange, but adds exceptions as necessary. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author [email protected] (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.WebKitRange'); + +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.browserrange.W3cRange'); +goog.require('goog.userAgent'); + + + +/** + * The constructor for WebKit specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.W3cRange} + * @final + */ +goog.dom.browserrange.WebKitRange = function(range) { + goog.dom.browserrange.W3cRange.call(this, range); +}; +goog.inherits(goog.dom.browserrange.WebKitRange, + goog.dom.browserrange.W3cRange); + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.WebKitRange} A WebKit range wrapper object. + */ +goog.dom.browserrange.WebKitRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.WebKitRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!goog.dom.browserrange.WebKitRange} A wrapper object. + */ +goog.dom.browserrange.WebKitRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.WebKitRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** @override */ +goog.dom.browserrange.WebKitRange.prototype.compareBrowserRangeEndpoints = + function(range, thisEndpoint, otherEndpoint) { + // Webkit pre-528 has some bugs where compareBoundaryPoints() doesn't work the + // way it is supposed to, but if we reverse the sense of two comparisons, + // it works fine. + // https://bugs.webkit.org/show_bug.cgi?id=20738 + if (goog.userAgent.isVersionOrHigher('528')) { + return (goog.dom.browserrange.WebKitRange.superClass_. + compareBrowserRangeEndpoints.call( + this, range, thisEndpoint, otherEndpoint)); + } + return this.range_.compareBoundaryPoints( + otherEndpoint == goog.dom.RangeEndpoint.START ? + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].START_TO_START : + goog.global['Range'].END_TO_START) : // Sense reversed + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].START_TO_END : // Sense reversed + goog.global['Range'].END_TO_END), + /** @type {Range} */ (range)); +}; + + +/** @override */ +goog.dom.browserrange.WebKitRange.prototype.selectInternal = function( + selection, reversed) { + if (reversed) { + selection.setBaseAndExtent(this.getEndNode(), this.getEndOffset(), + this.getStartNode(), this.getStartOffset()); + } else { + selection.setBaseAndExtent(this.getStartNode(), this.getStartOffset(), + this.getEndNode(), this.getEndOffset()); + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/bufferedviewportsizemonitor.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/bufferedviewportsizemonitor.js b/externs/GCL/externs/goog/dom/bufferedviewportsizemonitor.js new file mode 100644 index 0000000..2909d26 --- /dev/null +++ b/externs/GCL/externs/goog/dom/bufferedviewportsizemonitor.js @@ -0,0 +1,201 @@ +// 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 A viewport size monitor that buffers RESIZE events until the + * window size has stopped changing, within a specified period of time. For + * every RESIZE event dispatched, this will dispatch up to two *additional* + * events: + * - {@link #EventType.RESIZE_WIDTH} if the viewport's width has changed since + * the last buffered dispatch. + * - {@link #EventType.RESIZE_HEIGHT} if the viewport's height has changed since + * the last buffered dispatch. + * You likely only need to listen to one of the three events. But if you need + * more, just be cautious of duplicating effort. + * + */ + +goog.provide('goog.dom.BufferedViewportSizeMonitor'); + +goog.require('goog.asserts'); +goog.require('goog.async.Delay'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); + + + +/** + * Creates a new BufferedViewportSizeMonitor. + * @param {!goog.dom.ViewportSizeMonitor} viewportSizeMonitor The + * underlying viewport size monitor. + * @param {number=} opt_bufferMs The buffer time, in ms. If not specified, this + * value defaults to {@link #RESIZE_EVENT_DELAY_MS_}. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.dom.BufferedViewportSizeMonitor = function( + viewportSizeMonitor, opt_bufferMs) { + goog.dom.BufferedViewportSizeMonitor.base(this, 'constructor'); + + /** + * Delay for the resize event. + * @private {goog.async.Delay} + */ + this.resizeDelay_; + + /** + * The underlying viewport size monitor. + * @type {goog.dom.ViewportSizeMonitor} + * @private + */ + this.viewportSizeMonitor_ = viewportSizeMonitor; + + /** + * The current size of the viewport. + * @type {goog.math.Size} + * @private + */ + this.currentSize_ = this.viewportSizeMonitor_.getSize(); + + /** + * The resize buffer time in ms. + * @type {number} + * @private + */ + this.resizeBufferMs_ = opt_bufferMs || + goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_; + + /** + * Listener key for the viewport size monitor. + * @type {goog.events.Key} + * @private + */ + this.listenerKey_ = goog.events.listen( + viewportSizeMonitor, + goog.events.EventType.RESIZE, + this.handleResize_, + false, + this); +}; +goog.inherits(goog.dom.BufferedViewportSizeMonitor, goog.events.EventTarget); + + +/** + * Additional events to dispatch. + * @enum {string} + */ +goog.dom.BufferedViewportSizeMonitor.EventType = { + RESIZE_HEIGHT: goog.events.getUniqueId('resizeheight'), + RESIZE_WIDTH: goog.events.getUniqueId('resizewidth') +}; + + +/** + * Default number of milliseconds to wait after a resize event to relayout the + * page. + * @type {number} + * @const + * @private + */ +goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_ = 100; + + +/** @override */ +goog.dom.BufferedViewportSizeMonitor.prototype.disposeInternal = + function() { + goog.events.unlistenByKey(this.listenerKey_); + goog.dom.BufferedViewportSizeMonitor.base(this, 'disposeInternal'); +}; + + +/** + * Handles resize events on the underlying ViewportMonitor. + * @private + */ +goog.dom.BufferedViewportSizeMonitor.prototype.handleResize_ = + function() { + // Lazily create when needed. + if (!this.resizeDelay_) { + this.resizeDelay_ = new goog.async.Delay( + this.onWindowResize_, + this.resizeBufferMs_, + this); + this.registerDisposable(this.resizeDelay_); + } + this.resizeDelay_.start(); +}; + + +/** + * Window resize callback that determines whether to reflow the view contents. + * @private + */ +goog.dom.BufferedViewportSizeMonitor.prototype.onWindowResize_ = + function() { + if (this.viewportSizeMonitor_.isDisposed()) { + return; + } + + var previousSize = this.currentSize_; + var currentSize = this.viewportSizeMonitor_.getSize(); + + goog.asserts.assert(currentSize, + 'Viewport size should be set at this point'); + + this.currentSize_ = currentSize; + + if (previousSize) { + + var resized = false; + + // Width has changed + if (previousSize.width != currentSize.width) { + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_WIDTH); + resized = true; + } + + // Height has changed + if (previousSize.height != currentSize.height) { + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_HEIGHT); + resized = true; + } + + // If either has changed, this is a resize event. + if (resized) { + this.dispatchEvent(goog.events.EventType.RESIZE); + } + + } else { + // If we didn't have a previous size, we consider all events to have + // changed. + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_HEIGHT); + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_WIDTH); + this.dispatchEvent(goog.events.EventType.RESIZE); + } +}; + + +/** + * Returns the current size of the viewport. + * @return {goog.math.Size?} The current viewport size. + */ +goog.dom.BufferedViewportSizeMonitor.prototype.getSize = function() { + return this.currentSize_ ? this.currentSize_.clone() : null; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/dom/classes.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/dom/classes.js b/externs/GCL/externs/goog/dom/classes.js new file mode 100644 index 0000000..0f1db74 --- /dev/null +++ b/externs/GCL/externs/goog/dom/classes.js @@ -0,0 +1,239 @@ +// Copyright 2006 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 classes. Prefer + * {@link goog.dom.classlist} over these utilities since goog.dom.classlist + * conforms closer to the semantics of Element.classList, is faster (uses + * native methods rather than parsing strings on every call) and compiles + * to smaller code as a result. + * + * Note: these utilities are meant to operate on HTMLElements and + * will not work on elements with differing interfaces (such as SVGElements). + * + * @author [email protected] (Erik Arvidsson) + */ + + +goog.provide('goog.dom.classes'); + +goog.require('goog.array'); + + +/** + * Sets the entire class name of an element. + * @param {Node} element DOM node to set class of. + * @param {string} className Class name(s) to apply to element. + * @deprecated Use goog.dom.classlist.set instead. + */ +goog.dom.classes.set = function(element, className) { + element.className = className; +}; + + +/** + * Gets an array of class names on an element + * @param {Node} element DOM node to get class of. + * @return {!Array<?>} Class names on {@code element}. Some browsers add extra + * properties to the array. Do not depend on any of these! + * @deprecated Use goog.dom.classlist.get instead. + */ +goog.dom.classes.get = function(element) { + 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) || []; +}; + + +/** + * Adds a class or classes to an element. Does not add multiples of class names. + * @param {Node} element DOM node to add class to. + * @param {...string} var_args Class names to add. + * @return {boolean} Whether class was added (or all classes were added). + * @deprecated Use goog.dom.classlist.add or goog.dom.classlist.addAll instead. + */ +goog.dom.classes.add = function(element, var_args) { + var classes = goog.dom.classes.get(element); + var args = goog.array.slice(arguments, 1); + var expectedCount = classes.length + args.length; + goog.dom.classes.add_(classes, args); + goog.dom.classes.set(element, classes.join(' ')); + return classes.length == expectedCount; +}; + + +/** + * Removes a class or classes from an element. + * @param {Node} element DOM node to remove class from. + * @param {...string} var_args Class name(s) to remove. + * @return {boolean} Whether all classes in {@code var_args} were found and + * removed. + * @deprecated Use goog.dom.classlist.remove or goog.dom.classlist.removeAll + * instead. + */ +goog.dom.classes.remove = function(element, var_args) { + var classes = goog.dom.classes.get(element); + var args = goog.array.slice(arguments, 1); + var newClasses = goog.dom.classes.getDifference_(classes, args); + goog.dom.classes.set(element, newClasses.join(' ')); + return newClasses.length == classes.length - args.length; +}; + + +/** + * Helper method for {@link goog.dom.classes.add} and + * {@link goog.dom.classes.addRemove}. Adds one or more classes to the supplied + * classes array. + * @param {Array<string>} classes All class names for the element, will be + * updated to have the classes supplied in {@code args} added. + * @param {Array<string>} args Class names to add. + * @private + */ +goog.dom.classes.add_ = function(classes, args) { + for (var i = 0; i < args.length; i++) { + if (!goog.array.contains(classes, args[i])) { + classes.push(args[i]); + } + } +}; + + +/** + * Helper method for {@link goog.dom.classes.remove} and + * {@link goog.dom.classes.addRemove}. Calculates the difference of two arrays. + * @param {!Array<string>} arr1 First array. + * @param {!Array<string>} arr2 Second array. + * @return {!Array<string>} The first array without the elements of the second + * array. + * @private + */ +goog.dom.classes.getDifference_ = function(arr1, arr2) { + return goog.array.filter(arr1, function(item) { + return !goog.array.contains(arr2, item); + }); +}; + + +/** + * 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. + * @param {Node} 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. + * @deprecated Use goog.dom.classlist.swap instead. + */ +goog.dom.classes.swap = function(element, fromClass, toClass) { + var classes = goog.dom.classes.get(element); + + var removed = false; + for (var i = 0; i < classes.length; i++) { + if (classes[i] == fromClass) { + goog.array.splice(classes, i--, 1); + removed = true; + } + } + + if (removed) { + classes.push(toClass); + goog.dom.classes.set(element, classes.join(' ')); + } + + return removed; +}; + + +/** + * Adds zero or more classes to an element and removes zero or more as a single + * operation. Unlike calling {@link goog.dom.classes.add} and + * {@link goog.dom.classes.remove} separately, this is more efficient as it only + * parses the class property once. + * + * If a class is in both the remove and add lists, it will be added. Thus, + * you can use this instead of {@link goog.dom.classes.swap} when you have + * more than two class names that you want to swap. + * + * @param {Node} element DOM node to swap classes on. + * @param {?(string|Array<string>)} classesToRemove Class or classes to + * remove, if null no classes are removed. + * @param {?(string|Array<string>)} classesToAdd Class or classes to add, if + * null no classes are added. + * @deprecated Use goog.dom.classlist.addRemove instead. + */ +goog.dom.classes.addRemove = function(element, classesToRemove, classesToAdd) { + var classes = goog.dom.classes.get(element); + if (goog.isString(classesToRemove)) { + goog.array.remove(classes, classesToRemove); + } else if (goog.isArray(classesToRemove)) { + classes = goog.dom.classes.getDifference_(classes, classesToRemove); + } + + if (goog.isString(classesToAdd) && + !goog.array.contains(classes, classesToAdd)) { + classes.push(classesToAdd); + } else if (goog.isArray(classesToAdd)) { + goog.dom.classes.add_(classes, classesToAdd); + } + + goog.dom.classes.set(element, classes.join(' ')); +}; + + +/** + * Returns true if an element has a class. + * @param {Node} element DOM node to test. + * @param {string} className Class name to test for. + * @return {boolean} Whether element has the class. + * @deprecated Use goog.dom.classlist.contains instead. + */ +goog.dom.classes.has = function(element, className) { + return goog.array.contains(goog.dom.classes.get(element), className); +}; + + +/** + * Adds or removes a class depending on the enabled argument. + * @param {Node} 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). + * @deprecated Use goog.dom.classlist.enable or goog.dom.classlist.enableAll + * instead. + */ +goog.dom.classes.enable = function(element, className, enabled) { + if (enabled) { + goog.dom.classes.add(element, className); + } else { + goog.dom.classes.remove(element, className); + } +}; + + +/** + * 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. + * @param {Node} 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). + * @deprecated Use goog.dom.classlist.toggle instead. + */ +goog.dom.classes.toggle = function(element, className) { + var add = !goog.dom.classes.has(element, className); + goog.dom.classes.enable(element, className, add); + return add; +};
