@jdlien/validator 1.4.11 → 2.0.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.
- package/LICENSE +7 -0
- package/README.md +352 -59
- package/dist/Validator.d.ts +72 -43
- package/dist/validator.cjs +1 -0
- package/dist/validator.js +1 -1
- package/dist/validator.mjs +893 -0
- package/package.json +39 -23
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
const N = /^(?:[a-z+]+:)?\/\//i, F = /^(?:[-a-z+]+:)?\/\//i, V = /^\d{5}(-\d{4})?$/, x = /^[ABCEGHJKLMNPRSTVXY][0-9][ABCEGHJKLMNPRSTVWXYZ] ?[0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]$/, Y = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/, k = /^\d{3}-\d{3}-\d{4}$/, B = /^-?\d*$/, U = /^-?\d*\.?\d*$/;
|
|
2
|
+
function R(r) {
|
|
3
|
+
let t = r.trim().toLowerCase();
|
|
4
|
+
if (t === "now") {
|
|
5
|
+
const o = /* @__PURE__ */ new Date();
|
|
6
|
+
return { hour: o.getHours(), minute: o.getMinutes(), second: o.getSeconds() };
|
|
7
|
+
}
|
|
8
|
+
if (t === "noon") return { hour: 12, minute: 0, second: 0 };
|
|
9
|
+
t = t.replace(/\s+/g, "").replace(/\.+$/g, "");
|
|
10
|
+
const e = t.replace(/^(\d{1,2})(\d{2})([ap]?m?\.?)$/i, "$1:$2$3").match(/^(\d{1,2})(?::(\d{1,2}))?(?::(\d{1,2}))?\s*([ap])?\.?m?\.?$/i);
|
|
11
|
+
if (!e) return null;
|
|
12
|
+
let s = +e[1], a = +(e[2] || 0), i = +(e[3] || 0);
|
|
13
|
+
const l = e[4]?.toLowerCase();
|
|
14
|
+
return l === "p" && s < 12 && (s += 12), l === "a" && s === 12 && (s = 0), s > 23 || a > 59 || i > 59 ? null : { hour: s, minute: a, second: i };
|
|
15
|
+
}
|
|
16
|
+
function K(r) {
|
|
17
|
+
return R(r) !== null;
|
|
18
|
+
}
|
|
19
|
+
const z = "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec", $ = new RegExp(`(${z})[a-z]*`, "i");
|
|
20
|
+
function P(r) {
|
|
21
|
+
return (/* @__PURE__ */ new Date(`1 ${r} 2000`)).getMonth();
|
|
22
|
+
}
|
|
23
|
+
function g(r) {
|
|
24
|
+
if (typeof r == "string" && (r = parseInt(r.replace(/\D/g, ""))), r > 99) return r;
|
|
25
|
+
const t = ((/* @__PURE__ */ new Date()).getFullYear() + 20) % 100;
|
|
26
|
+
return r + (r < t ? 2e3 : 1900);
|
|
27
|
+
}
|
|
28
|
+
function y(r) {
|
|
29
|
+
if (r instanceof Date) return r;
|
|
30
|
+
let t = r.trim().toLowerCase();
|
|
31
|
+
const e = new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0));
|
|
32
|
+
if (/^(now|today)$/.test(t)) return e;
|
|
33
|
+
if (t === "tomorrow") return new Date(e.setDate(e.getDate() + 1));
|
|
34
|
+
t = t.replace(/\b(mon|tue|wed|thu|fri|sat|sun|lun|mar(?:di|tes)|mer|jeu|ven|sam|dim|dom)[a-z]*\.?\b/gi, "").trim();
|
|
35
|
+
let s = 0, a = 0, i = 0;
|
|
36
|
+
const l = t.match(/(\d{1,2}:\d{2}(?::\d{2})?\s*[ap]?\.?m?\.?)/i);
|
|
37
|
+
if (l) {
|
|
38
|
+
const h = R(l[1]);
|
|
39
|
+
if (h && ({ hour: s, minute: a, second: i } = h), t = t.replace(l[0], "").trim(), !t || t.length <= 2) {
|
|
40
|
+
const m = /* @__PURE__ */ new Date();
|
|
41
|
+
return new Date(m.getFullYear(), m.getMonth(), m.getDate(), s, a, i);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (/^\d{8}$/.test(t))
|
|
45
|
+
return new Date(+t.slice(0, 4), +t.slice(4, 6) - 1, +t.slice(6, 8), s, a, i);
|
|
46
|
+
if (/^\d{6}$/.test(t))
|
|
47
|
+
return new Date(g(+t.slice(0, 2)), +t.slice(2, 4) - 1, +t.slice(4, 6), s, a, i);
|
|
48
|
+
const o = t.match($);
|
|
49
|
+
let u = -1, n = 0, d = 0;
|
|
50
|
+
o && (u = P(o[1]), t = t.replace(o[0], " ").trim());
|
|
51
|
+
const f = t.match(/'(\d{2})\b/);
|
|
52
|
+
f && (n = g(+f[1]), t = t.replace(f[0], " ").trim());
|
|
53
|
+
const c = t.match(/\d+/g)?.map(Number) || [];
|
|
54
|
+
if (u >= 0)
|
|
55
|
+
c.length >= 2 ? c[0] > 99 ? (n = c[0], d = c[1]) : c[1] > 99 ? (n = c[1], d = c[0]) : c[0] > 31 ? (d = c[1], n = g(c[0])) : c[1] > 31 ? (d = c[0], n = g(c[1])) : (d = c[0], n = n || g(c[1])) : c.length === 1 && (d = c[0], n = n || (/* @__PURE__ */ new Date()).getFullYear());
|
|
56
|
+
else if (c.length >= 3) {
|
|
57
|
+
const [h, m, p] = c;
|
|
58
|
+
h > 31 ? (n = h, u = m - 1, d = p) : h > 12 && p > 12 ? (d = h, u = m - 1, n = p > 31 ? p : g(p)) : p > 31 || p > 12 ? (u = h - 1, d = m, n = p > 31 ? p : g(p)) : (u = h - 1, d = m, n = g(p));
|
|
59
|
+
} else c.length === 2 && (u = c[0] - 1, d = c[1], n = n || (/* @__PURE__ */ new Date()).getFullYear());
|
|
60
|
+
return n && u >= 0 && d && d >= 1 && d <= 31 ? new Date(n > 99 ? n : g(n), u, d, s, a, i) : /* @__PURE__ */ new Date("");
|
|
61
|
+
}
|
|
62
|
+
function w(r) {
|
|
63
|
+
if (r instanceof Date) return r;
|
|
64
|
+
let t = r.trim();
|
|
65
|
+
if (t.length < 3) return null;
|
|
66
|
+
if (t = t.replace(/(\d)T(\d)/i, "$1 $2"), t = t.replace(
|
|
67
|
+
/(^|[\sT])(\d{1,2})\.(\d{1,2})(?:\.(\d{1,2}))?(?=\s*[ap]\.?m?\.?\b|(?:\s|$))/gi,
|
|
68
|
+
(o, u, n, d, f) => `${u}${f ? `${n}:${d}:${f}` : `${n}:${d}`}`
|
|
69
|
+
), /^now$/i.test(t)) {
|
|
70
|
+
const o = /* @__PURE__ */ new Date();
|
|
71
|
+
return o.setMilliseconds(0), o;
|
|
72
|
+
}
|
|
73
|
+
if (/^noon$/i.test(t)) {
|
|
74
|
+
const o = /* @__PURE__ */ new Date();
|
|
75
|
+
return new Date(o.getFullYear(), o.getMonth(), o.getDate(), 12, 0, 0);
|
|
76
|
+
}
|
|
77
|
+
let e = null, s = t;
|
|
78
|
+
const a = [
|
|
79
|
+
/(\d{1,2}:\d{1,2}(?::\d{2})?)\s*([ap]\.?m?\.?)?/i,
|
|
80
|
+
// 14:30, 2:30pm, 1:5
|
|
81
|
+
/\b(\d{1,2})\s*([ap]\.?m?\.?)\b/i,
|
|
82
|
+
// 2pm, 2p.m., 2 PM, 1P
|
|
83
|
+
/\b(\d{3,4})([ap])m?\b/i
|
|
84
|
+
// 1430pm, 230p, 1200AM
|
|
85
|
+
];
|
|
86
|
+
for (const o of a) {
|
|
87
|
+
const u = t.match(o);
|
|
88
|
+
if (u) {
|
|
89
|
+
const n = R(u[0]);
|
|
90
|
+
if (n) {
|
|
91
|
+
e = n, s = t.replace(u[0], " ").replace(/[\s.]+$/g, "").replace(/\s+/g, " ").trim();
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!e) {
|
|
97
|
+
const o = s.match(
|
|
98
|
+
/^(\d{4}[\-\/\.\s]\d{1,2}[\-\/\.\s]\d{1,2}|\d{8})\s+(\d{1,6})(\s*[ap]\.?m?\.?)?(?:\s*(?:z|utc|gmt|[+-]\d{2}:?\d{2})\b)?$/i
|
|
99
|
+
);
|
|
100
|
+
if (o) {
|
|
101
|
+
const u = o[2], n = (o[3] || "").replace(/\s+/g, "");
|
|
102
|
+
let d = u + n;
|
|
103
|
+
u.length === 6 && (d = `${u.slice(0, 2)}:${u.slice(2, 4)}:${u.slice(4, 6)}${n}`);
|
|
104
|
+
const f = R(d);
|
|
105
|
+
f && (e = f, s = o[1]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!e) {
|
|
109
|
+
const o = s.match(/^(.+?)\s+(\d{1,2})(\s*[ap]\.?m?\.?)?$/i);
|
|
110
|
+
if (o) {
|
|
111
|
+
const u = o[2] + (o[3] || ""), n = R(u);
|
|
112
|
+
n && $.test(o[1]) && (e = n, s = o[1]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (e && (!s || /^,?\s*$/.test(s))) {
|
|
116
|
+
const o = /* @__PURE__ */ new Date();
|
|
117
|
+
return new Date(o.getFullYear(), o.getMonth(), o.getDate(), e.hour, e.minute, e.second);
|
|
118
|
+
}
|
|
119
|
+
const i = y(s);
|
|
120
|
+
if (isNaN(i.getTime())) return null;
|
|
121
|
+
const l = e || { hour: 0, minute: 0, second: 0 };
|
|
122
|
+
return new Date(i.getFullYear(), i.getMonth(), i.getDate(), l.hour, l.minute, l.second);
|
|
123
|
+
}
|
|
124
|
+
function A(r, t = "YYYY-MM-DD") {
|
|
125
|
+
if (typeof r == "string" && (r = y(r)), isNaN(r.getTime())) return "";
|
|
126
|
+
const e = r.getFullYear(), s = r.getMonth(), a = r.getDate(), i = r.getDay(), l = r.getHours(), o = r.getMinutes(), u = r.getSeconds(), n = r.getMilliseconds(), d = (v, b = 2) => String(v).padStart(b, "0"), f = l % 12 || 12, c = l < 12 ? "AM" : "PM", h = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], m = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], p = {
|
|
127
|
+
YYYY: e,
|
|
128
|
+
YY: String(e).slice(-2),
|
|
129
|
+
MMMM: h[s],
|
|
130
|
+
MMM: h[s].slice(0, 3),
|
|
131
|
+
MM: d(s + 1),
|
|
132
|
+
M: s + 1,
|
|
133
|
+
DD: d(a),
|
|
134
|
+
D: a,
|
|
135
|
+
dddd: m[i],
|
|
136
|
+
ddd: m[i].slice(0, 3),
|
|
137
|
+
dd: m[i].slice(0, 2),
|
|
138
|
+
d: i,
|
|
139
|
+
HH: d(l),
|
|
140
|
+
H: l,
|
|
141
|
+
hh: d(f),
|
|
142
|
+
h: f,
|
|
143
|
+
mm: d(o),
|
|
144
|
+
m: o,
|
|
145
|
+
ss: d(u),
|
|
146
|
+
s: u,
|
|
147
|
+
SSS: d(n, 3),
|
|
148
|
+
A: c,
|
|
149
|
+
a: c.toLowerCase()
|
|
150
|
+
};
|
|
151
|
+
return t.replace(
|
|
152
|
+
/\[([^\]]+)]|YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|dd|d|HH|H|hh|h|mm|m|ss|s|SSS|A|a/g,
|
|
153
|
+
(v, b) => b ?? String(p[v])
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
function Z(r) {
|
|
157
|
+
return typeof r != "string" && !(r instanceof Date) ? !1 : !isNaN(y(r).getTime());
|
|
158
|
+
}
|
|
159
|
+
function q(r) {
|
|
160
|
+
if (typeof r != "string" && !(r instanceof Date)) return !1;
|
|
161
|
+
const t = w(r);
|
|
162
|
+
return t !== null && !isNaN(t.getTime());
|
|
163
|
+
}
|
|
164
|
+
function G(r, t = "YYYY-MMM-DD") {
|
|
165
|
+
const e = y(r);
|
|
166
|
+
return isNaN(e.getTime()) ? "" : A(e, t);
|
|
167
|
+
}
|
|
168
|
+
function j(r, t = "YYYY-MMM-DD h:mm A") {
|
|
169
|
+
const e = w(r);
|
|
170
|
+
return e && !isNaN(e.getTime()) ? A(e, t) : "";
|
|
171
|
+
}
|
|
172
|
+
function X(r, t = "h:mm A") {
|
|
173
|
+
const e = R(r);
|
|
174
|
+
if (!e) return "";
|
|
175
|
+
const s = /* @__PURE__ */ new Date();
|
|
176
|
+
return s.setHours(e.hour, e.minute, e.second, 0), A(s, t);
|
|
177
|
+
}
|
|
178
|
+
function W(r) {
|
|
179
|
+
const t = r.match(/^([+-])?(\d+)([dwmy])$/i);
|
|
180
|
+
if (!t) return null;
|
|
181
|
+
const [, e, s, a] = t, i = parseInt(s) * (e === "-" ? -1 : 1), l = /* @__PURE__ */ new Date();
|
|
182
|
+
switch (l.setHours(0, 0, 0, 0), a.toLowerCase()) {
|
|
183
|
+
case "d":
|
|
184
|
+
l.setDate(l.getDate() + i);
|
|
185
|
+
break;
|
|
186
|
+
case "w":
|
|
187
|
+
l.setDate(l.getDate() + i * 7);
|
|
188
|
+
break;
|
|
189
|
+
case "m":
|
|
190
|
+
l.setMonth(l.getMonth() + i);
|
|
191
|
+
break;
|
|
192
|
+
case "y":
|
|
193
|
+
l.setFullYear(l.getFullYear() + i);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
return l;
|
|
197
|
+
}
|
|
198
|
+
const D = /(\d{1,2}:\d{2}|\d{1,2}\.\d{2}|\b\d{1,2}\s*[ap]\b|\b[ap]\.?m\.?\b|\bnoon\b|\bnow\b|T\d{1,2})/i, J = /(\d{1,2}[-/.]\d{1,2}(?:[-/.]\d{2,4})?|\b\d{4}\b|\b\d{6,8}\b|\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)\b|\btoday\b|\btomorrow\b|\byesterday\b)/i;
|
|
199
|
+
function Q(r, t) {
|
|
200
|
+
if (t === "past")
|
|
201
|
+
return r <= /* @__PURE__ */ new Date();
|
|
202
|
+
if (t === "future")
|
|
203
|
+
return r.getTime() >= (/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0);
|
|
204
|
+
if (t === "today") {
|
|
205
|
+
const n = /* @__PURE__ */ new Date();
|
|
206
|
+
return r.getFullYear() === n.getFullYear() && r.getMonth() === n.getMonth() && r.getDate() === n.getDate();
|
|
207
|
+
}
|
|
208
|
+
const e = (n) => {
|
|
209
|
+
const d = n.trim();
|
|
210
|
+
if (!d) return { date: null, hasTime: !1, valid: !0 };
|
|
211
|
+
const f = W(d);
|
|
212
|
+
if (f) return { date: f, hasTime: !1, valid: !0 };
|
|
213
|
+
const c = d.search(J);
|
|
214
|
+
if (c === -1) return { date: null, hasTime: !1, valid: !1 };
|
|
215
|
+
const h = d.search(D);
|
|
216
|
+
if (h !== -1 && h < c) return { date: null, hasTime: !1, valid: !1 };
|
|
217
|
+
const m = w(d);
|
|
218
|
+
return m && !isNaN(m.getTime()) ? { date: m, hasTime: D.test(d), valid: !0 } : { date: null, hasTime: !1, valid: !1 };
|
|
219
|
+
};
|
|
220
|
+
let s = null;
|
|
221
|
+
if (t.includes(":"))
|
|
222
|
+
for (let n = 0; n < t.length; n += 1) {
|
|
223
|
+
if (t[n] !== ":") continue;
|
|
224
|
+
const d = e(t.slice(0, n)), f = e(t.slice(n + 1));
|
|
225
|
+
if (d.valid && f.valid) {
|
|
226
|
+
s = { start: d, end: f };
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!s) {
|
|
231
|
+
const n = e(t);
|
|
232
|
+
return n.valid && n.date ? n.hasTime ? r.getTime() === n.date.getTime() : r.getFullYear() === n.date.getFullYear() && r.getMonth() === n.date.getMonth() && r.getDate() === n.date.getDate() : !0;
|
|
233
|
+
}
|
|
234
|
+
const a = s.start, i = s.end, l = a.hasTime || i.hasTime ? r.getTime() : new Date(r.getFullYear(), r.getMonth(), r.getDate()).getTime();
|
|
235
|
+
let o = null;
|
|
236
|
+
a.date && (o = a.hasTime ? a.date.getTime() : new Date(a.date.getFullYear(), a.date.getMonth(), a.date.getDate()).getTime());
|
|
237
|
+
let u = null;
|
|
238
|
+
return i.date && (u = i.hasTime ? i.date.getTime() : new Date(i.date.getFullYear(), i.date.getMonth(), i.date.getDate(), 23, 59, 59, 999).getTime()), !(o !== null && l < o || u !== null && l > u);
|
|
239
|
+
}
|
|
240
|
+
function tt(r) {
|
|
241
|
+
return r instanceof HTMLInputElement || r instanceof HTMLSelectElement || r instanceof HTMLTextAreaElement;
|
|
242
|
+
}
|
|
243
|
+
function I(r, t) {
|
|
244
|
+
return typeof t == "string" && (t = [t]), t.includes(r.dataset.type || "") || t.includes(r.type);
|
|
245
|
+
}
|
|
246
|
+
function et(r) {
|
|
247
|
+
return r.length <= 255 && Y.test(r);
|
|
248
|
+
}
|
|
249
|
+
function rt(r) {
|
|
250
|
+
return r.replace(/^[^2-90]+/g, "").replace(/(\d\d\d).*?(\d\d\d).*?(\d\d\d\d)(.*)/, "$1-$2-$3$4");
|
|
251
|
+
}
|
|
252
|
+
function st(r) {
|
|
253
|
+
return k.test(r);
|
|
254
|
+
}
|
|
255
|
+
function S(r) {
|
|
256
|
+
return r.replace(/[^0-9]/g, "");
|
|
257
|
+
}
|
|
258
|
+
function at(r) {
|
|
259
|
+
return B.test(r);
|
|
260
|
+
}
|
|
261
|
+
function _(r) {
|
|
262
|
+
const t = r.replace(/[^\-0-9.]/g, "");
|
|
263
|
+
let e = "", s = !1, a = !1;
|
|
264
|
+
for (let i = 0; i < t.length; i += 1) {
|
|
265
|
+
const l = t[i];
|
|
266
|
+
if (l === "-") {
|
|
267
|
+
!a && e.length === 0 && (e += "-", a = !0);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (l === ".") {
|
|
271
|
+
s || (e += ".", s = !0);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
e += l;
|
|
275
|
+
}
|
|
276
|
+
return e;
|
|
277
|
+
}
|
|
278
|
+
function it(r) {
|
|
279
|
+
return U.test(r);
|
|
280
|
+
}
|
|
281
|
+
const nt = { "": 1, K: 1e3, M: 1e6, G: 1e9, T: 1e12 }, ot = { "": 1, K: 1024, M: 1024 ** 2, G: 1024 ** 3, T: 1024 ** 4 };
|
|
282
|
+
function C(r) {
|
|
283
|
+
const t = r.trim().match(/^(\d+(?:\.\d*)?|\.\d+)\s*(B|Ki?B?|Mi?B?|Gi?B?|Ti?B?)?$/i);
|
|
284
|
+
if (!t) return NaN;
|
|
285
|
+
const e = Number.parseFloat(t[1]), s = t[2]?.toUpperCase() || "", a = s.includes("I"), i = s.replace(/I?B$/i, "").replace(/I$/i, "") || "";
|
|
286
|
+
return e * (a ? ot : nt)[i];
|
|
287
|
+
}
|
|
288
|
+
function H(r, t = !0) {
|
|
289
|
+
const e = t ? 1e3 : 1024, s = ["B", "KB", "MB", "GB", "TB"];
|
|
290
|
+
if (r < e) return `${r} B`;
|
|
291
|
+
let a = 0, i = r;
|
|
292
|
+
for (; i >= e && a < s.length - 1; )
|
|
293
|
+
i /= e, a++;
|
|
294
|
+
let l = Math.round(i * 10) / 10;
|
|
295
|
+
return l >= e && a < s.length - 1 && (l = 1, a++), `${l % 1 === 0 ? l.toFixed(0) : l.toFixed(1)} ${s[a]}`;
|
|
296
|
+
}
|
|
297
|
+
function lt(r) {
|
|
298
|
+
return r = r.trim(), N.test(r) ? r : "https://" + r;
|
|
299
|
+
}
|
|
300
|
+
function ut(r) {
|
|
301
|
+
return F.test(r);
|
|
302
|
+
}
|
|
303
|
+
function dt(r) {
|
|
304
|
+
return r = r.replace(/[^0-9]/g, "").replace(/(.{5})(.*)/, "$1-$2").trim(), r.length === 6 ? r.replace(/-/, "") : r;
|
|
305
|
+
}
|
|
306
|
+
function ct(r) {
|
|
307
|
+
return V.test(r);
|
|
308
|
+
}
|
|
309
|
+
function ht(r) {
|
|
310
|
+
return r.toUpperCase().replace(/[^A-Z0-9]/g, "").replace(/(.{3})\s*(.*)/, "$1 $2").trim();
|
|
311
|
+
}
|
|
312
|
+
function ft(r) {
|
|
313
|
+
return x.test(r);
|
|
314
|
+
}
|
|
315
|
+
function O(r) {
|
|
316
|
+
return ["transparent", "currentColor"].includes(r) ? !0 : !r.trim() || typeof CSS > "u" || !CSS.supports ? !1 : CSS.supports("color", r);
|
|
317
|
+
}
|
|
318
|
+
let M = null;
|
|
319
|
+
const L = /* @__PURE__ */ new Map();
|
|
320
|
+
function mt(r) {
|
|
321
|
+
if (r = r.trim().toLowerCase(), ["transparent", "currentcolor"].includes(r)) return r;
|
|
322
|
+
if (L.has(r)) return L.get(r);
|
|
323
|
+
M || (M = document.createElement("canvas"), M.willReadFrequently = !0);
|
|
324
|
+
const t = M.getContext("2d");
|
|
325
|
+
t.fillStyle = r, t.fillRect(0, 0, 1, 1);
|
|
326
|
+
const e = t.getImageData(0, 0, 1, 1).data, s = "#" + ("000000" + (e[0] << 16 | e[1] << 8 | e[2]).toString(16)).slice(-6);
|
|
327
|
+
return L.set(r, s), s;
|
|
328
|
+
}
|
|
329
|
+
function pt(r) {
|
|
330
|
+
if (typeof r == "boolean") return { valid: r, error: !1, messages: [] };
|
|
331
|
+
if (typeof r == "string") return { valid: !1, error: !1, messages: [r] };
|
|
332
|
+
const t = { valid: r.valid, error: r.error ?? !1, messages: [] };
|
|
333
|
+
return typeof r.message == "string" ? t.messages = [r.message] : typeof r.messages == "string" ? t.messages = [r.messages] : Array.isArray(r.messages) && (t.messages = r.messages), t;
|
|
334
|
+
}
|
|
335
|
+
const T = typeof CSS < "u" && typeof CSS.escape == "function" ? (r) => CSS.escape(r) : (r) => r.replace(/([.#{}()\\?*[\]-])/g, "\\$1");
|
|
336
|
+
class Et extends Event {
|
|
337
|
+
constructor(t, e) {
|
|
338
|
+
super(t, { cancelable: !0 }), this.submitEvent = e;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
class E {
|
|
342
|
+
// Static (global) validator registry
|
|
343
|
+
static globalValidators = {};
|
|
344
|
+
/** Register a validator function globally (available to all instances) */
|
|
345
|
+
static registerValidator(t, e) {
|
|
346
|
+
E.globalValidators[t] = e;
|
|
347
|
+
}
|
|
348
|
+
/** Remove a globally registered validator */
|
|
349
|
+
static unregisterValidator(t) {
|
|
350
|
+
delete E.globalValidators[t];
|
|
351
|
+
}
|
|
352
|
+
/** Get a copy of all globally registered validators */
|
|
353
|
+
static getValidators() {
|
|
354
|
+
return { ...E.globalValidators };
|
|
355
|
+
}
|
|
356
|
+
/** Remove all globally registered validators */
|
|
357
|
+
static clearValidators() {
|
|
358
|
+
E.globalValidators = {};
|
|
359
|
+
}
|
|
360
|
+
/** Validates a single input without needing a Validator instance. */
|
|
361
|
+
static async validateSingle(t, e = {}) {
|
|
362
|
+
if (!t || t.disabled) return !0;
|
|
363
|
+
const s = new E(document.createElement("form"), { autoInit: !1, ...e });
|
|
364
|
+
return s.inputs = [t], s.validateSingle(t);
|
|
365
|
+
}
|
|
366
|
+
/** Clears errors from a single input without needing a Validator instance. */
|
|
367
|
+
static clearInputErrors(t, e = {}) {
|
|
368
|
+
if (!t) return;
|
|
369
|
+
new E(document.createElement("form"), { autoInit: !1, ...e }).clearInputErrors(t);
|
|
370
|
+
}
|
|
371
|
+
form;
|
|
372
|
+
inputs = [];
|
|
373
|
+
// Keeps track of error messages accumulated for each input
|
|
374
|
+
inputErrors = {};
|
|
375
|
+
// Default error messages.
|
|
376
|
+
messages = {
|
|
377
|
+
ERROR_MAIN: "There is a problem with your submission.",
|
|
378
|
+
ERROR_GENERIC: "Enter a valid value.",
|
|
379
|
+
ERROR_REQUIRED: "This field is required.",
|
|
380
|
+
OPTION_REQUIRED: "An option must be selected.",
|
|
381
|
+
CHECKED_REQUIRED: "This must be checked.",
|
|
382
|
+
ERROR_MAXLENGTH: "This must be ${val} characters or fewer.",
|
|
383
|
+
ERROR_MINLENGTH: "This must be at least ${val} characters.",
|
|
384
|
+
ERROR_MIN_VALUE: "The value must be at least ${val}.",
|
|
385
|
+
ERROR_MAX_VALUE: "The value must be at most ${val}.",
|
|
386
|
+
ERROR_NUMBER: "This must be a number.",
|
|
387
|
+
ERROR_INTEGER: "This must be a whole number.",
|
|
388
|
+
ERROR_TEL: "This is not a valid telephone number.",
|
|
389
|
+
ERROR_EMAIL: "This is not a valid email address.",
|
|
390
|
+
ERROR_ZIP: "This is not a valid zip code.",
|
|
391
|
+
ERROR_POSTAL: "This is not a valid postal code.",
|
|
392
|
+
ERROR_DATE: "This is not a valid date.",
|
|
393
|
+
ERROR_DATE_PAST: "The date must be in the past.",
|
|
394
|
+
ERROR_DATE_FUTURE: "The date must be in the future.",
|
|
395
|
+
ERROR_DATE_TODAY: "The date must be today.",
|
|
396
|
+
ERROR_DATE_RANGE: "The date is outside the allowed range.",
|
|
397
|
+
ERROR_DATETIME: "This is not a valid date and time.",
|
|
398
|
+
ERROR_TIME: "This is not a valid time.",
|
|
399
|
+
ERROR_URL: "This is not a valid URL.",
|
|
400
|
+
ERROR_COLOR: "This is not a valid CSS colour.",
|
|
401
|
+
ERROR_FILE_TYPE: "This file type is not allowed.",
|
|
402
|
+
ERROR_FILE_MAX_FILES: "You can upload up to ${val} file(s).",
|
|
403
|
+
ERROR_FILE_MAX_SIZE: "Each file must be ${val} or smaller.",
|
|
404
|
+
ERROR_FILE_MIN_SIZE: "Each file must be at least ${val}.",
|
|
405
|
+
ERROR_CUSTOM_VALIDATION: "There was a problem validating this field."
|
|
406
|
+
};
|
|
407
|
+
// Show debug messages in the console
|
|
408
|
+
debug;
|
|
409
|
+
// Whether validation should be performed immediately on instantiation
|
|
410
|
+
autoInit;
|
|
411
|
+
// Whether to prevent the form from submitting if validation is successful
|
|
412
|
+
preventSubmit = !1;
|
|
413
|
+
// Class toggled hide an element (eg display:none)
|
|
414
|
+
hiddenClasses;
|
|
415
|
+
// Whether to show the main error message
|
|
416
|
+
showMainError = !0;
|
|
417
|
+
// Whether to scroll to the first error on validation failure
|
|
418
|
+
scrollToError = !1;
|
|
419
|
+
// Delay in ms before scrolling to the first error (allows animations to complete)
|
|
420
|
+
scrollToErrorDelay = 0;
|
|
421
|
+
// Whether to validate inputs when they lose focus (even if unchanged)
|
|
422
|
+
validateOnBlur = !1;
|
|
423
|
+
// Classes to apply to the main error message (space-separated)
|
|
424
|
+
errorMainClasses;
|
|
425
|
+
// Classes added to an invalid input (space-separated)
|
|
426
|
+
errorInputClasses;
|
|
427
|
+
// Timeout for dispatching events on input (used by syncColorInput)
|
|
428
|
+
dispatchTimeout = 0;
|
|
429
|
+
// Pre-split class arrays for performance (avoid repeated .split(' ') calls)
|
|
430
|
+
hiddenClassesArray = [];
|
|
431
|
+
errorMainClassesArray = [];
|
|
432
|
+
errorInputClassesArray = [];
|
|
433
|
+
// Whether the original form has a novalidate attribute
|
|
434
|
+
originalNoValidate = !1;
|
|
435
|
+
validationSuccessCallback;
|
|
436
|
+
validationErrorCallback;
|
|
437
|
+
// Instance validator registry (highest priority lookup)
|
|
438
|
+
validators = {};
|
|
439
|
+
// Sets defaults and adds event listeners
|
|
440
|
+
constructor(t, e = {}) {
|
|
441
|
+
if (!t) throw new Error("Validator requires a form to be passed as the first argument.");
|
|
442
|
+
if (!(t instanceof HTMLFormElement))
|
|
443
|
+
throw new Error("form argument must be an instance of HTMLFormElement");
|
|
444
|
+
this.form = t, (t.dataset.preventSubmit === "" || t.dataset.preventSubmit) && (this.preventSubmit = !0), Object.assign(this.messages, e.messages || {}), this.debug = e.debug || !1, this.autoInit = e.autoInit !== !1, this.preventSubmit = e.preventSubmit !== void 0 ? e.preventSubmit : this.preventSubmit, this.hiddenClasses = e.hiddenClasses || "hidden opacity-0", this.errorMainClasses = e.errorMainClasses || "m-2 border border-red-500 bg-red-100 p-3 dark:bg-red-900/80 text-center", this.errorInputClasses = e.errorInputClasses || "border-red-600 dark:border-red-500", this.showMainError = e.showMainError !== void 0 ? e.showMainError : !0, this.scrollToError = e.scrollToError || !1, this.scrollToErrorDelay = e.scrollToErrorDelay || 0, this.validateOnBlur = e.validateOnBlur || !1, this.hiddenClassesArray = this.hiddenClasses.split(" ").filter(Boolean), this.errorMainClassesArray = this.errorMainClasses.split(" ").filter(Boolean), this.errorInputClassesArray = this.errorInputClasses.split(" ").filter(Boolean), this.validationSuccessCallback = e.validationSuccessCallback || (() => {
|
|
445
|
+
}), this.validationErrorCallback = e.validationErrorCallback || (() => {
|
|
446
|
+
}), e.validators && Object.assign(this.validators, e.validators), this.autoInit && this.init();
|
|
447
|
+
}
|
|
448
|
+
// Event handler references
|
|
449
|
+
submitHandlerRef = this.submitHandler.bind(this);
|
|
450
|
+
inputInputHandlerRef = this.inputInputHandler.bind(this);
|
|
451
|
+
inputChangeHandlerRef = this.inputChangeHandler.bind(this);
|
|
452
|
+
inputBlurHandlerRef = this.inputBlurHandler.bind(this);
|
|
453
|
+
inputKeydownHandlerRef = this.inputKeydownHandler.bind(this);
|
|
454
|
+
addEventListeners() {
|
|
455
|
+
this.form.addEventListener("submit", this.submitHandlerRef), this.form.addEventListener("input", this.inputInputHandlerRef), this.form.addEventListener("change", this.inputChangeHandlerRef), this.form.addEventListener("keydown", this.inputKeydownHandlerRef), this.validateOnBlur && this.form.addEventListener("blur", this.inputBlurHandlerRef, !0);
|
|
456
|
+
}
|
|
457
|
+
removeEventListeners() {
|
|
458
|
+
this.form.removeEventListener("submit", this.submitHandlerRef), this.form.removeEventListener("input", this.inputInputHandlerRef), this.form.removeEventListener("change", this.inputChangeHandlerRef), this.form.removeEventListener("keydown", this.inputKeydownHandlerRef), this.form.removeEventListener("blur", this.inputBlurHandlerRef, !0);
|
|
459
|
+
}
|
|
460
|
+
// Adds event listeners to all formFields in a specified form
|
|
461
|
+
init() {
|
|
462
|
+
this.inputErrors = {}, this.inputs = Array.from(this.form.elements).filter(
|
|
463
|
+
(t) => t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement
|
|
464
|
+
), this.inputs.forEach((t) => {
|
|
465
|
+
!t.name && !t.id && (t.id = `vl-input-${Math.random().toString(36).slice(2)}`), this.inputErrors[t.name || t.id] = [];
|
|
466
|
+
}), this.originalNoValidate = this.form.hasAttribute("novalidate"), this.form.setAttribute("novalidate", "novalidate"), this.removeEventListeners(), this.addEventListeners();
|
|
467
|
+
}
|
|
468
|
+
// end init()
|
|
469
|
+
getErrorEl(t) {
|
|
470
|
+
const e = (o) => this.form.querySelector(`#${T(o)}`) || document.getElementById(o) || null, s = t.closest("[data-flux-field]");
|
|
471
|
+
if (s) {
|
|
472
|
+
const o = s.querySelector("[data-flux-error]");
|
|
473
|
+
if (o) return o;
|
|
474
|
+
}
|
|
475
|
+
const a = t.getAttribute("aria-describedby");
|
|
476
|
+
if (a) {
|
|
477
|
+
const o = e(a);
|
|
478
|
+
if (o) return o;
|
|
479
|
+
}
|
|
480
|
+
const i = e(t.id + "-error");
|
|
481
|
+
return i || e(t.name + "-error") || null;
|
|
482
|
+
}
|
|
483
|
+
addErrorMain(t) {
|
|
484
|
+
let e = this._getMainErrorElement();
|
|
485
|
+
e || (e = document.createElement("div"), e.id = "form-error-main", this.form.appendChild(e)), e.classList.add(...this.errorMainClassesArray), e.innerHTML = t || this.messages.ERROR_MAIN, e.classList.remove(...this.hiddenClassesArray);
|
|
486
|
+
}
|
|
487
|
+
// Helper method to find the main error element
|
|
488
|
+
_getMainErrorElement() {
|
|
489
|
+
if (this.form.id) {
|
|
490
|
+
const t = `${this.form.id}-error-main`, e = document.getElementById(t);
|
|
491
|
+
if (e)
|
|
492
|
+
return e;
|
|
493
|
+
}
|
|
494
|
+
return this.form.querySelector("#form-error-main");
|
|
495
|
+
}
|
|
496
|
+
// Adds an error to the array of strings to be displayed by an input that failed
|
|
497
|
+
addInputError(t, e = t.dataset.errorDefault || this.messages.ERROR_GENERIC) {
|
|
498
|
+
const s = t.name || t.id;
|
|
499
|
+
this.debug && console.log("Invalid value for " + s + ": " + e), s in this.inputErrors || (this.inputErrors[s] = []), this.inputErrors[s].includes(e) || this.inputErrors[s].push(e);
|
|
500
|
+
}
|
|
501
|
+
// Shows an error message in a container with the input's id suffixed with -error.
|
|
502
|
+
// A future version of this could inject an error element into the DOM if it doesn't exist
|
|
503
|
+
showInputErrors(t) {
|
|
504
|
+
if (!t || !t.name && !t.id) return;
|
|
505
|
+
const e = t.name || t.id, s = e in this.inputErrors ? this.inputErrors[e] : [];
|
|
506
|
+
if (!s.length) return;
|
|
507
|
+
t.setAttribute("aria-invalid", "true"), t.classList.add(...this.errorInputClassesArray);
|
|
508
|
+
let a = this.getErrorEl(t);
|
|
509
|
+
a && (a.innerHTML = s.join("<br>"), a.classList.remove(...this.hiddenClassesArray));
|
|
510
|
+
}
|
|
511
|
+
// Shows all the error messages for all the inputs of the form, and a main error message
|
|
512
|
+
showFormErrors() {
|
|
513
|
+
this.inputs.forEach((e) => this.showInputErrors(e));
|
|
514
|
+
const t = this.inputs.find((e) => this.inputErrors[e.name || e.id]?.length > 0);
|
|
515
|
+
if (this.showMainError && Object.values(this.inputErrors).some((e) => Array.isArray(e) && e.length)) {
|
|
516
|
+
let e = this._getMainErrorElement();
|
|
517
|
+
e ? (e.innerHTML || (e.innerHTML = this.messages.ERROR_MAIN), e.classList.remove(...this.hiddenClassesArray)) : this.addErrorMain();
|
|
518
|
+
}
|
|
519
|
+
if (this.scrollToError && t) {
|
|
520
|
+
const e = () => {
|
|
521
|
+
t.scrollIntoView({ behavior: "smooth", block: "center" }), t.focus({ preventScroll: !0 });
|
|
522
|
+
};
|
|
523
|
+
this.scrollToErrorDelay > 0 ? setTimeout(e, this.scrollToErrorDelay) : e();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/** Clears error messages from an input and removes its errors from the inputErrors array */
|
|
527
|
+
clearInputErrors(t) {
|
|
528
|
+
const e = t.name || t.id;
|
|
529
|
+
if (this.inputErrors[e] = [], t instanceof HTMLInputElement && (t.type === "checkbox" || t.type === "radio") && t.name) {
|
|
530
|
+
const a = T(t.name);
|
|
531
|
+
if (this.form.querySelector(`input[name="${a}"]:checked`)) {
|
|
532
|
+
this.form.querySelectorAll(`input[name="${a}"]`).forEach((o) => {
|
|
533
|
+
o.removeAttribute("aria-invalid"), o.classList.remove(...this.errorInputClassesArray);
|
|
534
|
+
});
|
|
535
|
+
const l = this.getErrorEl(t);
|
|
536
|
+
l && (l.classList.add(...this.hiddenClassesArray), l.textContent = "");
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
t.removeAttribute("aria-invalid"), t.classList.remove(...this.errorInputClassesArray);
|
|
541
|
+
let s = this.getErrorEl(t);
|
|
542
|
+
s && (s.classList.add(...this.hiddenClassesArray), s.textContent = "");
|
|
543
|
+
}
|
|
544
|
+
/** Clears the main error banner and all input errors */
|
|
545
|
+
clearAllErrors() {
|
|
546
|
+
const t = this._getMainErrorElement();
|
|
547
|
+
t && t.classList.add(...this.hiddenClassesArray), this.inputs.forEach((e) => this.clearInputErrors(e));
|
|
548
|
+
}
|
|
549
|
+
hasInputValue(t) {
|
|
550
|
+
return t instanceof HTMLInputElement && t.type === "file" ? !!t.files && t.files.length > 0 : t.value.length > 0;
|
|
551
|
+
}
|
|
552
|
+
// Validates a required input and returns true if it's valid.
|
|
553
|
+
// Shows an error if the input is required and empty.
|
|
554
|
+
validateRequired(t) {
|
|
555
|
+
let e = !0;
|
|
556
|
+
const s = t instanceof HTMLInputElement && ["checkbox", "radio"].includes(t.type), i = t instanceof HTMLInputElement && t.type === "file" ? !this.hasInputValue(t) : t.value === "" || s && !t.checked;
|
|
557
|
+
if (t.required && i)
|
|
558
|
+
if (s) {
|
|
559
|
+
let l = !1, o = t.name;
|
|
560
|
+
const u = this.form.querySelectorAll(`input[name="${o}"]`);
|
|
561
|
+
if (u.forEach((n) => {
|
|
562
|
+
if (n instanceof HTMLInputElement && n.checked === !0) {
|
|
563
|
+
l = !0;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
}), l === !1) {
|
|
567
|
+
e = !1;
|
|
568
|
+
let n = u.length > 1 ? this.messages.OPTION_REQUIRED : this.messages.CHECKED_REQUIRED;
|
|
569
|
+
t.dataset.errorDefault && (n = t.dataset.errorDefault), this.addInputError(t, n);
|
|
570
|
+
}
|
|
571
|
+
} else tt(t) && (e = !1, this.addInputError(t, t.dataset.errorDefault || this.messages.ERROR_REQUIRED));
|
|
572
|
+
return e;
|
|
573
|
+
}
|
|
574
|
+
// end validateRequired
|
|
575
|
+
// Validates a min and max length
|
|
576
|
+
validateLength(t) {
|
|
577
|
+
let e = !0;
|
|
578
|
+
if (t.disabled || t instanceof HTMLInputElement && t.type === "file") return e;
|
|
579
|
+
if ((t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) && t.value.length) {
|
|
580
|
+
let s = t.minLength > 0 ? t.minLength : t.dataset.minLength ? parseInt(t.dataset.minLength) : 0, a = t.maxLength > 0 && t.maxLength < 5e5 ? t.maxLength : t.dataset.maxLength ? parseInt(t.dataset.maxLength) : 1 / 0;
|
|
581
|
+
s > 0 && t.value.length < s && (e = !1, this.addInputError(
|
|
582
|
+
t,
|
|
583
|
+
this.messages.ERROR_MINLENGTH.replace("${val}", s.toString())
|
|
584
|
+
)), t.value.length > a && (e = !1, this.addInputError(
|
|
585
|
+
t,
|
|
586
|
+
this.messages.ERROR_MAXLENGTH.replace("${val}", a.toString())
|
|
587
|
+
));
|
|
588
|
+
}
|
|
589
|
+
return e;
|
|
590
|
+
}
|
|
591
|
+
// Validates min/max numeric value constraints
|
|
592
|
+
validateValue(t) {
|
|
593
|
+
let e = !0;
|
|
594
|
+
if (t.disabled || !(t instanceof HTMLInputElement) || !t.value.length) return e;
|
|
595
|
+
const s = ["number", "integer", "float", "decimal"], a = t.dataset.type || t.type;
|
|
596
|
+
if (!s.includes(a) && !s.includes(t.type)) return e;
|
|
597
|
+
const i = parseFloat(t.value);
|
|
598
|
+
if (isNaN(i)) return e;
|
|
599
|
+
const l = t.dataset.min ?? t.min, o = t.dataset.max ?? t.max;
|
|
600
|
+
if (l !== void 0 && l !== "") {
|
|
601
|
+
const u = parseFloat(l);
|
|
602
|
+
!isNaN(u) && i < u && (e = !1, this.addInputError(t, this.messages.ERROR_MIN_VALUE.replace("${val}", l)));
|
|
603
|
+
}
|
|
604
|
+
if (o !== void 0 && o !== "") {
|
|
605
|
+
const u = parseFloat(o);
|
|
606
|
+
!isNaN(u) && i > u && (e = !1, this.addInputError(t, this.messages.ERROR_MAX_VALUE.replace("${val}", o)));
|
|
607
|
+
}
|
|
608
|
+
return e;
|
|
609
|
+
}
|
|
610
|
+
// A map of input handlers that can be used for each type of input.
|
|
611
|
+
// errorKey references this.messages at validation time to support custom messages
|
|
612
|
+
// Handler for number types (shared by float/decimal aliases)
|
|
613
|
+
numberHandler = {
|
|
614
|
+
parse: _,
|
|
615
|
+
isValid: it,
|
|
616
|
+
errorKey: "ERROR_NUMBER"
|
|
617
|
+
};
|
|
618
|
+
inputHandlers = {
|
|
619
|
+
number: this.numberHandler,
|
|
620
|
+
float: this.numberHandler,
|
|
621
|
+
decimal: this.numberHandler,
|
|
622
|
+
integer: {
|
|
623
|
+
parse: S,
|
|
624
|
+
isValid: at,
|
|
625
|
+
errorKey: "ERROR_INTEGER"
|
|
626
|
+
},
|
|
627
|
+
tel: {
|
|
628
|
+
parse: rt,
|
|
629
|
+
isValid: st,
|
|
630
|
+
errorKey: "ERROR_TEL"
|
|
631
|
+
},
|
|
632
|
+
email: {
|
|
633
|
+
parse: (t) => t.trim(),
|
|
634
|
+
isValid: et,
|
|
635
|
+
errorKey: "ERROR_EMAIL"
|
|
636
|
+
},
|
|
637
|
+
zip: {
|
|
638
|
+
parse: dt,
|
|
639
|
+
isValid: ct,
|
|
640
|
+
errorKey: "ERROR_ZIP"
|
|
641
|
+
},
|
|
642
|
+
postal: {
|
|
643
|
+
parse: ht,
|
|
644
|
+
isValid: ft,
|
|
645
|
+
errorKey: "ERROR_POSTAL"
|
|
646
|
+
},
|
|
647
|
+
url: {
|
|
648
|
+
parse: lt,
|
|
649
|
+
isValid: ut,
|
|
650
|
+
errorKey: "ERROR_URL"
|
|
651
|
+
},
|
|
652
|
+
date: {
|
|
653
|
+
parse: G,
|
|
654
|
+
isValid: Z,
|
|
655
|
+
errorKey: "ERROR_DATE"
|
|
656
|
+
},
|
|
657
|
+
datetime: {
|
|
658
|
+
parse: j,
|
|
659
|
+
isValid: q,
|
|
660
|
+
errorKey: "ERROR_DATETIME"
|
|
661
|
+
},
|
|
662
|
+
time: {
|
|
663
|
+
parse: X,
|
|
664
|
+
isValid: K,
|
|
665
|
+
errorKey: "ERROR_TIME"
|
|
666
|
+
},
|
|
667
|
+
color: {
|
|
668
|
+
parse: (t) => t.trim().toLowerCase(),
|
|
669
|
+
isValid: O,
|
|
670
|
+
errorKey: "ERROR_COLOR"
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
validateInputType(t) {
|
|
674
|
+
const e = t.dataset.type || t.type, s = this.inputHandlers[t.type] || this.inputHandlers[e];
|
|
675
|
+
if (s) {
|
|
676
|
+
const a = t.dataset.dateFormat || t.dataset.timeFormat, i = s.parse(t.value, a), l = ["date", "time", "datetime-local", "month", "week"];
|
|
677
|
+
if (i.length && !l.includes(t.type) && (t.value = i), !s.isValid(t.value))
|
|
678
|
+
return this.addInputError(t, this.messages[s.errorKey]), !1;
|
|
679
|
+
}
|
|
680
|
+
return !0;
|
|
681
|
+
}
|
|
682
|
+
validateDateRange(t) {
|
|
683
|
+
if (t.dataset.dateRange) {
|
|
684
|
+
const e = t.dataset.dateRange, s = y(t.value);
|
|
685
|
+
if (!isNaN(s.getTime()) && !Q(s, e)) {
|
|
686
|
+
let a = t.dataset.errorDefault || this.messages.ERROR_DATE_RANGE;
|
|
687
|
+
return e === "past" ? a = this.messages.ERROR_DATE_PAST : e === "future" ? a = this.messages.ERROR_DATE_FUTURE : e === "today" && (a = this.messages.ERROR_DATE_TODAY), this.addInputError(t, a), !1;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return !0;
|
|
691
|
+
}
|
|
692
|
+
parseAcceptList(t) {
|
|
693
|
+
const e = [], s = [];
|
|
694
|
+
for (const a of t.split(",")) {
|
|
695
|
+
const i = a.trim().toLowerCase();
|
|
696
|
+
i && (i.startsWith(".") ? s.push(i) : i.includes("/") && e.push(i));
|
|
697
|
+
}
|
|
698
|
+
return { mimeTypes: e, extensions: s };
|
|
699
|
+
}
|
|
700
|
+
validateFileInput(t) {
|
|
701
|
+
if (t.type !== "file") return !0;
|
|
702
|
+
const e = Array.from(t.files || []);
|
|
703
|
+
if (!e.length) return !0;
|
|
704
|
+
let s = !0;
|
|
705
|
+
const a = Number.parseInt(t.dataset.maxFiles || "", 10);
|
|
706
|
+
Number.isFinite(a) && a >= 0 && e.length > a && (this.addInputError(
|
|
707
|
+
t,
|
|
708
|
+
this.messages.ERROR_FILE_MAX_FILES.replace("${val}", a.toString())
|
|
709
|
+
), s = !1);
|
|
710
|
+
const i = t.dataset.minFileSize || "";
|
|
711
|
+
if (i) {
|
|
712
|
+
const u = C(i);
|
|
713
|
+
Number.isNaN(u) ? (this.debug && console.warn(`Validator: Invalid min-file-size "${i}"`), this.addInputError(t, this.messages.ERROR_FILE_MIN_SIZE.replace("${val}", i)), s = !1) : e.some((d) => d.size < u) && (this.addInputError(
|
|
714
|
+
t,
|
|
715
|
+
this.messages.ERROR_FILE_MIN_SIZE.replace("${val}", H(u))
|
|
716
|
+
), s = !1);
|
|
717
|
+
}
|
|
718
|
+
const l = t.dataset.maxFileSize || "";
|
|
719
|
+
if (l) {
|
|
720
|
+
const u = C(l);
|
|
721
|
+
Number.isNaN(u) ? (this.debug && console.warn(`Validator: Invalid max-file-size "${l}"`), this.addInputError(t, this.messages.ERROR_FILE_MAX_SIZE.replace("${val}", l)), s = !1) : e.some((d) => d.size > u) && (this.addInputError(
|
|
722
|
+
t,
|
|
723
|
+
this.messages.ERROR_FILE_MAX_SIZE.replace("${val}", H(u))
|
|
724
|
+
), s = !1);
|
|
725
|
+
}
|
|
726
|
+
const o = (t.dataset.accept ?? t.accept ?? "").trim();
|
|
727
|
+
if (o) {
|
|
728
|
+
const { mimeTypes: u, extensions: n } = this.parseAcceptList(o);
|
|
729
|
+
if (u.length || n.length) {
|
|
730
|
+
const d = (c) => u.some((h) => h === "*/*" ? !0 : h.endsWith("/*") ? c.startsWith(h.slice(0, -1)) : h === c), f = (c) => {
|
|
731
|
+
const h = c.type.toLowerCase(), m = c.name.toLowerCase(), p = h && u.length ? d(h) : !1, v = n.length ? n.some((b) => m.endsWith(b)) : !1;
|
|
732
|
+
return p || v;
|
|
733
|
+
};
|
|
734
|
+
e.some((c) => !f(c)) && (this.addInputError(t, this.messages.ERROR_FILE_TYPE), s = !1);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return s;
|
|
738
|
+
}
|
|
739
|
+
// Validates a pattern from data-pattern or pattern; data-pattern takes precedence
|
|
740
|
+
// Anchors pattern to match HTML5 pattern attribute behavior (full value must match)
|
|
741
|
+
validatePattern(t) {
|
|
742
|
+
if (t instanceof HTMLInputElement && t.type === "file") return !0;
|
|
743
|
+
const e = t.dataset.pattern || t instanceof HTMLInputElement && t.pattern || null;
|
|
744
|
+
if (!e) return !0;
|
|
745
|
+
let s;
|
|
746
|
+
try {
|
|
747
|
+
s = new RegExp(`^(?:${e})$`);
|
|
748
|
+
} catch {
|
|
749
|
+
return !0;
|
|
750
|
+
}
|
|
751
|
+
return s.test(t.value) ? !0 : (this.addInputError(t), !1);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Resolves a validator function by name using three-tier lookup:
|
|
755
|
+
* 1. Instance registry (this.validators)
|
|
756
|
+
* 2. Static registry (Validator.globalValidators)
|
|
757
|
+
* 3. Window object (legacy fallback)
|
|
758
|
+
*/
|
|
759
|
+
resolveValidator(t) {
|
|
760
|
+
if (this.validators[t]) return this.validators[t];
|
|
761
|
+
if (E.globalValidators[t]) return E.globalValidators[t];
|
|
762
|
+
const e = window[t];
|
|
763
|
+
if (typeof e == "function") return e;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Specify a custom function in data-validation and it gets called to validate the input
|
|
767
|
+
* The custom function can return
|
|
768
|
+
* - a boolean
|
|
769
|
+
* - a Promise that resolves to a boolean
|
|
770
|
+
* - an object with a valid property that is a boolean
|
|
771
|
+
* - a Promise that resolves to an object with a valid property that is a boolean
|
|
772
|
+
* - and optionally a messages property that is a string or array of strings
|
|
773
|
+
* - OR optionally, a message property that is a string
|
|
774
|
+
* - optionaly, a boolean error property that is true if something went wrong
|
|
775
|
+
*/
|
|
776
|
+
async validateCustom(t) {
|
|
777
|
+
if (t.disabled) return !0;
|
|
778
|
+
const e = t.dataset.validation;
|
|
779
|
+
if (!e || typeof e != "string") return !0;
|
|
780
|
+
const s = this.resolveValidator(e);
|
|
781
|
+
if (!s) return !0;
|
|
782
|
+
let a;
|
|
783
|
+
try {
|
|
784
|
+
a = await Promise.resolve(s(t.value)), a = pt(a);
|
|
785
|
+
} catch {
|
|
786
|
+
return this.addInputError(t, this.messages.ERROR_CUSTOM_VALIDATION), !1;
|
|
787
|
+
}
|
|
788
|
+
const i = a.messages.join("<br>") || this.messages.ERROR_CUSTOM_VALIDATION;
|
|
789
|
+
return a.valid || this.addInputError(t, i), a.valid;
|
|
790
|
+
}
|
|
791
|
+
// Validates an input with a value and returns true if it's valid
|
|
792
|
+
// Checks inputs defined in the inputHandlers map, pattern, and date range,
|
|
793
|
+
async validateInput(t) {
|
|
794
|
+
if (!(t instanceof HTMLInputElement) || !this.hasInputValue(t)) return !0;
|
|
795
|
+
let e = !0;
|
|
796
|
+
const s = t.type === "file";
|
|
797
|
+
return t.disabled || (s ? e = this.validateFileInput(t) && e : (e = this.validateInputType(t) && e, e = this.validateDateRange(t) && e, e = this.validatePattern(t) && e), e = await this.validateCustom(t) && e), e;
|
|
798
|
+
}
|
|
799
|
+
// Validates all the fields in the form. It will show an error message
|
|
800
|
+
// in all invalid fields and return false if any are invalid.
|
|
801
|
+
async validate(t) {
|
|
802
|
+
let e = !0;
|
|
803
|
+
for (const s of this.inputs)
|
|
804
|
+
s.disabled || (e = this.validateRequired(s) && e, e = this.validateLength(s) && e, e = this.validateValue(s) && e, e = await this.validateInput(s) && e, this.hasInputValue(s) || (e = await this.validateCustom(s) && e));
|
|
805
|
+
return e;
|
|
806
|
+
}
|
|
807
|
+
//end validate()
|
|
808
|
+
/**
|
|
809
|
+
* Validates a single input programmatically and displays any error messages.
|
|
810
|
+
* Useful for validating inputs on demand (e.g., in multi-step forms or custom UI flows).
|
|
811
|
+
* @param input The input element to validate
|
|
812
|
+
* @returns A promise that resolves to true if the input is valid, false otherwise
|
|
813
|
+
*/
|
|
814
|
+
async validateSingle(t) {
|
|
815
|
+
if (!t || t.disabled) return !0;
|
|
816
|
+
if (!this.inputs.includes(t))
|
|
817
|
+
return console.warn(
|
|
818
|
+
"Validator.validateSingle(): input is not in this form. Use static Validator.validateSingle() for standalone inputs.",
|
|
819
|
+
t
|
|
820
|
+
), !0;
|
|
821
|
+
this.clearInputErrors(t);
|
|
822
|
+
let e = !0;
|
|
823
|
+
return e = this.validateRequired(t) && e, e = this.validateLength(t) && e, e = this.validateValue(t) && e, e = await this.validateInput(t) && e, this.hasInputValue(t) || (e = await this.validateCustom(t) && e), this.showInputErrors(t), e;
|
|
824
|
+
}
|
|
825
|
+
isSubmitting = !1;
|
|
826
|
+
async submitHandler(t) {
|
|
827
|
+
if (this.isSubmitting) return;
|
|
828
|
+
t.preventDefault(), this.clearAllErrors();
|
|
829
|
+
let e = await this.validate(t);
|
|
830
|
+
this.showFormErrors();
|
|
831
|
+
const s = new Et(e ? "validationSuccess" : "validationError", t);
|
|
832
|
+
this.form.dispatchEvent(s), e ? this.validationSuccessCallback && this.validationSuccessCallback(t) : this.validationErrorCallback && this.validationErrorCallback(t), e && !this.preventSubmit && (this.isSubmitting = !0, s.defaultPrevented || this.form.submit(), this.isSubmitting = !1);
|
|
833
|
+
}
|
|
834
|
+
// Skip inputs that have a data-novalidate attribute
|
|
835
|
+
// Useful when you don't want to return a validationSuccess event when a specific input is changed
|
|
836
|
+
shouldSkipValidation(t) {
|
|
837
|
+
return typeof t.dataset.novalidate > "u" ? !1 : t.dataset.novalidate === "" ? !0 : t.dataset.novalidate === "true";
|
|
838
|
+
}
|
|
839
|
+
async inputChangeHandler(t) {
|
|
840
|
+
const e = t.target;
|
|
841
|
+
!(e instanceof HTMLInputElement || e instanceof HTMLSelectElement || e instanceof HTMLTextAreaElement) || this.shouldSkipValidation(e) || (this.clearInputErrors(e), this.validateLength(e), await this.validateInput(e), this.showInputErrors(e));
|
|
842
|
+
}
|
|
843
|
+
// Validates on blur even if value unchanged (catches touched-but-empty required fields)
|
|
844
|
+
async inputBlurHandler(t) {
|
|
845
|
+
const e = t.target;
|
|
846
|
+
!(e instanceof HTMLInputElement || e instanceof HTMLSelectElement || e instanceof HTMLTextAreaElement) || this.shouldSkipValidation(e) || (this.clearInputErrors(e), this.validateRequired(e), this.validateLength(e), this.validateValue(e), await this.validateInput(e), this.hasInputValue(e) || await this.validateCustom(e), this.showInputErrors(e));
|
|
847
|
+
}
|
|
848
|
+
inputInputHandler(t) {
|
|
849
|
+
const e = t.target;
|
|
850
|
+
this.shouldSkipValidation(e) || (I(e, "integer") && (e.value = S(e.value)), e.type !== "number" && I(e, ["number", "float", "decimal"]) && (e.value = _(e.value)), I(e, "color") && this.syncColorInput(t));
|
|
851
|
+
}
|
|
852
|
+
// Sync color inputs (data-type="color") with an associated native color input type
|
|
853
|
+
syncColorInput(t) {
|
|
854
|
+
let e = t.target, s = e;
|
|
855
|
+
if (e.type === "color" && (s = this.form.querySelector(`#${T(e.id.replace(/-color$/, ""))}`), !s))
|
|
856
|
+
return;
|
|
857
|
+
let a = this.form.querySelector(
|
|
858
|
+
`#${T(s.id)}-color-label`
|
|
859
|
+
);
|
|
860
|
+
if ((e.dataset.type || "") === "color") {
|
|
861
|
+
let i = this.form.querySelector(
|
|
862
|
+
`input#${T(e.id)}-color`
|
|
863
|
+
);
|
|
864
|
+
if (!i || !O(e.value)) return;
|
|
865
|
+
i.value = mt(e.value);
|
|
866
|
+
}
|
|
867
|
+
e.type === "color" && s && (s.value = e.value), a && (a.style.backgroundColor = e.value), clearTimeout(this.dispatchTimeout), this.dispatchTimeout = window.setTimeout(() => {
|
|
868
|
+
s.dispatchEvent(new Event("change", { bubbles: !0 }));
|
|
869
|
+
}, 200);
|
|
870
|
+
}
|
|
871
|
+
// Support using arrow keys to increment/decrement numeric fields.
|
|
872
|
+
// Use data-arrow-step to customize step size, or set to "" to disable.
|
|
873
|
+
inputKeydownHandler(t) {
|
|
874
|
+
if (!(t.target instanceof HTMLInputElement) || t.key !== "ArrowUp" && t.key !== "ArrowDown") return;
|
|
875
|
+
const e = t.target, s = I(e, "integer"), a = I(e, ["number", "float", "decimal"]);
|
|
876
|
+
if (!s && !a || e.dataset.arrowStep === "") return;
|
|
877
|
+
t.preventDefault();
|
|
878
|
+
const i = parseFloat(e.dataset.arrowStep ?? "1") || 1, l = parseFloat(e.value) || 0, o = t.key === "ArrowUp" ? i : -i;
|
|
879
|
+
let u = l + o;
|
|
880
|
+
const n = e.dataset.min ?? e.min, d = e.dataset.max ?? e.max, f = n !== "" ? parseFloat(n) : s ? 0 : -1 / 0, c = d !== "" ? parseFloat(d) : 1 / 0;
|
|
881
|
+
isNaN(f) || (u = Math.max(f, u)), isNaN(c) || (u = Math.min(c, u));
|
|
882
|
+
const h = (i.toString().split(".")[1] || "").length, m = (e.value.split(".")[1] || "").length, p = Math.max(h, m);
|
|
883
|
+
e.value = s ? Math.round(u).toString() : p ? u.toFixed(p) : u.toString();
|
|
884
|
+
}
|
|
885
|
+
destroy() {
|
|
886
|
+
clearTimeout(this.dispatchTimeout), this.removeEventListeners(), this.originalNoValidate || this.form.removeAttribute("novalidate");
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
const gt = E;
|
|
890
|
+
export {
|
|
891
|
+
Et as ValidationEvent,
|
|
892
|
+
gt as default
|
|
893
|
+
};
|