Path: blob/trunk/third_party/closure/goog/dom/textrangeiterator.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 Iterator between two DOM text range positions.16*17* @author [email protected] (Robby Walker)18*/1920goog.provide('goog.dom.TextRangeIterator');2122goog.require('goog.array');23goog.require('goog.dom');24goog.require('goog.dom.NodeType');25goog.require('goog.dom.RangeIterator');26goog.require('goog.dom.TagName');27goog.require('goog.iter.StopIteration');28293031/**32* Subclass of goog.dom.TagIterator that iterates over a DOM range. It33* adds functions to determine the portion of each text node that is selected.34*35* @param {Node} startNode The starting node position.36* @param {number} startOffset The offset in to startNode. If startNode is37* an element, indicates an offset in to childNodes. If startNode is a38* text node, indicates an offset in to nodeValue.39* @param {Node} endNode The ending node position.40* @param {number} endOffset The offset in to endNode. If endNode is41* an element, indicates an offset in to childNodes. If endNode is a42* text node, indicates an offset in to nodeValue.43* @param {boolean=} opt_reverse Whether to traverse nodes in reverse.44* @constructor45* @extends {goog.dom.RangeIterator}46* @final47*/48goog.dom.TextRangeIterator = function(49startNode, startOffset, endNode, endOffset, opt_reverse) {50/**51* The first node in the selection.52* @private {Node}53*/54this.startNode_ = null;5556/**57* The last node in the selection.58* @private {Node}59*/60this.endNode_ = null;6162/**63* The offset within the first node in the selection.64* @private {number}65*/66this.startOffset_ = 0;6768/**69* The offset within the last node in the selection.70* @private {number}71*/72this.endOffset_ = 0;7374var goNext;7576if (startNode) {77this.startNode_ = startNode;78this.startOffset_ = startOffset;79this.endNode_ = endNode;80this.endOffset_ = endOffset;8182// Skip to the offset nodes - being careful to special case BRs since these83// have no children but still can appear as the startContainer of a range.84if (startNode.nodeType == goog.dom.NodeType.ELEMENT &&85/** @type {!Element} */ (startNode).tagName != goog.dom.TagName.BR) {86var startChildren = startNode.childNodes;87var candidate = startChildren[startOffset];88if (candidate) {89this.startNode_ = candidate;90this.startOffset_ = 0;91} else {92if (startChildren.length) {93this.startNode_ =94/** @type {Node} */ (goog.array.peek(startChildren));95}96goNext = true;97}98}99100if (endNode.nodeType == goog.dom.NodeType.ELEMENT) {101this.endNode_ = endNode.childNodes[endOffset];102if (this.endNode_) {103this.endOffset_ = 0;104} else {105// The offset was past the last element.106this.endNode_ = endNode;107}108}109}110111goog.dom.TextRangeIterator.base(112this, 'constructor', opt_reverse ? this.endNode_ : this.startNode_,113opt_reverse);114115if (goNext) {116try {117this.next();118} catch (e) {119if (e != goog.iter.StopIteration) {120throw e;121}122}123}124};125goog.inherits(goog.dom.TextRangeIterator, goog.dom.RangeIterator);126127128/** @override */129goog.dom.TextRangeIterator.prototype.getStartTextOffset = function() {130// Offsets only apply to text nodes. If our current node is the start node,131// return the saved offset. Otherwise, return 0.132return this.node.nodeType != goog.dom.NodeType.TEXT ?133-1 :134this.node == this.startNode_ ? this.startOffset_ : 0;135};136137138/** @override */139goog.dom.TextRangeIterator.prototype.getEndTextOffset = function() {140// Offsets only apply to text nodes. If our current node is the end node,141// return the saved offset. Otherwise, return the length of the node.142return this.node.nodeType != goog.dom.NodeType.TEXT ?143-1 :144this.node == this.endNode_ ? this.endOffset_ : this.node.nodeValue.length;145};146147148/** @override */149goog.dom.TextRangeIterator.prototype.getStartNode = function() {150return this.startNode_;151};152153154/**155* Change the start node of the iterator.156* @param {Node} node The new start node.157*/158goog.dom.TextRangeIterator.prototype.setStartNode = function(node) {159if (!this.isStarted()) {160this.setPosition(node);161}162163this.startNode_ = node;164this.startOffset_ = 0;165};166167168/** @override */169goog.dom.TextRangeIterator.prototype.getEndNode = function() {170return this.endNode_;171};172173174/**175* Change the end node of the iterator.176* @param {Node} node The new end node.177*/178goog.dom.TextRangeIterator.prototype.setEndNode = function(node) {179this.endNode_ = node;180this.endOffset_ = 0;181};182183184/** @override */185goog.dom.TextRangeIterator.prototype.isLast = function() {186return this.isStarted() && this.node == this.endNode_ &&187(!this.endOffset_ || !this.isStartTag());188};189190191/**192* Move to the next position in the selection.193* Throws {@code goog.iter.StopIteration} when it passes the end of the range.194* @return {Node} The node at the next position.195* @override196*/197goog.dom.TextRangeIterator.prototype.next = function() {198if (this.isLast()) {199throw goog.iter.StopIteration;200}201202// Call the super function.203return goog.dom.TextRangeIterator.superClass_.next.call(this);204};205206207/** @override */208goog.dom.TextRangeIterator.prototype.skipTag = function() {209goog.dom.TextRangeIterator.superClass_.skipTag.apply(this);210211// If the node we are skipping contains the end node, we just skipped past212// the end, so we stop the iteration.213if (goog.dom.contains(this.node, this.endNode_)) {214throw goog.iter.StopIteration;215}216};217218219/** @override */220goog.dom.TextRangeIterator.prototype.copyFrom = function(other) {221this.startNode_ = other.startNode_;222this.endNode_ = other.endNode_;223this.startOffset_ = other.startOffset_;224this.endOffset_ = other.endOffset_;225this.isReversed_ = other.isReversed_;226227goog.dom.TextRangeIterator.superClass_.copyFrom.call(this, other);228};229230231/**232* @return {!goog.dom.TextRangeIterator} An identical iterator.233* @override234*/235goog.dom.TextRangeIterator.prototype.clone = function() {236var copy = new goog.dom.TextRangeIterator(237this.startNode_, this.startOffset_, this.endNode_, this.endOffset_,238this.isReversed_);239copy.copyFrom(this);240return copy;241};242243244