kilvalidate 1.0.70 → 1.0.71
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/README.md +324 -6
- package/kilvish.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,15 +78,37 @@ Example: data-kilvish-file="pdf_1_4" restricts the selection to between 1 and 4
|
|
|
78
78
|
|
|
79
79
|
Example: // limits to 1–4 files
|
|
80
80
|
|
|
81
|
-
data-kilvish-password="8_20" for min and max pass
|
|
82
|
-
Usage: To use these validation attributes, simply add the appropriate data-kilvish-* attribute to the tag in your HTML form. The validation will be applied automatically based on the specified rules.
|
|
83
81
|
|
|
84
|
-
Example:
|
|
85
82
|
|
|
86
|
-
Phone Number (7-20 digits):
|
|
87
|
-
Username (letters and numbers):
|
|
88
83
|
|
|
89
|
-
Password
|
|
84
|
+
## Password
|
|
85
|
+
|
|
86
|
+
### Length & composition
|
|
87
|
+
- `data-kilvish-password="MIN_MAX"` – requires:
|
|
88
|
+
- at least one lowercase, one uppercase, one digit, one special character
|
|
89
|
+
- **no sequential runs** like `1234` or `abcd`
|
|
90
|
+
- Example: `data-kilvish-password="8_20"` ⇒ min 8, max 20.
|
|
91
|
+
|
|
92
|
+
### Eye icon (show/hide)
|
|
93
|
+
An eye icon is auto-injected at the end of every password field and stays inside the input (works with/without `.input-group`).
|
|
94
|
+
|
|
95
|
+
**Attributes**
|
|
96
|
+
- `data-kileye-show` — behavior mode:
|
|
97
|
+
- `hold` *(default if omitted)*: press & hold to show; release/blur to hide
|
|
98
|
+
- `toggle`: click to show/hide
|
|
99
|
+
- `fix`: start visible; click to hide/show (no holding needed)
|
|
100
|
+
- `data-kileye-initial` — initial visibility: `on` | `off` *(default `off`; `fix` forces visible)*
|
|
101
|
+
- `data-kileye-color` — icon color (e.g., `#005555`)
|
|
102
|
+
- `data-kileye-false` — disable auto eye for this field
|
|
103
|
+
|
|
104
|
+
> The validator reserves right padding so text never sits under the icon, and the password meter + rules always render **below** the field.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
90
112
|
|
|
91
113
|
data-kilvish-amount // for setting amount or Price related validation
|
|
92
114
|
Case 1: data-kilvish-amount="USD" Input: 2 → Auto-formats to 2.00 with cursor at 2|.00. Input: 25. → Allows further typing for decimal values, e.g., 25.5 or 25.55. Input: 25.555 → Automatically truncates to 25.55.
|
|
@@ -245,3 +267,299 @@ Author: Vasu Birla kilvishbirla@gmail.com
|
|
|
245
267
|
|
|
246
268
|
You can include this script in your project via CDN:
|
|
247
269
|
<script src="https://cdn.jsdelivr.net/npm/kilvalidate@1.0.62/kilvish.min.js"></script>
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# KilValidate (Kil Validation)
|
|
273
|
+
A lightweight, drop‑in **form validation** CDN for web apps. Validates text, number, email, phone, files, amounts, dates, grouped dates, and **passwords with strength meter + eye icon**—all via **data‑attributes**. No initialization required.
|
|
274
|
+
|
|
275
|
+
> **CDN (latest)**
|
|
276
|
+
> ```html
|
|
277
|
+
> <script src="https://cdn.jsdelivr.net/npm/kilvalidate@1.0.62/kilvish.min.js"></script>
|
|
278
|
+
> ```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Table of Contents
|
|
283
|
+
- [Why KilValidate](#why-kilvalidate)
|
|
284
|
+
- [Quick Start](#quick-start)
|
|
285
|
+
- [Global Behavior](#global-behavior)
|
|
286
|
+
- [Validation Attributes](#validation-attributes)
|
|
287
|
+
- [Numbers](#numbers)
|
|
288
|
+
- [Alphanumeric / Characters](#alphanumeric--characters)
|
|
289
|
+
- [Files](#files)
|
|
290
|
+
- [Amounts / Currency](#amounts--currency)
|
|
291
|
+
- [Dates](#dates)
|
|
292
|
+
- [Date Group (From/To)](#date-group-fromto)
|
|
293
|
+
- [Age](#age)
|
|
294
|
+
- [Password + Eye Icon](#password--eye-icon)
|
|
295
|
+
- [Notes](#notes)
|
|
296
|
+
- [Author](#author)
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Why KilValidate
|
|
301
|
+
- **Zero setup**: just add the CDN
|
|
302
|
+
- **Declarative**: use HTML `data-kilvish-*` attributes
|
|
303
|
+
- **UX friendly**: inline error slots, persistent labels/asterisks, no layout jumps
|
|
304
|
+
- **Robust**: works with Bootstrap `.input-group`, Select2, and dynamic DOM
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Quick Start
|
|
309
|
+
Add the CDN and then add attributes to your inputs. Raw HTML below will render as live inputs on your website.
|
|
310
|
+
|
|
311
|
+
### Minimal form
|
|
312
|
+
<form id="demo-minimal" style="max-width:520px;padding:12px;border:1px solid #e5e7eb;border-radius:10px">
|
|
313
|
+
<label class="form-label" for="username">Username</label>
|
|
314
|
+
<input class="form-control" id="username" name="username" data-kilvish-char="mix" required placeholder="john_doe123">
|
|
315
|
+
<br>
|
|
316
|
+
<label class="form-label" for="contact">Phone</label>
|
|
317
|
+
<input class="form-control" id="contact" name="contact" type="tel" required placeholder="+1 555 123 4567">
|
|
318
|
+
<br>
|
|
319
|
+
<label class="form-label" for="pwd">Password</label>
|
|
320
|
+
<input class="form-control" id="pwd" name="password" type="password" data-kilvish-password="12_64" data-kileye-show="toggle" data-kileye-color="#005555" required placeholder="Strong password">
|
|
321
|
+
<br>
|
|
322
|
+
<button type="submit" class="btn btn-primary">Submit</button>
|
|
323
|
+
</form>
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Global Behavior
|
|
328
|
+
- Required fields get a red asterisk automatically.
|
|
329
|
+
- Errors render **under** the correct control (incl. `.input-group` and Select2).
|
|
330
|
+
- Password **strength meter + rule list** render **below** the field.
|
|
331
|
+
- Phone (`type="tel"` or `data-kilvish-tel`) avoids false positives (country code only).
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Validation Attributes
|
|
336
|
+
|
|
337
|
+
### Numbers
|
|
338
|
+
#### Numbers only
|
|
339
|
+
- **Attribute:** `data-kilvish-num="yes"`
|
|
340
|
+
- Ensures only digits.
|
|
341
|
+
|
|
342
|
+
<form style="max-width:520px">
|
|
343
|
+
<label for="numonly">Numbers Only</label>
|
|
344
|
+
<input id="numonly" class="form-control" data-kilvish-num="yes" required placeholder="123456">
|
|
345
|
+
</form>
|
|
346
|
+
|
|
347
|
+
#### Numbers with length limits
|
|
348
|
+
- **Attribute:** `data-kilvish-num="_7_20"`
|
|
349
|
+
- Only digits, length **7–20**.
|
|
350
|
+
|
|
351
|
+
<form style="max-width:520px">
|
|
352
|
+
<label for="numlen">Numbers (7–20)</label>
|
|
353
|
+
<input id="numlen" class="form-control" data-kilvish-num="_7_20" required placeholder="9876543">
|
|
354
|
+
</form>
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### Alphanumeric / Characters
|
|
359
|
+
#### Mix (letters+numbers)
|
|
360
|
+
- **Attribute:** `data-kilvish-char="mix"`
|
|
361
|
+
|
|
362
|
+
<form style="max-width:520px">
|
|
363
|
+
<label for="uname">Username (letters & numbers)</label>
|
|
364
|
+
<input id="uname" class="form-control" data-kilvish-char="mix" required placeholder="john123">
|
|
365
|
+
</form>
|
|
366
|
+
|
|
367
|
+
#### Mix with specific specials
|
|
368
|
+
- **Attribute:** `data-kilvish-char="mix_%_$"`
|
|
369
|
+
|
|
370
|
+
<form style="max-width:520px">
|
|
371
|
+
<label for="mixspec">Username (allow %, _, $)</label>
|
|
372
|
+
<input id="mixspec" class="form-control" data-kilvish-char="mix_%_$" required placeholder="john_doe%">
|
|
373
|
+
</form>
|
|
374
|
+
|
|
375
|
+
#### Address‑friendly (space, dash, comma)
|
|
376
|
+
- **Attribute:** `data-kilvish-char="mix_ ,-_”`
|
|
377
|
+
|
|
378
|
+
<form style="max-width:520px">
|
|
379
|
+
<label for="addr">Address</label>
|
|
380
|
+
<input id="addr" class="form-control" data-kilvish-char="mix_ ,-_" required placeholder="221B Baker Street, London">
|
|
381
|
+
</form>
|
|
382
|
+
|
|
383
|
+
#### Letters only
|
|
384
|
+
- **Attribute:** `data-kilvish-char="Yes"`
|
|
385
|
+
|
|
386
|
+
<form style="max-width:520px">
|
|
387
|
+
<label for="fname">First Name (letters only)</label>
|
|
388
|
+
<input id="fname" class="form-control" data-kilvish-char="Yes" required placeholder="John">
|
|
389
|
+
</form>
|
|
390
|
+
|
|
391
|
+
#### Letters + specific specials
|
|
392
|
+
- **Attribute:** `data-kilvish-char="Yes_%_$"`
|
|
393
|
+
|
|
394
|
+
<form style="max-width:520px">
|
|
395
|
+
<label for="brand">Brand (letters + %,_,$)</label>
|
|
396
|
+
<input id="brand" class="form-control" data-kilvish-char="Yes_%_$" required placeholder="ACME_Pro%">
|
|
397
|
+
</form>
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
### Files
|
|
402
|
+
Use `data-kilvish-file` on `<input type="file">`.
|
|
403
|
+
|
|
404
|
+
- `image` (default): images only
|
|
405
|
+
- `image_MIN_MAX`: min/max count (e.g., `image_1_3`)
|
|
406
|
+
- `doc`: PDF, Word, Excel
|
|
407
|
+
- `doc_MIN_MAX` (e.g., `doc_1_4`)
|
|
408
|
+
- `pdf`: only PDF
|
|
409
|
+
- `pdf_MIN_MAX`
|
|
410
|
+
- `mix`: images + docs
|
|
411
|
+
- `only_pdf_image`: PDFs + images
|
|
412
|
+
- `only_doc_image`: DOC‑related + images
|
|
413
|
+
|
|
414
|
+
<form style="max-width:520px">
|
|
415
|
+
<label for="fileimg">Images (1–3)</label>
|
|
416
|
+
<input id="fileimg" class="form-control" type="file" data-kilvish-file="image_1_3" multiple>
|
|
417
|
+
</form>
|
|
418
|
+
|
|
419
|
+
<form style="max-width:520px">
|
|
420
|
+
<label for="filedoc">Documents (1–4)</label>
|
|
421
|
+
<input id="filedoc" class="form-control" type="file" data-kilvish-file="doc_1_4" multiple>
|
|
422
|
+
</form>
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### Amounts / Currency
|
|
427
|
+
Use `data-kilvish-amount="CUR[_MIN_MAX]"`
|
|
428
|
+
|
|
429
|
+
- `USD` → formats to 2 decimals (e.g., `2` → `2.00`).
|
|
430
|
+
- `USD_200_1000` → min/max enforced with inline messages.
|
|
431
|
+
|
|
432
|
+
<form style="max-width:520px">
|
|
433
|
+
<label for="amt1">Amount (USD)</label>
|
|
434
|
+
<input id="amt1" class="form-control" data-kilvish-amount="USD" placeholder="25 or 25.50">
|
|
435
|
+
</form>
|
|
436
|
+
|
|
437
|
+
<form style="max-width:520px">
|
|
438
|
+
<label for="amt2">Amount (USD 200–1000)</label>
|
|
439
|
+
<input id="amt2" class="form-control" data-kilvish-amount="USD_200_1000" placeholder="250">
|
|
440
|
+
</form>
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
### Dates
|
|
445
|
+
Attach `data-kilvish-date` to `<input type="date">` or `<input type="month">`.
|
|
446
|
+
|
|
447
|
+
- Age window: `_18_60` (between 18 and 60)
|
|
448
|
+
- **Future**: `future`, `today_future`, `30_days_future`, `2_years_future`, `2_years_after`
|
|
449
|
+
- **Past**: `past`, `10_years_past`, `2_years_before`
|
|
450
|
+
- **Ranges**: `Between_2000_2010`, `from_2023`, `till_2025`
|
|
451
|
+
- **Specific**: `not_today`, `weekday`, `weekend`, `first_of_month`, `last_of_month`, `exact_21`
|
|
452
|
+
|
|
453
|
+
<form style="max-width:520px">
|
|
454
|
+
<label for="dob">DOB (18–60)</label>
|
|
455
|
+
<input id="dob" class="form-control" type="date" data-kilvish-date="_18_60" required>
|
|
456
|
+
</form>
|
|
457
|
+
|
|
458
|
+
<form style="max-width:520px">
|
|
459
|
+
<label for="future">Future date (next 30 days)</label>
|
|
460
|
+
<input id="future" class="form-control" type="date" data-kilvish-date="30_days_future">
|
|
461
|
+
</form>
|
|
462
|
+
|
|
463
|
+
<form style="max-width:520px">
|
|
464
|
+
<label for="wk">Weekday only</label>
|
|
465
|
+
<input id="wk" class="form-control" type="date" data-kilvish-date="weekday">
|
|
466
|
+
</form>
|
|
467
|
+
|
|
468
|
+
> `leap_year` & combined multi‑conditions like `future_weekday` are **reserved** / not implemented yet.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
### Date Group (From/To)
|
|
473
|
+
Link two fields so **From ≤ To** (or months).
|
|
474
|
+
|
|
475
|
+
- **Format:** `data-kilvish-dategroup="EMP_1"` and `data-kilvish-dategroup="EMP_2"`
|
|
476
|
+
- Optional labels: `data-kilvish-date_name="From"` / `"To"`
|
|
477
|
+
|
|
478
|
+
<form style="max-width:520px">
|
|
479
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
|
|
480
|
+
<div>
|
|
481
|
+
<label for="empFrom">Employment From</label>
|
|
482
|
+
<input id="empFrom" class="form-control" type="month" data-kilvish-dategroup="EMP_1" data-kilvish-date_name="From" required>
|
|
483
|
+
</div>
|
|
484
|
+
<div>
|
|
485
|
+
<label for="empTo">Employment To</label>
|
|
486
|
+
<input id="empTo" class="form-control" type="month" data-kilvish-dategroup="EMP_2" data-kilvish-date_name="To" required>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
</form>
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
### Age
|
|
494
|
+
Validate a plain age number.
|
|
495
|
+
|
|
496
|
+
- **Attribute:** `data-kilvish-age="value"` → exact age
|
|
497
|
+
- **Attribute:** `data-kilvish-age="min_max"` → range
|
|
498
|
+
|
|
499
|
+
<form style="max-width:520px">
|
|
500
|
+
<label for="ageExact">Exact age 18</label>
|
|
501
|
+
<input id="ageExact" class="form-control" name="age" data-kilvish-age="18" placeholder="18">
|
|
502
|
+
</form>
|
|
503
|
+
|
|
504
|
+
<form style="max-width:520px">
|
|
505
|
+
<label for="ageRange">Age 18–60</label>
|
|
506
|
+
<input id="ageRange" class="form-control" name="age" data-kilvish-age="18_60" placeholder="e.g., 25">
|
|
507
|
+
</form>
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## Password + Eye Icon
|
|
512
|
+
|
|
513
|
+
### Length & composition
|
|
514
|
+
- `data-kilvish-password="MIN_MAX"` – requires:
|
|
515
|
+
- at least one lowercase, one uppercase, one digit, one special character
|
|
516
|
+
- **no sequential runs** like `1234` or `abcd`
|
|
517
|
+
- Example: `data-kilvish-password="8_20"` ⇒ min 8, max 20.
|
|
518
|
+
|
|
519
|
+
### Eye icon (show/hide)
|
|
520
|
+
An eye icon is auto-injected at the end of every password field and stays inside the input (works with/without `.input-group`).
|
|
521
|
+
|
|
522
|
+
**Attributes**
|
|
523
|
+
- `data-kileye-show` — behavior:
|
|
524
|
+
- `hold` *(default if omitted)*: press & hold to show; release/blur to hide
|
|
525
|
+
- `toggle`: click to show/hide
|
|
526
|
+
- `fix`: start visible; click to hide/show (no holding needed)
|
|
527
|
+
- `data-kileye-initial` — `on` | `off` *(default `off`; `fix` forces visible)*
|
|
528
|
+
- `data-kileye-color` — icon color (e.g., `#005555`)
|
|
529
|
+
- `data-kileye-false` — disable auto eye for this field
|
|
530
|
+
|
|
531
|
+
<form style="max-width:520px">
|
|
532
|
+
<label for="pass1">Password (toggle eye)</label>
|
|
533
|
+
<input id="pass1" class="form-control" type="password" data-kilvish-password="12_64" data-kileye-show="toggle" data-kileye-color="#005555" placeholder="Strong password">
|
|
534
|
+
</form>
|
|
535
|
+
|
|
536
|
+
<form style="max-width:520px">
|
|
537
|
+
<label for="pass2">Password (fixed visible)</label>
|
|
538
|
+
<input id="pass2" class="form-control" type="password" data-kilvish-password="12_64" data-kileye-show="fix" placeholder="Visible until you click eye">
|
|
539
|
+
</form>
|
|
540
|
+
|
|
541
|
+
<form style="max-width:520px">
|
|
542
|
+
<label for="pass3">Confirm Password (hold)</label>
|
|
543
|
+
<div class="input-group">
|
|
544
|
+
<span class="input-group-text"><i class="ti ti-key"></i></span>
|
|
545
|
+
<input id="pass3" class="form-control" type="password" data-kilvish-password="8_20" placeholder="Re-enter password">
|
|
546
|
+
</div>
|
|
547
|
+
</form>
|
|
548
|
+
|
|
549
|
+
> The password strength meter and rule list render **below** the field and update in real time.
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## Notes
|
|
554
|
+
- Works with plain HTML or frameworks (EJS/Blade/etc.).
|
|
555
|
+
- Error slots are **persistent nodes** (no layout shift).
|
|
556
|
+
- Add `data-ignore-kilvish="Yes"` to opt a field out of realtime checks.
|
|
557
|
+
- Required stars are normalized & auto‑deduped.
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## Author
|
|
562
|
+
**Vasu Birla (Kilvish)**
|
|
563
|
+
<kilvishbirla@gmail.com>
|
|
564
|
+
|
|
565
|
+
|
package/kilvish.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){if(document.getElementById("kil-error-style"))return;const e=document.createElement("style");e.id="kil-error-style",e.textContent="\n .error-message {\n display:block;\n min-height:16px; /* reserves space even when empty */\n margin-top:4px;\n font-size:12px;\n color:#d32f2f;\n line-height:1.25;\n opacity:1;\n transition: opacity .12s ease;\n }\n .error-message.is-hidden{ opacity:0; }\n ",document.head.appendChild(e)}();const _escKey=e=>String(e||"").replace(/[^a-zA-Z0-9_\-]/g,"_");function getErrorAnchor(e){let t=null;if(e&&e.tagName&&/^(INPUT|SELECT|TEXTAREA)$/.test(e.tagName))t=e;else if(e&&e.querySelector){const r=e.querySelectorAll("input,select,textarea");t=r.length?r[r.length-1]:null}if(!t)return{anchor:e,mode:"beforeend",key:"container"};const r=t.nextElementSibling;return"SELECT"===t.tagName&&r&&(r.classList.contains("select2")||r.classList.contains("select2-container"))?{anchor:r,mode:"afterend",key:t.name||t.id||"select"}:t.parentElement&&t.parentElement.classList.contains("input-group")?{anchor:t.parentElement,mode:"afterend",key:t.name||t.id||"inputgroup"}:{anchor:t,mode:"afterend",key:t.name||t.id||"field"}}function getOrCreateErrorNode(e,t,r,a){const s=`kil_err_${_escKey(a)}`;let n=e.querySelector(`#${s}`);return n||(n=document.createElement("div"),n.id=s,n.className="error-message is-hidden","beforeend"===r?t.insertAdjacentElement("beforeend",n):t.insertAdjacentElement("afterend",n)),n}function findLabelFor(e){if(!e)return null;const t=e.form||document;let r=null;if(e.id&&(r=t.querySelector(`label[for="${CSS.escape(e.id)}"]`)),!r&&e.name&&(r=t.querySelector(`label[for="${CSS.escape(e.name)}"]`)),r||(r=e.closest("label")),!r){const t=e.closest("fieldset");t&&(r=t.querySelector("legend"))}return r}function ensureStarVisibleFor(e){const t=findLabelFor(e);if(!t)return;let r=t.querySelector(".kil-required-star");r||(r=document.createElement("span"),r.className="kil-required-star",r.textContent=" *",t.appendChild(r)),r.style.visibility="visible"}function hideStarFor(e){const t=findLabelFor(e);if(!t)return;const r=t.querySelector(".kil-required-star");r&&(r.style.visibility="hidden")}function isKiltelEmpty(e){const t=e.closest(".kiltel-phone-wrap")||e.parentElement,r=t&&(t.querySelector('input[name="contact"]')||t.querySelector("#contact"));if(r){return(r.value||"").replace(/\D/g,"").length<7}const a=(e.value||"").trim().replace(/\s|-/g,"");if(!a)return!0;if(/^\+?$/.test(a))return!0;if(/^\+?\d{1,4}$/.test(a))return!0;return a.replace(/\D/g,"").length<7}function validateRequiredFields(e){const t=document.querySelector(`${e}`),r=t.querySelectorAll("input[required], select[required], textarea[required]"),a=(t.querySelectorAll("fieldset.radios"),t.querySelectorAll('input[type="radio"]')),s={},n=new Set;t.querySelectorAll('input[type="checkbox"]').forEach((e=>{const t=e.name;s[t]||(s[t]=[]),s[t].push(e),e.hasAttribute("required")&&n.add(t)}));t.querySelectorAll(".select2");let o=!0,l="This Field is Required.";if(t.querySelectorAll(".error-message").forEach((e=>{e.textContent="",e.classList.add("is-hidden")})),r.forEach((e=>{e.style.border=""})),r.forEach((e=>{const t=e.closest(".form-group")||e.parentNode;e.parentNode;if("date"==e.type){l="Please Select Date",e.style.border="";e.value.trim()||(o=!1,e.style.border="1px solid red",addErrorMessage(t,l))}else l="comments"==e.name?"Please Enter Comments":"email"==e.type?"Please Enter Email":"password"==e.type?"Please Enter Password":"This Field is Required.";const r=e.getAttribute("data-kilvish-name");let a;if(l=`${r||"This"} Field is Required.`,("tel"===e.type||e.hasAttribute("data-kilvish-tel"))&&(a=isKiltelEmpty(e),a&&(l=`${r||"Phone"} is required (enter full number, not just country code).`)),a)return o=!1,e.style.border="1px solid red",void addErrorMessage(e,l);e.value.trim()||(o=!1,e.style.border="1px solid red",addErrorMessage(t,l))})),a.length>0){const e={};a.forEach((t=>{const r=t.name;e[r]||(e[r]={isSelected:!1,groupElement:t.closest(".form-group")||t.closest(".mb-3")||t.closest(".form-check")||t.closest(".radios")||t.closest(".col-md-6")}),t.checked&&(e[r].isSelected=!0)})),Object.entries(e).forEach((([e,t])=>{if(!t.isSelected){o=!1;const r="Please select one option.";if(!t.groupElement)return void console.error("Invalid group element for radio group:",e);addErrorMessage(t.groupElement,r)}}))}return Object.keys(s).forEach((e=>{const t=s[e];if(t.some((e=>e.hasAttribute("data-ignore-kilvish"))))return;if(!t.some((e=>e.checked))){o=!1;const r=n.has(e)?"Please check this box to proceed":"Please select at least one option.";addErrorMessage(t[0].closest(".form-group")||t[0].closest(".form-check")||t[0].closest(".checkbox-container")||t[0].closest(".some-other-container"),r)}})),o}function addRealTimeValidation(e){const t=document.getElementById(e),r=t.querySelectorAll("input[required], select[required], textarea[required]"),a=(e,r=!0)=>{let a=t.querySelector(`label[for='${e.name}']`);if(!a){const t=e.closest("fieldset");t&&(a=t.querySelector("legend"))}a&&(r&&!a.innerHTML.includes("*")?a.innerHTML+=' <span style="color:red;">*</span>':!r&&a.innerHTML.includes("*")&&(a.innerHTML=a.innerHTML.replace(' <span style="color:red;">*</span>',"")))};r.forEach((e=>{const t=()=>{e.value.trim()?(!function(e){const t=e.form||e.closest("form");if(!t)return;const r=e.nextElementSibling;let a=e,s="afterend",n=e.name||e.id||"field";"SELECT"===e.tagName&&r&&(r.classList.contains("select2")||r.classList.contains("select2-container"))?(a=r,s="afterend",n=e.name||e.id||"select"):e.parentElement&&e.parentElement.classList.contains("input-group")&&(a=e.parentElement,s="afterend",n=e.name||e.id||"inputgroup");const o=t.querySelector(`#kil_err_${String(n).replace(/[^a-zA-Z0-9_\-]/g,"_")}`);o&&(o.textContent="",o.classList.add("is-hidden")),e.style.border="",hideStarFor(e)}(e),a(e,!1)):a(e,!0)};e.addEventListener("input",t),e.addEventListener("change",t)}));const s=t.querySelectorAll('input[type="radio"]');[...new Set(Array.from(s).map((e=>e.name)))].forEach((e=>{const r=t.querySelectorAll(`input[type="radio"][name="${e}"]`);r.forEach((e=>{e.addEventListener("change",(()=>{if(Array.from(r).some((e=>e.checked))){const t=e.closest(".form-group")||e.closest(".form-check")||e.closest(".radios")||e.closest(".mb-3")||e.closest(".col-md-6"),r=t?.querySelector(".error-message");r&&r.remove()}}))}))}));const n=t.querySelectorAll('input[type="checkbox"]');[...new Set(Array.from(n).map((e=>e.name)))].forEach((e=>{const r=t.querySelectorAll(`input[type="checkbox"][name="${e}"]`);r.forEach((e=>{e.addEventListener("change",(()=>{if(Array.from(r).some((e=>e.checked))){const t=e.closest(".form-group")||e.closest(".form-check")||e.closest(".checkbox-container")||e.closest(".some-other-container"),r=t?.querySelector(".error-message");r&&r.remove()}}))}))}))}function validateForm(e){validateRequiredFields(e)||event.preventDefault()}function normalizeRequiredStars(e=document){e.querySelectorAll("label, legend").forEach((e=>{e.querySelectorAll("span").forEach((e=>{e.classList.contains("kil-required-star")||"*"!==e.textContent.trim()||e.remove()}));const t=e.querySelectorAll(".kil-required-star");for(let e=1;e<t.length;e++)t[e].remove()}))}function addAsteriskToRequiredFields_working(){document.querySelectorAll("input, select, textarea").forEach((e=>{if(e.required){let t;if(t=document.querySelector(`label[for='${e.name}']`),!t){const r=e.closest("fieldset");r&&(t=r.querySelector("legend"))}t&&!t.innerHTML.includes("*")&&(t.innerHTML+=' <span style="color:red;">*</span>')}}))}function addAsteriskToRequiredFields(){document.querySelectorAll("input[required], select[required], textarea[required]").forEach(ensureStarVisibleFor)}function attachInputEvents(){document.querySelectorAll("input, select, textarea").forEach((e=>{e.hasAttribute("data-ignore-kilvish")||(e.addEventListener("keyup",validateKilvishInput),e.addEventListener("change",validateKilvishInput))}))}function addValidateFormToSubmitButtons(){document.querySelectorAll("form").forEach((e=>{const t=e.id;if(t){e.querySelectorAll('button[type="submit"], input[type="submit"]').forEach((e=>{e.setAttribute("onclick",`validateForm('#${t}')`)}))}else console.warn("Form without ID found. Skipping validation.")}))}function validateKilvishInput(e){const t=e.target;function r(e){const t=e.form||e.closest("form");if(!t)return;const r=e.nextElementSibling;let a=e.name||e.id||"field";"SELECT"===e.tagName&&r&&(r.classList.contains("select2")||r.classList.contains("select2-container"))?a=e.name||e.id||"select":e.parentElement&&e.parentElement.classList.contains("input-group")&&(a=e.name||e.id||"inputgroup");const s=t.querySelector(`#kil_err_${String(a).replace(/[^a-zA-Z0-9_\-]/g,"_")}`);s&&(s.textContent="",s.classList.add("is-hidden")),e.style.border=""}let a=/^[a-zA-Z\s]*$/;const s={image:["image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml"],doc:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],pdf:["application/pdf"],mix:["image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],only_pdf_image:["application/pdf","image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml"],only_doc_image:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml"]},n={image:"Image",doc:"Document",pdf:"PDF",mix:"Image & Document",only_pdf_image:"PDF & Image",only_doc_image:"Document & Image"};if(r(t),"description"===t.name||"address"===t.name){if(a=/^[a-zA-Z0-9@,._\s-'"*]*$/,t.value.length>400)return addErrorMessage(t,"Input exceeds the maximum length of 400 characters!"),t.style.border="1px solid red",void(t.value=t.value.substring(0,400))}else if("firstname"===t.name||"lastname"===t.name||"appointment_by"===t.name){a=/^[a-zA-Z\s-]*$/;const r=/^[a-zA-Z0-9._@-\s]+$/,s=t.value;if(r.test(s)&&(s.length>20||s.length<2))return addErrorMessage(t,"This Field Must Be Between 2 And 20 Characters."),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("otp"===t.name){a=/^\d{6}$/;const r=/^\d+$/,s=t.value;if(r.test(s)&&6!==s.length)return addErrorMessage(t,"OTP must be exactly 6 digits"),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("number_of_items"===t.name){a=/^\d{0,6}(\.\d{0,2})?$/;const r=/^\d+$/,s=t.value;if(r.test(s)&&s.length>3)return addErrorMessage(t,"Pacakge Number Cannot Be Exceed 3 Digits"),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("contact"===t.name||"agent_contact"===t.id||"fullcontact"===t.name)a=/^[\d\-\s]{0,20}$/;else if("number"===t.type){a=/^\d{0,20}$/;const r=/^\d+$/,s=t.value;if(r.test(s)&&(s.length>20||s.length<1))return addErrorMessage(t,"This Must Be Between 1 And 20 Digits."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("tel"===t.type){let r=t.value.trim().replace(/\s+/g,"");t.value=r;if(a=/^\+?\d{7,15}$/,!/^\+?\d{7,15}$/.test(r))return addErrorMessage(t,"Enter a valid phone number (7–15 digits, optional +)."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("kildate"===t.name){a=/^(?:\d{4}[-\/]\d{2}[-\/]\d{2})$/;const r=t.value.trim(),s=new Date(r.replace(/[-\/]/g,"/")),n=new Date;n.setHours(0,0,0,0);const o=t.getAttribute("data-kilvish-date");if(o){const[r,a]=o.replace("_","").split("_").map(Number),l=new Date(n),d=new Date(n);if(l.setFullYear(n.getFullYear()-r),d.setFullYear(n.getFullYear()-a),s>l||s<d)return addErrorMessage(t,`Birthday cannot be less than ${r} years ago or more than ${a} years in the future.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(s<n)return addErrorMessage(t,"The date cannot be in the past."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else{if("date"===t.type||"month"===t.type){a=/^(?:\d{4}[-\/]\d{2}[-\/]\d{2})$/;const r=t.value.trim(),s="month"===t.type?new Date(r+"-01"):new Date(r.replace(/[-\/]/g,"/")),n=new Date;n.setHours(0,0,0,0);const o=t.getAttribute("data-kilvish-date");if(o)if(/^_(\d+)_(\d+)$/.test(o)){const[r,a]=o.replace("_","").split("_").map(Number),l=new Date(n),d=new Date(n);if(l.setFullYear(n.getFullYear()-r),d.setFullYear(n.getFullYear()-a),s>l||s<d)return addErrorMessage(t,`Age must be between ${r} and ${a} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^_(\d+)$/.test(o)){const r=Number(o.replace("_","")),a=new Date(n);if(a.setFullYear(n.getFullYear()-r),s.getTime()!==a.getTime())return addErrorMessage(t,`You must be exactly ${r} years old.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/future/.test(o)){if("future"===o&&s<=n)return addErrorMessage(t,"The selected date must be in the future."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("today_future"===o&&s<n)return addErrorMessage(t,"The selected date cannot be in the past."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(/(\d+)_days_future/.test(o)){const r=Number(o.match(/(\d+)_days_future/)[1]),a=new Date(n);if(a.setDate(n.getDate()+r),s>a||s<n)return addErrorMessage(t,`The selected date must be within the next ${r} days.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_years_future$/.test(o)){const r=Number(o.match(/^(\d+)_years_future$/)[1]),a=new Date(n);if(a.setFullYear(n.getFullYear()+r),s<n||s>a)return addErrorMessage(t,`The date must be within the next ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_years_after$/.test(o)){const r=Number(o.match(/^(\d+)_years_after$/)[1]),a=new Date(n);if(a.setFullYear(n.getFullYear()+r),s.getTime()!==a.getTime())return addErrorMessage(t,`The date must be exactly ${r} years after today.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}else if(/past/.test(o)){if("past"===o&&s>=n)return addErrorMessage(t,"The selected date must be in the past."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(/^(\d+)_years_past$/.test(o)){const r=Number(o.match(/^(\d+)_years_past$/)[1]),a=new Date(n);if(a.setFullYear(n.getFullYear()-r),s>n||s<a)return addErrorMessage(t,`The date must be within the past ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}else if(/^(\d+)_years_before$/.test(o)){const r=Number(o.match(/^(\d+)_years_before$/)[1]),a=new Date(n);if(a.setFullYear(n.getFullYear()-r),s>=a)return addErrorMessage(t,`The date must be before ${r} years ago.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_years_after$/.test(o)){const r=Number(o.match(/^(\d+)_years_after$/)[1]),a=new Date(n);if(a.setFullYear(n.getFullYear()+r),s<=a)return addErrorMessage(t,`The date must be after ${r} years from today.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/Between_(\d+)_(\d+)/.test(o)){const[r,a]=o.match(/Between_(\d+)_(\d+)/).slice(1,3).map(Number),n=new Date(r,0,1),l=new Date(a,11,31);if(s<n||s>l)return addErrorMessage(t,`The date must be between ${r} and ${a}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/from_(\d+)/.test(o)){const r=Number(o.match(/from_(\d+)/)[1]);if(s<new Date(r,0,1))return addErrorMessage(t,`The date must be from the year ${r} onwards.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/till_(\d+)/.test(o)){const r=Number(o.match(/till_(\d+)/)[1]);if(s>new Date(r,11,31))return addErrorMessage(t,`The date must be till the year ${r}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else{if("not_today"===o&&s.getTime()===n.getTime())return addErrorMessage(t,"Today's date is not allowed."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("weekday"===o&&(0===s.getDay()||6===s.getDay()))return addErrorMessage(t,"Only weekdays (Monday to Friday) are allowed."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("weekend"===o&&s.getDay()>=1&&s.getDay()<=5)return addErrorMessage(t,"Only weekends (Saturday and Sunday) are allowed."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("first_of_month"===o&&1!==s.getDate())return addErrorMessage(t,"The date must be the first day of the month."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(/^exact_(\d+)$/.test(o)){const r=Number(o.match(/^exact_(\d+)$/)[1]),a=new Date(n);if(a.setFullYear(n.getFullYear()-r),s.getTime()!==a.getTime())return addErrorMessage(t,`You must be exactly ${r} years old.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if("last_of_month"===o){const r=new Date(s.getFullYear(),s.getMonth()+1,0).getDate();if(s.getDate()!==r)return addErrorMessage(t,"The date must be the last day of the month."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}const l=t.getAttribute("data-kilvish-dategroup");if(l&&/.+_[12]$/.test(l)){const[r,a]=l.split("_"),s="1"===a?"2":"1",n=document.querySelector(`[data-kilvish-dategroup="${r}_${s}"]`);if(n&&n.value){const o="month"===t.type?new Date(t.value+"-01"):new Date(t.value.replace(/[-\/]/g,"/")),l="month"===n.type?new Date(n.value+"-01"):new Date(n.value.replace(/[-\/]/g,"/"));if(!("1"===a&&o<=l||"2"===a&&o>=l)){const o=t.dataset.kilvishDate_name||`Field (${r}_${a})`,l=n.dataset.kilvishDate_name||`Field (${r}_${s})`;return addErrorMessage(t,`${o} must be ${"1"===a?"before or same as":"after or same as"} ${l}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}}return void(t.style.border="")}if("age"===t.name){const r=t.value.trim(),a=parseInt(r,10);if(isNaN(a))return addErrorMessage(t,"Please enter a valid number for age."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));const s=t.getAttribute("data-kilvish-age")||"18";if(/^(\d+)$/.test(s)){const r=Number(s);if(a!==r)return addErrorMessage(t,`Age must be exactly ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_(\d+)$/.test(s)){const[r,n]=s.split("_").map(Number);if(a<r)return addErrorMessage(t,`Minimum age must be ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(a>n)return addErrorMessage(t,`Age cannot exceed ${n} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}return void(t.style.border="")}}if("model_name"===t.name||"models[]"===t.name)a=/^[a-zA-Z0-9]{4,20}$/;else if("email"===t.type){a=/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;const r=/^[a-zA-Z0-9._@-]+$/,s=t.value;if(r.test(s)&&!a.test(s))return addErrorMessage(t,"Invalid Email Format."),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}if(t.hasAttribute("data-kilvish-num")){const r=t.value,s=t.getAttribute("data-kilvish-num");a=/^[0-9]*$/;let n=null,o=null;const l=s.match(/^_(\d+)_(\d+)$/);l&&(n=parseInt(l[1],10),o=parseInt(l[2],10));const d=s.match(/^exact_(\d+)$/),i=d?parseInt(d[1],10):null;/^\d*$/.test(r)?null!==i&&r.length!==i?(addErrorMessage(t,`This field must contain exactly ${i} digits.`),t.style.border="2px solid red","change"===e.type&&(t.value="")):n&&r.length<n||o&&r.length>o?(addErrorMessage(t,`This Field Must be between ${n} and ${o} digits.`),t.style.border="2px solid red","change"===e.type&&(t.value="")):t.style.border="":(addErrorMessage(t,"Only numerical characters are allowed."),t.style.border="2px solid red")}if(t.hasAttribute("data-kilvish-num11")){const r=t.value,s=t.getAttribute("data-kilvish-num");a=/^[0-9]*$/;let n=null,o=null;const l=s?s.match(/^_(\d+)_(\d+)$/):null;l&&(n=parseInt(l[1],10),o=parseInt(l[2],10)),"exact_20"===s&&(a=/^[0-9]{20}$/),a.test(r)?n&&r.length<n||o&&r.length>o?(addErrorMessage(t,`This Field Must be between ${n} and ${o} digits.`),t.style.border="2px solid red","change"===e.type&&(t.value="")):t.style.border="":(addErrorMessage(t,"exact_20"===s?"This field must contain exactly 20 digits.":"Only numerical characters are allowed."),t.style.border="2px solid red")}if(t.hasAttribute("data-kilvish-char")){const r=t.value,s=t.getAttribute("data-kilvish-char");if(s.startsWith("Yes")||s.startsWith("mix")){"mix"===s?a=/^[a-zA-Z0-9]*$/:"Yes"===s&&(a=/^[a-zA-Z]*$/);const n=s.split("_").slice(1).join("");if(n&&(a=new RegExp(`^[a-zA-Z0-9${n}]*$`)),!a.test(r))return addErrorMessage(t,"Invalid character entered!"),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}if(t.hasAttribute("data-kilvish-amount")){const r=t.getAttribute("data-kilvish-amount");a=/^\d+(\.\d{0,2})?$/;let s="USD",n=null,o=null;const l=r.match(/^([A-Z]{3})(?:_(\d+)_(\d+))?$/);l&&(s=l[1],n=l[2]?parseFloat(l[2]):null,o=l[3]?parseFloat(l[3]):null),t.addEventListener("input",(()=>{let e=t.selectionStart,r=t.value;a.test(r)||(r=r.replace(/[^0-9.]/g,"").replace(/(\..*?)\..*/,"$1").replace(/(\.\d{2}).*/,"$1")),!r||r.includes(".")||r.endsWith(".")||(r=parseFloat(r).toFixed(2)),t.value=r;const n=r.indexOf(".");-1!==n&&e>n&&(e=Math.min(e,r.length)),t.setSelectionRange(e,e);let o=t.nextElementSibling;o&&o.classList.contains("amount-display")||(o=document.createElement("div"),o.className="amount-display",o.style.color="green",t.parentNode.insertBefore(o,t.nextSibling)),o.textContent=`${s} ${r||"0.00"} .`})),t.addEventListener("blur",(()=>{if(t.readOnly)return;let r=parseFloat(t.value||"0").toFixed(2),a=t.nextElementSibling;return null!==n&&r<n?(addErrorMessage(t,`Amount must be at least ${n.toFixed(2)} ${s}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value="",a&&(a.textContent="")))):null!==o&&r>o?(addErrorMessage(t,`Amount must not exceed ${o.toFixed(2)} ${s}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value="",a&&(a.textContent="")))):(t.style.border="",void(t.value=r))}))}if("password"===t.type){let r=t.parentElement.querySelector(".password-validation");r||(r=document.createElement("ul"),r.classList.add("password-validation"),r.style.listStyleType="none",r.style.padding="0",r.style.marginTop="5px",t.insertAdjacentElement("afterend",r));const s=t.getAttribute("data-kilvish-password")||"12",[n,o]=s.split("_").map(Number);a=new RegExp(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[\\W_]).{${n},${o}}$`);const l={length:`Password must be between ${n} and ${o} characters long`,uppercase:"At least one uppercase letter (A-Z)",lowercase:"At least one lowercase letter (a-z)",number:"At least one number (0-9)",special:"At least one special character (!@#$%^&*)",sequential:"No sequential characters (e.g., 1234, abcd)"},d={};Object.keys(l).forEach((e=>{let t=r.querySelector(`li[data-rule="${e}"]`);t||(t=document.createElement("li"),t.setAttribute("data-rule",e),t.innerHTML=`❌ ${l[e]}`,t.style.color="red",t.style.fontSize="14px",r.appendChild(t)),d[e]=t}));const i=e=>{for(let t=0;t<e.length-3;t++){if(e.charCodeAt(t+1)===e.charCodeAt(t)+1&&e.charCodeAt(t+2)===e.charCodeAt(t)+2&&e.charCodeAt(t+3)===e.charCodeAt(t)+3)return!0;if(e.charCodeAt(t+1)===e.charCodeAt(t)-1&&e.charCodeAt(t+2)===e.charCodeAt(t)-2&&e.charCodeAt(t+3)===e.charCodeAt(t)-3)return!0}return!1},c=t.value,u={length:c.length>=n&&c.length<=o,uppercase:/[A-Z]/.test(c),lowercase:/[a-z]/.test(c),number:/\d/.test(c),special:/[\W_]/.test(c),sequential:!i(c)};Object.keys(u).forEach((e=>{d[e].innerHTML=u[e]?`✅ ${l[e]}`:`❌ ${l[e]}`,d[e].style.color=u[e]?"green":"red",u[e]}));const m=a.test(c);t.style.border=m?"1px solid green":"1px solid red",m||"change"!==e.type||(t.value="")}if("password_working"===t.type){const r=t.getAttribute("data-kilvish-password")||"12",[s,n]=r.split("_").map(Number),o=s||12,l=n||1/0;a=new RegExp(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[\\W_]).{${o},${l}}$`);const d=e=>{for(let t=0;t<e.length-3;t++){if(e.charCodeAt(t+1)===e.charCodeAt(t)+1&&e.charCodeAt(t+2)===e.charCodeAt(t)+2&&e.charCodeAt(t+3)===e.charCodeAt(t)+3)return!0;if(e.charCodeAt(t+1)===e.charCodeAt(t)-1&&e.charCodeAt(t+2)===e.charCodeAt(t)-2&&e.charCodeAt(t+3)===e.charCodeAt(t)-3)return!0}return!1};if(!a.test(t.value))return addErrorMessage(t,`Password must be between ${o} and ${l} characters long and contain at least one uppercase letter, one lowercase letter, one special character, and one number.`),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));if(d(t.value))return addErrorMessage(t,"Password should not contain sequential characters."),t.style.border="1px solid red",void("change"===e.type&&(t.value=""))}if("file"===t.type){const e=t.getAttribute("data-kilvish-file")||"image",[r,...a]=e.split("_"),o=s[e]||s[r]||s.image,l=t.files;let d=1,i=1/0;if(a.length>=2&&(d=parseInt(a[0],10),i=parseInt(a[1],10)),l.length<d||l.length>i)return addErrorMessage(t,`Please select between ${d} and ${i} valid file(s).`),t.value="",t.style.border="1px solid red",!1;const c=n[e]||r.toUpperCase();for(const e of l)if(!o.includes(e.type))return addErrorMessage(t,`Invalid file type. Allowed types: ${c}.`),t.value="",t.style.border="1px solid red",!1}else"keyup"===e.type&&(a.test(t.value)||("password"!==t.type&&addErrorMessage(t,"Invalid character entered!"),t.style.border="1px solid red")),"change"===e.type&&(a.test(t.value)?(t.style.border="",r(t)):(t.value="",addErrorMessage(t,"Invalid character or format!"),t.style.border="1px solid red"))}function addErrorMessage_working(e,t){if(!e.querySelector(".error-message")){const r=document.createElement("div");r.className="error-message",r.style.color="red",r.style.fontSize="12px",r.innerText=t,e.appendChild(r)}}function addErrorMessage_working2(e,t){const r=e.form?e.form:e.closest("form");if(!r)return;const{anchor:a,mode:s,key:n}=getErrorAnchor(e),o=getOrCreateErrorNode(r,a,s,n);o.textContent=t||"",o.classList.remove("is-hidden")}function addErrorMessage(e,t){const r=e.form?e.form:e.closest("form");if(!r)return;let a=null;if(e&&e.tagName&&/^(INPUT|SELECT|TEXTAREA)$/.test(e.tagName))a=e;else if(e&&e.querySelector){const t=e.querySelectorAll("input,select,textarea");a=t.length?t[t.length-1]:null}let s=e,n="beforeend",o="container";if(a){const e=a.nextElementSibling;"SELECT"===a.tagName&&e&&(e.classList.contains("select2")||e.classList.contains("select2-container"))?(s=e,n="afterend",o=a.name||a.id||"select"):a.parentElement&&a.parentElement.classList.contains("input-group")?(s=a.parentElement,n="afterend",o=a.name||a.id||"inputgroup"):(s=a,n="afterend",o=a.name||a.id||"field")}const l=`kil_err_${String(o).replace(/[^a-zA-Z0-9_\-]/g,"_")}`;let d=r.querySelector(`#${l}`);if(d||(d=document.createElement("div"),d.id=l,d.className="error-message is-hidden","beforeend"===n?s.insertAdjacentElement("beforeend",d):s.insertAdjacentElement("afterend",d)),d.textContent=t||"",d.classList.remove("is-hidden"),a)ensureStarVisibleFor(a);else{const t=e.querySelector("input[required],select[required],textarea[required]")||e.querySelector("input,select,textarea");t&&ensureStarVisibleFor(t)}}!function(){if(document.getElementById("kil-req-style"))return;const e=document.createElement("style");e.id="kil-req-style",e.textContent="\n .kil-required-star{ color:#d32f2f; margin-left:2px; }\n .error-message{ display:block; min-height:16px; margin-top:4px; font-size:12px; color:#d32f2f; line-height:1.25; opacity:1; transition:opacity .12s ease; }\n .error-message.is-hidden{ opacity:0; }\n ",document.head.appendChild(e)}(),document.addEventListener("DOMContentLoaded",(()=>{normalizeRequiredStars(),addAsteriskToRequiredFields()})),document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll("form").forEach((e=>{const t=e.id;t?addRealTimeValidation(t):console.warn("Form without ID found. Skipping validation.")})),attachInputEvents(),addAsteriskToRequiredFields(),addValidateFormToSubmitButtons()})),document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll("input[type='password']").forEach((e=>{if(e.hasAttribute("data-ignore-kilvish"))return;const t=document.createElement("div");t.style.display="flex",t.style.alignItems="center",t.style.marginTop="5px";const r=[];for(let e=0;e<3;e++){const e=document.createElement("div");e.style.height="5px",e.style.width="30px",e.style.margin="0 2px",e.style.borderRadius="3px",e.style.background="lightgray",r.push(e),t.appendChild(e)}const a=document.createElement("span");a.textContent="Weak",a.style.marginLeft="10px",a.style.fontSize="12px",a.style.color="gray",t.appendChild(a),e.parentNode.insertBefore(t,e.nextSibling);e.addEventListener("input",(e=>{const t=(e=>{let t=0;return e.length>=12&&(t+=2),/[A-Z]/.test(e)&&(t+=1),/[a-z]/.test(e)&&(t+=1),/\d/.test(e)&&(t+=1),/[\W_]/.test(e)&&(t+=1),(e=>{for(let t=0;t<e.length-3;t++){if(e.charCodeAt(t+1)===e.charCodeAt(t)+1&&e.charCodeAt(t+2)===e.charCodeAt(t)+2&&e.charCodeAt(t+3)===e.charCodeAt(t)+3)return!0;if(e.charCodeAt(t+1)===e.charCodeAt(t)-1&&e.charCodeAt(t+2)===e.charCodeAt(t)-2&&e.charCodeAt(t+3)===e.charCodeAt(t)-3)return!0}return!1})(e)&&(t-=2),Math.max(0,t)})(e.target.value);r.forEach((e=>e.style.background="lightgray")),t<=2?(r[0].style.background="red",a.textContent="Weak",a.style.color="red"):t<=4?(r[0].style.background="yellow",r[1].style.background="yellow",a.textContent="Medium",a.style.color="orange"):(r[0].style.background="green",r[1].style.background="green",r[2].style.background="green",a.textContent="Strong",a.style.color="green")}))}))}));
|
|
1
|
+
!function(){if(document.getElementById("kil-error-style"))return;const e=document.createElement("style");e.id="kil-error-style",e.textContent="\n .error-message {\n display:block;\n min-height:16px; /* reserves space even when empty */\n margin-top:4px;\n font-size:12px;\n color:#d32f2f;\n line-height:1.25;\n opacity:1;\n transition: opacity .12s ease;\n }\n .error-message.is-hidden{ opacity:0; }\n ",document.head.appendChild(e)}();const _escKey=e=>String(e||"").replace(/[^a-zA-Z0-9_\-]/g,"_");function getErrorAnchor(e){let t=null;if(e&&e.tagName&&/^(INPUT|SELECT|TEXTAREA)$/.test(e.tagName))t=e;else if(e&&e.querySelector){const r=e.querySelectorAll("input,select,textarea");t=r.length?r[r.length-1]:null}if(!t)return{anchor:e,mode:"beforeend",key:"container"};const r=t.nextElementSibling;return"SELECT"===t.tagName&&r&&(r.classList.contains("select2")||r.classList.contains("select2-container"))?{anchor:r,mode:"afterend",key:t.name||t.id||"select"}:t.parentElement&&t.parentElement.classList.contains("input-group")?{anchor:t.parentElement,mode:"afterend",key:t.name||t.id||"inputgroup"}:{anchor:t,mode:"afterend",key:t.name||t.id||"field"}}function getOrCreateErrorNode(e,t,r,a){const n=`kil_err_${_escKey(a)}`;let s=e.querySelector(`#${n}`);return s||(s=document.createElement("div"),s.id=n,s.className="error-message is-hidden","beforeend"===r?t.insertAdjacentElement("beforeend",s):t.insertAdjacentElement("afterend",s)),s}function findLabelFor(e){if(!e)return null;const t=e.form||document;let r=null;if(e.id&&(r=t.querySelector(`label[for="${CSS.escape(e.id)}"]`)),!r&&e.name&&(r=t.querySelector(`label[for="${CSS.escape(e.name)}"]`)),r||(r=e.closest("label")),!r){const t=e.closest("fieldset");t&&(r=t.querySelector("legend"))}return r}function ensureStarVisibleFor(e){const t=findLabelFor(e);if(!t)return;let r=t.querySelector(".kil-required-star");r||(r=document.createElement("span"),r.className="kil-required-star",r.textContent=" *",t.appendChild(r)),r.style.visibility="visible"}function hideStarFor(e){const t=findLabelFor(e);if(!t)return;const r=t.querySelector(".kil-required-star");r&&(r.style.visibility="hidden")}function isKiltelEmpty(e){const t=e.closest(".kiltel-phone-wrap")||e.parentElement,r=t&&(t.querySelector('input[name="contact"]')||t.querySelector("#contact"));if(r){return(r.value||"").replace(/\D/g,"").length<7}const a=(e.value||"").trim().replace(/\s|-/g,"");if(!a)return!0;if(/^\+?$/.test(a))return!0;if(/^\+?\d{1,4}$/.test(a))return!0;return a.replace(/\D/g,"").length<7}function validateRequiredFields(e){const t=document.querySelector(`${e}`),r=t.querySelectorAll("input[required], select[required], textarea[required]"),a=(t.querySelectorAll("fieldset.radios"),t.querySelectorAll('input[type="radio"]')),n={},s=new Set;t.querySelectorAll('input[type="checkbox"]').forEach((e=>{const t=e.name;n[t]||(n[t]=[]),n[t].push(e),e.hasAttribute("required")&&s.add(t)}));t.querySelectorAll(".select2");let o=!0,l="This Field is Required.";if(t.querySelectorAll(".error-message").forEach((e=>{e.textContent="",e.classList.add("is-hidden")})),r.forEach((e=>{e.style.border=""})),r.forEach((e=>{const t=e.closest(".form-group")||e.parentNode;e.parentNode;if("date"==e.type){l="Please Select Date",e.style.border="";e.value.trim()||(o=!1,e.style.border="1px solid red",addErrorMessage(t,l))}else l="comments"==e.name?"Please Enter Comments":"email"==e.type?"Please Enter Email":"password"==e.type?"Please Enter Password":"This Field is Required.";const r=e.getAttribute("data-kilvish-name");let a;if(l=`${r||"This"} Field is Required.`,("tel"===e.type||e.hasAttribute("data-kilvish-tel"))&&(a=isKiltelEmpty(e),a&&(l=`${r||"Phone"} is required (enter full number, not just country code).`)),a)return o=!1,e.style.border="1px solid red",void addErrorMessage(e,l);e.value.trim()||(o=!1,e.style.border="1px solid red",addErrorMessage(t,l))})),a.length>0){const e={};a.forEach((t=>{const r=t.name;e[r]||(e[r]={isSelected:!1,groupElement:t.closest(".form-group")||t.closest(".mb-3")||t.closest(".form-check")||t.closest(".radios")||t.closest(".col-md-6")}),t.checked&&(e[r].isSelected=!0)})),Object.entries(e).forEach((([e,t])=>{if(!t.isSelected){o=!1;const r="Please select one option.";if(!t.groupElement)return void console.error("Invalid group element for radio group:",e);addErrorMessage(t.groupElement,r)}}))}return Object.keys(n).forEach((e=>{const t=n[e];if(t.some((e=>e.hasAttribute("data-ignore-kilvish"))))return;if(!t.some((e=>e.checked))){o=!1;const r=s.has(e)?"Please check this box to proceed":"Please select at least one option.";addErrorMessage(t[0].closest(".form-group")||t[0].closest(".form-check")||t[0].closest(".checkbox-container")||t[0].closest(".some-other-container"),r)}})),o}function addRealTimeValidation(e){const t=document.getElementById(e),r=t.querySelectorAll("input[required], select[required], textarea[required]"),a=(e,r=!0)=>{let a=t.querySelector(`label[for='${e.name}']`);if(!a){const t=e.closest("fieldset");t&&(a=t.querySelector("legend"))}a&&(r&&!a.innerHTML.includes("*")?a.innerHTML+=' <span style="color:red;">*</span>':!r&&a.innerHTML.includes("*")&&(a.innerHTML=a.innerHTML.replace(' <span style="color:red;">*</span>',"")))};r.forEach((e=>{const t=()=>{e.value.trim()?(!function(e){const t=e.form||e.closest("form");if(!t)return;const r=e.nextElementSibling;let a=e,n="afterend",s=e.name||e.id||"field";"SELECT"===e.tagName&&r&&(r.classList.contains("select2")||r.classList.contains("select2-container"))?(a=r,n="afterend",s=e.name||e.id||"select"):e.parentElement&&e.parentElement.classList.contains("input-group")&&(a=e.parentElement,n="afterend",s=e.name||e.id||"inputgroup");const o=t.querySelector(`#kil_err_${String(s).replace(/[^a-zA-Z0-9_\-]/g,"_")}`);o&&(o.textContent="",o.classList.add("is-hidden")),e.style.border="",hideStarFor(e)}(e),a(e,!1)):a(e,!0)};e.addEventListener("input",t),e.addEventListener("change",t)}));const n=t.querySelectorAll('input[type="radio"]');[...new Set(Array.from(n).map((e=>e.name)))].forEach((e=>{const r=t.querySelectorAll(`input[type="radio"][name="${e}"]`);r.forEach((e=>{e.addEventListener("change",(()=>{if(Array.from(r).some((e=>e.checked))){const t=e.closest(".form-group")||e.closest(".form-check")||e.closest(".radios")||e.closest(".mb-3")||e.closest(".col-md-6"),r=t?.querySelector(".error-message");r&&r.remove()}}))}))}));const s=t.querySelectorAll('input[type="checkbox"]');[...new Set(Array.from(s).map((e=>e.name)))].forEach((e=>{const r=t.querySelectorAll(`input[type="checkbox"][name="${e}"]`);r.forEach((e=>{e.addEventListener("change",(()=>{if(Array.from(r).some((e=>e.checked))){const t=e.closest(".form-group")||e.closest(".form-check")||e.closest(".checkbox-container")||e.closest(".some-other-container"),r=t?.querySelector(".error-message");r&&r.remove()}}))}))}))}function validateForm(e){validateRequiredFields(e)||event.preventDefault()}function normalizeRequiredStars(e=document){e.querySelectorAll("label, legend").forEach((e=>{e.querySelectorAll("span").forEach((e=>{e.classList.contains("kil-required-star")||"*"!==e.textContent.trim()||e.remove()}));const t=e.querySelectorAll(".kil-required-star");for(let e=1;e<t.length;e++)t[e].remove()}))}function addAsteriskToRequiredFields_working(){document.querySelectorAll("input, select, textarea").forEach((e=>{if(e.required){let t;if(t=document.querySelector(`label[for='${e.name}']`),!t){const r=e.closest("fieldset");r&&(t=r.querySelector("legend"))}t&&!t.innerHTML.includes("*")&&(t.innerHTML+=' <span style="color:red;">*</span>')}}))}function addAsteriskToRequiredFields(){document.querySelectorAll("input[required], select[required], textarea[required]").forEach(ensureStarVisibleFor)}function attachInputEvents(){document.querySelectorAll("input, select, textarea").forEach((e=>{e.hasAttribute("data-ignore-kilvish")||(e.addEventListener("keyup",validateKilvishInput),e.addEventListener("change",validateKilvishInput))}))}function addValidateFormToSubmitButtons(){document.querySelectorAll("form").forEach((e=>{const t=e.id;if(t){e.querySelectorAll('button[type="submit"], input[type="submit"]').forEach((e=>{e.setAttribute("onclick",`validateForm('#${t}')`)}))}else console.warn("Form without ID found. Skipping validation.")}))}function validateKilvishInput(e){const t=e.target;function r(e){const t=e.form||e.closest("form");if(!t)return;const r=e.nextElementSibling;let a=e.name||e.id||"field";"SELECT"===e.tagName&&r&&(r.classList.contains("select2")||r.classList.contains("select2-container"))?a=e.name||e.id||"select":e.parentElement&&e.parentElement.classList.contains("input-group")&&(a=e.name||e.id||"inputgroup");const n=t.querySelector(`#kil_err_${String(a).replace(/[^a-zA-Z0-9_\-]/g,"_")}`);n&&(n.textContent="",n.classList.add("is-hidden")),e.style.border=""}let a=/^[a-zA-Z\s]*$/;const n={image:["image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml"],doc:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],pdf:["application/pdf"],mix:["image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],only_pdf_image:["application/pdf","image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml"],only_doc_image:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","image/jpeg","image/png","image/bmp","image/tiff","image/webp","image/svg+xml"]},s={image:"Image",doc:"Document",pdf:"PDF",mix:"Image & Document",only_pdf_image:"PDF & Image",only_doc_image:"Document & Image"};if(r(t),"description"===t.name||"address"===t.name){if(a=/^[a-zA-Z0-9@,._\s-'"*]*$/,t.value.length>400)return addErrorMessage(t,"Input exceeds the maximum length of 400 characters!"),t.style.border="1px solid red",void(t.value=t.value.substring(0,400))}else if("firstname"===t.name||"lastname"===t.name||"appointment_by"===t.name){a=/^[a-zA-Z\s-]*$/;const r=/^[a-zA-Z0-9._@-\s]+$/,n=t.value;if(r.test(n)&&(n.length>20||n.length<2))return addErrorMessage(t,"This Field Must Be Between 2 And 20 Characters."),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("otp"===t.name){a=/^\d{6}$/;const r=/^\d+$/,n=t.value;if(r.test(n)&&6!==n.length)return addErrorMessage(t,"OTP must be exactly 6 digits"),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("number_of_items"===t.name){a=/^\d{0,6}(\.\d{0,2})?$/;const r=/^\d+$/,n=t.value;if(r.test(n)&&n.length>3)return addErrorMessage(t,"Pacakge Number Cannot Be Exceed 3 Digits"),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("contact"===t.name||"agent_contact"===t.id||"fullcontact"===t.name)a=/^[\d\-\s]{0,20}$/;else if("number"===t.type){a=/^\d{0,20}$/;const r=/^\d+$/,n=t.value;if(r.test(n)&&(n.length>20||n.length<1))return addErrorMessage(t,"This Must Be Between 1 And 20 Digits."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("tel"===t.type){let r=t.value.trim().replace(/\s+/g,"");t.value=r;if(a=/^\+?\d{7,15}$/,!/^\+?\d{7,15}$/.test(r))return addErrorMessage(t,"Enter a valid phone number (7–15 digits, optional +)."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else if("kildate"===t.name){a=/^(?:\d{4}[-\/]\d{2}[-\/]\d{2})$/;const r=t.value.trim(),n=new Date(r.replace(/[-\/]/g,"/")),s=new Date;s.setHours(0,0,0,0);const o=t.getAttribute("data-kilvish-date");if(o){const[r,a]=o.replace("_","").split("_").map(Number),l=new Date(s),i=new Date(s);if(l.setFullYear(s.getFullYear()-r),i.setFullYear(s.getFullYear()-a),n>l||n<i)return addErrorMessage(t,`Birthday cannot be less than ${r} years ago or more than ${a} years in the future.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(n<s)return addErrorMessage(t,"The date cannot be in the past."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}else{if("date"===t.type||"month"===t.type){a=/^(?:\d{4}[-\/]\d{2}[-\/]\d{2})$/;const r=t.value.trim(),n="month"===t.type?new Date(r+"-01"):new Date(r.replace(/[-\/]/g,"/")),s=new Date;s.setHours(0,0,0,0);const o=t.getAttribute("data-kilvish-date");if(o)if(/^_(\d+)_(\d+)$/.test(o)){const[r,a]=o.replace("_","").split("_").map(Number),l=new Date(s),i=new Date(s);if(l.setFullYear(s.getFullYear()-r),i.setFullYear(s.getFullYear()-a),n>l||n<i)return addErrorMessage(t,`Age must be between ${r} and ${a} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^_(\d+)$/.test(o)){const r=Number(o.replace("_","")),a=new Date(s);if(a.setFullYear(s.getFullYear()-r),n.getTime()!==a.getTime())return addErrorMessage(t,`You must be exactly ${r} years old.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/future/.test(o)){if("future"===o&&n<=s)return addErrorMessage(t,"The selected date must be in the future."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("today_future"===o&&n<s)return addErrorMessage(t,"The selected date cannot be in the past."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(/(\d+)_days_future/.test(o)){const r=Number(o.match(/(\d+)_days_future/)[1]),a=new Date(s);if(a.setDate(s.getDate()+r),n>a||n<s)return addErrorMessage(t,`The selected date must be within the next ${r} days.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_years_future$/.test(o)){const r=Number(o.match(/^(\d+)_years_future$/)[1]),a=new Date(s);if(a.setFullYear(s.getFullYear()+r),n<s||n>a)return addErrorMessage(t,`The date must be within the next ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_years_after$/.test(o)){const r=Number(o.match(/^(\d+)_years_after$/)[1]),a=new Date(s);if(a.setFullYear(s.getFullYear()+r),n.getTime()!==a.getTime())return addErrorMessage(t,`The date must be exactly ${r} years after today.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}else if(/past/.test(o)){if("past"===o&&n>=s)return addErrorMessage(t,"The selected date must be in the past."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(/^(\d+)_years_past$/.test(o)){const r=Number(o.match(/^(\d+)_years_past$/)[1]),a=new Date(s);if(a.setFullYear(s.getFullYear()-r),n>s||n<a)return addErrorMessage(t,`The date must be within the past ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}else if(/^(\d+)_years_before$/.test(o)){const r=Number(o.match(/^(\d+)_years_before$/)[1]),a=new Date(s);if(a.setFullYear(s.getFullYear()-r),n>=a)return addErrorMessage(t,`The date must be before ${r} years ago.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_years_after$/.test(o)){const r=Number(o.match(/^(\d+)_years_after$/)[1]),a=new Date(s);if(a.setFullYear(s.getFullYear()+r),n<=a)return addErrorMessage(t,`The date must be after ${r} years from today.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/Between_(\d+)_(\d+)/.test(o)){const[r,a]=o.match(/Between_(\d+)_(\d+)/).slice(1,3).map(Number),s=new Date(r,0,1),l=new Date(a,11,31);if(n<s||n>l)return addErrorMessage(t,`The date must be between ${r} and ${a}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/from_(\d+)/.test(o)){const r=Number(o.match(/from_(\d+)/)[1]);if(n<new Date(r,0,1))return addErrorMessage(t,`The date must be from the year ${r} onwards.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/till_(\d+)/.test(o)){const r=Number(o.match(/till_(\d+)/)[1]);if(n>new Date(r,11,31))return addErrorMessage(t,`The date must be till the year ${r}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else{if("not_today"===o&&n.getTime()===s.getTime())return addErrorMessage(t,"Today's date is not allowed."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("weekday"===o&&(0===n.getDay()||6===n.getDay()))return addErrorMessage(t,"Only weekdays (Monday to Friday) are allowed."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("weekend"===o&&n.getDay()>=1&&n.getDay()<=5)return addErrorMessage(t,"Only weekends (Saturday and Sunday) are allowed."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if("first_of_month"===o&&1!==n.getDate())return addErrorMessage(t,"The date must be the first day of the month."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(/^exact_(\d+)$/.test(o)){const r=Number(o.match(/^exact_(\d+)$/)[1]),a=new Date(s);if(a.setFullYear(s.getFullYear()-r),n.getTime()!==a.getTime())return addErrorMessage(t,`You must be exactly ${r} years old.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if("last_of_month"===o){const r=new Date(n.getFullYear(),n.getMonth()+1,0).getDate();if(n.getDate()!==r)return addErrorMessage(t,"The date must be the last day of the month."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}const l=t.getAttribute("data-kilvish-dategroup");if(l&&/.+_[12]$/.test(l)){const[r,a]=l.split("_"),n="1"===a?"2":"1",s=document.querySelector(`[data-kilvish-dategroup="${r}_${n}"]`);if(s&&s.value){const o="month"===t.type?new Date(t.value+"-01"):new Date(t.value.replace(/[-\/]/g,"/")),l="month"===s.type?new Date(s.value+"-01"):new Date(s.value.replace(/[-\/]/g,"/"));if(!("1"===a&&o<=l||"2"===a&&o>=l)){const o=t.dataset.kilvishDate_name||`Field (${r}_${a})`,l=s.dataset.kilvishDate_name||`Field (${r}_${n})`;return addErrorMessage(t,`${o} must be ${"1"===a?"before or same as":"after or same as"} ${l}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}}return void(t.style.border="")}if("age"===t.name){const r=t.value.trim(),a=parseInt(r,10);if(isNaN(a))return addErrorMessage(t,"Please enter a valid number for age."),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));const n=t.getAttribute("data-kilvish-age")||"18";if(/^(\d+)$/.test(n)){const r=Number(n);if(a!==r)return addErrorMessage(t,`Age must be exactly ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}else if(/^(\d+)_(\d+)$/.test(n)){const[r,s]=n.split("_").map(Number);if(a<r)return addErrorMessage(t,`Minimum age must be ${r} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""));if(a>s)return addErrorMessage(t,`Age cannot exceed ${s} years.`),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}return void(t.style.border="")}}if("model_name"===t.name||"models[]"===t.name)a=/^[a-zA-Z0-9]{4,20}$/;else if("email"===t.type){a=/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;const r=/^[a-zA-Z0-9._@-]+$/,n=t.value;if(r.test(n)&&!a.test(n))return addErrorMessage(t,"Invalid Email Format."),t.style.border="1px solid red",void("change"===e.type&&(t.value=""));t.style.border=""}if(t.hasAttribute("data-kilvish-num")){const r=t.value,n=t.getAttribute("data-kilvish-num");a=/^[0-9]*$/;let s=null,o=null;const l=n.match(/^_(\d+)_(\d+)$/);l&&(s=parseInt(l[1],10),o=parseInt(l[2],10));const i=n.match(/^exact_(\d+)$/),d=i?parseInt(i[1],10):null;/^\d*$/.test(r)?null!==d&&r.length!==d?(addErrorMessage(t,`This field must contain exactly ${d} digits.`),t.style.border="2px solid red","change"===e.type&&(t.value="")):s&&r.length<s||o&&r.length>o?(addErrorMessage(t,`This Field Must be between ${s} and ${o} digits.`),t.style.border="2px solid red","change"===e.type&&(t.value="")):t.style.border="":(addErrorMessage(t,"Only numerical characters are allowed."),t.style.border="2px solid red")}if(t.hasAttribute("data-kilvish-num11")){const r=t.value,n=t.getAttribute("data-kilvish-num");a=/^[0-9]*$/;let s=null,o=null;const l=n?n.match(/^_(\d+)_(\d+)$/):null;l&&(s=parseInt(l[1],10),o=parseInt(l[2],10)),"exact_20"===n&&(a=/^[0-9]{20}$/),a.test(r)?s&&r.length<s||o&&r.length>o?(addErrorMessage(t,`This Field Must be between ${s} and ${o} digits.`),t.style.border="2px solid red","change"===e.type&&(t.value="")):t.style.border="":(addErrorMessage(t,"exact_20"===n?"This field must contain exactly 20 digits.":"Only numerical characters are allowed."),t.style.border="2px solid red")}if(t.hasAttribute("data-kilvish-char")){const r=t.value,n=t.getAttribute("data-kilvish-char");if(n.startsWith("Yes")||n.startsWith("mix")){"mix"===n?a=/^[a-zA-Z0-9]*$/:"Yes"===n&&(a=/^[a-zA-Z]*$/);const s=n.split("_").slice(1).join("");if(s&&(a=new RegExp(`^[a-zA-Z0-9${s}]*$`)),!a.test(r))return addErrorMessage(t,"Invalid character entered!"),t.style.border="2px solid red",void("change"===e.type&&(t.value=""))}}if(t.hasAttribute("data-kilvish-amount")){const r=t.getAttribute("data-kilvish-amount");a=/^\d+(\.\d{0,2})?$/;let n="USD",s=null,o=null;const l=r.match(/^([A-Z]{3})(?:_(\d+)_(\d+))?$/);l&&(n=l[1],s=l[2]?parseFloat(l[2]):null,o=l[3]?parseFloat(l[3]):null),t.addEventListener("input",(()=>{let e=t.selectionStart,r=t.value;a.test(r)||(r=r.replace(/[^0-9.]/g,"").replace(/(\..*?)\..*/,"$1").replace(/(\.\d{2}).*/,"$1")),!r||r.includes(".")||r.endsWith(".")||(r=parseFloat(r).toFixed(2)),t.value=r;const s=r.indexOf(".");-1!==s&&e>s&&(e=Math.min(e,r.length)),t.setSelectionRange(e,e);let o=t.nextElementSibling;o&&o.classList.contains("amount-display")||(o=document.createElement("div"),o.className="amount-display",o.style.color="green",t.parentNode.insertBefore(o,t.nextSibling)),o.textContent=`${n} ${r||"0.00"} .`})),t.addEventListener("blur",(()=>{if(t.readOnly)return;let r=parseFloat(t.value||"0").toFixed(2),a=t.nextElementSibling;return null!==s&&r<s?(addErrorMessage(t,`Amount must be at least ${s.toFixed(2)} ${n}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value="",a&&(a.textContent="")))):null!==o&&r>o?(addErrorMessage(t,`Amount must not exceed ${o.toFixed(2)} ${n}.`),t.style.border="2px solid red",void("change"===e.type&&(t.value="",a&&(a.textContent="")))):(t.style.border="",void(t.value=r))}))}if("password"===t.type){const{bars:r,label:n,rulesUl:s}=ensurePassUI(t),o=t.getAttribute("data-kilvish-password")||"12",[l,i]=o.split("_"),d=parseInt(l,10)||12,c=parseInt(i||"128",10);a=new RegExp(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[\\W_]).{${d},${c}}$`);const u={length:`Password must be between ${d} and ${c} characters long`,uppercase:"At least one uppercase letter (A-Z)",lowercase:"At least one lowercase letter (a-z)",number:"At least one number (0-9)",special:"At least one special character (!@#$%^&*)",sequential:"No sequential characters (e.g., 1234, abcd)"},p={};Object.keys(u).forEach((e=>{let t=s.querySelector(`li[data-rule="${e}"]`);t||(t=document.createElement("li"),t.setAttribute("data-rule",e),t.textContent=`❌ ${u[e]}`,t.style.color="red",t.style.fontSize="12px",s.appendChild(t)),p[e]=t}));const m=e=>{for(let t=0;t<e.length-3;t++){if(e.charCodeAt(t+1)===e.charCodeAt(t)+1&&e.charCodeAt(t+2)===e.charCodeAt(t)+2&&e.charCodeAt(t+3)===e.charCodeAt(t)+3)return!0;if(e.charCodeAt(t+1)===e.charCodeAt(t)-1&&e.charCodeAt(t+2)===e.charCodeAt(t)-2&&e.charCodeAt(t+3)===e.charCodeAt(t)-3)return!0}return!1},g=e=>{let t=0;return e.length>=d&&(t+=2),/[A-Z]/.test(e)&&(t+=1),/[a-z]/.test(e)&&(t+=1),/\d/.test(e)&&(t+=1),/[\W_]/.test(e)&&(t+=1),m(e)&&(t-=2),Math.max(0,t)},y=t.value||"",f={length:y.length>=d&&y.length<=c,uppercase:/[A-Z]/.test(y),lowercase:/[a-z]/.test(y),number:/\d/.test(y),special:/[\W_]/.test(y),sequential:!m(y)};Object.keys(f).forEach((e=>{const t=f[e];p[e].textContent=`${t?"✅":"❌"} ${u[e]}`,p[e].style.color=t?"green":"red"}));const h=g(y);r.forEach((e=>e.style.background="lightgray")),h<=2?(r[0]&&(r[0].style.background="red"),n.textContent="Weak",n.style.color="red"):h<=4?(r[0]&&(r[0].style.background="yellow"),r[1]&&(r[1].style.background="yellow"),n.textContent="Medium",n.style.color="orange"):(r[0]&&(r[0].style.background="green"),r[1]&&(r[1].style.background="green"),r[2]&&(r[2].style.background="green"),n.textContent="Strong",n.style.color="green");const b=a.test(y)&&Object.values(f).every(Boolean);return t.style.border=b?"1px solid green":"1px solid red",void(b||"change"!==e.type||(t.value=""))}if("file"===t.type){const e=t.getAttribute("data-kilvish-file")||"image",[r,...a]=e.split("_"),o=n[e]||n[r]||n.image,l=t.files;let i=1,d=1/0;if(a.length>=2&&(i=parseInt(a[0],10),d=parseInt(a[1],10)),l.length<i||l.length>d)return addErrorMessage(t,`Please select between ${i} and ${d} valid file(s).`),t.value="",t.style.border="1px solid red",!1;const c=s[e]||r.toUpperCase();for(const e of l)if(!o.includes(e.type))return addErrorMessage(t,`Invalid file type. Allowed types: ${c}.`),t.value="",t.style.border="1px solid red",!1}else"keyup"===e.type&&(a.test(t.value)||("password"!==t.type&&addErrorMessage(t,"Invalid character entered!"),t.style.border="1px solid red")),"change"===e.type&&(a.test(t.value)?(t.style.border="",r(t)):(t.value="",addErrorMessage(t,"Invalid character or format!"),t.style.border="1px solid red"))}function addErrorMessage_working(e,t){if(!e.querySelector(".error-message")){const r=document.createElement("div");r.className="error-message",r.style.color="red",r.style.fontSize="12px",r.innerText=t,e.appendChild(r)}}function addErrorMessage_working2(e,t){const r=e.form?e.form:e.closest("form");if(!r)return;const{anchor:a,mode:n,key:s}=getErrorAnchor(e),o=getOrCreateErrorNode(r,a,n,s);o.textContent=t||"",o.classList.remove("is-hidden")}function addErrorMessage(e,t){const r=e.form?e.form:e.closest("form");if(!r)return;let a=null;if(e&&e.tagName&&/^(INPUT|SELECT|TEXTAREA)$/.test(e.tagName))a=e;else if(e&&e.querySelector){const t=e.querySelectorAll("input,select,textarea");a=t.length?t[t.length-1]:null}let n=e,s="beforeend",o="container";if(a){const e=a.nextElementSibling;"SELECT"===a.tagName&&e&&(e.classList.contains("select2")||e.classList.contains("select2-container"))?(n=e,s="afterend",o=a.name||a.id||"select"):a.parentElement&&a.parentElement.classList.contains("input-group")?(n=a.parentElement,s="afterend",o=a.name||a.id||"inputgroup"):(n=a,s="afterend",o=a.name||a.id||"field")}const l=`kil_err_${String(o).replace(/[^a-zA-Z0-9_\-]/g,"_")}`;let i=r.querySelector(`#${l}`);if(i||(i=document.createElement("div"),i.id=l,i.className="error-message is-hidden","beforeend"===s?n.insertAdjacentElement("beforeend",i):n.insertAdjacentElement("afterend",i)),i.textContent=t||"",i.classList.remove("is-hidden"),a)ensureStarVisibleFor(a);else{const t=e.querySelector("input[required],select[required],textarea[required]")||e.querySelector("input,select,textarea");t&&ensureStarVisibleFor(t)}}function getFieldAnchorForUI(e){return e.parentElement&&e.parentElement.classList.contains("input-group")?e.parentElement:e}function ensurePassUI(e){const t=getFieldAnchorForUI(e),r=e.name||e.id||"password",a=`kil_passui_${String(r).replace(/[^a-zA-Z0-9_-]/g,"_")}`;let n=t.parentNode.querySelector(`#${a}`);if(!n){n=document.createElement("div"),n.id=a,n.className="kil-passui";const e=document.createElement("div");e.className="password-strength";const r=document.createElement("div");r.className="bars";const s=document.createElement("div");s.className="bar";const o=document.createElement("div");o.className="bar";const l=document.createElement("div");l.className="bar",r.append(s,o,l);const i=document.createElement("span");i.className="label",i.textContent="Weak",i.style.fontSize="12px",e.append(r,i);const d=document.createElement("ul");d.className="password-validation",n.append(e,d),t.insertAdjacentElement("afterend",n)}const s=n.querySelectorAll(".bar"),o=n.querySelector(".password-strength .label"),l=n.querySelector(".password-validation");return{wrap:n,bars:s,label:o,rulesUl:l}}function getEyeAnchor(e){const t=e.closest(".input-group");if(t)return t.classList.add("kil-eye-rel"),e.classList.add("kil-eye-pad"),{anchor:t};if(!e.parentElement.classList.contains("kil-eye-wrap")){const t=document.createElement("span");t.className="kil-eye-wrap",e.parentNode.insertBefore(t,e),t.appendChild(e)}return e.classList.add("kil-eye-pad"),{anchor:e.parentElement}}function ensureEyeForPassword(e){if("1"===e.dataset.kileyeAttached)return;if(e.hasAttribute("data-kileye-false"))return;const{anchor:t}=getEyeAnchor(e);t.style.overflow="visible";let r=t.querySelector(".kil-eye-btn");r||(r=document.createElement("button"),r.type="button",r.className="kil-eye-btn",r.setAttribute("tabindex","-1"),r.setAttribute("aria-label","Show/Hide password"),r.innerHTML='<i class="fa fa-eye-slash" aria-hidden="true"></i>',t.appendChild(r));const a=e.getAttribute("data-kileye-color");a&&(r.style.color=a);const n=()=>r.querySelector("i");function s(){e.type="text",n()&&(n().classList.remove("fa-eye-slash"),n().classList.add("fa-eye"))}function o(){e.type="password",n()&&(n().classList.remove("fa-eye"),n().classList.add("fa-eye-slash"))}function l(){"password"===e.type?s():o()}const i=(e.getAttribute("data-kileye-show")||"hold").toLowerCase(),d=(e.getAttribute("data-kileye-initial")||"auto").toLowerCase();"fix"===i||"on"===d?s():"off"!==d&&"auto"!==d||o(),r.onmousedown=r.onmouseup=r.onmouseleave=r.onclick=null,r.ontouchstart=r.ontouchend=r.ontouchcancel=null,"toggle"===i||"fix"===i?(r.onclick=e=>{e.preventDefault(),l()},r.addEventListener("touchstart",(e=>{e.preventDefault(),l()}),{passive:!1})):(r.onmousedown=e=>{e.preventDefault(),s()},r.onmouseup=()=>o(),r.onmouseleave=()=>o(),r.onblur=()=>o(),r.addEventListener("touchstart",(e=>{e.preventDefault(),s()}),{passive:!1}),r.addEventListener("touchend",(()=>o())),r.addEventListener("touchcancel",(()=>o()))),e.addEventListener("blur",(()=>{"hold"===i&&o()})),e.dataset.kileyeAttached="1"}!function(){if(document.getElementById("kil-req-style"))return;const e=document.createElement("style");e.id="kil-req-style",e.textContent="\n .kil-required-star{ color:#d32f2f; margin-left:2px; }\n .error-message{ display:block; min-height:16px; margin-top:4px; font-size:12px; color:#d32f2f; line-height:1.25; opacity:1; transition:opacity .12s ease; }\n .error-message.is-hidden{ opacity:0; }\n ",document.head.appendChild(e)}(),document.addEventListener("DOMContentLoaded",(()=>{normalizeRequiredStars(),addAsteriskToRequiredFields()})),document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll("form").forEach((e=>{const t=e.id;t?addRealTimeValidation(t):console.warn("Form without ID found. Skipping validation.")})),attachInputEvents(),addAsteriskToRequiredFields(),addValidateFormToSubmitButtons()})),document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll("input[type='password']").forEach((e=>{if(e.hasAttribute("data-ignore-kilvish"))return;const{bars:t,label:r}=ensurePassUI(e),a=e.getAttribute("data-kilvish-password")||"12",[n]=a.split("_").map(Number);e.addEventListener("input",(e=>{const a=(e=>{let t=0;return e.length>=n&&(t+=2),/[A-Z]/.test(e)&&(t+=1),/[a-z]/.test(e)&&(t+=1),/\d/.test(e)&&(t+=1),/[\W_]/.test(e)&&(t+=1),(e=>{for(let t=0;t<e.length-3;t++){if(e.charCodeAt(t+1)===e.charCodeAt(t)+1&&e.charCodeAt(t+2)===e.charCodeAt(t)+2&&e.charCodeAt(t+3)===e.charCodeAt(t)+3)return!0;if(e.charCodeAt(t+1)===e.charCodeAt(t)-1&&e.charCodeAt(t+2)===e.charCodeAt(t)-2&&e.charCodeAt(t+3)===e.charCodeAt(t)-3)return!0}return!1})(e)&&(t-=2),Math.max(0,t)})(e.target.value);t.forEach((e=>e.style.background="lightgray")),a<=2?(t[0].style.background="red",r.textContent="Weak",r.style.color="red"):a<=4?(t[0].style.background="yellow",t[1].style.background="yellow",r.textContent="Medium",r.style.color="orange"):(t[0].style.background="green",t[1].style.background="green",t[2].style.background="green",r.textContent="Strong",r.style.color="green")}))}))})),function(){if(document.getElementById("kil-pass-style"))return;const e=document.createElement("style");e.id="kil-pass-style",e.textContent="\n .kil-passui { display:block; width:100%; margin-top:6px; }\n .kil-passui .password-strength { display:flex; align-items:center; gap:10px; }\n .kil-passui .password-strength .bars { display:flex; gap:4px; }\n .kil-passui .password-strength .bar { height:5px; width:30px; border-radius:3px; background:lightgray; }\n .kil-passui .password-validation { list-style:none; padding:0; margin:6px 0 0; }\n .kil-passui .password-validation li { font-size:12px; margin:2px 0; }\n ",document.head.appendChild(e)}(),function(){if(document.getElementById("kil-eye-style"))return;const e=document.createElement("style");e.id="kil-eye-style",e.textContent="\n .kil-eye-rel { position: relative !important; overflow: visible !important; }\n .kil-eye-wrap{ position:relative; display:block; }\n .kil-eye-btn{\n position:absolute; top:50%; right:10px; transform:translateY(-50%);\n display:inline-flex; align-items:center; justify-content:center;\n background:transparent; border:0; padding:0; margin:0;\n height:auto; width:auto; line-height:1; cursor:pointer; outline:none;\n z-index: 10; /* <-- stays above focused input */\n pointer-events: auto;\n }\n .kil-eye-btn i{ font-size:16px; }\n .kil-eye-pad { padding-right: 38px !important; }\n .kil-eye-btn { flex: 0 0 auto; }\n ",document.head.appendChild(e)}(),document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll('input[type="password"]').forEach(ensureEyeForPassword)}));const _kilEyeObserver=new MutationObserver((e=>{for(const t of e)t.addedNodes&&t.addedNodes.forEach((e=>{e instanceof HTMLElement&&(e.matches&&e.matches('input[type="password"]')&&ensureEyeForPassword(e),e.querySelectorAll&&e.querySelectorAll('input[type="password"]').forEach(ensureEyeForPassword))}))}));_kilEyeObserver.observe(document.documentElement,{childList:!0,subtree:!0});
|