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.html;
16  
17  import static org.htmlunit.BrowserVersionFeatures.HTMLINPUT_TYPE_MONTH_SUPPORTED;
18  import static org.htmlunit.BrowserVersionFeatures.JS_INPUT_CHANGE_TYPE_DROPS_VALUE_WEEK_MONTH;
19  
20  import java.time.YearMonth;
21  import java.time.format.DateTimeFormatter;
22  import java.time.format.DateTimeParseException;
23  import java.util.Map;
24  
25  import org.htmlunit.BrowserVersion;
26  import org.htmlunit.SgmlPage;
27  import org.htmlunit.util.StringUtils;
28  
29  /**
30   * Wrapper for the HTML element "input" where type is "month".
31   *
32   * @author Ahmed Ashour
33   * @author Frank Danek
34   * @author Anton Demydenko
35   * @author Ronald Brill
36   */
37  public class HtmlMonthInput extends HtmlInput implements LabelableElement {
38  
39      private static final DateTimeFormatter FORMATTER_ = DateTimeFormatter.ofPattern("yyyy-MM");
40  
41      /**
42       * Creates an instance.
43       *
44       * @param qualifiedName the qualified name of the element type to instantiate
45       * @param page the page that contains this element
46       * @param attributes the initial attributes
47       */
48      HtmlMonthInput(final String qualifiedName, final SgmlPage page,
49              final Map<String, DomAttr> attributes) {
50          super(qualifiedName, page, attributes);
51      }
52  
53      /**
54       * {@inheritDoc}
55       */
56      @Override
57      public void setDefaultChecked(final boolean defaultChecked) {
58          // Empty.
59      }
60  
61      /**
62       * {@inheritDoc}
63       */
64      @Override
65      public void setValue(final String newValue) {
66          try {
67              if (hasFeature(HTMLINPUT_TYPE_MONTH_SUPPORTED) && !StringUtils.isEmptyOrNull(newValue)) {
68                  FORMATTER_.parse(newValue);
69              }
70              super.setValue(newValue);
71          }
72          catch (final DateTimeParseException ignored) {
73              // ignore
74          }
75      }
76  
77      /**
78       * {@inheritDoc}
79       */
80      @Override
81      public boolean isValid() {
82          return super.isValid() && isMaxValid() && isMinValid();
83      }
84  
85      /**
86       * Returns if the input element has a valid min value. Refer to the
87       * <a href="https://www.w3.org/TR/html5/sec-forms.html">HTML 5</a>
88       * documentation for details.
89       *
90       * @return if the input element has a valid min value
91       */
92      private boolean isMinValid() {
93          if (hasFeature(HTMLINPUT_TYPE_MONTH_SUPPORTED) && !getMin().isEmpty()) {
94              try {
95                  final YearMonth dateValue = YearMonth.parse(getRawValue(), FORMATTER_);
96                  final YearMonth minDate = YearMonth.parse(getMin(), FORMATTER_);
97                  return minDate.equals(dateValue) || minDate.isBefore(dateValue);
98              }
99              catch (final DateTimeParseException ignored) {
100                 // ignore
101             }
102         }
103         return true;
104     }
105 
106     /**
107      * Returns if the input element has a valid max value. Refer to the
108      * <a href="https://www.w3.org/TR/html5/sec-forms.html">HTML 5</a>
109      * documentation for details.
110      *
111      * @return if the input element has a valid max value
112      */
113     private boolean isMaxValid() {
114         if (hasFeature(HTMLINPUT_TYPE_MONTH_SUPPORTED) && !getMax().isEmpty()) {
115             try {
116                 final YearMonth dateValue = YearMonth.parse(getRawValue(), FORMATTER_);
117                 final YearMonth maxDate = YearMonth.parse(getMax(), FORMATTER_);
118                 return maxDate.equals(dateValue) || maxDate.isAfter(dateValue);
119             }
120             catch (final DateTimeParseException ignored) {
121                 // ignore
122             }
123         }
124         return true;
125     }
126 
127     @Override
128     protected void adjustValueAfterTypeChange(final HtmlInput oldInput, final BrowserVersion browserVersion) {
129         if (browserVersion.hasFeature(JS_INPUT_CHANGE_TYPE_DROPS_VALUE_WEEK_MONTH)) {
130             setValue("");
131             return;
132         }
133         super.adjustValueAfterTypeChange(oldInput, browserVersion);
134     }
135 }