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