happy-dom 10.0.7 → 10.1.1
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.
- package/cjs/nodes/document/Document.cjs +1 -0
- package/cjs/nodes/document/Document.cjs.map +1 -1
- package/cjs/nodes/document/Document.d.ts.map +1 -1
- package/cjs/nodes/html-input-element/HTMLInputDateUtility.cjs +24 -0
- package/cjs/nodes/html-input-element/HTMLInputDateUtility.cjs.map +1 -0
- package/cjs/nodes/html-input-element/HTMLInputDateUtility.d.ts +8 -0
- package/cjs/nodes/html-input-element/HTMLInputDateUtility.d.ts.map +1 -0
- package/cjs/nodes/html-input-element/HTMLInputElement.cjs +98 -1
- package/cjs/nodes/html-input-element/HTMLInputElement.cjs.map +1 -1
- package/cjs/nodes/html-input-element/HTMLInputElement.d.ts +6 -0
- package/cjs/nodes/html-input-element/HTMLInputElement.d.ts.map +1 -1
- package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.cjs +151 -1
- package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.cjs.map +1 -1
- package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts +32 -0
- package/cjs/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts.map +1 -1
- package/lib/nodes/document/Document.d.ts.map +1 -1
- package/lib/nodes/document/Document.js +1 -0
- package/lib/nodes/document/Document.js.map +1 -1
- package/lib/nodes/html-input-element/HTMLInputDateUtility.d.ts +8 -0
- package/lib/nodes/html-input-element/HTMLInputDateUtility.d.ts.map +1 -0
- package/lib/nodes/html-input-element/HTMLInputDateUtility.js +20 -0
- package/lib/nodes/html-input-element/HTMLInputDateUtility.js.map +1 -0
- package/lib/nodes/html-input-element/HTMLInputElement.d.ts +6 -0
- package/lib/nodes/html-input-element/HTMLInputElement.d.ts.map +1 -1
- package/lib/nodes/html-input-element/HTMLInputElement.js +98 -1
- package/lib/nodes/html-input-element/HTMLInputElement.js.map +1 -1
- package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts +32 -0
- package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.d.ts.map +1 -1
- package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.js +153 -2
- package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.js.map +1 -1
- package/package.json +1 -1
- package/src/nodes/document/Document.ts +1 -0
- package/src/nodes/html-input-element/HTMLInputDateUtility.ts +21 -0
- package/src/nodes/html-input-element/HTMLInputElement.ts +99 -1
- package/src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts +153 -1
@@ -1,8 +1,9 @@
|
|
1
1
|
const NEW_LINES_REGEXP = /[\n\r]/gm;
|
2
|
+
const parseInts = (a) => a.map((v) => parseInt(v, 10));
|
2
3
|
/**
|
3
4
|
* HTML input element value sanitizer.
|
4
5
|
*/
|
5
|
-
|
6
|
+
class HTMLInputElementValueSanitizer {
|
6
7
|
/**
|
7
8
|
* Sanitizes a value.
|
8
9
|
*
|
@@ -32,7 +33,7 @@ export default class HTMLInputElementValueSanitizer {
|
|
32
33
|
case 'number':
|
33
34
|
// https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
|
34
35
|
return !isNaN(Number.parseFloat(value)) ? value : '';
|
35
|
-
case 'range':
|
36
|
+
case 'range': {
|
36
37
|
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
|
37
38
|
const number = Number.parseFloat(value);
|
38
39
|
const min = parseFloat(input.min) || 0;
|
@@ -47,11 +48,161 @@ export default class HTMLInputElementValueSanitizer {
|
|
47
48
|
return String(max);
|
48
49
|
}
|
49
50
|
return value;
|
51
|
+
}
|
50
52
|
case 'url':
|
51
53
|
// https://html.spec.whatwg.org/multipage/forms.html#url-state-(type=url):value-sanitization-algorithm
|
52
54
|
return value.trim().replace(NEW_LINES_REGEXP, '');
|
55
|
+
case 'date':
|
56
|
+
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):value-sanitization-algorithm
|
57
|
+
value = this.sanitizeDate(value);
|
58
|
+
return value && this.checkBoundaries(value, input.min, input.max) ? value : '';
|
59
|
+
case 'datetime-local': {
|
60
|
+
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):value-sanitization-algorithm
|
61
|
+
const match = value.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d)(?::(\d\d)(?:\.(\d{1,3}))?)?$/);
|
62
|
+
if (!match) {
|
63
|
+
return '';
|
64
|
+
}
|
65
|
+
const dateString = this.sanitizeDate(value.slice(0, 10));
|
66
|
+
let timeString = this.sanitizeTime(value.slice(11));
|
67
|
+
if (!(dateString && timeString)) {
|
68
|
+
return '';
|
69
|
+
}
|
70
|
+
// Has seconds so needs to remove trailing zeros
|
71
|
+
if (match[6] !== undefined) {
|
72
|
+
if (timeString.indexOf('.') !== -1) {
|
73
|
+
// Remove unecessary zeros milliseconds
|
74
|
+
timeString = timeString.replace(/(?:\.0*|(\.\d+?)0+)$/, '$1');
|
75
|
+
}
|
76
|
+
timeString = timeString.replace(/(\d\d:\d\d)(:00)$/, '$1');
|
77
|
+
}
|
78
|
+
return dateString + 'T' + timeString;
|
79
|
+
}
|
80
|
+
case 'month':
|
81
|
+
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):value-sanitization-algorithm
|
82
|
+
if (!(value.match(/^(\d\d\d\d)-(\d\d)$/) && this.parseMonthComponent(value))) {
|
83
|
+
return '';
|
84
|
+
}
|
85
|
+
return this.checkBoundaries(value, input.min, input.max) ? value : '';
|
86
|
+
case 'time': {
|
87
|
+
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):value-sanitization-algorithm
|
88
|
+
value = this.sanitizeTime(value);
|
89
|
+
return value && this.checkBoundaries(value, input.min, input.max) ? value : '';
|
90
|
+
}
|
91
|
+
case 'week': {
|
92
|
+
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):value-sanitization-algorithm
|
93
|
+
const match = value.match(/^(\d\d\d\d)-W(\d\d)$/);
|
94
|
+
if (!match) {
|
95
|
+
return '';
|
96
|
+
}
|
97
|
+
const [intY, intW] = parseInts(match.slice(1, 3));
|
98
|
+
if (intY <= 0 || intW < 1 || intW > 53) {
|
99
|
+
return '';
|
100
|
+
}
|
101
|
+
// Check date is valid
|
102
|
+
const lastWeek = this.lastIsoWeekOfYear(intY);
|
103
|
+
if (intW < 1 || intW > 52 + lastWeek) {
|
104
|
+
return '';
|
105
|
+
}
|
106
|
+
if (!this.checkBoundaries(value, input.min, input.max)) {
|
107
|
+
return '';
|
108
|
+
}
|
109
|
+
return value;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
return value;
|
113
|
+
}
|
114
|
+
/**
|
115
|
+
* Checks if a value is within the boundaries of min and max.
|
116
|
+
*
|
117
|
+
* @param value
|
118
|
+
* @param min
|
119
|
+
* @param max
|
120
|
+
*/
|
121
|
+
static checkBoundaries(value, min, max) {
|
122
|
+
if (min && min > value) {
|
123
|
+
return false;
|
124
|
+
}
|
125
|
+
else if (max && max < value) {
|
126
|
+
return false;
|
127
|
+
}
|
128
|
+
return true;
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* Parses the month component of a date string.
|
132
|
+
*
|
133
|
+
* @param value
|
134
|
+
*/
|
135
|
+
static parseMonthComponent(value) {
|
136
|
+
const [Y, M] = value.split('-');
|
137
|
+
const [intY, intM] = parseInts([Y, M]);
|
138
|
+
if (isNaN(intY) || isNaN(intM) || intY <= 0 || intM < 1 || intM > 12) {
|
139
|
+
return '';
|
53
140
|
}
|
54
141
|
return value;
|
55
142
|
}
|
143
|
+
/**
|
144
|
+
* Sanitizes a date string.
|
145
|
+
*
|
146
|
+
* @param value
|
147
|
+
*/
|
148
|
+
static sanitizeDate(value) {
|
149
|
+
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
150
|
+
if (!match) {
|
151
|
+
return '';
|
152
|
+
}
|
153
|
+
const month = this.parseMonthComponent(value.slice(0, 7));
|
154
|
+
if (!month) {
|
155
|
+
return '';
|
156
|
+
}
|
157
|
+
const [intY, intM, intD] = parseInts(match.slice(1, 4));
|
158
|
+
if (intD < 1 || intD > 31) {
|
159
|
+
return '';
|
160
|
+
}
|
161
|
+
// Check date is valid
|
162
|
+
const lastDayOfMonth = new Date(intY, intM, 0).getDate();
|
163
|
+
if (intD > lastDayOfMonth) {
|
164
|
+
return '';
|
165
|
+
}
|
166
|
+
return value;
|
167
|
+
}
|
168
|
+
/**
|
169
|
+
* Sanitizes a time string.
|
170
|
+
*
|
171
|
+
* @param value
|
172
|
+
*/
|
173
|
+
static sanitizeTime(value) {
|
174
|
+
const match = value.match(/^(\d{2}):(\d{2})(?::(\d{2}(?:\.(\d{1,3}))?))?$/);
|
175
|
+
if (!match) {
|
176
|
+
return '';
|
177
|
+
}
|
178
|
+
const [intH, intM] = parseInts(match.slice(1, 3));
|
179
|
+
const ms = parseFloat(match[3] || '0') * 1000;
|
180
|
+
if (intH > 23 || intM > 59 || ms > 59999) {
|
181
|
+
return '';
|
182
|
+
}
|
183
|
+
if (ms === 0) {
|
184
|
+
return `${match[1]}:${match[2]}`;
|
185
|
+
}
|
186
|
+
else {
|
187
|
+
return `${match[1]}:${match[2]}${ms >= 10000 ? `:${ms / 1000}` : `:0${ms / 1000}`}`;
|
188
|
+
}
|
189
|
+
}
|
56
190
|
}
|
191
|
+
/**
|
192
|
+
* Returns the last ISO week of a year.
|
193
|
+
*
|
194
|
+
* @param year
|
195
|
+
*/
|
196
|
+
HTMLInputElementValueSanitizer.lastIsoWeekOfYear = (year) => {
|
197
|
+
const date = new Date(+year, 11, 31);
|
198
|
+
const day = (date.getDay() + 6) % 7;
|
199
|
+
date.setDate(date.getDate() - day + 3);
|
200
|
+
const firstThursday = date.getTime();
|
201
|
+
date.setMonth(0, 1);
|
202
|
+
if (date.getDay() !== 4) {
|
203
|
+
date.setMonth(0, 1 + ((4 - date.getDay() + 7) % 7));
|
204
|
+
}
|
205
|
+
return 1 + Math.ceil((firstThursday - date.getTime()) / 604800000);
|
206
|
+
};
|
207
|
+
export default HTMLInputElementValueSanitizer;
|
57
208
|
//# sourceMappingURL=HTMLInputElementValueSanitizer.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"HTMLInputElementValueSanitizer.js","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,UAAU,CAAC;
|
1
|
+
{"version":3,"file":"HTMLInputElementValueSanitizer.js","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -198,6 +198,7 @@ export default class Document extends Node implements IDocument {
|
|
198
198
|
this.implementation = new DOMImplementation(this);
|
199
199
|
|
200
200
|
this._readyStateManager = new DocumentReadyStateManager(this.defaultView);
|
201
|
+
this._rootNode = this;
|
201
202
|
|
202
203
|
const doctype = this.implementation.createDocumentType('html', '', '');
|
203
204
|
const documentElement = this.createElement('html');
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/**
|
2
|
+
* Return iso-week number from given date
|
3
|
+
*
|
4
|
+
* @param date Date|number.
|
5
|
+
* @returns Iso-week string.
|
6
|
+
*/
|
7
|
+
export const dateIsoWeek = (date: Date | number): string => {
|
8
|
+
date = new Date(date);
|
9
|
+
const day = (date.getUTCDay() + 6) % 7;
|
10
|
+
date.setUTCDate(date.getUTCDate() - day + 3);
|
11
|
+
const firstThursday = date.getTime();
|
12
|
+
date.setUTCMonth(0, 1);
|
13
|
+
if (date.getDay() !== 4) {
|
14
|
+
date.setUTCMonth(0, 1 + ((4 - date.getDay() + 7) % 7));
|
15
|
+
}
|
16
|
+
return (
|
17
|
+
date.getUTCFullYear() +
|
18
|
+
'-W' +
|
19
|
+
String(1 + Math.ceil((firstThursday - date.getTime()) / 604800000)).padStart(2, '0')
|
20
|
+
);
|
21
|
+
};
|
@@ -22,6 +22,7 @@ import IDocument from '../document/IDocument.js';
|
|
22
22
|
import IShadowRoot from '../shadow-root/IShadowRoot.js';
|
23
23
|
import NodeList from '../node/NodeList.js';
|
24
24
|
import EventPhaseEnum from '../../event/EventPhaseEnum.js';
|
25
|
+
import { dateIsoWeek } from './HTMLInputDateUtility.js';
|
25
26
|
|
26
27
|
/**
|
27
28
|
* HTML Input Element.
|
@@ -797,7 +798,104 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
|
|
797
798
|
* @returns Number.
|
798
799
|
*/
|
799
800
|
public get valueAsNumber(): number {
|
800
|
-
|
801
|
+
const value = this.value;
|
802
|
+
if (!this.type.match(/^(range|number|date|datetime-local|month|time|week)$/) || !value) {
|
803
|
+
return NaN;
|
804
|
+
}
|
805
|
+
switch (this.type) {
|
806
|
+
case 'number':
|
807
|
+
return parseFloat(value);
|
808
|
+
case 'range': {
|
809
|
+
const number = parseFloat(value);
|
810
|
+
const min = parseFloat(this.min) || 0;
|
811
|
+
const max = parseFloat(this.max) || 100;
|
812
|
+
if (isNaN(number)) {
|
813
|
+
return max < min ? min : (min + max) / 2;
|
814
|
+
} else if (number < min) {
|
815
|
+
return min;
|
816
|
+
} else if (number > max) {
|
817
|
+
return max;
|
818
|
+
}
|
819
|
+
return number;
|
820
|
+
}
|
821
|
+
case 'date':
|
822
|
+
return new Date(value).getTime();
|
823
|
+
case 'datetime-local':
|
824
|
+
return new Date(value).getTime() - new Date(value).getTimezoneOffset() * 60000;
|
825
|
+
case 'month':
|
826
|
+
return (new Date(value).getUTCFullYear() - 1970) * 12 + new Date(value).getUTCMonth();
|
827
|
+
case 'time':
|
828
|
+
return (
|
829
|
+
new Date('1970-01-01T' + value).getTime() - new Date('1970-01-01T00:00:00').getTime()
|
830
|
+
);
|
831
|
+
case 'week': {
|
832
|
+
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week)
|
833
|
+
const match = value.match(/^(\d{4})-W(\d{2})$/);
|
834
|
+
if (!match) {
|
835
|
+
return NaN;
|
836
|
+
}
|
837
|
+
const d = new Date(Date.UTC(parseInt(match[1], 10), 0));
|
838
|
+
const day = d.getUTCDay();
|
839
|
+
const diff = ((day === 0 ? -6 : 1) - day) * 86400000 + parseInt(match[2], 10) * 604800000;
|
840
|
+
return d.getTime() + diff;
|
841
|
+
}
|
842
|
+
}
|
843
|
+
}
|
844
|
+
|
845
|
+
/**
|
846
|
+
* Sets value from a number.
|
847
|
+
*
|
848
|
+
* @param value number.
|
849
|
+
*/
|
850
|
+
public set valueAsNumber(value: number) {
|
851
|
+
// Specs at https://html.spec.whatwg.org/multipage/input.html
|
852
|
+
switch (this.type) {
|
853
|
+
case 'number':
|
854
|
+
case 'range':
|
855
|
+
// We Rely on HTMLInputElementValueSanitizer
|
856
|
+
this.value = Number(value).toString();
|
857
|
+
break;
|
858
|
+
case 'date':
|
859
|
+
case 'datetime-local': {
|
860
|
+
const d = new Date(Number(value));
|
861
|
+
if (isNaN(d.getTime())) {
|
862
|
+
// Reset to default value
|
863
|
+
this.value = '';
|
864
|
+
break;
|
865
|
+
}
|
866
|
+
if (this.type == 'date') {
|
867
|
+
this.value = d.toISOString().slice(0, 10);
|
868
|
+
} else {
|
869
|
+
this.value = d.toISOString().slice(0, -1);
|
870
|
+
}
|
871
|
+
break;
|
872
|
+
}
|
873
|
+
case 'month':
|
874
|
+
if (!Number.isInteger(value) || value < 0) {
|
875
|
+
this.value = '';
|
876
|
+
} else {
|
877
|
+
this.value = new Date(Date.UTC(1970, Number(value))).toISOString().slice(0, 7);
|
878
|
+
}
|
879
|
+
break;
|
880
|
+
case 'time':
|
881
|
+
if (!Number.isInteger(value) || value < 0) {
|
882
|
+
this.value = '';
|
883
|
+
} else {
|
884
|
+
this.value = new Date(Number(value)).toISOString().slice(11, -1);
|
885
|
+
}
|
886
|
+
break;
|
887
|
+
case 'week':
|
888
|
+
case 'week': {
|
889
|
+
const d = new Date(Number(value));
|
890
|
+
this.value = isNaN(d.getTime()) ? '' : dateIsoWeek(d);
|
891
|
+
break;
|
892
|
+
}
|
893
|
+
default:
|
894
|
+
throw new DOMException(
|
895
|
+
"Failed to set the 'valueAsNumber' property on 'HTMLInputElement': This input element does not support Number values.",
|
896
|
+
DOMExceptionNameEnum.invalidStateError
|
897
|
+
);
|
898
|
+
}
|
801
899
|
}
|
802
900
|
|
803
901
|
/**
|
@@ -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
|
}
|