happy-dom 10.0.7 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of happy-dom might be problematic. Click here for more details.

Files changed (28) hide show
  1. package/cjs/nodes/html-input-element/HTMLInputDateUtility.cjs +24 -0
  2. package/cjs/nodes/html-input-element/HTMLInputDateUtility.cjs.map +1 -0
  3. package/cjs/nodes/html-input-element/HTMLInputDateUtility.d.ts +8 -0
  4. package/cjs/nodes/html-input-element/HTMLInputDateUtility.d.ts.map +1 -0
  5. package/cjs/nodes/html-input-element/HTMLInputElement.cjs +98 -1
  6. package/cjs/nodes/html-input-element/HTMLInputElement.cjs.map +1 -1
  7. package/cjs/nodes/html-input-element/HTMLInputElement.d.ts +6 -0
  8. package/cjs/nodes/html-input-element/HTMLInputElement.d.ts.map +1 -1
  9. package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.cjs +151 -1
  10. package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.cjs.map +1 -1
  11. package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts +32 -0
  12. package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts.map +1 -1
  13. package/lib/nodes/html-input-element/HTMLInputDateUtility.d.ts +8 -0
  14. package/lib/nodes/html-input-element/HTMLInputDateUtility.d.ts.map +1 -0
  15. package/lib/nodes/html-input-element/HTMLInputDateUtility.js +20 -0
  16. package/lib/nodes/html-input-element/HTMLInputDateUtility.js.map +1 -0
  17. package/lib/nodes/html-input-element/HTMLInputElement.d.ts +6 -0
  18. package/lib/nodes/html-input-element/HTMLInputElement.d.ts.map +1 -1
  19. package/lib/nodes/html-input-element/HTMLInputElement.js +98 -1
  20. package/lib/nodes/html-input-element/HTMLInputElement.js.map +1 -1
  21. package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts +32 -0
  22. package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts.map +1 -1
  23. package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.js +153 -2
  24. package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/nodes/html-input-element/HTMLInputDateUtility.ts +21 -0
  27. package/src/nodes/html-input-element/HTMLInputElement.ts +99 -1
  28. package/src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts +153 -1
@@ -1,6 +1,7 @@
1
1
  import HTMLInputElement from './HTMLInputElement.js';
2
2
 
3
3
  const NEW_LINES_REGEXP = /[\n\r]/gm;
4
+ const parseInts = (a: string[]): number[] => a.map((v) => parseInt(v, 10));
4
5
 
