1 /*
2 * Copyright (c) 2002-2026 Gargoyle Software Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 * https://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
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.Context;
21 import org.htmlunit.corejs.javascript.Scriptable;
22 import org.htmlunit.corejs.javascript.ScriptableObject;
23 import org.htmlunit.html.DomElement;
24 import org.htmlunit.html.DomNode;
25 import org.htmlunit.javascript.JavaScriptEngine;
26 import org.htmlunit.javascript.configuration.JsxClass;
27 import org.htmlunit.javascript.configuration.JsxConstructor;
28 import org.htmlunit.javascript.configuration.JsxFunction;
29 import org.htmlunit.javascript.configuration.JsxSymbol;
30 import org.htmlunit.javascript.host.dom.RadioNodeList;
31
32 /**
33 * A JavaScript object for {@code HTMLFormControlsCollection}.
34 *
35 * @author Ahmed Ashour
36 * @author Ronald Brill
37 * @author Lai Quang Duong
38 *
39 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormControlsCollection">MDN Documentation</a>
40 */
41 @JsxClass
42 public class HTMLFormControlsCollection extends HTMLCollection {
43
44 /**
45 * Creates an instance.
46 */
47 public HTMLFormControlsCollection() {
48 super();
49 }
50
51 /**
52 * Creates an instance.
53 * @param domNode the parent scope
54 * @param attributeChangeSensitive indicates if the content of the collection may change when an attribute
55 * of a descendant node of domNode changes (attribute added, modified or removed)
56 */
57 public HTMLFormControlsCollection(final DomNode domNode, final boolean attributeChangeSensitive) {
58 super(domNode, attributeChangeSensitive);
59 }
60
61 /**
62 * Constructs an instance with an initial cache value.
63 * @param domNode the parent scope, on which we listen for changes
64 * @param initialElements the initial content for the cache
65 */
66 HTMLFormControlsCollection(final DomNode domNode, final List<DomNode> initialElements) {
67 super(domNode, initialElements);
68 }
69
70 /**
71 * JavaScript constructor.
72 */
73 @Override
74 @JsxConstructor
75 public void jsConstructor() {
76 super.jsConstructor();
77 }
78
79 /**
80 * Returns the element whose ID or name matches the specified value, from the collection.
81 * If there are multiple matching elements, then a RadioNodeList object containing all those elements is returned.
82 * @param name the name or id of the element or elements to return
83 * @return the element or elements corresponding to the specified name or id
84 * @see <a href="https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#the-htmlformcontrolscollection-interface">HTML Standard</a>
85 */
86 @Override
87 @JsxFunction
88 public Scriptable namedItem(final String name) {
89 if (name.isEmpty()) {
90 return null;
91 }
92
93 final List<DomNode> elements = new ArrayList<>();
94 for (final Object next : getElements()) {
95 if (next instanceof DomElement elem) {
96 final String nodeName = elem.getAttributeDirect(DomElement.NAME_ATTRIBUTE);
97 if (name.equals(nodeName)) {
98 elements.add(elem);
99 continue;
100 }
101
102 final String id = elem.getId();
103 if (name.equals(id)) {
104 elements.add(elem);
105 }
106 }
107 }
108
109 if (elements.isEmpty()) {
110 return null;
111 }
112 if (elements.size() == 1) {
113 return getScriptableForElement(elements.get(0));
114 }
115
116 final RadioNodeList nodeList = new RadioNodeList(getDomNodeOrDie(), elements);
117 nodeList.setElementsSupplier(getElementSupplier());
118 return nodeList;
119 }
120
121 /**
122 * Overridden to allow the retrieval of certain form elements by ID or name.
123 *
124 * @param cx {@inheritDoc}
125 * @param id {@inheritDoc}
126 * @return {@inheritDoc}
127 */
128 @Override
129 protected DescriptorInfo getOwnPropertyDescriptor(final Context cx, final Object id) {
130 final DescriptorInfo descInfo = super.getOwnPropertyDescriptor(cx, id);
131 if (descInfo != null) {
132 return descInfo;
133 }
134
135 if (id instanceof CharSequence) {
136 final Scriptable element = namedItem(id.toString());
137 if (element != null) {
138 return ScriptableObject.buildDataDescriptor(element, ScriptableObject.READONLY);
139 }
140 }
141
142 return null;
143 }
144
145 /**
146 * {@inheritDoc}
147 */
148 @JsxSymbol
149 @Override
150 public Scriptable iterator() {
151 return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this);
152 }
153 }