Path: blob/trunk/third_party/closure/goog/dom/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 Interface definitions for working with ranges16* in HTML documents.17*18* @author [email protected] (Robby Walker)19*/202122goog.provide('goog.dom.AbstractRange');23goog.provide('goog.dom.RangeIterator');24goog.provide('goog.dom.RangeType');2526goog.require('goog.dom');27goog.require('goog.dom.NodeType');28goog.require('goog.dom.SavedCaretRange');29goog.require('goog.dom.TagIterator');30goog.require('goog.userAgent');313233/**34* Types of ranges.35* @enum {string}36*/37goog.dom.RangeType = {38TEXT: 'text',39CONTROL: 'control',40MULTI: 'mutli'41};42434445/**46* Creates a new selection with no properties. Do not use this constructor -47* use one of the goog.dom.Range.from* methods instead.48* @constructor49*/50goog.dom.AbstractRange = function() {};515253/**54* Gets the browser native selection object from the given window.55* @param {Window} win The window to get the selection object from.56* @return {Object} The browser native selection object, or null if it could57* not be retrieved.58*/59goog.dom.AbstractRange.getBrowserSelectionForWindow = function(win) {60if (win.getSelection) {61// W3C62return win.getSelection();63} else {64// IE65var doc = win.document;66var sel = doc.selection;67if (sel) {68// IE has a bug where it sometimes returns a selection from the wrong69// document. Catching these cases now helps us avoid problems later.70try {71var range = sel.createRange();72// Only TextRanges have a parentElement method.73if (range.parentElement) {74if (range.parentElement().document != doc) {75return null;76}77} else if (78!range.length ||79/** @type {ControlRange} */ (range).item(0).document != doc) {80// For ControlRanges, check that the range has items, and that81// the first item in the range is in the correct document.82return null;83}84} catch (e) {85// If the selection is in the wrong document, and the wrong document is86// in a different domain, IE will throw an exception.87return null;88}89// TODO(user|robbyw) Sometimes IE 6 returns a selection instance90// when there is no selection. This object has a 'type' property equals91// to 'None' and a typeDetail property bound to undefined. Ideally this92// function should not return this instance.93return sel;94}95return null;96}97};9899100/**101* Tests if the given Object is a controlRange.102* @param {Object} range The range object to test.103* @return {boolean} Whether the given Object is a controlRange.104*/105goog.dom.AbstractRange.isNativeControlRange = function(range) {106// For now, tests for presence of a control range function.107return !!range && !!range.addElement;108};109110111/**112* @return {!goog.dom.AbstractRange} A clone of this range.113*/114goog.dom.AbstractRange.prototype.clone = goog.abstractMethod;115116117/**118* @return {goog.dom.RangeType} The type of range represented by this object.119*/120goog.dom.AbstractRange.prototype.getType = goog.abstractMethod;121122123/**124* @return {Range|TextRange} The native browser range object.125*/126goog.dom.AbstractRange.prototype.getBrowserRangeObject = goog.abstractMethod;127128129/**130* Sets the native browser range object, overwriting any state this range was131* storing.132* @param {Range|TextRange} nativeRange The native browser range object.133* @return {boolean} Whether the given range was accepted. If not, the caller134* will need to call goog.dom.Range.createFromBrowserRange to create a new135* range object.136*/137goog.dom.AbstractRange.prototype.setBrowserRangeObject = function(nativeRange) {138return false;139};140141142/**143* @return {number} The number of text ranges in this range.144*/145goog.dom.AbstractRange.prototype.getTextRangeCount = goog.abstractMethod;146147148/**149* Get the i-th text range in this range. The behavior is undefined if150* i >= getTextRangeCount or i < 0.151* @param {number} i The range number to retrieve.152* @return {goog.dom.TextRange} The i-th text range.153*/154goog.dom.AbstractRange.prototype.getTextRange = goog.abstractMethod;155156157/**158* Gets an array of all text ranges this range is comprised of. For non-multi159* ranges, returns a single element array containing this.160* @return {!Array<goog.dom.TextRange>} Array of text ranges.161*/162goog.dom.AbstractRange.prototype.getTextRanges = function() {163var output = [];164for (var i = 0, len = this.getTextRangeCount(); i < len; i++) {165output.push(this.getTextRange(i));166}167return output;168};169170171/**172* @return {Node} The deepest node that contains the entire range.173*/174goog.dom.AbstractRange.prototype.getContainer = goog.abstractMethod;175176177/**178* Returns the deepest element in the tree that contains the entire range.179* @return {Element} The deepest element that contains the entire range.180*/181goog.dom.AbstractRange.prototype.getContainerElement = function() {182var node = this.getContainer();183return /** @type {Element} */ (184node.nodeType == goog.dom.NodeType.ELEMENT ? node : node.parentNode);185};186187188/**189* @return {Node} The element or text node the range starts in. For text190* ranges, the range comprises all text between the start and end position.191* For other types of range, start and end give bounds of the range but192* do not imply all nodes in those bounds are selected.193*/194goog.dom.AbstractRange.prototype.getStartNode = goog.abstractMethod;195196197/**198* @return {number} The offset into the node the range starts in. For text199* nodes, this is an offset into the node value. For elements, this is200* an offset into the childNodes array.201*/202goog.dom.AbstractRange.prototype.getStartOffset = goog.abstractMethod;203204205/**206* @return {goog.math.Coordinate} The coordinate of the selection start node207* and offset.208*/209goog.dom.AbstractRange.prototype.getStartPosition = goog.abstractMethod;210211212/**213* @return {Node} The element or text node the range ends in.214*/215goog.dom.AbstractRange.prototype.getEndNode = goog.abstractMethod;216217218/**219* @return {number} The offset into the node the range ends in. For text220* nodes, this is an offset into the node value. For elements, this is221* an offset into the childNodes array.222*/223goog.dom.AbstractRange.prototype.getEndOffset = goog.abstractMethod;224225226/**227* @return {goog.math.Coordinate} The coordinate of the selection end228* node and offset.229*/230goog.dom.AbstractRange.prototype.getEndPosition = goog.abstractMethod;231232233/**234* @return {Node} The element or text node the range is anchored at.235*/236goog.dom.AbstractRange.prototype.getAnchorNode = function() {237return this.isReversed() ? this.getEndNode() : this.getStartNode();238};239240241/**242* @return {number} The offset into the node the range is anchored at. For243* text nodes, this is an offset into the node value. For elements, this244* is an offset into the childNodes array.245*/246goog.dom.AbstractRange.prototype.getAnchorOffset = function() {247return this.isReversed() ? this.getEndOffset() : this.getStartOffset();248};249250251/**252* @return {Node} The element or text node the range is focused at - i.e. where253* the cursor is.254*/255goog.dom.AbstractRange.prototype.getFocusNode = function() {256return this.isReversed() ? this.getStartNode() : this.getEndNode();257};258259260/**261* @return {number} The offset into the node the range is focused at - i.e.262* where the cursor is. For text nodes, this is an offset into the node263* value. For elements, this is an offset into the childNodes array.264*/265goog.dom.AbstractRange.prototype.getFocusOffset = function() {266return this.isReversed() ? this.getStartOffset() : this.getEndOffset();267};268269270/**271* @return {boolean} Whether the selection is reversed.272*/273goog.dom.AbstractRange.prototype.isReversed = function() {274return false;275};276277278/**279* @return {!Document} The document this selection is a part of.280*/281goog.dom.AbstractRange.prototype.getDocument = function() {282// Using start node in IE was crashing the browser in some cases so use283// getContainer for that browser. It's also faster for IE, but still slower284// than start node for other browsers so we continue to use getStartNode when285// it is not problematic. See bug 1687309.286return goog.dom.getOwnerDocument(287goog.userAgent.IE ? this.getContainer() : this.getStartNode());288};289290291/**292* @return {!Window} The window this selection is a part of.293*/294goog.dom.AbstractRange.prototype.getWindow = function() {295return goog.dom.getWindow(this.getDocument());296};297298299/**300* Tests if this range contains the given range.301* @param {goog.dom.AbstractRange} range The range to test.302* @param {boolean=} opt_allowPartial If true, the range can be partially303* contained in the selection, otherwise the range must be entirely304* contained.305* @return {boolean} Whether this range contains the given range.306*/307goog.dom.AbstractRange.prototype.containsRange = goog.abstractMethod;308309310/**311* Tests if this range contains the given node.312* @param {Node} node The node to test for.313* @param {boolean=} opt_allowPartial If not set or false, the node must be314* entirely contained in the selection for this function to return true.315* @return {boolean} Whether this range contains the given node.316*/317goog.dom.AbstractRange.prototype.containsNode = goog.abstractMethod;318319320321/**322* Tests whether this range is valid (i.e. whether its endpoints are still in323* the document). A range becomes invalid when, after this object was created,324* either one or both of its endpoints are removed from the document. Use of325* an invalid range can lead to runtime errors, particularly in IE.326* @return {boolean} Whether the range is valid.327*/328goog.dom.AbstractRange.prototype.isRangeInDocument = goog.abstractMethod;329330331/**332* @return {boolean} Whether the range is collapsed.333*/334goog.dom.AbstractRange.prototype.isCollapsed = goog.abstractMethod;335336337/**338* @return {string} The text content of the range.339*/340goog.dom.AbstractRange.prototype.getText = goog.abstractMethod;341342343/**344* Returns the HTML fragment this range selects. This is slow on all browsers.345* The HTML fragment may not be valid HTML, for instance if the user selects346* from a to b inclusively in the following html:347*348* <div>a</div>b349*350* This method will return351*352* a</div>b353*354* If you need valid HTML, use {@link #getValidHtml} instead.355*356* @return {string} HTML fragment of the range, does not include context357* containing elements.358*/359goog.dom.AbstractRange.prototype.getHtmlFragment = goog.abstractMethod;360361362/**363* Returns valid HTML for this range. This is fast on IE, and semi-fast on364* other browsers.365* @return {string} Valid HTML of the range, including context containing366* elements.367*/368goog.dom.AbstractRange.prototype.getValidHtml = goog.abstractMethod;369370371/**372* Returns pastable HTML for this range. This guarantees that any child items373* that must have specific ancestors will have them, for instance all TDs will374* be contained in a TR in a TBODY in a TABLE and all LIs will be contained in375* a UL or OL as appropriate. This is semi-fast on all browsers.376* @return {string} Pastable HTML of the range, including context containing377* elements.378*/379goog.dom.AbstractRange.prototype.getPastableHtml = goog.abstractMethod;380381382/**383* Returns a RangeIterator over the contents of the range. Regardless of the384* direction of the range, the iterator will move in document order.385* @param {boolean=} opt_keys Unused for this iterator.386* @return {!goog.dom.RangeIterator} An iterator over tags in the range.387*/388goog.dom.AbstractRange.prototype.__iterator__ = goog.abstractMethod;389390391// RANGE ACTIONS392393394/**395* Sets this range as the selection in its window.396*/397goog.dom.AbstractRange.prototype.select = goog.abstractMethod;398399400/**401* Removes the contents of the range from the document.402*/403goog.dom.AbstractRange.prototype.removeContents = goog.abstractMethod;404405406/**407* Inserts a node before (or after) the range. The range may be disrupted408* beyond recovery because of the way this splits nodes.409* @param {Node} node The node to insert.410* @param {boolean} before True to insert before, false to insert after.411* @return {Node} The node added to the document. This may be different412* than the node parameter because on IE we have to clone it.413*/414goog.dom.AbstractRange.prototype.insertNode = goog.abstractMethod;415416417/**418* Replaces the range contents with (possibly a copy of) the given node. The419* range may be disrupted beyond recovery because of the way this splits nodes.420* @param {Node} node The node to insert.421* @return {Node} The node added to the document. This may be different422* than the node parameter because on IE we have to clone it.423*/424goog.dom.AbstractRange.prototype.replaceContentsWithNode = function(node) {425if (!this.isCollapsed()) {426this.removeContents();427}428429return this.insertNode(node, true);430};431432433/**434* Surrounds this range with the two given nodes. The range may be disrupted435* beyond recovery because of the way this splits nodes.436* @param {Element} startNode The node to insert at the start.437* @param {Element} endNode The node to insert at the end.438*/439goog.dom.AbstractRange.prototype.surroundWithNodes = goog.abstractMethod;440441442// SAVE/RESTORE443444445/**446* Saves the range so that if the start and end nodes are left alone, it can447* be restored.448* @return {!goog.dom.SavedRange} A range representation that can be restored449* as long as the endpoint nodes of the selection are not modified.450*/451goog.dom.AbstractRange.prototype.saveUsingDom = goog.abstractMethod;452453454/**455* Saves the range using HTML carets. As long as the carets remained in the456* HTML, the range can be restored...even when the HTML is copied across457* documents.458* @return {goog.dom.SavedCaretRange?} A range representation that can be459* restored as long as carets are not removed. Returns null if carets460* could not be created.461*/462goog.dom.AbstractRange.prototype.saveUsingCarets = function() {463return (this.getStartNode() && this.getEndNode()) ?464new goog.dom.SavedCaretRange(this) :465null;466};467468469// RANGE MODIFICATION470471472/**473* Collapses the range to one of its boundary points.474* @param {boolean} toAnchor Whether to collapse to the anchor of the range.475*/476goog.dom.AbstractRange.prototype.collapse = goog.abstractMethod;477478// RANGE ITERATION479480481482/**483* Subclass of goog.dom.TagIterator that iterates over a DOM range. It484* adds functions to determine the portion of each text node that is selected.485* @param {Node} node The node to start traversal at. When null, creates an486* empty iterator.487* @param {boolean=} opt_reverse Whether to traverse nodes in reverse.488* @constructor489* @extends {goog.dom.TagIterator}490*/491goog.dom.RangeIterator = function(node, opt_reverse) {492goog.dom.TagIterator.call(this, node, opt_reverse, true);493};494goog.inherits(goog.dom.RangeIterator, goog.dom.TagIterator);495496497/**498* @return {number} The offset into the current node, or -1 if the current node499* is not a text node.500*/501goog.dom.RangeIterator.prototype.getStartTextOffset = goog.abstractMethod;502503504/**505* @return {number} The end offset into the current node, or -1 if the current506* node is not a text node.507*/508goog.dom.RangeIterator.prototype.getEndTextOffset = goog.abstractMethod;509510511/**512* @return {Node} node The iterator's start node.513*/514goog.dom.RangeIterator.prototype.getStartNode = goog.abstractMethod;515516517/**518* @return {Node} The iterator's end node.519*/520goog.dom.RangeIterator.prototype.getEndNode = goog.abstractMethod;521522523/**524* @return {boolean} Whether a call to next will fail.525*/526goog.dom.RangeIterator.prototype.isLast = goog.abstractMethod;527528529