5
6
  /**
6
7
  * HTML input element value sanitizer.
@@ -35,7 +36,7 @@ export default class HTMLInputElementValueSanitizer {
35
36
  case 'number':
36
37
  // https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
37
38
  return !isNaN(Number.parseFloat(value)) ? value : '';
38
- case 'range':
39
+ case 'range': {
39
40
  // https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
40
41
  const number = Number.parseFloat(value);
41
42
  const min = parseFloat(input.min) || 0;
@@ -50,11 +51,162 @@ export default class HTMLInputElementValueSanitizer {
50
51
  }
51
52
 
52
53
  return value;
54
+ }
53
55
  case 'url':
54
56
  // https://html.spec.whatwg.org/multipage/forms.html#url-state-(type=url):value-sanitization-algorithm
55
57
  return value.trim().replace(NEW_LINES_REGEXP, '');
58
+ case 'date':
59
+ // https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):value-sanitization-algorithm
60
+ value = this.sanitizeDate(value);
61
+ return value && this.checkBoundaries(value, input.min, input.max) ? value : '';
62
+ case 'datetime-local': {
63
+ // https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):value-sanitization-algorithm
64
+ const match = value.match(
65
+ /^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d)(?::(\d\d)(?:\.(\d{1,3}))?)?$/
66
+ );
67
+ if (!match) {
68
+ return '';
69
+ }
70
+ const dateString = this.sanitizeDate(value.slice(0, 10));
71
+ let timeString = this.sanitizeTime(value.slice(11));
72
+ if (!(dateString && timeString)) {
73
+ return '';
74
+ }
75
+ // Has seconds so needs to remove trailing zeros
76
+ if (match[6] !== undefined) {
77
+ if (timeString.indexOf('.') !== -1) {
78
+ // Remove unecessary zeros milliseconds
79
+ timeString = timeString.replace(/(?:\.0*|(\.\d+?)0+)$/, '$1');
80
+ }
81
+ timeString = timeString.replace(/(\d\d:\d\d)(:00)$/, '$1');
82
+ }
83
+ return dateString + 'T' + timeString;
84
+ }
85
+ case 'month':
86
+ // https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):value-sanitization-algorithm
87
+ if (!(value.match(/^(\d\d\d\d)-(\d\d)$/) && this.parseMonthComponent(value))) {
88
+ return '';
89
+ }
90
+ return this.checkBoundaries(value, input.min, input.max) ? value : '';
91
+ case 'time': {
92
+ // https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):value-sanitization-algorithm
93
+ value = this.sanitizeTime(value);
94
+ return value && this.checkBoundaries(value, input.min, input.max) ? value : '';
95
+ }
96
+ case 'week': {
97
+ // https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):value-sanitization-algorithm
98
+ const match = value.match(/^(\d\d\d\d)-W(\d\d)$/);
99
+ if (!match) {
100
+ return '';
101
+ }
102
+ const [intY, intW] = parseInts(match.slice(1, 3));
103
+ if (intY <= 0 || intW < 1 || intW > 53) {
104
+ return '';
105
+ }
106
+ // Check date is valid
107
+ const lastWeek = this.lastIsoWeekOfYear(intY);
108
+ if (intW < 1 || intW > 52 + lastWeek) {
109
+ return '';
110
+ }
111
+ if (!this.checkBoundaries(value, input.min, input.max)) {
112
+ return '';
113
+ }
114
+ return value;
115
+ }
116
+ }
117
+
118
+ return value;
119
+ }
120
+ /**
121
+ * Checks if a value is within the boundaries of min and max.
122
+ *
123
+ * @param value
124
+ * @param min
125
+ * @param max
126
+ */
127
+ private static checkBoundaries<T>(value: T, min: T, max: T): boolean {
128
+ if (min && min > value) {
129
+ return false;
130
+ } else if (max && max < value) {
131
+ return false;
132
+ }
133
+ return true;
134
+ }
135
+ /**
136
+ * Parses the month component of a date string.
137
+ *
138
+ * @param value
139
+ */
140
+ private static parseMonthComponent(value: string): string {
141
+ const [Y, M] = value.split('-');
142
+ const [intY, intM] = parseInts([Y, M]);
143
+ if (isNaN(intY) || isNaN(intM) || intY <= 0 || intM < 1 || intM > 12) {
144
+ return '';
56
145
  }
146
+ return value;
147
+ }
148
+ /**
149
+ * Returns the last ISO week of a year.
150
+ *
151
+ * @param year
152
+ */
153
+ private static lastIsoWeekOfYear = (year: string | number): number => {
154
+ const date = new Date(+year, 11, 31);
155
+ const day = (date.getDay() + 6) % 7;
156
+ date.setDate(date.getDate() - day + 3);
157
+ const firstThursday = date.getTime();
158
+ date.setMonth(0, 1);
159
+ if (date.getDay() !== 4) {
160
+ date.setMonth(0, 1 + ((4 - date.getDay() + 7) % 7));
161
+ }
162
+ return 1 + Math.ceil((firstThursday - date.getTime()) / 604800000);
163
+ };
57
164
 
165
+ /**
166
+ * Sanitizes a date string.
167
+ *
168
+ * @param value
169
+ */
170
+ private static sanitizeDate(value: string): string {
171
+ const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
172
+ if (!match) {
173
+ return '';
174
+ }
175
+ const month = this.parseMonthComponent(value.slice(0, 7));
176
+ if (!month) {
177
+ return '';
178
+ }
179
+ const [intY, intM, intD] = parseInts(match.slice(1, 4));
180
+ if (intD < 1 || intD > 31) {
181
+ return '';
182
+ }
183
+ // Check date is valid
184
+ const lastDayOfMonth = new Date(intY, intM, 0).getDate();
185
+ if (intD > lastDayOfMonth) {
186
+ return '';
187
+ }
58
188
  return value;
59
189
  }
190
+
191
+ /**
192
+ * Sanitizes a time string.
193
+ *
194
+ * @param value
195
+ */
196
+ private static sanitizeTime(value: string): string {
197
+ const match = value.match(/^(\d{2}):(\d{2})(?::(\d{2}(?:\.(\d{1,3}))?))?$/);
198
+ if (!match) {
199
+ return '';
200
+ }
201
+ const [intH, intM] = parseInts(match.slice(1, 3));
202
+ const ms = parseFloat(match[3] || '0') * 1000;
203
+ if (intH > 23 || intM > 59 || ms > 59999) {
204
+ return '';
205
+ }
206
+ if (ms === 0) {
207
+ return `${match[1]}:${match[2]}`;
208
+ } else {
209
+ return `${match[1]}:${match[2]}${ms >= 10000 ? `:${ms / 1000}` : `:0${ms / 1000}`}`;
210
+ }
211
+ }
60
212
  }