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