Path: blob/trunk/third_party/closure/goog/dom/browserrange/abstractrange.js
2868 views
// Copyright 2007 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Definition of the browser range interface.16*17* DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead.18*19* @author [email protected] (Robby Walker)20*/212223goog.provide('goog.dom.browserrange.AbstractRange');2425goog.require('goog.array');26goog.require('goog.asserts');27goog.require('goog.dom');28goog.require('goog.dom.NodeType');29goog.require('goog.dom.RangeEndpoint');30goog.require('goog.dom.TagName');31goog.require('goog.dom.TextRangeIterator');32goog.require('goog.iter');33goog.require('goog.math.Coordinate');34goog.require('goog.string');35goog.require('goog.string.StringBuffer');36goog.require('goog.userAgent');37383940/**41* The constructor for abstract ranges. Don't call this from subclasses.42* @constructor43*/44goog.dom.browserrange.AbstractRange = function() {};454647/**48* @return {goog.dom.browserrange.AbstractRange} A clone of this range.49*/50goog.dom.browserrange.AbstractRange.prototype.clone = goog.abstractMethod;515253/**54* Returns the browser native implementation of the range. Please refrain from55* using this function - if you find you need the range please add wrappers for56* the functionality you need rather than just using the native range.57* @return {Range|TextRange} The browser native range object.58*/59goog.dom.browserrange.AbstractRange.prototype.getBrowserRange =60goog.abstractMethod;616263/**64* Returns the deepest node in the tree that contains the entire range.65* @return {Node} The deepest node that contains the entire range.66*/67goog.dom.browserrange.AbstractRange.prototype.getContainer =68goog.abstractMethod;697071/**72* Returns the node the range starts in.73* @return {Node} The element or text node the range starts in.74*/75goog.dom.browserrange.AbstractRange.prototype.getStartNode =76goog.abstractMethod;777879/**80* Returns the offset into the node the range starts in.81* @return {number} The offset into the node the range starts in. For text82* nodes, this is an offset into the node value. For elements, this is83* an offset into the childNodes array.84*/85goog.dom.browserrange.AbstractRange.prototype.getStartOffset =86goog.abstractMethod;878889/**90* @return {goog.math.Coordinate} The coordinate of the selection start node91* and offset.92*/93goog.dom.browserrange.AbstractRange.prototype.getStartPosition = function() {94return this.getPosition_(true);95};969798/**99* Returns the node the range ends in.100* @return {Node} The element or text node the range ends in.101*/102goog.dom.browserrange.AbstractRange.prototype.getEndNode = goog.abstractMethod;103104105/**106* Returns the offset into the node the range ends in.107* @return {number} The offset into the node the range ends in. For text108* nodes, this is an offset into the node value. For elements, this is109* an offset into the childNodes array.110*/111goog.dom.browserrange.AbstractRange.prototype.getEndOffset =112goog.abstractMethod;113114115/**116* @return {goog.math.Coordinate} The coordinate of the selection end node117* and offset.118*/119goog.dom.browserrange.AbstractRange.prototype.getEndPosition = function() {120return this.getPosition_(false);121};122123124/**125* @param {boolean} start Whether to get the position of the start or end.126* @return {goog.math.Coordinate} The coordinate of the selection point.127* @private128*/129goog.dom.browserrange.AbstractRange.prototype.getPosition_ = function(start) {130goog.asserts.assert(131this.range_.getClientRects,132'Getting selection coordinates is not supported.');133134var rects = this.range_.getClientRects();135if (rects.length) {136var r = start ? rects[0] : goog.array.peek(rects);137return new goog.math.Coordinate(138start ? r.left : r.right, start ? r.top : r.bottom);139}140return null;141};142143144/**145* Compares one endpoint of this range with the endpoint of another browser146* native range object.147* @param {Range|TextRange} range The browser native range to compare against.148* @param {goog.dom.RangeEndpoint} thisEndpoint The endpoint of this range149* to compare with.150* @param {goog.dom.RangeEndpoint} otherEndpoint The endpoint of the other151* range to compare with.152* @return {number} 0 if the endpoints are equal, negative if this range153* endpoint comes before the other range endpoint, and positive otherwise.154*/155goog.dom.browserrange.AbstractRange.prototype.compareBrowserRangeEndpoints =156goog.abstractMethod;157158159/**160* Tests if this range contains the given range.161* @param {goog.dom.browserrange.AbstractRange} abstractRange The range to test.162* @param {boolean=} opt_allowPartial If not set or false, the range must be163* entirely contained in the selection for this function to return true.164* @return {boolean} Whether this range contains the given range.165*/166goog.dom.browserrange.AbstractRange.prototype.containsRange = function(167abstractRange, opt_allowPartial) {168// IE sometimes misreports the boundaries for collapsed ranges. So if the169// other range is collapsed, make sure the whole range is contained. This is170// logically equivalent, and works around IE's bug.171var checkPartial = opt_allowPartial && !abstractRange.isCollapsed();172173var range = abstractRange.getBrowserRange();174var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END;175176try {177if (checkPartial) {178// There are two ways to not overlap. Being before, and being after.179// Before is represented by this.end before range.start: comparison < 0.180// After is represented by this.start after range.end: comparison > 0.181// The below is the negation of not overlapping.182return this.compareBrowserRangeEndpoints(range, end, start) >= 0 &&183this.compareBrowserRangeEndpoints(range, start, end) <= 0;184185} else {186// Return true if this range bounds the parameter range from both sides.187return this.compareBrowserRangeEndpoints(range, end, end) >= 0 &&188this.compareBrowserRangeEndpoints(range, start, start) <= 0;189}190} catch (e) {191if (!goog.userAgent.IE) {192throw e;193}194// IE sometimes throws exceptions when one range is invalid, i.e. points195// to a node that has been removed from the document. Return false in this196// case.197return false;198}199};200201202/**203* Tests if this range contains the given node.204* @param {Node} node The node to test.205* @param {boolean=} opt_allowPartial If not set or false, the node must be206* entirely contained in the selection for this function to return true.207* @return {boolean} Whether this range contains the given node.208* @suppress {missingRequire} Cannot depend on goog.dom.browserrange because it209* creates a circular dependency.210*/211goog.dom.browserrange.AbstractRange.prototype.containsNode = function(212node, opt_allowPartial) {213/** @suppress {missingRequire} Circular dep with browserrange */214return this.containsRange(215goog.dom.browserrange.createRangeFromNodeContents(node),216opt_allowPartial);217};218219220/**221* Tests if the selection is collapsed - i.e. is just a caret.222* @return {boolean} Whether the range is collapsed.223*/224goog.dom.browserrange.AbstractRange.prototype.isCollapsed = goog.abstractMethod;225226227/**228* @return {string} The text content of the range.229*/230goog.dom.browserrange.AbstractRange.prototype.getText = goog.abstractMethod;231232233/**234* Returns the HTML fragment this range selects. This is slow on all browsers.235* @return {string} HTML fragment of the range, does not include context236* containing elements.237*/238goog.dom.browserrange.AbstractRange.prototype.getHtmlFragment = function() {239var output = new goog.string.StringBuffer();240goog.iter.forEach(this, function(node, ignore, it) {241if (node.nodeType == goog.dom.NodeType.TEXT) {242output.append(243goog.string.htmlEscape(244node.nodeValue.substring(245it.getStartTextOffset(), it.getEndTextOffset())));246} else if (node.nodeType == goog.dom.NodeType.ELEMENT) {247if (it.isEndTag()) {248if (goog.dom.canHaveChildren(node)) {249output.append('</' + node.tagName + '>');250}251} else {252var shallow = node.cloneNode(false);253var html = goog.dom.getOuterHtml(shallow);254if (goog.userAgent.IE && node.tagName == goog.dom.TagName.LI) {255// For an LI, IE just returns "<li>" with no closing tag256output.append(html);257} else {258var index = html.lastIndexOf('<');259output.append(index ? html.substr(0, index) : html);260}261}262}263}, this);264265return output.toString();266};267268269/**270* Returns valid HTML for this range. This is fast on IE, and semi-fast on271* other browsers.272* @return {string} Valid HTML of the range, including context containing273* elements.274*/275goog.dom.browserrange.AbstractRange.prototype.getValidHtml =276goog.abstractMethod;277278279/**280* Returns a RangeIterator over the contents of the range. Regardless of the281* direction of the range, the iterator will move in document order.282* @param {boolean=} opt_keys Unused for this iterator.283* @return {!goog.dom.RangeIterator} An iterator over tags in the range.284*/285goog.dom.browserrange.AbstractRange.prototype.__iterator__ = function(286opt_keys) {287return new goog.dom.TextRangeIterator(288this.getStartNode(), this.getStartOffset(), this.getEndNode(),289this.getEndOffset());290};291292293// SELECTION MODIFICATION294295296/**297* Set this range as the selection in its window.298* @param {boolean=} opt_reverse Whether to select the range in reverse,299* if possible.300*/301goog.dom.browserrange.AbstractRange.prototype.select = goog.abstractMethod;302303304/**305* Removes the contents of the range from the document. As a side effect, the306* selection will be collapsed. The behavior of content removal is normalized307* across browsers. For instance, IE sometimes creates extra text nodes that308* a W3C browser does not. That behavior is corrected for.309*/310goog.dom.browserrange.AbstractRange.prototype.removeContents =311goog.abstractMethod;312313314/**315* Surrounds the text range with the specified element (on Mozilla) or with a316* clone of the specified element (on IE). Returns a reference to the317* surrounding element if the operation was successful; returns null if the318* operation failed.319* @param {Element} element The element with which the selection is to be320* surrounded.321* @return {Element} The surrounding element (same as the argument on Mozilla,322* but not on IE), or null if unsuccessful.323*/324goog.dom.browserrange.AbstractRange.prototype.surroundContents =325goog.abstractMethod;326327328/**329* Inserts a node before (or after) the range. The range may be disrupted330* beyond recovery because of the way this splits nodes.331* @param {Node} node The node to insert.332* @param {boolean} before True to insert before, false to insert after.333* @return {Node} The node added to the document. This may be different334* than the node parameter because on IE we have to clone it.335*/336goog.dom.browserrange.AbstractRange.prototype.insertNode = goog.abstractMethod;337338339/**340* Surrounds this range with the two given nodes. The range may be disrupted341* beyond recovery because of the way this splits nodes.342* @param {Element} startNode The node to insert at the start.343* @param {Element} endNode The node to insert at the end.344*/345goog.dom.browserrange.AbstractRange.prototype.surroundWithNodes =346goog.abstractMethod;347348349/**350* Collapses the range to one of its boundary points.351* @param {boolean} toStart Whether to collapse to the start of the range.352*/353goog.dom.browserrange.AbstractRange.prototype.collapse = goog.abstractMethod;354355356