kilvalidate 1.0.69 → 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 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 (letters, numbers, and special chars):
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"))&&(alert(r),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});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kilvalidate",
3
- "version": "1.0.69",
3
+ "version": "1.0.71",
4
4
  "main": "kilvish.min.js",
5
5
  "files": [
6
6
  "kilvish.min.js",