Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/dom/savedcaretrange.js
2868 views
1
// Copyright 2008 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 An API for saving and restoring ranges as HTML carets.
17
*
18
* @author [email protected] (Nick Santos)
19
*/
20
21
22
goog.provide('goog.dom.SavedCaretRange');
23
24
goog.require('goog.array');
25
goog.require('goog.dom');
26
goog.require('goog.dom.SavedRange');
27
goog.require('goog.dom.TagName');
28
goog.require('goog.string');
29
30
31
32
/**
33
* A struct for holding context about saved selections.
34
* This can be used to preserve the selection and restore while the DOM is
35
* manipulated, or through an asynchronous call. Use goog.dom.Range factory
36
* methods to obtain an {@see goog.dom.AbstractRange} instance, and use
37
* {@see goog.dom.AbstractRange#saveUsingCarets} to obtain a SavedCaretRange.
38
* For editor ranges under content-editable elements or design-mode iframes,
39
* prefer using {@see goog.editor.range.saveUsingNormalizedCarets}.
40
* @param {goog.dom.AbstractRange} range The range being saved.
41
* @constructor
42
* @extends {goog.dom.SavedRange}
43
*/
44
goog.dom.SavedCaretRange = function(range) {
45
goog.dom.SavedRange.call(this);
46
47
/**
48
* The DOM id of the caret at the start of the range.
49
* @type {string}
50
* @private
51
*/
52
this.startCaretId_ = goog.string.createUniqueString();
53
54
/**
55
* The DOM id of the caret at the end of the range.
56
* @type {string}
57
* @private
58
*/
59
this.endCaretId_ = goog.string.createUniqueString();
60
61
/**
62
* Whether the range is reversed (anchor at the end).
63
* @private {boolean}
64
*/
65
this.reversed_ = range.isReversed();
66
67
/**
68
* A DOM helper for storing the current document context.
69
* @type {goog.dom.DomHelper}
70
* @private
71
*/
72
this.dom_ = goog.dom.getDomHelper(range.getDocument());
73
74
range.surroundWithNodes(this.createCaret_(true), this.createCaret_(false));
75
};
76
goog.inherits(goog.dom.SavedCaretRange, goog.dom.SavedRange);
77
78
79
/**
80
* Gets the range that this SavedCaretRage represents, without selecting it
81
* or removing the carets from the DOM.
82
* @return {goog.dom.AbstractRange?} An abstract range.
83
* @suppress {missingRequire} circular dependency
84
*/
85
goog.dom.SavedCaretRange.prototype.toAbstractRange = function() {
86
var range = null;
87
var startCaret = this.getCaret(true);
88
var endCaret = this.getCaret(false);
89
if (startCaret && endCaret) {
90
range = goog.dom.Range.createFromNodes(startCaret, 0, endCaret, 0);
91
}
92
return range;
93
};
94
95
96
/**
97
* Gets carets.
98
* @param {boolean} start If true, returns the start caret. Otherwise, get the
99
* end caret.
100
* @return {Element} The start or end caret in the given document.
101
*/
102
goog.dom.SavedCaretRange.prototype.getCaret = function(start) {
103
return this.dom_.getElement(start ? this.startCaretId_ : this.endCaretId_);
104
};
105
106
107
/**
108
* Removes the carets from the current restoration document.
109
* @param {goog.dom.AbstractRange=} opt_range A range whose offsets have already
110
* been adjusted for caret removal; it will be adjusted if it is also
111
* affected by post-removal operations, such as text node normalization.
112
* @return {goog.dom.AbstractRange|undefined} The adjusted range, if opt_range
113
* was provided.
114
*/
115
goog.dom.SavedCaretRange.prototype.removeCarets = function(opt_range) {
116
goog.dom.removeNode(this.getCaret(true));
117
goog.dom.removeNode(this.getCaret(false));
118
return opt_range;
119
};
120
121
122
/**
123
* Sets the document where the range will be restored.
124
* @param {!Document} doc An HTML document.
125
*/
126
goog.dom.SavedCaretRange.prototype.setRestorationDocument = function(doc) {
127
this.dom_.setDocument(doc);
128
};
129
130
131
/**
132
* Reconstruct the selection from the given saved range. Removes carets after
133
* restoring the selection. If restore does not dispose this saved range, it may
134
* only be restored a second time if innerHTML or some other mechanism is used
135
* to restore the carets to the dom.
136
* @return {goog.dom.AbstractRange?} Restored selection.
137
* @override
138
* @protected
139
*/
140
goog.dom.SavedCaretRange.prototype.restoreInternal = function() {
141
var range = null;
142
var anchorCaret = this.getCaret(!this.reversed_);
143
var focusCaret = this.getCaret(this.reversed_);
144
if (anchorCaret && focusCaret) {
145
var anchorNode = anchorCaret.parentNode;
146
var anchorOffset = goog.array.indexOf(anchorNode.childNodes, anchorCaret);
147
var focusNode = focusCaret.parentNode;
148
var focusOffset = goog.array.indexOf(focusNode.childNodes, focusCaret);
149
if (focusNode == anchorNode) {
150
// Compensate for the start caret being removed.
151
if (this.reversed_) {
152
anchorOffset--;
153
} else {
154
focusOffset--;
155
}
156
}
157
/** @suppress {missingRequire} circular dependency */
158
range = goog.dom.Range.createFromNodes(
159
anchorNode, anchorOffset, focusNode, focusOffset);
160
range = this.removeCarets(range);
161
range.select();
162
} else {
163
// If only one caret was found, remove it.
164
this.removeCarets();
165
}
166
return range;
167
};
168
169
170
/**
171
* Dispose the saved range and remove the carets from the DOM.
172
* @override
173
* @protected
174
*/
175
goog.dom.SavedCaretRange.prototype.disposeInternal = function() {
176
this.removeCarets();
177
this.dom_ = null;
178
};
179
180
181
/**
182
* Creates a caret element.
183
* @param {boolean} start If true, creates the start caret. Otherwise,
184
* creates the end caret.
185
* @return {!Element} The new caret element.
186
* @private
187
*/
188
goog.dom.SavedCaretRange.prototype.createCaret_ = function(start) {
189
return this.dom_.createDom(
190
goog.dom.TagName.SPAN,
191
{'id': start ? this.startCaretId_ : this.endCaretId_});
192
};
193
194
195
/**
196
* A regex that will match all saved range carets in a string.
197
* @type {RegExp}
198
*/
199
goog.dom.SavedCaretRange.CARET_REGEX = /<span\s+id="?goog_\d+"?><\/span>/ig;
200
201
202
/**
203
* Returns whether two strings of html are equal, ignoring any saved carets.
204
* Thus two strings of html whose only difference is the id of their saved
205
* carets will be considered equal, since they represent html with the
206
* same selection.
207
* @param {string} str1 The first string.
208
* @param {string} str2 The second string.
209
* @return {boolean} Whether two strings of html are equal, ignoring any
210
* saved carets.
211
*/
212
goog.dom.SavedCaretRange.htmlEqual = function(str1, str2) {
213
return str1 == str2 ||
214
str1.replace(goog.dom.SavedCaretRange.CARET_REGEX, '') ==
215
str2.replace(goog.dom.SavedCaretRange.CARET_REGEX, '');
216
};
217
218