1 /*
2 * Copyright (c) 2002-2025 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 * @see <a href="http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1780488922">DOM Level 2 Core Spec</a>
41 * @see <a href="http://msdn2.microsoft.com/en-us/library/ms763824.aspx">IXMLDOMNamedNodeMap</a>
42 */
43 @JsxClass
44 public class NamedNodeMap extends HtmlUnitScriptable {
45
46 private final org.w3c.dom.NamedNodeMap attributes_;
47
48 /**
49 * We need default constructors to build the prototype instance.
50 */
51 public NamedNodeMap() {
52 super();
53 attributes_ = null;
54 }
55
56 /**
57 * JavaScript constructor.
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());
72 setPrototype(getPrototype(getClass()));
73
74 attributes_ = element.getAttributes();
75 setDomNode(element, false);
76 }
77
78 /**
79 * Returns the element at the specified index, or {@link #NOT_FOUND} if the index is invalid.
80 * <p>
81 * {@inheritDoc}
82 */
83 @Override
84 public final Object get(final int index, final Scriptable start) {
85 final NamedNodeMap startMap = (NamedNodeMap) start;
86 final Object response = startMap.item(index);
87 if (response != null) {
88 return response;
89 }
90 return NOT_FOUND;
91 }
92
93 /**
94 * {@inheritDoc}
95 */
96 @Override
97 public Object get(final String name, final Scriptable start) {
98 Object response = super.get(name, start);
99 if (response != NOT_FOUND) {
100 return response;
101 }
102
103 response = getNamedItem(name);
104 if (response != null) {
105 return response;
106 }
107
108 return NOT_FOUND;
109 }
110
111 /**
112 * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
113 *
114 * Gets the specified attribute but does not handle the synthetic class attribute for IE.
115 * @see #getNamedItem(String)
116 *
117 * @param name attribute name
118 * @return the attribute node, {@code null} if the attribute is not defined
119 */
120 public HtmlUnitScriptable getNamedItemWithoutSytheticClassAttr(final String name) {
121 if (attributes_ != null) {
122 final DomNode attr = (DomNode) attributes_.getNamedItem(name);
123 if (attr != null) {
124 return attr.getScriptableObject();
125 }
126 }
127
128 return null;
129 }
130
131 /**
132 * Gets the specified attribute.
133 * @param name attribute name
134 * @return the attribute node, {@code null} if the attribute is not defined
135 */
136 @JsxFunction
137 public HtmlUnitScriptable getNamedItem(final String name) {
138 return getNamedItemWithoutSytheticClassAttr(name);
139 }
140
141 /**
142 * Gets the specified attribute.
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, {@code null} if the attribute is not defined
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.
161 * @param node the attribute
162 */
163 @JsxFunction
164 public void setNamedItem(final Node node) {
165 attributes_.setNamedItem(node.getDomNodeOrDie());
166 }
167
168 /**
169 * Sets the specified attribute.
170 * @param node the attribute
171 */
172 @JsxFunction
173 public void setNamedItemNS(final Node node) {
174 attributes_.setNamedItemNS(node.getDomNodeOrDie());
175 }
176
177 /**
178 * Removes the specified attribute.
179 * @param name the name of the item to remove
180 */
181 @JsxFunction
182 public void removeNamedItem(final String name) {
183 attributes_.removeNamedItem(name);
184 }
185
186 /**
187 * Removes the specified attribute.
188 * @param namespaceURI the namespace URI of the node to retrieve.
189 * @param localName the local name of the node to retrieve.
190 * @return the attribute node, {@code null} if the attribute is not defined
191 */
192 @JsxFunction
193 public Attr removeNamedItemNS(final String namespaceURI, final String localName) {
194 return (Attr) attributes_.removeNamedItemNS(namespaceURI, localName);
195 }
196
197 /**
198 * Returns the item at the specified index.
199 * @param index the index
200 * @return the item at the specified index
201 */
202 @JsxFunction
203 public HtmlUnitScriptable item(final int index) {
204 final DomNode attr = (DomNode) attributes_.item(index);
205 if (attr != null) {
206 return attr.getScriptableObject();
207 }
208 return null;
209 }
210
211 /**
212 * Returns the number of attributes in this named node map.
213 * @return the number of attributes in this named node map
214 */
215 @JsxGetter
216 public int getLength() {
217 return attributes_.getLength();
218 }
219
220 /**
221 * {@inheritDoc}
222 */
223 @Override
224 public boolean has(final int index, final Scriptable start) {
225 return index >= 0 && index < getLength();
226 }
227
228 /**
229 * @return the Iterator symbol
230 */
231 @JsxSymbol
232 public Scriptable iterator() {
233 return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this);
234 }
235 }