1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.dom;
16
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import org.htmlunit.html.HtmlPage;
21 import org.htmlunit.html.impl.SimpleRange;
22 import org.htmlunit.javascript.HtmlUnitScriptable;
23 import org.htmlunit.javascript.JavaScriptEngine;
24 import org.htmlunit.javascript.configuration.JsxClass;
25 import org.htmlunit.javascript.configuration.JsxConstructor;
26 import org.htmlunit.javascript.configuration.JsxFunction;
27 import org.htmlunit.javascript.configuration.JsxGetter;
28
29
30
31
32
33
34
35
36
37
38
39 @JsxClass
40 public class Selection extends HtmlUnitScriptable {
41 private static final String TYPE_NONE = "None";
42 private static final String TYPE_CARET = "Caret";
43 private static final String TYPE_RANGE = "Range";
44
45 private String type_ = TYPE_NONE;
46
47
48
49
50 @JsxConstructor
51 public void jsConstructor() {
52
53 }
54
55
56
57
58
59 @JsxFunction(functionName = "toString")
60 public String jsToString() {
61 final StringBuilder sb = new StringBuilder();
62 for (final SimpleRange r : getRanges()) {
63 sb.append(r.toString());
64 }
65 return sb.toString();
66 }
67
68
69
70
71 @Override
72 public Object getDefaultValue(final Class<?> hint) {
73 if (getPrototype() != null && (String.class.equals(hint) || hint == null)) {
74 return jsToString();
75 }
76 return super.getDefaultValue(hint);
77 }
78
79
80
81
82
83 @JsxGetter
84 public Node getAnchorNode() {
85 final SimpleRange last = getLastRange();
86 if (last == null) {
87 return null;
88 }
89 return (Node) getScriptableNullSafe(last.getStartContainer());
90 }
91
92
93
94
95
96 @JsxGetter
97 public int getAnchorOffset() {
98 final SimpleRange last = getLastRange();
99 if (last == null) {
100 return 0;
101 }
102 return last.getStartOffset();
103 }
104
105
106
107
108
109 @JsxGetter
110 public Node getFocusNode() {
111 final SimpleRange last = getLastRange();
112 if (last == null) {
113 return null;
114 }
115 return (Node) getScriptableNullSafe(last.getEndContainer());
116 }
117
118
119
120
121
122 @JsxGetter
123 public int getFocusOffset() {
124 final SimpleRange last = getLastRange();
125 if (last == null) {
126 return 0;
127 }
128 return last.getEndOffset();
129 }
130
131
132
133
134
135 @JsxGetter
136 public boolean isIsCollapsed() {
137 final List<SimpleRange> ranges = getRanges();
138 return ranges.isEmpty() || (ranges.size() == 1 && ranges.get(0).isCollapsed());
139 }
140
141
142
143
144
145 @JsxGetter
146 public int getRangeCount() {
147 return getRanges().size();
148 }
149
150
151
152
153
154 @JsxGetter
155 public String getType() {
156 return type_;
157 }
158
159
160
161
162
163 @JsxFunction
164 public void addRange(final Range range) {
165 final SimpleRange rg = range.getSimpleRange();
166 getRanges().add(rg);
167
168 if (TYPE_CARET.equals(type_) && rg.isCollapsed()) {
169 return;
170 }
171 type_ = TYPE_RANGE;
172 }
173
174
175
176
177
178 @JsxFunction
179 public void removeRange(final Range range) {
180 getRanges().remove(range.getSimpleRange());
181
182 if (getRangeCount() < 1) {
183 type_ = TYPE_NONE;
184 }
185 }
186
187
188
189
190 @JsxFunction
191 public void removeAllRanges() {
192 getRanges().clear();
193
194 type_ = TYPE_NONE;
195 }
196
197
198
199
200
201
202
203 @JsxFunction
204 public Range getRangeAt(final int index) {
205 final List<SimpleRange> ranges = getRanges();
206 if (index < 0 || index >= ranges.size()) {
207 throw JavaScriptEngine.asJavaScriptException(
208 getWindow(), "Invalid range index: " + index, DOMException.INDEX_SIZE_ERR);
209 }
210 final SimpleRange range = ranges.get(index);
211 final Range jsRange = new Range(range);
212 jsRange.setParentScope(getWindow());
213 jsRange.setPrototype(getPrototype(Range.class));
214
215 return jsRange;
216 }
217
218
219
220
221
222
223 @JsxFunction
224 public void collapse(final Node parentNode, final int offset) {
225 final List<SimpleRange> ranges = getRanges();
226 ranges.clear();
227 ranges.add(new SimpleRange(parentNode.getDomNodeOrDie(), offset));
228
229 type_ = TYPE_CARET;
230 }
231
232
233
234
235 @JsxFunction
236 public void collapseToEnd() {
237 final SimpleRange last = getLastRange();
238 if (last != null) {
239 final List<SimpleRange> ranges = getRanges();
240 ranges.clear();
241 ranges.add(last);
242 last.collapse(false);
243 }
244
245 type_ = TYPE_CARET;
246 }
247
248
249
250
251 @JsxFunction
252 public void collapseToStart() {
253 final SimpleRange first = getFirstRange();
254 if (first != null) {
255 final List<SimpleRange> ranges = getRanges();
256 ranges.clear();
257 ranges.add(first);
258 first.collapse(true);
259 }
260
261 type_ = TYPE_CARET;
262 }
263
264
265
266
267 @JsxFunction
268 public void empty() {
269 removeAllRanges();
270 }
271
272
273
274
275
276
277 @JsxFunction
278 public void extend(final Node parentNode, final int offset) {
279 final SimpleRange last = getLastRange();
280 if (last != null) {
281 last.setEnd(parentNode.getDomNodeOrDie(), offset);
282
283 type_ = TYPE_RANGE;
284 }
285 }
286
287
288
289
290
291 @JsxFunction
292 public void selectAllChildren(final Node parentNode) {
293 final List<SimpleRange> ranges = getRanges();
294 ranges.clear();
295 final SimpleRange simpleRange = new SimpleRange(parentNode.getDomNodeOrDie());
296 ranges.add(simpleRange);
297
298 if (simpleRange.isCollapsed()) {
299 type_ = TYPE_CARET;
300 }
301 else {
302 type_ = TYPE_RANGE;
303 }
304 }
305
306
307
308
309
310 private List<SimpleRange> getRanges() {
311 final HtmlPage page = (HtmlPage) getWindow().getDomNodeOrDie();
312 return page.getSelectionRanges();
313 }
314
315
316
317
318
319 private SimpleRange getFirstRange() {
320
321 final List<SimpleRange> ranges = new ArrayList<>(getRanges());
322
323 SimpleRange first = null;
324 for (final SimpleRange range : ranges) {
325 if (first == null) {
326 first = range;
327 }
328 else {
329 final org.w3c.dom.Node firstStart = first.getStartContainer();
330 final org.w3c.dom.Node rangeStart = range.getStartContainer();
331 if ((firstStart.compareDocumentPosition(rangeStart) & Node.DOCUMENT_POSITION_PRECEDING) != 0) {
332 first = range;
333 }
334 }
335 }
336 return first;
337 }
338
339
340
341
342
343 private SimpleRange getLastRange() {
344
345 final List<SimpleRange> ranges = new ArrayList<>(getRanges());
346
347 SimpleRange last = null;
348 for (final SimpleRange range : ranges) {
349 if (last == null) {
350 last = range;
351 }
352 else {
353 final org.w3c.dom.Node lastStart = last.getStartContainer();
354 final org.w3c.dom.Node rangeStart = range.getStartContainer();
355 if ((lastStart.compareDocumentPosition(rangeStart) & Node.DOCUMENT_POSITION_FOLLOWING) != 0) {
356 last = range;
357 }
358 }
359 }
360 return last;
361 }
362
363
364
365
366
367
368
369 private HtmlUnitScriptable getScriptableNullSafe(final Object object) {
370 final HtmlUnitScriptable scriptable;
371 if (object != null) {
372 scriptable = getScriptableFor(object);
373 }
374 else {
375 scriptable = null;
376 }
377 return scriptable;
378 }
379 }