Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/dom/selection.js
4111 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for working with selections in input boxes and text
9
* areas.
10
*
11
* @see ../demos/dom_selection.html
12
*/
13
14
15
goog.provide('goog.dom.selection');
16
17
goog.require('goog.dom.InputType');
18
goog.require('goog.string');
19
20
21
/**
22
* Sets the place where the selection should start inside a textarea or a text
23
* input
24
* @param {Element} textfield A textarea or text input.
25
* @param {number} pos The position to set the start of the selection at.
26
*/
27
goog.dom.selection.setStart = function(textfield, pos) {
28
'use strict';
29
if (goog.dom.selection.useSelectionProperties_(textfield)) {
30
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
31
textfield.selectionStart = pos;
32
}
33
};
34
35
36
/**
37
* Return the place where the selection starts inside a textarea or a text
38
* input
39
* @param {Element} textfield A textarea or text input.
40
* @return {number} The position where the selection starts or 0 if it was
41
* unable to find the position or no selection exists. Note that we can't
42
* reliably tell the difference between an element that has no selection and
43
* one where it starts at 0.
44
*/
45
goog.dom.selection.getStart = function(textfield) {
46
'use strict';
47
return goog.dom.selection.getEndPoints_(textfield, true)[0];
48
};
49
50
51
/**
52
* Returns the start and end points of the selection within a textarea in IE.
53
* IE treats newline characters as \r\n characters, and we need to check for
54
* these characters at the edge of our selection, to ensure that we return the
55
* right cursor position.
56
* @param {TextRange} range Complete range object, e.g., "Hello\r\n".
57
* @param {TextRange} selRange Selected range object.
58
* @param {boolean} getOnlyStart Value indicating if only start
59
* cursor position is to be returned. In IE, obtaining the end position
60
* involves extra work, hence we have this parameter for calls which need
61
* only start position.
62
* @return {!Array<number>} An array with the start and end positions where the
63
* selection starts and ends or [0,0] if it was unable to find the
64
* positions or no selection exists. Note that we can't reliably tell the
65
* difference between an element that has no selection and one where
66
* it starts and ends at 0. If getOnlyStart was true, we return
67
* -1 as end offset.
68
* @private
69
*/
70
goog.dom.selection.getEndPointsTextareaIe_ = function(
71
range, selRange, getOnlyStart) {
72
'use strict';
73
// Create a duplicate of the selected range object to perform our actions
74
// against. Example of selectionRange = "" (assuming that the cursor is
75
// just after the \r\n combination)
76
var selectionRange = selRange.duplicate();
77
78
// Text before the selection start, e.g.,"Hello" (notice how range.text
79
// excludes the \r\n sequence)
80
var beforeSelectionText = range.text;
81
// Text before the selection start, e.g., "Hello" (this will later include
82
// the \r\n sequences also)
83
var untrimmedBeforeSelectionText = beforeSelectionText;
84
// Text within the selection , e.g. "" assuming that the cursor is just after
85
// the \r\n combination.
86
var selectionText = selectionRange.text;
87
// Text within the selection, e.g., "" (this will later include the \r\n
88
// sequences also)
89
var untrimmedSelectionText = selectionText;
90
91
// Boolean indicating whether we are done dealing with the text before the
92
// selection's beginning.
93
var isRangeEndTrimmed = false;
94
// Go over the range until it becomes a 0-lengthed range or until the range
95
// text starts changing when we move the end back by one character.
96
// If after moving the end back by one character, the text remains the same,
97
// then we need to add a "\r\n" at the end to get the actual text.
98
while (!isRangeEndTrimmed) {
99
if (range.compareEndPoints('StartToEnd', range) == 0) {
100
isRangeEndTrimmed = true;
101
} else {
102
range.moveEnd('character', -1);
103
if (range.text == beforeSelectionText) {
104
// If the start position of the cursor was after a \r\n string,
105
// we would skip over it in one go with the moveEnd call, but
106
// range.text will still show "Hello" (because of the IE range.text
107
// bug) - this implies that we should add a \r\n to our
108
// untrimmedBeforeSelectionText string.
109
untrimmedBeforeSelectionText += '\r\n';
110
} else {
111
isRangeEndTrimmed = true;
112
}
113
}
114
}
115
116
if (getOnlyStart) {
117
// We return -1 as end, since the caller is only interested in the start
118
// value.
119
return [untrimmedBeforeSelectionText.length, -1];
120
}
121
// Boolean indicating whether we are done dealing with the text inside the
122
// selection.
123
var isSelectionRangeEndTrimmed = false;
124
// Go over the selected range until it becomes a 0-lengthed range or until
125
// the range text starts changing when we move the end back by one character.
126
// If after moving the end back by one character, the text remains the same,
127
// then we need to add a "\r\n" at the end to get the actual text.
128
while (!isSelectionRangeEndTrimmed) {
129
if (selectionRange.compareEndPoints('StartToEnd', selectionRange) == 0) {
130
isSelectionRangeEndTrimmed = true;
131
} else {
132
selectionRange.moveEnd('character', -1);
133
if (selectionRange.text == selectionText) {
134
// If the selection was not empty, and the end point of the selection
135
// was just after a \r\n, we would have skipped it in one go with the
136
// moveEnd call, and this implies that we should add a \r\n to the
137
// untrimmedSelectionText string.
138
untrimmedSelectionText += '\r\n';
139
} else {
140
isSelectionRangeEndTrimmed = true;
141
}
142
}
143
}
144
return [
145
untrimmedBeforeSelectionText.length,
146
untrimmedBeforeSelectionText.length + untrimmedSelectionText.length
147
];
148
};
149
150
151
/**
152
* Returns the start and end points of the selection inside a textarea or a
153
* text input.
154
* @param {Element} textfield A textarea or text input.
155
* @return {!Array<number>} An array with the start and end positions where the
156
* selection starts and ends or [0,0] if it was unable to find the
157
* positions or no selection exists. Note that we can't reliably tell the
158
* difference between an element that has no selection and one where
159
* it starts and ends at 0.
160
*/
161
goog.dom.selection.getEndPoints = function(textfield) {
162
'use strict';
163
return goog.dom.selection.getEndPoints_(textfield, false);
164
};
165
166
167
/**
168
* Returns the start and end points of the selection inside a textarea or a
169
* text input.
170
* @param {Element} textfield A textarea or text input.
171
* @param {boolean} getOnlyStart Value indicating if only start
172
* cursor position is to be returned. In IE, obtaining the end position
173
* involves extra work, hence we have this parameter. In FF, there is not
174
* much extra effort involved.
175
* @return {!Array<number>} An array with the start and end positions where the
176
* selection starts and ends or [0,0] if it was unable to find the
177
* positions or no selection exists. Note that we can't reliably tell the
178
* difference between an element that has no selection and one where
179
* it starts and ends at 0. If getOnlyStart was true, we return
180
* -1 as end offset.
181
* @private
182
*/
183
goog.dom.selection.getEndPoints_ = function(textfield, getOnlyStart) {
184
'use strict';
185
textfield = /** @type {!HTMLInputElement|!HTMLTextAreaElement} */ (textfield);
186
var startPos = 0;
187
var endPos = 0;
188
if (goog.dom.selection.useSelectionProperties_(textfield)) {
189
startPos = textfield.selectionStart;
190
endPos = getOnlyStart ? -1 : textfield.selectionEnd;
191
}
192
return [startPos, endPos];
193
};
194
195
196
/**
197
* Sets the place where the selection should end inside a text area or a text
198
* input
199
* @param {Element} textfield A textarea or text input.
200
* @param {number} pos The position to end the selection at.
201
*/
202
goog.dom.selection.setEnd = function(textfield, pos) {
203
'use strict';
204
if (goog.dom.selection.useSelectionProperties_(textfield)) {
205
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
206
textfield.selectionEnd = pos;
207
}
208
};
209
210
211
/**
212
* Returns the place where the selection ends inside a textarea or a text input
213
* @param {Element} textfield A textarea or text input.
214
* @return {number} The position where the selection ends or 0 if it was
215
* unable to find the position or no selection exists.
216
*/
217
goog.dom.selection.getEnd = function(textfield) {
218
'use strict';
219
return goog.dom.selection.getEndPoints_(textfield, false)[1];
220
};
221
222
223
/**
224
* Sets the cursor position within a textfield.
225
* @param {Element} textfield A textarea or text input.
226
* @param {number} pos The position within the text field.
227
*/
228
goog.dom.selection.setCursorPosition = function(textfield, pos) {
229
'use strict';
230
if (goog.dom.selection.useSelectionProperties_(textfield)) {
231
// Mozilla directly supports this
232
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
233
textfield.selectionStart = pos;
234
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
235
textfield.selectionEnd = pos;
236
}
237
};
238
239
240
/**
241
* Sets the selected text inside a textarea or a text input
242
* @param {Element} textfield A textarea or text input.
243
* @param {string} text The text to change the selection to.
244
*/
245
goog.dom.selection.setText = function(textfield, text) {
246
'use strict';
247
textfield = /** @type {!HTMLInputElement|!HTMLTextAreaElement} */ (textfield);
248
if (goog.dom.selection.useSelectionProperties_(textfield)) {
249
var value = textfield.value;
250
var oldSelectionStart = textfield.selectionStart;
251
var before = value.slice(0, oldSelectionStart);
252
var after = value.slice(textfield.selectionEnd);
253
textfield.value = before + text + after;
254
textfield.selectionStart = oldSelectionStart;
255
textfield.selectionEnd = oldSelectionStart + text.length;
256
} else {
257
throw new Error('Cannot set the selection end');
258
}
259
};
260
261
262
/**
263
* Returns the selected text inside a textarea or a text input
264
* @param {Element} textfield A textarea or text input.
265
* @return {string} The selected text.
266
*/
267
goog.dom.selection.getText = function(textfield) {
268
'use strict';
269
textfield = /** @type {!HTMLInputElement|!HTMLTextAreaElement} */ (textfield);
270
if (goog.dom.selection.useSelectionProperties_(textfield)) {
271
var s = textfield.value;
272
return s.substring(textfield.selectionStart, textfield.selectionEnd);
273
}
274
275
throw new Error('Cannot get the selection text');
276
};
277
278
279
/**
280
* Returns the selected text within a textarea in IE.
281
* IE treats newline characters as \r\n characters, and we need to check for
282
* these characters at the edge of our selection, to ensure that we return the
283
* right string.
284
* @param {TextRange} selRange Selected range object.
285
* @return {string} Selected text in the textarea.
286
* @private
287
*/
288
goog.dom.selection.getSelectionRangeText_ = function(selRange) {
289
'use strict';
290
// Create a duplicate of the selected range object to perform our actions
291
// against. Suppose the text in the textarea is "Hello\r\nWorld" and the
292
// selection encompasses the "o\r\n" bit, initial selectionRange will be "o"
293
// (assuming that the cursor is just after the \r\n combination)
294
var selectionRange = selRange.duplicate();
295
296
// Text within the selection , e.g. "o" assuming that the cursor is just after
297
// the \r\n combination.
298
var selectionText = selectionRange.text;
299
// Text within the selection, e.g., "o" (this will later include the \r\n
300
// sequences also)
301
var untrimmedSelectionText = selectionText;
302
303
// Boolean indicating whether we are done dealing with the text inside the
304
// selection.
305
var isSelectionRangeEndTrimmed = false;
306
// Go over the selected range until it becomes a 0-lengthed range or until
307
// the range text starts changing when we move the end back by one character.
308
// If after moving the end back by one character, the text remains the same,
309
// then we need to add a "\r\n" at the end to get the actual text.
310
while (!isSelectionRangeEndTrimmed) {
311
if (selectionRange.compareEndPoints('StartToEnd', selectionRange) == 0) {
312
isSelectionRangeEndTrimmed = true;
313
} else {
314
selectionRange.moveEnd('character', -1);
315
if (selectionRange.text == selectionText) {
316
// If the selection was not empty, and the end point of the selection
317
// was just after a \r\n, we would have skipped it in one go with the
318
// moveEnd call, and this implies that we should add a \r\n to the
319
// untrimmedSelectionText string.
320
untrimmedSelectionText += '\r\n';
321
} else {
322
isSelectionRangeEndTrimmed = true;
323
}
324
}
325
}
326
return untrimmedSelectionText;
327
};
328
329
330
/**
331
* Helper function for returning the range for an object as well as the
332
* selection range
333
* @private
334
* @param {Element} el The element to get the range for.
335
* @return {!Array<TextRange>} Range of object and selection range in two
336
* element array.
337
*/
338
goog.dom.selection.getRangeIe_ = function(el) {
339
'use strict';
340
var doc = el.ownerDocument || el.document;
341
342
var selectionRange = doc.selection.createRange();
343
// el.createTextRange() doesn't work on textareas
344
var range;
345
346
if (/** @type {?} */ (el).type == goog.dom.InputType.TEXTAREA) {
347
range = doc.body.createTextRange();
348
range.moveToElementText(el);
349
} else {
350
range = el.createTextRange();
351
}
352
353
return [range, selectionRange];
354
};
355
356
357
/**
358
* Helper function for canonicalizing a position inside a textfield in IE.
359
* Deals with the issue that \r\n counts as 2 characters, but
360
* move('character', n) passes over both characters in one move.
361
* @private
362
* @param {Element} textfield The text element.
363
* @param {number} pos The position desired in that element.
364
* @return {number} The canonicalized position that will work properly with
365
* move('character', pos).
366
*/
367
goog.dom.selection.canonicalizePositionIe_ = function(textfield, pos) {
368
'use strict';
369
textfield = /** @type {!HTMLTextAreaElement} */ (textfield);
370
if (textfield.type == goog.dom.InputType.TEXTAREA) {
371
// We do this only for textarea because it is the only one which can
372
// have a \r\n (input cannot have this).
373
var value = textfield.value.substring(0, pos);
374
pos = goog.string.canonicalizeNewlines(value).length;
375
}
376
return pos;
377
};
378
379
380
/**
381
* Helper function to determine whether it's okay to use
382
* selectionStart/selectionEnd.
383
*
384
* @param {Element} el The element to check for.
385
* @return {boolean} Whether it's okay to use the selectionStart and
386
* selectionEnd properties on `el`.
387
* @private
388
*/
389
goog.dom.selection.useSelectionProperties_ = function(el) {
390
'use strict';
391
try {
392
return typeof el.selectionStart == 'number';
393
} catch (e) {
394
// Firefox throws an exception if you try to access selectionStart
395
// on an element with display: none.
396
return false;
397
}
398
};
399
400