| // 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 interface. |
| * |
| * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. |
| * |
| * @author robbyw@google.com (Robby Walker) |
| */ |
| |
| |
| goog.provide('goog.dom.browserrange.AbstractRange'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.asserts'); |
| goog.require('goog.dom'); |
| goog.require('goog.dom.NodeType'); |
| goog.require('goog.dom.RangeEndpoint'); |
| goog.require('goog.dom.TagName'); |
| goog.require('goog.dom.TextRangeIterator'); |
| goog.require('goog.iter'); |
| goog.require('goog.math.Coordinate'); |
| goog.require('goog.string'); |
| goog.require('goog.string.StringBuffer'); |
| goog.require('goog.userAgent'); |
| |
| |
| |
| /** |
| * The constructor for abstract ranges. Don't call this from subclasses. |
| * @constructor |
| */ |
| goog.dom.browserrange.AbstractRange = function() { |
| }; |
| |
| |
| /** |
| * @return {goog.dom.browserrange.AbstractRange} A clone of this range. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.clone = goog.abstractMethod; |
| |
| |
| /** |
| * Returns the browser native implementation of the range. Please refrain from |
| * using this function - if you find you need the range please add wrappers for |
| * the functionality you need rather than just using the native range. |
| * @return {Range|TextRange} The browser native range object. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getBrowserRange = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Returns the deepest node in the tree that contains the entire range. |
| * @return {Node} The deepest node that contains the entire range. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getContainer = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Returns the node the range starts in. |
| * @return {Node} The element or text node the range starts in. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getStartNode = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Returns the offset into the node the range starts in. |
| * @return {number} The offset into the node the range starts in. For text |
| * nodes, this is an offset into the node value. For elements, this is |
| * an offset into the childNodes array. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getStartOffset = |
| goog.abstractMethod; |
| |
| |
| /** |
| * @return {goog.math.Coordinate} The coordinate of the selection start node |
| * and offset. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getStartPosition = function() { |
| goog.asserts.assert(this.range_.getClientRects, |
| 'Getting selection coordinates is not supported.'); |
| |
| var rects = this.range_.getClientRects(); |
| if (rects.length) { |
| return new goog.math.Coordinate(rects[0]['left'], rects[0]['top']); |
| } |
| return null; |
| }; |
| |
| |
| /** |
| * Returns the node the range ends in. |
| * @return {Node} The element or text node the range ends in. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getEndNode = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Returns the offset into the node the range ends in. |
| * @return {number} The offset into the node the range ends in. For text |
| * nodes, this is an offset into the node value. For elements, this is |
| * an offset into the childNodes array. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getEndOffset = |
| goog.abstractMethod; |
| |
| |
| /** |
| * @return {goog.math.Coordinate} The coordinate of the selection end node |
| * and offset. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getEndPosition = function() { |
| goog.asserts.assert(this.range_.getClientRects, |
| 'Getting selection coordinates is not supported.'); |
| |
| var rects = this.range_.getClientRects(); |
| if (rects.length) { |
| var lastRect = goog.array.peek(rects); |
| return new goog.math.Coordinate(lastRect['right'], lastRect['bottom']); |
| } |
| return null; |
| }; |
| |
| |
| /** |
| * Compares one endpoint of this range with the endpoint of another browser |
| * native range object. |
| * @param {Range|TextRange} range The browser native range 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 other |
| * range to compare with. |
| * @return {number} 0 if the endpoints are equal, negative if this range |
| * endpoint comes before the other range endpoint, and positive otherwise. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.compareBrowserRangeEndpoints = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Tests if this range contains the given range. |
| * @param {goog.dom.browserrange.AbstractRange} abstractRange The range to test. |
| * @param {boolean=} opt_allowPartial If not set or false, the range must be |
| * entirely contained in the selection for this function to return true. |
| * @return {boolean} Whether this range contains the given range. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.containsRange = |
| function(abstractRange, opt_allowPartial) { |
| // IE sometimes misreports the boundaries for collapsed ranges. So if the |
| // other range is collapsed, make sure the whole range is contained. This is |
| // logically equivalent, and works around IE's bug. |
| var checkPartial = opt_allowPartial && !abstractRange.isCollapsed(); |
| |
| var range = abstractRange.getBrowserRange(); |
| var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END; |
| /** @preserveTry */ |
| try { |
| if (checkPartial) { |
| // There are two ways to not overlap. Being before, and being after. |
| // Before is represented by this.end before range.start: comparison < 0. |
| // After is represented by this.start after range.end: comparison > 0. |
| // The below is the negation of not overlapping. |
| return this.compareBrowserRangeEndpoints(range, end, start) >= 0 && |
| this.compareBrowserRangeEndpoints(range, start, end) <= 0; |
| |
| } else { |
| // Return true if this range bounds the parameter range from both sides. |
| return this.compareBrowserRangeEndpoints(range, end, end) >= 0 && |
| this.compareBrowserRangeEndpoints(range, start, start) <= 0; |
| } |
| } catch (e) { |
| if (!goog.userAgent.IE) { |
| throw e; |
| } |
| // IE sometimes throws exceptions when one range is invalid, i.e. points |
| // to a node that has been removed from the document. Return false in this |
| // case. |
| return false; |
| } |
| }; |
| |
| |
| /** |
| * Tests if this range contains the given node. |
| * @param {Node} node The node to test. |
| * @param {boolean=} opt_allowPartial If not set or false, the node must be |
| * entirely contained in the selection for this function to return true. |
| * @return {boolean} Whether this range contains the given node. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.containsNode = function(node, |
| opt_allowPartial) { |
| return this.containsRange( |
| goog.dom.browserrange.createRangeFromNodeContents(node), |
| opt_allowPartial); |
| }; |
| |
| |
| /** |
| * Tests if the selection is collapsed - i.e. is just a caret. |
| * @return {boolean} Whether the range is collapsed. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.isCollapsed = |
| goog.abstractMethod; |
| |
| |
| /** |
| * @return {string} The text content of the range. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getText = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Returns the HTML fragment this range selects. This is slow on all browsers. |
| * @return {string} HTML fragment of the range, does not include context |
| * containing elements. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getHtmlFragment = function() { |
| var output = new goog.string.StringBuffer(); |
| goog.iter.forEach(this, function(node, ignore, it) { |
| if (node.nodeType == goog.dom.NodeType.TEXT) { |
| output.append(goog.string.htmlEscape(node.nodeValue.substring( |
| it.getStartTextOffset(), it.getEndTextOffset()))); |
| } else if (node.nodeType == goog.dom.NodeType.ELEMENT) { |
| if (it.isEndTag()) { |
| if (goog.dom.canHaveChildren(node)) { |
| output.append('</' + node.tagName + '>'); |
| } |
| } else { |
| var shallow = node.cloneNode(false); |
| var html = goog.dom.getOuterHtml(shallow); |
| if (goog.userAgent.IE && node.tagName == goog.dom.TagName.LI) { |
| // For an LI, IE just returns "<li>" with no closing tag |
| output.append(html); |
| } else { |
| var index = html.lastIndexOf('<'); |
| output.append(index ? html.substr(0, index) : html); |
| } |
| } |
| } |
| }, this); |
| |
| return output.toString(); |
| }; |
| |
| |
| /** |
| * Returns valid HTML for this range. This is fast on IE, and semi-fast on |
| * other browsers. |
| * @return {string} Valid HTML of the range, including context containing |
| * elements. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.getValidHtml = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Returns a RangeIterator over the contents of the range. Regardless of the |
| * direction of the range, the iterator will move in document order. |
| * @param {boolean=} opt_keys Unused for this iterator. |
| * @return {!goog.dom.RangeIterator} An iterator over tags in the range. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.__iterator__ = function( |
| opt_keys) { |
| return new goog.dom.TextRangeIterator(this.getStartNode(), |
| this.getStartOffset(), this.getEndNode(), this.getEndOffset()); |
| }; |
| |
| |
| // SELECTION MODIFICATION |
| |
| |
| /** |
| * Set this range as the selection in its window. |
| * @param {boolean=} opt_reverse Whether to select the range in reverse, |
| * if possible. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.select = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Removes the contents of the range from the document. As a side effect, the |
| * selection will be collapsed. The behavior of content removal is normalized |
| * across browsers. For instance, IE sometimes creates extra text nodes that |
| * a W3C browser does not. That behavior is corrected for. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.removeContents = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Surrounds the text range with the specified element (on Mozilla) or with a |
| * clone of the specified element (on IE). Returns a reference to the |
| * surrounding element if the operation was successful; returns null if the |
| * operation failed. |
| * @param {Element} element The element with which the selection is to be |
| * surrounded. |
| * @return {Element} The surrounding element (same as the argument on Mozilla, |
| * but not on IE), or null if unsuccessful. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.surroundContents = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Inserts a node before (or after) the range. The range may be disrupted |
| * beyond recovery because of the way this splits nodes. |
| * @param {Node} node The node to insert. |
| * @param {boolean} before True to insert before, false to insert after. |
| * @return {Node} The node added to the document. This may be different |
| * than the node parameter because on IE we have to clone it. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.insertNode = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Surrounds this range with the two given nodes. The range may be disrupted |
| * beyond recovery because of the way this splits nodes. |
| * @param {Element} startNode The node to insert at the start. |
| * @param {Element} endNode The node to insert at the end. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.surroundWithNodes = |
| goog.abstractMethod; |
| |
| |
| /** |
| * Collapses the range to one of its boundary points. |
| * @param {boolean} toStart Whether to collapse to the start of the range. |
| */ |
| goog.dom.browserrange.AbstractRange.prototype.collapse = |
| goog.abstractMethod; |