1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.html;
16
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import org.htmlunit.corejs.javascript.Callable;
21 import org.htmlunit.corejs.javascript.Context;
22 import org.htmlunit.corejs.javascript.Scriptable;
23 import org.htmlunit.corejs.javascript.VarScope;
24 import org.htmlunit.html.DomElement;
25 import org.htmlunit.html.DomNode;
26 import org.htmlunit.html.HtmlForm;
27 import org.htmlunit.html.HtmlInput;
28 import org.htmlunit.javascript.JavaScriptEngine;
29 import org.htmlunit.javascript.configuration.JsxClass;
30 import org.htmlunit.javascript.configuration.JsxConstructor;
31 import org.htmlunit.javascript.configuration.JsxFunction;
32 import org.htmlunit.javascript.configuration.JsxGetter;
33 import org.htmlunit.javascript.configuration.JsxSymbol;
34 import org.htmlunit.javascript.host.dom.AbstractList;
35
36
37
38
39
40
41
42
43
44 @JsxClass
45 public class HTMLAllCollection extends AbstractList implements Callable {
46
47
48
49
50 public HTMLAllCollection() {
51 super();
52 }
53
54
55
56
57 @JsxConstructor
58 public void jsConstructor() {
59
60 }
61
62
63
64
65
66 public HTMLAllCollection(final DomNode parentScope) {
67 super(parentScope, false, null);
68 }
69
70
71
72
73
74
75
76 @JsxFunction
77 public Object item(final Object index) {
78 final double numb;
79
80 if (index instanceof String name) {
81 final Object result = namedItem(name);
82 if (null != result && !JavaScriptEngine.isUndefined(result)) {
83 return result;
84 }
85 numb = JavaScriptEngine.toNumber(index);
86 if (Double.isNaN(numb)) {
87 return null;
88 }
89 }
90 else {
91 numb = JavaScriptEngine.toNumber(index);
92 }
93
94 if (numb < 0) {
95 return null;
96 }
97
98 if (Double.isInfinite(numb) || numb != Math.floor(numb)) {
99 return null;
100 }
101
102 final Object object = get((int) numb, this);
103 if (object == NOT_FOUND) {
104 return null;
105 }
106 return object;
107 }
108
109
110
111
112
113
114
115
116 @JsxFunction
117 public final Scriptable namedItem(final String name) {
118 final List<DomNode> elements = getElements();
119
120
121 final List<DomElement> matching = new ArrayList<>();
122
123 for (final DomNode next : elements) {
124 if (next instanceof DomElement elem) {
125 if (name.equals(elem.getAttributeDirect(DomElement.NAME_ATTRIBUTE))
126 || name.equals(elem.getId())) {
127 matching.add(elem);
128 }
129 }
130 }
131
132 if (matching.size() == 1) {
133 return getScriptableForElement(matching.get(0));
134 }
135 if (matching.isEmpty()) {
136 return null;
137 }
138
139
140 final DomNode domNode = getDomNodeOrNull();
141 final List<DomNode> nodes = new ArrayList<>(matching);
142 final HTMLCollection collection = new HTMLCollection(domNode, nodes);
143 collection.setAvoidObjectDetection(true);
144 return collection;
145 }
146
147
148
149
150 @Override
151 public Object call(final Context cx, final VarScope scope, final Scriptable thisObj, final Object[] args) {
152 boolean nullIfNotFound = false;
153 if (args[0] instanceof Number number) {
154 final double val = number.doubleValue();
155 if (val != (int) val) {
156 return null;
157 }
158 if (val >= 0) {
159 nullIfNotFound = true;
160 }
161 }
162 else {
163 final String val = JavaScriptEngine.toString(args[0]);
164 try {
165 args[0] = Integer.parseInt(val);
166 }
167 catch (final NumberFormatException ignored) {
168
169 }
170 }
171
172 if (args.length == 0) {
173 throw JavaScriptEngine.reportRuntimeError("Zero arguments; need an index or a key.");
174 }
175 Object value = getIt(args[0]);
176 if (value == NOT_FOUND) {
177 value = null;
178 }
179 if (nullIfNotFound && JavaScriptEngine.isUndefined(value)) {
180 return null;
181 }
182 return value;
183 }
184
185
186
187
188 @Override
189 protected Object equivalentValues(final Object value) {
190 if (value == null || JavaScriptEngine.isUndefined(value)) {
191 return Boolean.TRUE;
192 }
193
194 return super.equivalentValues(value);
195 }
196
197
198
199
200
201 @JsxSymbol
202 public Scriptable iterator() {
203 return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this);
204 }
205
206
207
208
209
210 @JsxGetter
211 @Override
212 public final int getLength() {
213 return super.getLength();
214 }
215
216
217
218
219 @Override
220 protected Object getWithPreemptionByName(final String name, final List<DomNode> elements) {
221 final List<DomNode> matchingElements = new ArrayList<>();
222 final boolean searchName = isGetWithPreemptionSearchName();
223 for (final DomNode next : elements) {
224 if (next instanceof DomElement element
225 && (searchName || next instanceof HtmlInput || next instanceof HtmlForm)) {
226 final String nodeName = element.getAttributeDirect(DomElement.NAME_ATTRIBUTE);
227 if (name.equals(nodeName)) {
228 matchingElements.add(next);
229 }
230 }
231 }
232
233 if (matchingElements.isEmpty()) {
234 return NOT_FOUND;
235 }
236
237 if (matchingElements.size() == 1) {
238 return getScriptableForElement(matchingElements.get(0));
239 }
240
241
242 final DomNode domNode = getDomNodeOrNull();
243 final HTMLCollection collection = new HTMLCollection(domNode, matchingElements);
244 collection.setAvoidObjectDetection(true);
245 return collection;
246 }
247
248
249
250
251
252 protected boolean isGetWithPreemptionSearchName() {
253 return true;
254 }
255
256
257
258
259 @Override
260 protected HTMLCollection create(final DomNode parentScope, final List<DomNode> initialElements) {
261 return new HTMLCollection(parentScope, initialElements);
262 }
263 }