@jdlien/validator 1.5.0 → 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.
@@ -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
+ };