View Javadoc
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 static org.htmlunit.html.DomElement.ATTRIBUTE_NOT_DEFINED;
18  
19  import org.htmlunit.SgmlPage;
20  import org.htmlunit.html.DomElement;
21  import org.htmlunit.html.DomNode;
22  import org.htmlunit.html.DomText;
23  import org.htmlunit.html.HtmlForm;
24  import org.htmlunit.html.HtmlOption;
25  import org.htmlunit.html.HtmlOptionGroup;
26  import org.htmlunit.html.HtmlSelect;
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.JsxGetter;
31  import org.htmlunit.javascript.configuration.JsxSetter;
32  import org.xml.sax.helpers.AttributesImpl;
33  
34  /**
35   * The JavaScript object for {@link HtmlOption}.
36   *
37   * @author Mike Bowler
38   * @author David K. Taylor
39   * @author Chris Erskine
40   * @author Marc Guillemot
41   * @author Ahmed Ashour
42   * @author Ronald Brill
43   * @author Frank Danek
44   *
45   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionElement">MDN Documentation</a>
46   */
47  @JsxClass(domClass = HtmlOption.class)
48  public class HTMLOptionElement extends HTMLElement {
49  
50      /**
51       * JavaScript constructor.
52       * @param newText the text
53       * @param newValue the value
54       * @param defaultSelected whether the option is initially selected
55       * @param selected the current selection state of the option
56       */
57      @JsxConstructor
58      public void jsConstructor(final Object newText, final String newValue,
59              final boolean defaultSelected, final boolean selected) {
60          final SgmlPage page = (SgmlPage) getWindow().getWebWindow().getEnclosedPage();
61          AttributesImpl attributes = null;
62          if (defaultSelected) {
63              attributes = new AttributesImpl();
64              attributes.addAttribute(null, "selected", "selected", null, "selected");
65          }
66  
67          final HtmlOption htmlOption = (HtmlOption) page.getWebClient().getPageCreator().getHtmlParser()
68                                                      .getFactory(HtmlOption.TAG_NAME)
69                                                      .createElement(page, HtmlOption.TAG_NAME, attributes);
70          htmlOption.setSelected(selected);
71          setDomNode(htmlOption);
72  
73          if (!JavaScriptEngine.isUndefined(newText)) {
74              final String newTextString = JavaScriptEngine.toString(newText);
75              htmlOption.appendChild(new DomText(page, newTextString));
76              htmlOption.setLabelAttribute(newTextString);
77          }
78          if (!"undefined".equals(newValue)) {
79              htmlOption.setValueAttribute(newValue);
80          }
81      }
82  
83      /**
84       * JavaScript constructor.
85       * @param newText the text
86       * @param newValue the value
87       * @param defaultSelected whether the option is initially selected
88       * @param selected the current selection state of the option
89       */
90      public void jsConstructorOption(final Object newText, final String newValue,
91              final boolean defaultSelected, final boolean selected) {
92          jsConstructor(newText, newValue, defaultSelected, selected);
93      }
94  
95      /**
96       * Returns the value of the {@code value} property.
97       * @return the value property
98       */
99      @JsxGetter
100     @Override
101     public String getValue() {
102         String value = getDomNodeOrNull().getAttributeDirect(DomElement.VALUE_ATTRIBUTE);
103         if (ATTRIBUTE_NOT_DEFINED == value) {
104             value = ((HtmlOption) getDomNodeOrNull()).getText();
105         }
106         return value;
107     }
108 
109     /**
110      * Sets the value of the {@code value} property.
111      * @param newValue the value property
112      */
113     @JsxSetter
114     public void setValue(final String newValue) {
115         final DomNode dom = getDomNodeOrNull();
116         if (dom instanceof HtmlOption option) {
117             option.setValueAttribute(newValue);
118         }
119     }
120 
121     /**
122      * Returns the value of the {@code text} property.
123      * @return the text property
124      */
125     @JsxGetter
126     public String getText() {
127         final DomNode dom = getDomNodeOrNull();
128         if (dom instanceof HtmlOption option) {
129             return option.getText();
130         }
131         return null;
132     }
133 
134     /**
135      * Sets the value of the {@code text} property.
136      * @param newText the text property
137      */
138     @JsxSetter
139     public void setText(final String newText) {
140         final DomNode dom = getDomNodeOrNull();
141         if (dom instanceof HtmlOption option) {
142             option.setText(newText);
143 
144             if (!hasAttribute("label")) {
145                 setLabel(newText);
146             }
147         }
148     }
149 
150     /**
151      * Returns the value of the {@code selected} property.
152      * @return the selected property
153      */
154     @JsxGetter
155     public boolean isSelected() {
156         final DomNode dom = getDomNodeOrNull();
157         if (dom instanceof HtmlOption option) {
158             return option.isSelected();
159         }
160         return false;
161     }
162 
163     /**
164      * Sets the value of the {@code selected} property.
165      * @param selected the new value of the {@code selected} property
166      */
167     @JsxSetter
168     public void setSelected(final boolean selected) {
169         final HtmlOption optionNode = (HtmlOption) getDomNodeOrNull();
170         final HtmlSelect enclosingSelect = optionNode.getEnclosingSelect();
171 
172         if (!selected && optionNode.isSelected()
173                 && enclosingSelect != null
174                 && enclosingSelect.getSize() <= 1
175                 && !enclosingSelect.isMultipleSelectEnabled()) {
176             enclosingSelect.getOption(0).setSelectedFromJavaScript(true);
177             return;
178         }
179 
180         optionNode.setSelectedFromJavaScript(selected);
181     }
182 
183     /**
184      * Returns the value of the {@code defaultSelected} property.
185      * @return the defaultSelected property
186      */
187     @JsxGetter
188     public boolean isDefaultSelected() {
189         final DomNode dom = getDomNodeOrNull();
190         if (dom instanceof HtmlOption option) {
191             return option.isDefaultSelected();
192         }
193         return false;
194     }
195 
196     /**
197      * Returns the value of the {@code label} property.
198      * @return the label property
199      */
200     @JsxGetter
201     public String getLabel() {
202         final DomNode domNode = getDomNodeOrNull();
203         if (domNode instanceof HtmlOption option) {
204             return option.getLabelAttribute();
205         }
206         return ((HtmlOptionGroup) domNode).getLabelAttribute();
207     }
208 
209     /**
210      * Sets the value of the {@code label} property.
211      * @param label the new value of the {@code label} property
212      */
213     @JsxSetter
214     public void setLabel(final String label) {
215         final DomNode domNode = getDomNodeOrNull();
216         if (domNode instanceof HtmlOption option) {
217             option.setLabelAttribute(label);
218         }
219         else {
220             ((HtmlOptionGroup) domNode).setLabelAttribute(label);
221         }
222     }
223 
224     /**
225      * {@inheritDoc} Overridden to modify browser configurations.
226      */
227     @Override
228     @JsxGetter
229     public boolean isDisabled() {
230         return super.isDisabled();
231     }
232 
233     /**
234      * {@inheritDoc} Overridden to modify browser configurations.
235      */
236     @Override
237     @JsxSetter
238     public void setDisabled(final boolean disabled) {
239         super.setDisabled(disabled);
240     }
241 
242     /**
243      * Returns the {@code index} property.
244      * @return the {@code index} property
245      */
246     @JsxGetter
247     public int getIndex() {
248         final HtmlOption optionNode = (HtmlOption) getDomNodeOrNull();
249         if (optionNode != null) {
250             final HtmlSelect enclosingSelect = optionNode.getEnclosingSelect();
251             if (enclosingSelect != null) {
252                 return enclosingSelect.indexOf(optionNode);
253             }
254         }
255         return 0;
256     }
257 
258     /**
259      * {@inheritDoc}
260      */
261     @Override
262     public void setAttribute(final String name, final String value) {
263         super.setAttribute(name, value);
264         if ("selected".equals(name)) {
265             setSelected(true);
266         }
267     }
268 
269     /**
270      * {@inheritDoc}
271      */
272     @Override
273     public void removeAttribute(final String name) {
274         super.removeAttribute(name);
275         if ("selected".equals(name)) {
276             setSelected(false);
277         }
278     }
279 
280     /**
281      * Returns the value of the JavaScript {@code form} attribute.
282      *
283      * @return the value of the JavaScript {@code form} attribute
284      */
285     @JsxGetter
286     @Override
287     public HTMLFormElement getForm() {
288         final HtmlForm form = getDomNodeOrDie().getEnclosingForm();
289         if (form == null) {
290             return null;
291         }
292         return (HTMLFormElement) getScriptableFor(form);
293     }
294 }