Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/dom/textrangeiterator.js
2868 views
1
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Iterator between two DOM text range positions.
17
*
18
* @author [email protected] (Robby Walker)
19
*/
20
21
goog.provide('goog.dom.TextRangeIterator');
22
23
goog.require('goog.array');
24
goog.require('goog.dom');
25
goog.require('goog.dom.NodeType');
26
goog.require('goog.dom.RangeIterator');
27
goog.require('goog.dom.TagName');
28
goog.require('goog.iter.StopIteration');
29
30
31
32
/**
33
* Subclass of goog.dom.TagIterator that iterates over a DOM range. It
34
* adds functions to determine the portion of each text node that is selected.
35
*
36
* @param {Node} startNode The starting node position.
37
* @param {number} startOffset The offset in to startNode. If startNode is
38
* an element, indicates an offset in to childNodes. If startNode is a
39
* text node, indicates an offset in to nodeValue.
40
* @param {Node} endNode The ending node position.
41
* @param {number} endOffset The offset in to endNode. If endNode is
42
* an element, indicates an offset in to childNodes. If endNode is a
43
* text node, indicates an offset in to nodeValue.
44
* @param {boolean=} opt_reverse Whether to traverse nodes in reverse.
45
* @constructor
46
* @extends {goog.dom.RangeIterator}
47
* @final
48
*/
49
goog.dom.TextRangeIterator = function(
50
startNode, startOffset, endNode, endOffset, opt_reverse) {
51
/**
52
* The first node in the selection.
53
* @private {Node}
54
*/
55
this.startNode_ = null;
56
57
/**
58
* The last node in the selection.
59
* @private {Node}
60
*/
61
this.endNode_ = null;
62
63
/**
64
* The offset within the first node in the selection.
65
* @private {number}
66
*/
67
this.startOffset_ = 0;
68
69
/**
70
* The offset within the last node in the selection.
71
* @private {number}
72
*/
73
this.endOffset_ = 0;
74
75
var goNext;
76
77
if (startNode) {
78
this.startNode_ = startNode;
79
this.startOffset_ = startOffset;
80
this.endNode_ = endNode;
81
this.endOffset_ = endOffset;
82
83
// Skip to the offset nodes - being careful to special case BRs since these
84
// have no children but still can appear as the startContainer of a range.
85
if (startNode.nodeType == goog.dom.NodeType.ELEMENT &&
86
/** @type {!Element} */ (startNode).tagName != goog.dom.TagName.BR) {
87
var startChildren = startNode.childNodes;
88
var candidate = startChildren[startOffset];
89
if (candidate) {
90
this.startNode_ = candidate;
91
this.startOffset_ = 0;
92
} else {
93
if (startChildren.length) {
94
this.startNode_ =
95
/** @type {Node} */ (goog.array.peek(startChildren));
96
}
97
goNext = true;
98
}
99
}
100
101
if (endNode.nodeType == goog.dom.NodeType.ELEMENT) {
102
this.endNode_ = endNode.childNodes[endOffset];
103
if (this.endNode_) {
104
this.endOffset_ = 0;
105
} else {
106
// The offset was past the last element.
107
this.endNode_ = endNode;
108
}
109
}
110
}
111
112
goog.dom.TextRangeIterator.base(
113
this, 'constructor', opt_reverse ? this.endNode_ : this.startNode_,
114
opt_reverse);
115
116
if (goNext) {
117
try {
118
this.next();
119
} catch (e) {
120
if (e != goog.iter.StopIteration) {
121
throw e;
122
}
123
}
124
}
125
};
126
goog.inherits(goog.dom.TextRangeIterator, goog.dom.RangeIterator);
127
128
129
/** @override */
130
goog.dom.TextRangeIterator.prototype.getStartTextOffset = function() {
131
// Offsets only apply to text nodes. If our current node is the start node,
132
// return the saved offset. Otherwise, return 0.
133
return this.node.nodeType != goog.dom.NodeType.TEXT ?
134
-1 :
135
this.node == this.startNode_ ? this.startOffset_ : 0;
136
};
137
138
139
/** @override */
140
goog.dom.TextRangeIterator.prototype.getEndTextOffset = function() {
141
// Offsets only apply to text nodes. If our current node is the end node,
142
// return the saved offset. Otherwise, return the length of the node.
143
return this.node.nodeType != goog.dom.NodeType.TEXT ?
144
-1 :
145
this.node == this.endNode_ ? this.endOffset_ : this.node.nodeValue.length;
146
};
147
148
149
/** @override */
150
goog.dom.TextRangeIterator.prototype.getStartNode = function() {
151
return this.startNode_;
152
};
153
154
155
/**
156
* Change the start node of the iterator.
157
* @param {Node} node The new start node.
158
*/
159
goog.dom.TextRangeIterator.prototype.setStartNode = function(node) {
160
if (!this.isStarted()) {
161
this.setPosition(node);
162
}
163
164
this.startNode_ = node;
165
this.startOffset_ = 0;
166
};
167
168
169
/** @override */
170
goog.dom.TextRangeIterator.prototype.getEndNode = function() {
171
return this.endNode_;
172
};
173
174
175
/**
176
* Change the end node of the iterator.
177
* @param {Node} node The new end node.
178
*/
179
goog.dom.TextRangeIterator.prototype.setEndNode = function(node) {
180
this.endNode_ = node;
181
this.endOffset_ = 0;
182
};
183
184
185
/** @override */
186
goog.dom.TextRangeIterator.prototype.isLast = function() {
187
return this.isStarted() && this.node == this.endNode_ &&
188
(!this.endOffset_ || !this.isStartTag());
189
};
190
191
192
/**
193
* Move to the next position in the selection.
194
* Throws {@code goog.iter.StopIteration} when it passes the end of the range.
195
* @return {Node} The node at the next position.
196
* @override
197
*/
198
goog.dom.TextRangeIterator.prototype.next = function() {
199
if (this.isLast()) {
200
throw goog.iter.StopIteration;
201
}
202
203
// Call the super function.
204
return goog.dom.TextRangeIterator.superClass_.next.call(this);
205
};
206
207
208
/** @override */
209
goog.dom.TextRangeIterator.prototype.skipTag = function() {
210
goog.dom.TextRangeIterator.superClass_.skipTag.apply(this);
211
212
// If the node we are skipping contains the end node, we just skipped past
213
// the end, so we stop the iteration.
214
if (goog.dom.contains(this.node, this.endNode_)) {
215
throw goog.iter.StopIteration;
216
}
217
};
218
219
220
/** @override */
221
goog.dom.TextRangeIterator.prototype.copyFrom = function(other) {
222
this.startNode_ = other.startNode_;
223
this.endNode_ = other.endNode_;
224
this.startOffset_ = other.startOffset_;
225
this.endOffset_ = other.endOffset_;
226
this.isReversed_ = other.isReversed_;
227
228
goog.dom.TextRangeIterator.superClass_.copyFrom.call(this, other);
229
};
230
231
232
/**
233
* @return {!goog.dom.TextRangeIterator} An identical iterator.
234
* @override
235
*/
236
goog.dom.TextRangeIterator.prototype.clone = function() {
237
var copy = new goog.dom.TextRangeIterator(
238
this.startNode_, this.startOffset_, this.endNode_, this.endOffset_,
239
this.isReversed_);
240
copy.copyFrom(this);
241
return copy;
242
};
243
244