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