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;
16
17 import org.htmlunit.corejs.javascript.Scriptable;
18 import org.htmlunit.html.DomElement;
19 import org.htmlunit.html.DomNode;
20 import org.htmlunit.javascript.HtmlUnitScriptable;
21 import org.htmlunit.javascript.JavaScriptEngine;
22 import org.htmlunit.javascript.configuration.JsxClass;
23 import org.htmlunit.javascript.configuration.JsxConstructor;
24 import org.htmlunit.javascript.configuration.JsxFunction;
25 import org.htmlunit.javascript.configuration.JsxGetter;
26 import org.htmlunit.javascript.configuration.JsxSymbol;
27 import org.htmlunit.javascript.host.dom.Attr;
28 import org.htmlunit.javascript.host.dom.Node;
29
30 /**
31 * A collection of nodes that can be accessed by name. String comparisons in this class are case-insensitive when
32 * used with an {@link org.htmlunit.html.HtmlElement},
33 * but case-sensitive when used with a {@link org.htmlunit.html.DomElement}.
34 *
35 * @author Daniel Gredler
36 * @author Ahmed Ashour
37 * @author Marc Guillemot
38 * @author Ronald Brill
39 * @author Frank Danek
40 *
41 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap">MDN Documentation</a>
42 */
43 @JsxClass
44 public class NamedNodeMap extends HtmlUnitScriptable {
45
46 private final org.w3c.dom.NamedNodeMap attributes_;
47
48 /**
49 * Default constructor for prototype instantiation.
50 */
51 public NamedNodeMap() {
52 super();
53 attributes_ = null;
54 }
55
56 /**
57 * Creates an instance of this object.
58 */
59 @JsxConstructor
60 public void jsConstructor() {
61 // nothing to do
62 }
63
64 /**
65 * Creates a new named node map for the specified element.
66 *
67 * @param element the owning element
68 */
69 public NamedNodeMap(final DomElement element) {
70 super();
71 setParentScope(element.getScriptableObject().getParentScope());
72 setPrototype(getPrototype(getClass()));
73
74 attributes_ = element.getAttributes();
75 setDomNode(element, false);
76 }
77
78 /**
79 * {@inheritDoc}
80 */
81 @Override
82 public final Object get(final int index, final Scriptable start) {
83 final NamedNodeMap startMap = (NamedNodeMap) start;
84 final Object response = startMap.item(index);
85 if (response != null) {
86 return response;
87 }
88 return NOT_FOUND;
89 }
90
91 /**
92 * {@inheritDoc}
93 */
94 @Override
95 public Object get(final String name, final Scriptable start) {
96 Object response = super.get(name, start);
97 if (response != NOT_FOUND) {
98 return response;
99 }
100
101 response = getNamedItem(name);
102 if (response != null) {
103 return response;
104 }
105
106 return NOT_FOUND;
107 }
108
109 /**
110 * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
111 *
112 * Gets the specified attribute without handling the synthetic class attribute.
113 *
114 * @param name the attribute name
115 * @return the attribute node, or {@code null} if not found
116 * @see #getNamedItem(String)
117 */
118 public HtmlUnitScriptable getNamedItemWithoutSytheticClassAttr(final String name) {
119 if (attributes_ != null) {
120 final DomNode attr = (DomNode) attributes_.getNamedItem(name);
121 if (attr != null) {
122 return attr.getScriptableObject();
123 }
124 }
125
126 return null;
127 }
128
129 /**
130 * Returns the attribute node with the specified name.
131 *
132 * @param name the attribute name
133 * @return the attribute node, or {@code null} if not defined
134 */
135 @JsxFunction
136 public HtmlUnitScriptable getNamedItem(final String name) {
137 return getNamedItemWithoutSytheticClassAttr(name);
138 }
139
140 /**
141 * Returns the attribute node with the given namespace URI and local name.
142 *
143 * @param namespaceURI the namespace URI of the node to retrieve
144 * @param localName the local name of the node to retrieve
145 * @return the attribute node, or {@code null} if not found
146 */
147 @JsxFunction
148 public Node getNamedItemNS(final String namespaceURI, final String localName) {
149 if (attributes_ != null) {
150 final DomNode attr = (DomNode) attributes_.getNamedItemNS(namespaceURI, localName);
151 if (attr != null) {
152 return attr.getScriptableObject();
153 }
154 }
155
156 return null;
157 }
158
159 /**
160 * Sets the specified attribute node.
161 *
162 * @param node the attribute node to set
163 */
164 @JsxFunction
165 public void setNamedItem(final Node node) {
166 attributes_.setNamedItem(node.getDomNodeOrDie());
167 }
168
169 /**
170 * Sets the specified attribute node using its namespace URI and local name.
171 *
172 * @param node the attribute node to set
173 */
174 @JsxFunction
175 public void setNamedItemNS(final Node node) {
176 attributes_.setNamedItemNS(node.getDomNodeOrDie());
177 }
178
179 /**
180 * Removes the attribute with the specified name.
181 *
182 * @param name the name of the attribute to remove
183 */
184 @JsxFunction
185 public void removeNamedItem(final String name) {
186 attributes_.removeNamedItem(name);
187 }
188
189 /**
190 * Removes the attribute with the given namespace URI and local name.
191 *
192 * @param namespaceURI the namespace URI of the attribute to remove
193 * @param localName the local name of the attribute to remove
194 * @return the removed attribute node, or {@code null} if not found
195 */
196 @JsxFunction
197 public Attr removeNamedItemNS(final String namespaceURI, final String localName) {
198 return (Attr) attributes_.removeNamedItemNS(namespaceURI, localName);
199 }
200
201 /**
202 * Returns the attribute node at the specified index.
203 *
204 * @param index the index
205 * @return the attribute node at the given index, or {@code null} if out of range
206 */
207 @JsxFunction
208 public HtmlUnitScriptable item(final int index) {
209 final DomNode attr = (DomNode) attributes_.item(index);
210 if (attr != null) {
211 return attr.getScriptableObject();
212 }
213 return null;
214 }
215
216 /**
217 * Returns the number of attributes in this map.
218 *
219 * @return the number of attributes
220 */
221 @JsxGetter
222 public int getLength() {
223 return attributes_.getLength();
224 }
225
226 /**
227 * {@inheritDoc}
228 */
229 @Override
230 public boolean has(final int index, final Scriptable start) {
231 return index >= 0 && index < getLength();
232 }
233
234 /**
235 * Returns an iterator over the values in this map.
236 *
237 * @return the iterator
238 */
239 @JsxSymbol
240 public Scriptable iterator() {
241 return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this);
242 }
243 }