View Javadoc
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.html;
16  
17  import java.util.Map;
18  
19  import org.apache.commons.lang3.StringUtils;
20  import org.htmlunit.SgmlPage;
21  
22  /**
23   * Wrapper for the HTML element "input" where type is "range".
24   *
25   * @author Ahmed Ashour
26   * @author Ronald Brill
27   * @author Frank Danek
28   * @author Anton Demydenko
29   */
30  public class HtmlRangeInput extends HtmlInput implements LabelableElement {
31  
32      /**
33       * Creates an instance.
34       *
35       * @param qualifiedName the qualified name of the element type to instantiate
36       * @param page the page that contains this element
37       * @param attributes the initial attributes
38       */
39      HtmlRangeInput(final String qualifiedName, final SgmlPage page,
40              final Map<String, DomAttr> attributes) {
41          super(qualifiedName, page, attributes);
42  
43          final String value = getValueAttribute();
44          if (value == ATTRIBUTE_NOT_DEFINED) {
45              final double min = getMinNumeric();
46              final double max = getMaxNumeric();
47              if (max < min) {
48                  setValue(min);
49                  unmarkValueDirty();
50                  return;
51              }
52  
53              final double val = min + ((max - min) / 2);
54              setValue(val);
55              unmarkValueDirty();
56          }
57          else {
58              setValue(value);
59              unmarkValueDirty();
60          }
61      }
62  
63      /**
64       * @return the min as double
65       */
66      public double getMinNumeric() {
67          final String min = getAttributeDirect("min");
68          if (min == ATTRIBUTE_NOT_DEFINED) {
69              return 0;
70          }
71          try {
72              return Double.parseDouble(min);
73          }
74          catch (final NumberFormatException e) {
75              return 0;
76          }
77      }
78  
79      /**
80       * @return the max as double
81       */
82      public double getMaxNumeric() {
83          final String max = getAttributeDirect("max");
84          if (max == ATTRIBUTE_NOT_DEFINED) {
85              return 100;
86          }
87          try {
88              return Double.parseDouble(max);
89          }
90          catch (final NumberFormatException e) {
91              return 100;
92          }
93      }
94  
95      /**
96       * @return the max as double
97       */
98      public double getStepNumeric() {
99          final String step = getAttributeDirect("step");
100         if (step == ATTRIBUTE_NOT_DEFINED) {
101             return 1;
102         }
103         try {
104             return Double.parseDouble(step);
105         }
106         catch (final NumberFormatException e) {
107             return 1;
108         }
109     }
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
115     public void setDefaultChecked(final boolean defaultChecked) {
116         // Empty.
117     }
118 
119     /**
120      * {@inheritDoc}
121      */
122     @Override
123     public void setValue(final String newValue) {
124         try {
125             if (StringUtils.isNotEmpty(newValue)) {
126                 setValue(Double.parseDouble(newValue));
127             }
128             else {
129                 final double min = getMinNumeric();
130                 final double max = getMaxNumeric();
131 
132                 // place in the middle
133                 setValue(min + ((max - min) / 2));
134             }
135         }
136         catch (final NumberFormatException ignored) {
137             // ignore
138         }
139     }
140 
141     private void setValue(final double newValue) {
142         double value = newValue;
143 
144         final double min = getMinNumeric();
145         final double max = getMaxNumeric();
146 
147         if (value > max) {
148             value = max;
149         }
150         else {
151             if (value < min) {
152                 value = min;
153             }
154         }
155 
156         final double step = getStepNumeric();
157         value = value - min;
158         int fact = (int) (value / step);
159         final double rest = value % step;
160         if (rest >= step / 2) {
161             fact++;
162         }
163         value = min + step * fact;
164 
165         if (!Double.isInfinite(value) && (value == Math.floor(value))) {
166             super.setValue(Integer.toString((int) value));
167         }
168         else {
169             super.setValue(Double.toString(value));
170         }
171     }
172 
173     @Override
174     protected void valueAttributeChanged(final String attributeValue, final boolean isValueDirty) {
175         if (isValueDirty) {
176             return;
177         }
178 
179         try {
180             if (StringUtils.isNotEmpty(attributeValue)) {
181                 setRawValue(Double.parseDouble(attributeValue));
182             }
183             else {
184                 final double min = getMinNumeric();
185                 final double max = getMaxNumeric();
186 
187                 // place in the middle
188                 setRawValue(min + ((max - min) / 2));
189             }
190         }
191         catch (final NumberFormatException ignored) {
192             // ignore
193         }
194     }
195 
196     private void setRawValue(final double newValue) {
197         double value = newValue;
198 
199         final double min = getMinNumeric();
200         final double max = getMaxNumeric();
201 
202         if (value > max) {
203             value = max;
204         }
205         else {
206             if (value < min) {
207                 value = min;
208             }
209         }
210 
211         final double step = getStepNumeric();
212         value = value - min;
213         int fact = (int) (value / step);
214         final double rest = value % step;
215         if (rest >= step / 2) {
216             fact++;
217         }
218         value = min + step * fact;
219 
220         if (!Double.isInfinite(value) && value == Math.floor(value)) {
221             setRawValue(Integer.toString((int) value));
222         }
223         else {
224             setRawValue(Double.toString(value));
225         }
226     }
227 
228     /**
229      * {@inheritDoc}
230      */
231     @Override
232     protected boolean isRequiredSupported() {
233         return false;
234     }
235 
236     /**
237      * {@inheritDoc}
238      */
239     @Override
240     public boolean isValid() {
241         return super.isValid() && isMaxValid() && isMinValid();
242     }
243 
244     /**
245      * Returns if the input element has a valid min value. Refer to the
246      * <a href="https://www.w3.org/TR/html5/sec-forms.html">HTML 5</a>
247      * documentation for details.
248      *
249      * @return if the input element has a valid min value
250      */
251     private boolean isMinValid() {
252         if (!getRawValue().isEmpty() && !getMin().isEmpty()) {
253             try {
254                 final double value = Double.parseDouble(getRawValue());
255                 return getMinNumeric() <= value;
256             }
257             catch (final NumberFormatException ignored) {
258                 // ignore
259             }
260         }
261         return true;
262     }
263 
264     /**
265      * Returns if the input element has a valid max value. Refer to the
266      * <a href="https://www.w3.org/TR/html5/sec-forms.html">HTML 5</a>
267      * documentation for details.
268      *
269      * @return if the input element has a valid max value
270      */
271     private boolean isMaxValid() {
272         if (!getRawValue().isEmpty() && !getMax().isEmpty()) {
273             try {
274                 final int value = Integer.parseInt(getRawValue());
275                 return getMaxNumeric() >= value;
276             }
277             catch (final NumberFormatException ignored) {
278                 // ignore
279             }
280         }
281         return true;
282     }
283 }