@jdlien/validator 1.0.0 → 1.0.2
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 +191 -26
- package/dist/validator.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,26 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
Validator is a utility class that
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
5
|
+
Validator is a utility class that adds automatic validation to your HTML forms that works much like
|
|
6
|
+
the HTML5 form validation provided by browsers, but it is much more powerful, flexible, and
|
|
7
|
+
customizable.
|
|
8
|
+
|
|
9
|
+
It can sanitize and check user input in forms, resulting in clean, consistent submissions that
|
|
10
|
+
are very user-friendly without unnecessarily constraining the user from entering data in ways that
|
|
11
|
+
are convenient for them.
|
|
12
|
+
|
|
13
|
+
Validator includes the following built-in validation types:
|
|
14
|
+
|
|
15
|
+
- Required
|
|
16
|
+
- Minimum or maximum length
|
|
17
|
+
- Pattern (regular expression)
|
|
18
|
+
- Numbers (with decimals and negative values)
|
|
19
|
+
- Integers (positive whole numbers)
|
|
20
|
+
- North-American Phone Numbers
|
|
21
|
+
- US Zip Codes
|
|
22
|
+
- Email Addresses
|
|
23
|
+
- Canadian Postal Codes
|
|
24
|
+
- Colors (CSS colors, with color picker support)
|
|
25
|
+
- Dates (optionally constrained to past or future dates)
|
|
26
|
+
- Time of day
|
|
27
|
+
- URLs
|
|
28
|
+
|
|
29
|
+
You can also add custom validation, and can customize error messages per field or for the whole form.
|
|
30
|
+
|
|
31
|
+
Validator has no dependencies and is written in TypeScript. It is compatible with all modern browsers.
|
|
25
32
|
|
|
26
33
|
## Installation
|
|
27
34
|
|
|
@@ -35,26 +42,34 @@ yarn add @jdlien/validator
|
|
|
35
42
|
|
|
36
43
|
## Basic Usage
|
|
37
44
|
|
|
38
|
-
Create a form as you normally would,
|
|
45
|
+
Create a form as you normally would, adding attributes for inputs to control how Validator will
|
|
46
|
+
check the input, such as `required`, `type`, or `data-type`. Native HTML5 attributes are supported,
|
|
47
|
+
although often the browser's built-in validation is problematic or inflexible. In those cases, you
|
|
48
|
+
can use a `data-` variant of these attributes to avoid the browser's built-in validation.
|
|
49
|
+
|
|
50
|
+
Any input that you want to validate should have a unique name attribute. If you want to display error messages for the input, you must also have a div with an id that is the name of the input + `-error`.
|
|
39
51
|
|
|
40
|
-
Then create a new Validator instance and pass it the form element as the first argument. An optional second argument allows you to pass in options.
|
|
52
|
+
Then create a new Validator instance and pass it the form element as the first argument. An optional second argument allows you to pass in options. Here is a simplified example:
|
|
41
53
|
|
|
42
54
|
```html
|
|
43
55
|
<form id="myForm">
|
|
56
|
+
<label for="name">Name</label>
|
|
44
57
|
<input
|
|
45
58
|
type="text"
|
|
46
59
|
name="name"
|
|
60
|
+
id="name"
|
|
47
61
|
required
|
|
48
62
|
data-min-length="2"
|
|
49
63
|
data-max-length="20"
|
|
50
|
-
data-error-default="
|
|
64
|
+
data-error-default="Enter between 2 and 20 characters."
|
|
51
65
|
/>
|
|
52
66
|
<div id="name-error"></div>
|
|
53
67
|
|
|
54
|
-
<
|
|
68
|
+
<label for="email">Email</label>
|
|
69
|
+
<input type="email" name="email" id="email" required />
|
|
55
70
|
<div id="email-error"></div>
|
|
56
71
|
|
|
57
|
-
<input type="text" data-type="tel" name="phone" />
|
|
72
|
+
<input type="text" data-type="tel" name="phone" id="phone" />
|
|
58
73
|
<div id="phone-error"></div>
|
|
59
74
|
|
|
60
75
|
<input type="submit" value="Submit" />
|
|
@@ -74,7 +89,157 @@ You can also pass in a custom default error message for a field using `data-erro
|
|
|
74
89
|
|
|
75
90
|
## Demo
|
|
76
91
|
|
|
77
|
-
|
|
92
|
+
[Working demo](https://jdlien.com/validator/demo.html) [source on GitHub](./demo.html).
|
|
93
|
+
|
|
94
|
+
## Supported Input Types and Atributes
|
|
95
|
+
|
|
96
|
+
Validator works by checking for certain attributes on the form inputs and applying validation based on those.
|
|
97
|
+
In many cases you can use the native HTML5 attributes, but you can also use the `data-` attributes if you do not want the behavior to be affected by built-in browser validation behavior (eg for min-length, max-length, and input types such as date and time).
|
|
98
|
+
|
|
99
|
+
There are a few attributes that Validator looks for on the form element:
|
|
100
|
+
|
|
101
|
+
- `data-prevent-submit` - If this attribute is present, the form will never be submitted, even is valid. This is useful if you want to handle the submission yourself. (By default the form will be submitted if it is valid and not if it is invalid.)
|
|
102
|
+
|
|
103
|
+
- `novalidate` - This is a native HTML5 attribute that will disable browser validation on the form. If this attribute is present. Validator adds this by default and remove it if `destroy()` is called. If you add it yourself, it will not be added back by Validator.
|
|
104
|
+
|
|
105
|
+
On input (and sometimes select and textarea) elements the following attributes are supported:
|
|
106
|
+
|
|
107
|
+
- `required` - The input must have a value.
|
|
108
|
+
- `minlength`/`data-min-length` - The input must be at least the specified number of characters.
|
|
109
|
+
- `maxlength`/`data-max-length` - The input must be no more than the specified number of characters.
|
|
110
|
+
- `pattern`/`data-pattern` - The input must match the specified regular expression.
|
|
111
|
+
- `type`/`data-type` - The input must match the specified type. The following types are supported:
|
|
112
|
+
|
|
113
|
+
- `number` - The input must be a number (use `data-type` to avoid quirky browser behavior)
|
|
114
|
+
- `integer` - The input must be a positive whole number.
|
|
115
|
+
- `tel` - The input must be a valid North-American phone number.
|
|
116
|
+
- `email` - The input must be a valid email address.
|
|
117
|
+
- `zip` - The input must be a valid US zip code.
|
|
118
|
+
- `postal` - The input must be a valid Canadian postal code.
|
|
119
|
+
- `color` - The input must be a valid CSS color.
|
|
120
|
+
- `date` - The input must be a valid date.
|
|
121
|
+
- `time` - The input must be a valid time.
|
|
122
|
+
- `url` - The input must be a valid URL.
|
|
123
|
+
- `color` - The input must be a valid CSS color. (This can be used in conjunction with a native color input - see Color Picker Support for details.)
|
|
124
|
+
|
|
125
|
+
- `data-date-format`/`data-time-format` - Applies formatting to time input types (these are interchangeable). The format must be a valid moment.js format string. See [moment.js docs](https://momentjs.com/docs/#/displaying/format/) for more information.
|
|
126
|
+
- `date-range` - Applies to date input types. Supported values are `past` and `future`.
|
|
127
|
+
- `data-error-default` - A custom error message to display if the input is invalid. This will be used for required, pattern, and date-range validation failures.
|
|
128
|
+
- `data-validation` - The name of a custom validation function.
|
|
129
|
+
|
|
130
|
+
A validation function will be called with the input value as the argument. The function may either return a boolean (true/false) or an object with a `valid` property that is a boolean. If the function returns string, an object with a `message` property, that will be used as the error message for the input. A `messages` array may also be specified which will be used to display multiple error messages for the input.
|
|
131
|
+
|
|
132
|
+
You may also use a promise that resolves to such an object for asynchronous validation.
|
|
133
|
+
|
|
134
|
+
An example of such a function is:
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
function customValidation(value) {
|
|
138
|
+
if (value === 'foo') return 'The value cannot be foo.'
|
|
139
|
+
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Here is an example of a custom validation function that uses a promise:
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
function customValidationPromise(value) {
|
|
148
|
+
return fetch(`https://api.example.com/validate-username?username=${value}`)
|
|
149
|
+
.then((response) => response.json())
|
|
150
|
+
.then((result) => {
|
|
151
|
+
if (result.valid) return true
|
|
152
|
+
else return 'Email is invalid'
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Displaying Error Messages
|
|
158
|
+
|
|
159
|
+
Validator will display error messages in the divs with the name of the input + `-error`. For example, if the input name is `name`, the error message will be displayed in the div with the id `name-error`. You will need to create these divs in your HTML. They should initially be hidden with a class that sets properties such as `display: none;`, `visibility: hidden;`, or `opacity: 0;`.
|
|
160
|
+
|
|
161
|
+
You can customize the class(es) that Validator uses to hide the error messages by passing in a `hideErrorClass` option to the Validator constructor. The default is `hidden opacity-0`.
|
|
162
|
+
|
|
163
|
+
## Color Picker Support
|
|
164
|
+
|
|
165
|
+
If you need to allow a user to pick a color, you can use data-type="color" and the input will be required to be any valid CSS color supported by the browser. This type can also work in conjunction with a native color input. If you do this, you will need to add an input with the name of the data-color input + `-color`, and this should be inside a linked label which will become the color preview swatch. Such a label should have a label of the color input's name + `-color-label`.
|
|
166
|
+
|
|
167
|
+
A basic example that would work:
|
|
168
|
+
|
|
169
|
+
```html
|
|
170
|
+
<input type="text" id="yourColor" data-type="color" name="yourColor" data-error-default="" />
|
|
171
|
+
|
|
172
|
+
<!-- A sample of the color in "yourColor" will be displayed as the background of this label -->
|
|
173
|
+
<label for="yourColor-color" id="yourColor-color-label" style="width: 40px; height: 40px">
|
|
174
|
+
<!-- Clicking this invokes the browser's native color picker -->
|
|
175
|
+
<input type="color" id="yourColor-color" name="yourColor-color" style="visibility: hidden" />
|
|
176
|
+
</label>
|
|
177
|
+
|
|
178
|
+
<div class="error hidden" id="yourColor-error"></div>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Options
|
|
182
|
+
|
|
183
|
+
The second parameter to the Validator constructor is an options object. The following options are available:
|
|
184
|
+
|
|
185
|
+
- `messages - An object containing custom error messages. The default messages can be overridden by `passing in a custom message object. These are all the default messages:
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
messages = {
|
|
189
|
+
ERROR_MAIN: 'There is a problem with your submission.',
|
|
190
|
+
ERROR_GENERIC: 'Enter a valid value.',
|
|
191
|
+
ERROR_REQUIRED: 'This field is required.',
|
|
192
|
+
OPTION_REQUIRED: 'An option must be selected.',
|
|
193
|
+
CHECKED_REQUIRED: 'This must be checked.',
|
|
194
|
+
ERROR_MAXLENGTH: 'This must be ${val} characters or fewer.',
|
|
195
|
+
ERROR_MINLENGTH: 'This must be at least ${val} characters.',
|
|
196
|
+
ERROR_NUMBER: 'This must be a number.',
|
|
197
|
+
ERROR_INTEGER: 'This must be a whole number.',
|
|
198
|
+
ERROR_TEL: 'This is not a valid telephone number.',
|
|
199
|
+
ERROR_EMAIL: 'This is not a valid email address.',
|
|
200
|
+
ERROR_ZIP: 'This is not a valid zip code.',
|
|
201
|
+
ERROR_POSTAL: 'This is not a valid postal code.',
|
|
202
|
+
ERROR_DATE: 'This is not a valid date.',
|
|
203
|
+
ERROR_DATE_PAST: 'The date must be in the past.',
|
|
204
|
+
ERROR_DATE_FUTURE: 'The date must be in the future.',
|
|
205
|
+
ERROR_DATE_RANGE: 'The date is outside the allowed range.',
|
|
206
|
+
ERROR_TIME: 'This is not a valid time.',
|
|
207
|
+
ERROR_TIME_RANGE: 'The time is outside the allowed range.',
|
|
208
|
+
ERROR_URL: 'This is not a valid URL.',
|
|
209
|
+
ERROR_COLOR: 'This is not a valid CSS colour.',
|
|
210
|
+
ERROR_CUSTOM_VALIDATION: 'There was a problem validating this field.',
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- `debug` - A boolean indicating whether or not to show debug messages in the console. Defaults to false.
|
|
215
|
+
- `autoInit` - A boolean indicating whether or not to automatically initialize the Validator instance on page load. Defaults to true.
|
|
216
|
+
- `preventSubmit` - A boolean indicating whether or not to prevent form submission if validation is successful. Defaults to false.
|
|
217
|
+
- `hiddenClasses` - A string containing one or more space-separated classes to toggle the hidden mode (eg `display: none` CSS property) on hidden elements. Defaults to `hidden opacity-0`.
|
|
218
|
+
- `errorMainClasses` - A string containing one or more space-separated classes to apply to the main error message.
|
|
219
|
+
- `errorInputClasses` - A string containing one or more space-separated classes to apply to invalid `inputs.
|
|
220
|
+
- `validationSuccessCallback` - A function to be called when validation is successful.
|
|
221
|
+
- `validationErrorCallback` - A function to be called when validation fails.
|
|
222
|
+
|
|
223
|
+
### Example:
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
import Validator from 'validator'
|
|
227
|
+
|
|
228
|
+
const myForm = document.querySelector('form')
|
|
229
|
+
const myValidator = new Validator(myForm, {
|
|
230
|
+
messages: {
|
|
231
|
+
ERROR_REQUIRED: 'This is required.',
|
|
232
|
+
ERROR_EMAIL: 'Invalid email address.',
|
|
233
|
+
},
|
|
234
|
+
debug: true,
|
|
235
|
+
preventSubmit: true,
|
|
236
|
+
hiddenClasses: 'hidden',
|
|
237
|
+
errorMainClasses: 'error-main',
|
|
238
|
+
errorInputClasses: 'error-input',
|
|
239
|
+
validationSuccessCallback: () => console.log('Validation successful!'),
|
|
240
|
+
validationErrorCallback: () => console.log('Validation failed.'),
|
|
241
|
+
})
|
|
242
|
+
```
|
|
78
243
|
|
|
79
244
|
## Contributing
|
|
80
245
|
|
package/dist/validator.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(m,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(m=typeof globalThis<"u"?globalThis:m||self,m.Validator=c())})(this,function(){"use strict";var W=Object.defineProperty;var B=(m,c,f)=>c in m?W(m,c,{enumerable:!0,configurable:!0,writable:!0,value:f}):m[c]=f;var d=(m,c,f)=>(B(m,typeof c!="symbol"?c+"":c,f),f);function m(r){return r instanceof HTMLInputElement||r instanceof HTMLSelectElement||r instanceof HTMLTextAreaElement}function c(r,e){typeof e=="string"&&(e=[e]);const t=r.dataset.type||"",s=r.type||"";return!!(e.includes(t)||e.includes(s))}function f(r){const e=parseInt(r);if(typeof r=="number"||!isNaN(e))return e-1;const t=new Date(`1 ${r} 2000`).getMonth();if(!isNaN(t))return t;const s={ja:0,en:0,fe:1,fé:1,ap:3,ab:3,av:3,mai:4,juin:5,juil:6,au:7,ag:7,ao:7,se:8,o:9,n:10,d:11};for(const i in s)if(r.toLowerCase().startsWith(i))return s[i];throw new Error("Invalid month name: "+r)}function v(r){return typeof r=="string"&&(r=parseInt(r.replace(/\D/g,""))),r>99?r:r<(new Date().getFullYear()+20)%100?r+2e3:r+1900}function p(r){if(r instanceof Date)return r;r=r.trim().toLowerCase();let e=0,t=0,s=0,i=0,a=0,n=0;const l=new RegExp(/\d{1,2}\:\d\d(?:\:\d\ds?)?\s?(?:[a|p]m?)?/gi);if(l.test(r)){const h=r.match(l)[0];r=r.replace(h,"").trim();const g=R(h);if(g!==null&&({hour:i,minute:a,second:n}=g),r.length<=2){const y=new Date;return new Date(y.getFullYear(),y.getMonth(),y.getDate(),i,a,n)}}const u=/(^|\b)(mo|tu|we|th|fr|sa|su|lu|mard|mer|jeu|ve|dom)[\w]*\.?/gi;r=r.replace(u,"").trim();const o=new Date(new Date().setHours(0,0,0,0));if(/(now|today)/.test(r))return o;if(r.includes("tomorrow"))return new Date(o.setDate(o.getDate()+1));r.length===8&&(r=r.replace(/(\d\d\d\d)(\d\d)(\d\d)/,"$1-$2-$3")),r.length===6&&(r=r.replace(/(\d\d)(\d\d)(\d\d)/,v(r.slice(0,2))+"-$2-$3"));try{({year:e,month:t,day:s}=M(r))}catch{return new Date("")}return new Date(e,t-1,s,i,a,n)}function M(r){function e(n,l=[null,null,null]){const u=o=>o.filter(h=>!l.includes(h));return n===0||n>31?u(["y"]):n>12?u(["d","y"]):n>=1||n<=12?u(["m","d","y"]):[]}const t=r.split(/[\s-/:.,]+/).filter(n=>n!=="");if(t.length<3){if(r.match(/\d{4}/)!==null)throw new Error("Invalid Date");t.unshift(String(new Date().getFullYear()))}const s={year:0,month:0,day:0};function i(n,l){n==="year"?s.year=v(l):s[n]=l}let a=0;for(;!(s.year&&s.month&&s.day);){e:for(const n of t){if(a++,/^[a-zA-Zé]+$/.test(n)){s.month||i("month",f(n)+1);continue}if(/^'\d\d$/.test(n)||/^\d{3,5}$/.test(n)){s.year||i("year",parseInt(n.replace(/'/,"")));continue}const l=parseInt(n);if(isNaN(l))throw console.error(`not date because ${n} isNaN`),new Error("Invalid Date");const u=e(l,[s.year?"y":null,s.month?"m":null,s.day?"d":null]);if(u.length==1)for(let o=0;o<u.length;o++){if(u[o]==="m"&&!s.month){i("month",l);continue e}if(u[o]==="d"&&!s.day){i("day",l);continue e}if(u[o]==="y"&&!s.year){i("year",l);continue e}}a>3&&(!s.month&&u.includes("m")?i("month",l):!s.day&&u.includes("d")?i("day",l):!s.year&&u.includes("y")&&i("year",l))}if(a>6)throw new Error("Invalid Date")}if(s.year&&s.month&&s.day)return s;throw new Error("Invalid Date")}function R(r){if(r=r.trim().toLowerCase(),r==="now"){const o=new Date;return{hour:o.getHours(),minute:o.getMinutes(),second:o.getSeconds()}}const e=r.match(/(\d{3,4})/);if(e){const o=e[1].length,h=e[1].slice(0,o==3?1:2),g=e[1].slice(-2);r=r.replace(e[1],h+":"+g)}const t=new RegExp(/^(\d{1,2})(?::(\d{1,2}))?\s*(?:(a|p)m?)?$/i);if(t.test(r)){const o=r.match(t);if(o===null)return null;r=o[1]+":"+(o[2]||"00")+(o[3]||"")}const s=new RegExp(/^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?\s*(?:(a|p)m?)?$/i);if(!s.test(r))return null;const i=r.match(s);if(i===null)return null;const a=parseInt(i[1]),n=parseInt(i[2]),l=i[3]?parseInt(i[3]):0,u=i[4];return isNaN(a)||isNaN(n)||isNaN(l)?null:u==="p"&&a<12?{hour:a+12,minute:n,second:l}:u==="a"&&a===12?{hour:0,minute:n,second:l}:a<0||a>23||n<0||n>59||l<0||l>59?null:{hour:a,minute:n,second:l}}function S(r,e="h:mm A"){const t=R(r);if(t){const s=new Date;return s.setHours(t.hour),s.setMinutes(t.minute),s.setSeconds(t.second),s.setMilliseconds(0),w(s,e)}return""}function w(r,e="YYYY-MM-DD"){if(r=p(r),isNaN(r.getTime()))return"";const t={y:r.getFullYear(),M:r.getMonth(),D:r.getDate(),W:r.getDay(),H:r.getHours(),m:r.getMinutes(),s:r.getSeconds(),ms:r.getMilliseconds()},s=(o,h=2)=>(o+"").padStart(h,"0"),i=()=>t.H%12||12,a=o=>o<12?"AM":"PM",n=o=>"January|February|March|April|May|June|July|August|September|October|November|December".split("|")[o];function l(o,h=0){const g="Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday".split("|");return h?g[o].slice(0,h):g[o]}const u={YY:String(t.y).slice(-2),YYYY:t.y,M:t.M+1,MM:s(t.M+1),MMMM:n(t.M),MMM:n(t.M).slice(0,3),D:String(t.D),DD:s(t.D),d:String(t.W),dd:l(t.W,2),ddd:l(t.W,3),dddd:l(t.W),H:String(t.H),HH:s(t.H),h:i(),hh:s(i()),A:a(t.H),a:a(t.H).toLowerCase(),m:String(t.m),mm:s(t.m),s:String(t.s),ss:s(t.s),SSS:s(t.ms,3)};return e.replace(/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,(o,h)=>h||u[o])}function x(r,e){const t=p(r);return isNaN(t.getTime())?"":((!e||e.length===0)&&(e="YYYY-MMM-DD"),w(t,e))}function C(r){let e=p(r);return e==null?!1:!isNaN(e.getTime())}function D(r,e){return!(e==="past"&&r>new Date||e==="future"&&r.getTime()<new Date().setHours(0,0,0,0))}function A(r){let e=R(r);return e===null?!1:!isNaN(e.hour)&&!isNaN(e.minute)&&!isNaN(e.second)}function H(r){if(r.length>255||!new RegExp(/^.+@.+\.[a-zA-Z0-9]{2,}$/).test(r))return!1;let t="";return t+="^([a-zA-Z0-9!#$%'*+/=?^_`{|}~-]+",t+="(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*",t+="|",t+='"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*"',t+=")@(",t+="(",t+="(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+",t+="[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?",t+=")",t+=")$",new RegExp(t).test(r)}function N(r){return r=r.replace(/^[^2-90]+/g,""),r=r.replace(/(\d\d\d).*?(\d\d\d).*?(\d\d\d\d)(.*)/,"$1-$2-$3$4"),r}function k(r){return/^\d\d\d-\d\d\d-\d\d\d\d$/.test(r)}function T(r){return r.replace(/[^0-9]/g,"")}function O(r){return/^\-?\d*\.?\d*$/.test(r)}function I(r){return r.replace(/[^\-0-9.]/g,"").replace(/(^-)|(-)/g,(e,t)=>t?"-":"").replace(/(\..*)\./g,"$1")}function _(r){return/^\-?\d*$/.test(r)}function $(r){return r=r.trim(),new RegExp("^(?:[a-z+]+:)?//","i").test(r)?r:"https://"+r}function P(r){return new RegExp("^(?:[-a-z+]+:)?//","i").test(r)}function V(r){return r=r.replace(/[^0-9]/g,"").replace(/(.{5})(.*)/,"$1-$2").trim(),r.length===6&&(r=r.replace(/-/,"")),r}function U(r){return new RegExp(/^\d{5}(-\d{4})?$/).test(r)}function Y(r){return r=r.toUpperCase().replace(/[^A-Z0-9]/g,"").replace(/(.{3})\s*(.*)/,"$1 $2").trim(),r}function q(r){return new RegExp(/^[ABCEGHJKLMNPRSTVXY][0-9][ABCEGHJKLMNPRSTVWXYZ] ?[0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]$/).test(r)}function L(r){return["transparent","currentColor"].includes(r)?!0:typeof r!="string"||!r.trim()?!1:typeof CSS=="object"&&typeof CSS.supports=="function"?CSS.supports("color",r):F(r)}function F(r){const e=new RegExp(/^rgba?\(\s*(\d{1,3}%?,\s*){2}\d{1,3}%?\s*(?:,\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),t=new RegExp(/^hsla?\(\s*\d+(deg|grad|rad|turn)?,\s*\d{1,3}%,\s*\s*\d{1,3}%(?:,\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),s=new RegExp(/^rgba?\(\s*(\d{1,3}%?\s+){2}\d{1,3}%?\s*(?:\s*\/\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),i=new RegExp(/^hsla?\(\s*\d+(deg|grad|rad|turn)?\s+\d{1,3}%\s+\s*\d{1,3}%(?:\s*\/\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),a=new RegExp(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);let n="aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen";const l=new RegExp(`^(${n})$`,"i");return e.test(r)||t.test(r)||s.test(r)||i.test(r)||a.test(r)||l.test(r)}let E=null;const b=new Map;function z(r){if(r=r.trim().toLowerCase(),r==="transparent")return"transparent";if(b.has(r))return b.get(r);E===null&&(E=document.createElement("canvas"),E.willReadFrequently=!0);let e=E.getContext("2d");if(!e)throw new Error("Can't get context from colorCanvas");e.fillStyle=r,e.fillRect(0,0,1,1);let t=e.getImageData(0,0,1,1).data,s="#"+("000000"+(t[0]<<16|t[1]<<8|t[2]).toString(16)).slice(-6);return b.set(r,s),s}function Z(r){let e={valid:!1,error:!1,messages:[]};return typeof r=="boolean"?{valid:r,error:!1,messages:[]}:(typeof r.valid=="boolean"&&(e.valid=r.valid),typeof r.message=="string"&&(e.messages=[r.message]),typeof r.messages=="string"&&(e.messages=[r.messages]),Array.isArray(r.messages)&&(e.messages=r.messages),r.error===!0&&(e.error=!0),e)}class G extends Event{constructor(t){super("validationSuccess",{cancelable:!0});d(this,"submitEvent");this.submitEvent=t}}class j extends Event{constructor(t){super("validationError",{cancelable:!0});d(this,"submitEvent");this.submitEvent=t}}class K{constructor(e,t={}){d(this,"form");d(this,"inputs",[]);d(this,"inputErrors",{});d(this,"messages",{ERROR_MAIN:"There is a problem with your submission.",ERROR_GENERIC:"Enter a valid value.",ERROR_REQUIRED:"This field is required.",OPTION_REQUIRED:"An option must be selected.",CHECKED_REQUIRED:"This must be checked.",ERROR_MAXLENGTH:"This must be ${val} characters or fewer.",ERROR_MINLENGTH:"This must be at least ${val} characters.",ERROR_NUMBER:"This must be a number.",ERROR_INTEGER:"This must be a whole number.",ERROR_TEL:"This is not a valid telephone number.",ERROR_EMAIL:"This is not a valid email address.",ERROR_ZIP:"This is not a valid zip code.",ERROR_POSTAL:"This is not a valid postal code.",ERROR_DATE:"This is not a valid date.",ERROR_DATE_PAST:"The date must be in the past.",ERROR_DATE_FUTURE:"The date must be in the future.",ERROR_DATE_RANGE:"The date is outside the allowed range.",ERROR_TIME:"This is not a valid time.",ERROR_TIME_RANGE:"The time is outside the allowed range.",ERROR_URL:"This is not a valid URL.",ERROR_COLOR:"This is not a valid CSS colour.",ERROR_CUSTOM_VALIDATION:"There was a problem validating this field."});d(this,"debug");d(this,"autoInit");d(this,"preventSubmit",!1);d(this,"hiddenClasses");d(this,"errorMainClasses");d(this,"errorInputClasses");d(this,"dispatchTimeout",0);d(this,"originalNoValidate",!1);d(this,"validationSuccessCallback");d(this,"validationErrorCallback");d(this,"submitHandlerRef",this.submitHandler.bind(this));d(this,"inputInputHandlerRef",this.inputInputHandler.bind(this));d(this,"inputChangeHandlerRef",this.inputChangeHandler.bind(this));d(this,"inputKeydownHandlerRef",this.inputKeydownHandler.bind(this));d(this,"inputHandlers",{number:{parse:I,isValid:O,error:this.messages.ERROR_NUMBER},integer:{parse:T,isValid:_,error:this.messages.ERROR_INTEGER},tel:{parse:N,isValid:k,error:this.messages.ERROR_TEL},email:{parse:e=>e.trim(),isValid:H,error:this.messages.ERROR_EMAIL},zip:{parse:V,isValid:U,error:this.messages.ERROR_ZIP},postal:{parse:Y,isValid:q,error:this.messages.ERROR_POSTAL},url:{parse:$,isValid:P,error:this.messages.ERROR_URL},date:{parse:x,isValid:C,error:this.messages.ERROR_DATE},time:{parse:S,isValid:A,error:this.messages.ERROR_TIME},color:{parse:e=>e.trim().toLowerCase(),isValid:L,error:this.messages.ERROR_COLOR}});d(this,"isSubmitting",!1);if(!e)throw new Error("Validator requires a form to be passed as the first argument.");if(!(e instanceof HTMLFormElement))throw new Error("form argument must be an instance of HTMLFormElement");this.form=e,(e.dataset.preventSubmit===""||e.dataset.preventSubmit)&&(this.preventSubmit=!0),Object.assign(this.messages,t.messages||{}),this.debug=t.debug||!1,this.autoInit=t.autoInit!==!1,this.preventSubmit=t.preventSubmit===!1?!1:this.preventSubmit,this.hiddenClasses=t.hiddenClasses||"hidden opacity-0",this.errorMainClasses=t.errorMainClasses||"m-2 border border-red-500 bg-red-100 p-3 dark:bg-red-900/80 text-center",this.errorInputClasses=t.errorInputClasses||"border-red-600 dark:border-red-500",this.validationSuccessCallback=t.validationSuccessCallback||(()=>{}),this.validationErrorCallback=t.validationErrorCallback||(()=>{}),this.autoInit&&this.init(),new MutationObserver(()=>this.autoInit&&this.init()).observe(e,{childList:!0})}addEventListeners(){this.form.addEventListener("submit",this.submitHandlerRef),this.form.addEventListener("input",this.inputInputHandlerRef),this.form.addEventListener("change",this.inputChangeHandlerRef),this.form.addEventListener("keydown",this.inputKeydownHandlerRef),this.form.addEventListener("remove",this.destroy,{once:!0})}removeEventListeners(){this.form.removeEventListener("submit",this.submitHandlerRef),this.form.removeEventListener("input",this.inputInputHandlerRef),this.form.removeEventListener("change",this.inputChangeHandlerRef),this.form.removeEventListener("keydown",this.inputKeydownHandlerRef),this.form.removeEventListener("remove",this.destroy)}init(){this.inputs=Array.from(this.form.elements),this.inputs.forEach(e=>{!e.name&&!e.id&&(e.id=`vl-input-${Math.random().toString(36).slice(2)}`),this.inputErrors[e.name||e.id]=[]}),this.originalNoValidate=this.form.hasAttribute("novalidate"),this.form.setAttribute("novalidate","novalidate"),this.removeEventListeners(),this.addEventListeners()}getErrorEl(e){const t=document.getElementById(e.name+"-error");return t||document.getElementById(e.id+"-error")||null}addErrorMain(e){const t=document.createElement("div");t.id="form-error-main",this.errorMainClasses.split(" ").forEach(s=>{t.classList.add(s)}),e?t.innerHTML=e:t.innerHTML=this.messages.ERROR_MAIN,this.form.appendChild(t)}addInputError(e,t=e.dataset.errorDefault||this.messages.ERROR_GENERIC){const s=e.name||e.id;this.debug&&console.log("Invalid value for "+s+": "+t),s in this.inputErrors||(this.inputErrors[s]=[]),this.inputErrors[s].includes(t)||this.inputErrors[s].push(t)}showInputErrors(e){if(!e||!e.name&&!e.id)return;const t=e.name||e.id,s=t in this.inputErrors?this.inputErrors[t]:[];if(!s.length)return;e.setAttribute("aria-invalid","true"),this.errorInputClasses.split(" ").forEach(a=>{e.classList.add(a)});let i=this.getErrorEl(e);i&&(i.innerHTML=s.join("<br>"),this.hiddenClasses.split(" ").forEach(a=>{i&&i.classList.remove(a)}))}showFormErrors(){if(this.inputs.forEach(e=>this.showInputErrors(e)),Object.values(this.inputErrors).some(e=>Array.isArray(e)&&e.length)){const e=this.form.querySelectorAll("#form-error-main");e.length?e.forEach(t=>{t.innerHTML||(t.innerHTML=this.messages.ERROR_MAIN),this.hiddenClasses.split(" ").forEach(s=>{t.classList.remove(s)})}):this.addErrorMain()}}clearInputErrors(e){this.inputErrors[e.name||e.id]=[],e.removeAttribute("aria-invalid");let t=this.getErrorEl(e);t&&(this.errorInputClasses.split(" ").forEach(s=>{e.classList.remove(s)}),this.hiddenClasses.split(" ").forEach(s=>{t&&t.classList.add(s)}),t.textContent="")}clearFormErrors(){this.form.querySelectorAll("#form-error-main").forEach(e=>{this.hiddenClasses.split(" ").forEach(t=>{e.classList.add(t)})}),this.inputs.forEach(e=>this.clearInputErrors(e))}validateRequired(e){let t=!0;if(e.required&&(e.value===""||e instanceof HTMLInputElement&&["checkbox","radio"].includes(e.type)&&!e.checked))if(e instanceof HTMLInputElement&&["checkbox","radio"].includes(e.type)){let s=!1,i=e.name;const a=this.form.querySelectorAll(`input[name="${i}"]`);if(a.forEach(n=>{if(n instanceof HTMLInputElement&&n.checked===!0){s=!0;return}}),s===!1){t=!1;let n=a.length>1?this.messages.OPTION_REQUIRED:this.messages.CHECKED_REQUIRED;e.dataset.errorDefault&&(n=e.dataset.errorDefault),this.addInputError(e,n)}}else m(e)&&(t=!1,this.addInputError(e,e.dataset.errorDefault||this.messages.ERROR_REQUIRED));return t}validateLength(e){let t=!0;if((e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&e.value.length){let s=e.minLength>0?e.minLength:e.dataset.minLength?parseInt(e.dataset.minLength):0,i=e.maxLength>0&&e.maxLength<5e5?e.maxLength:e.dataset.maxLength?parseInt(e.dataset.maxLength):1/0;s>0&&e.value.length<s&&(t=!1,this.addInputError(e,this.messages.ERROR_MINLENGTH.replace("${val}",s.toString()))),e.value.length>i&&(t=!1,this.addInputError(e,this.messages.ERROR_MAXLENGTH.replace("${val}",i.toString())))}return t}validateInputType(e){const t=e.dataset.type||e.type,s=this.inputHandlers[e.type]||this.inputHandlers[t];if(s){const i=e.dataset.dateFormat||e.dataset.timeFormat,a=s.parse(e.value,i),n=["date","time","datetime-local","month","week"];if(a.length&&!n.includes(e.type)&&(e.value=a),!s.isValid(e.value))return this.addInputError(e,s.error),!1}return!0}validateDateRange(e){if(e.dataset.dateRange){const t=e.dataset.dateRange,s=p(e.value);if(!isNaN(s.getTime())&&!D(s,t)){let i=e.dataset.errorDefault||this.messages.ERROR_DATE_RANGE;return t==="past"?i=this.messages.ERROR_DATE_PAST:t==="future"&&(i=this.messages.ERROR_DATE_FUTURE),this.addInputError(e,i),!1}}return!0}validatePattern(e){const t=e.dataset.pattern||e instanceof HTMLInputElement&&e.pattern||null;return t&&!new RegExp(t).test(e.value)?(this.addInputError(e,e.dataset.message),!1):!0}async validateCustom(e){const t=e.dataset.validation;if(!t||typeof t!="string")return!0;const s=window[t];if(!s||typeof s!="function")return!0;let i;try{i=await Promise.resolve(s(e.value)),i=Z(i)}catch{return this.addInputError(e,this.messages.ERROR_CUSTOM_VALIDATION),!1}const a=i.messages.join("<br>")||this.messages.ERROR_CUSTOM_VALIDATION;return i.valid||this.addInputError(e,a),i.valid}async validateInput(e){if(!(e instanceof HTMLInputElement)||!e.value.length)return!0;let t=!0;return t=this.validateInputType(e)&&t,t=this.validateDateRange(e)&&t,t=this.validatePattern(e)&&t,t=await this.validateCustom(e)&&t,t}async validate(e){let t=!0;for(const s of this.inputs)t=this.validateRequired(s)&&t,t=this.validateLength(s)&&t,t=await this.validateInput(s)&&t;return t}async submitHandler(e){if(this.isSubmitting)return;e.preventDefault(),this.clearFormErrors();let t=await this.validate(e);this.showFormErrors();const s=new G(e),i=new j(e);t?(this.form.dispatchEvent(s),this.validationSuccessCallback&&this.validationSuccessCallback(e)):(this.form.dispatchEvent(i),this.validationErrorCallback&&this.validationErrorCallback(e)),t&&!this.preventSubmit&&(this.isSubmitting=!0,s.defaultPrevented||this.form.submit(),this.isSubmitting=!1)}async inputChangeHandler(e){e.target instanceof HTMLInputElement&&(this.clearInputErrors(e.target),await this.validateInput(e.target),this.showInputErrors(e.target))}inputInputHandler(e){const t=e.target;c(t,"integer")&&(t.value=T(t.value)),t.type!=="number"&&c(t,["number","float","decimal"])&&(t.value=I(t.value)),c(t,"color")&&this.syncColorInput(e)}syncColorInput(e){let t=e.target,s=t;t.type==="color"&&(s=this.form.querySelector(`#${t.id.replace(/-color/,"")}`));let i=this.form.querySelector(`#${s.id}-color-label`);if((t.dataset.type||"")==="color"){let a=this.form.querySelector(`input#${t.id}-color`);if(!a||!L(t.value))return;a.value=z(t.value)}t.type==="color"&&(s.value=t.value),i&&(i.style.backgroundColor=t.value),clearTimeout(this.dispatchTimeout),this.dispatchTimeout=window.setTimeout(()=>{s.dispatchEvent(new Event("change",{bubbles:!0}))},200)}inputKeydownHandler(e){e.target instanceof HTMLInputElement&&c(e.target,"integer")&&(e.key==="ArrowUp"?(e.preventDefault(),e.target.value===""&&(e.target.value="0"),e.target.value=(parseInt(e.target.value)+1).toString()):e.key==="ArrowDown"&&(parseInt(e.target.value)>0?e.target.value=(parseInt(e.target.value)-1).toString():e.target.value="0"))}destroy(){this.removeEventListeners(),this.originalNoValidate||this.form.removeAttribute("novalidate")}}return K});
|
|
1
|
+
(function(m,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(m=typeof globalThis<"u"?globalThis:m||self,c(m.Validator={}))})(this,function(m){"use strict";var Q=Object.defineProperty;var X=(m,c,f)=>c in m?Q(m,c,{enumerable:!0,configurable:!0,writable:!0,value:f}):m[c]=f;var d=(m,c,f)=>(X(m,typeof c!="symbol"?c+"":c,f),f);function c(r){return r instanceof HTMLInputElement||r instanceof HTMLSelectElement||r instanceof HTMLTextAreaElement}function f(r,e){typeof e=="string"&&(e=[e]);const t=r.dataset.type||"",s=r.type||"";return!!(e.includes(t)||e.includes(s))}function Z(r){return r.replace(/YYYY/g,"Y").replace(/YY/g,"y").replace(/MMMM/g,"F").replace(/MMM/g,"{3}").replace(/MM/g,"{2}").replace(/M/g,"n").replace(/DD/g,"{5}").replace(/D/g,"j").replace(/dddd/g,"l").replace(/ddd/g,"D").replace(/dd/g,"D").replace(/d/g,"w").replace(/HH/g,"{6}").replace(/H/g,"G").replace(/hh/g,"h").replace(/mm/g,"i").replace(/m/g,"i").replace(/ss/g,"S").replace(/s/g,"s").replace(/A/gi,"K").replace(/\{3\}/g,"M").replace(/\{2\}/g,"m").replace(/\{5\}/g,"d").replace(/\{6\}/g,"H")}function L(r){const e=parseInt(r);if(typeof r=="number"||!isNaN(e))return e-1;const t=new Date(`1 ${r} 2000`).getMonth();if(!isNaN(t))return t;const s={ja:0,en:0,fe:1,fé:1,ap:3,ab:3,av:3,mai:4,juin:5,juil:6,au:7,ag:7,ao:7,se:8,o:9,n:10,d:11};for(const i in s)if(r.toLowerCase().startsWith(i))return s[i];throw new Error("Invalid month name: "+r)}function b(r){return typeof r=="string"&&(r=parseInt(r.replace(/\D/g,""))),r>99?r:r<(new Date().getFullYear()+20)%100?r+2e3:r+1900}function p(r){if(r instanceof Date)return r;r=r.trim().toLowerCase();let e=0,t=0,s=0,i=0,a=0,n=0;const l=new RegExp(/\d{1,2}\:\d\d(?:\:\d\ds?)?\s?(?:[a|p]m?)?/gi);if(l.test(r)){const h=r.match(l)[0];r=r.replace(h,"").trim();const g=E(h);if(g!==null&&({hour:i,minute:a,second:n}=g),r.length<=2){const M=new Date;return new Date(M.getFullYear(),M.getMonth(),M.getDate(),i,a,n)}}const u=/(^|\b)(mo|tu|we|th|fr|sa|su|lu|mard|mer|jeu|ve|dom)[\w]*\.?/gi;r=r.replace(u,"").trim();const o=new Date(new Date().setHours(0,0,0,0));if(/(now|today)/.test(r))return o;if(r.includes("tomorrow"))return new Date(o.setDate(o.getDate()+1));r.length===8&&(r=r.replace(/(\d\d\d\d)(\d\d)(\d\d)/,"$1-$2-$3")),r.length===6&&(r=r.replace(/(\d\d)(\d\d)(\d\d)/,b(r.slice(0,2))+"-$2-$3"));try{({year:e,month:t,day:s}=j(r))}catch{return new Date("")}return new Date(e,t-1,s,i,a,n)}function j(r){function e(n,l=[null,null,null]){const u=o=>o.filter(h=>!l.includes(h));return n===0||n>31?u(["y"]):n>12?u(["d","y"]):n>=1||n<=12?u(["m","d","y"]):[]}const t=r.split(/[\s-/:.,]+/).filter(n=>n!=="");if(t.length<3){if(r.match(/\d{4}/)!==null)throw new Error("Invalid Date");t.unshift(String(new Date().getFullYear()))}const s={year:0,month:0,day:0};function i(n,l){n==="year"?s.year=b(l):s[n]=l}let a=0;for(;!(s.year&&s.month&&s.day);){e:for(const n of t){if(a++,/^[a-zA-Zé]+$/.test(n)){s.month||i("month",L(n)+1);continue}if(/^'\d\d$/.test(n)||/^\d{3,5}$/.test(n)){s.year||i("year",parseInt(n.replace(/'/,"")));continue}const l=parseInt(n);if(isNaN(l))throw console.error(`not date because ${n} isNaN`),new Error("Invalid Date");const u=e(l,[s.year?"y":null,s.month?"m":null,s.day?"d":null]);if(u.length==1)for(let o=0;o<u.length;o++){if(u[o]==="m"&&!s.month){i("month",l);continue e}if(u[o]==="d"&&!s.day){i("day",l);continue e}if(u[o]==="y"&&!s.year){i("year",l);continue e}}a>3&&(!s.month&&u.includes("m")?i("month",l):!s.day&&u.includes("d")?i("day",l):!s.year&&u.includes("y")&&i("year",l))}if(a>6)throw new Error("Invalid Date")}if(s.year&&s.month&&s.day)return s;throw new Error("Invalid Date")}function E(r){if(r=r.trim().toLowerCase(),r==="now"){const o=new Date;return{hour:o.getHours(),minute:o.getMinutes(),second:o.getSeconds()}}const e=r.match(/(\d{3,4})/);if(e){const o=e[1].length,h=e[1].slice(0,o==3?1:2),g=e[1].slice(-2);r=r.replace(e[1],h+":"+g)}const t=new RegExp(/^(\d{1,2})(?::(\d{1,2}))?\s*(?:(a|p)m?)?$/i);if(t.test(r)){const o=r.match(t);if(o===null)return null;r=o[1]+":"+(o[2]||"00")+(o[3]||"")}const s=new RegExp(/^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?\s*(?:(a|p)m?)?$/i);if(!s.test(r))return null;const i=r.match(s);if(i===null)return null;const a=parseInt(i[1]),n=parseInt(i[2]),l=i[3]?parseInt(i[3]):0,u=i[4];return isNaN(a)||isNaN(n)||isNaN(l)?null:u==="p"&&a<12?{hour:a+12,minute:n,second:l}:u==="a"&&a===12?{hour:0,minute:n,second:l}:a<0||a>23||n<0||n>59||l<0||l>59?null:{hour:a,minute:n,second:l}}function S(r,e="h:mm A"){const t=E(r);if(t){const s=new Date;return s.setHours(t.hour),s.setMinutes(t.minute),s.setSeconds(t.second),s.setMilliseconds(0),y(s,e)}return""}function y(r,e="YYYY-MM-DD"){if(r=p(r),isNaN(r.getTime()))return"";const t={y:r.getFullYear(),M:r.getMonth(),D:r.getDate(),W:r.getDay(),H:r.getHours(),m:r.getMinutes(),s:r.getSeconds(),ms:r.getMilliseconds()},s=(o,h=2)=>(o+"").padStart(h,"0"),i=()=>t.H%12||12,a=o=>o<12?"AM":"PM",n=o=>"January|February|March|April|May|June|July|August|September|October|November|December".split("|")[o];function l(o,h=0){const g="Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday".split("|");return h?g[o].slice(0,h):g[o]}const u={YY:String(t.y).slice(-2),YYYY:t.y,M:t.M+1,MM:s(t.M+1),MMMM:n(t.M),MMM:n(t.M).slice(0,3),D:String(t.D),DD:s(t.D),d:String(t.W),dd:l(t.W,2),ddd:l(t.W,3),dddd:l(t.W),H:String(t.H),HH:s(t.H),h:i(),hh:s(i()),A:a(t.H),a:a(t.H).toLowerCase(),m:String(t.m),mm:s(t.m),s:String(t.s),ss:s(t.s),SSS:s(t.ms,3)};return e.replace(/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,(o,h)=>h||u[o])}function D(r,e){const t=p(r);return isNaN(t.getTime())?"":((!e||e.length===0)&&(e="YYYY-MMM-DD"),y(t,e))}function C(r){let e=p(r);return e==null?!1:!isNaN(e.getTime())}function x(r,e){return!(e==="past"&&r>new Date||e==="future"&&r.getTime()<new Date().setHours(0,0,0,0))}function H(r){let e=E(r);return e===null?!1:!isNaN(e.hour)&&!isNaN(e.minute)&&!isNaN(e.second)}function A(r){if(r.length>255||!new RegExp(/^.+@.+\.[a-zA-Z0-9]{2,}$/).test(r))return!1;let t="";return t+="^([a-zA-Z0-9!#$%'*+/=?^_`{|}~-]+",t+="(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*",t+="|",t+='"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*"',t+=")@(",t+="(",t+="(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+",t+="[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?",t+=")",t+=")$",new RegExp(t).test(r)}function N(r){return r=r.replace(/^[^2-90]+/g,""),r=r.replace(/(\d\d\d).*?(\d\d\d).*?(\d\d\d\d)(.*)/,"$1-$2-$3$4"),r}function O(r){return/^\d\d\d-\d\d\d-\d\d\d\d$/.test(r)}function v(r){return r.replace(/[^0-9]/g,"")}function _(r){return/^\-?\d*\.?\d*$/.test(r)}function w(r){return r.replace(/[^\-0-9.]/g,"").replace(/(^-)|(-)/g,(e,t)=>t?"-":"").replace(/(\..*)\./g,"$1")}function k(r){return/^\-?\d*$/.test(r)}function $(r){return r=r.trim(),new RegExp("^(?:[a-z+]+:)?//","i").test(r)?r:"https://"+r}function P(r){return new RegExp("^(?:[-a-z+]+:)?//","i").test(r)}function Y(r){return r=r.replace(/[^0-9]/g,"").replace(/(.{5})(.*)/,"$1-$2").trim(),r.length===6&&(r=r.replace(/-/,"")),r}function V(r){return new RegExp(/^\d{5}(-\d{4})?$/).test(r)}function F(r){return r=r.toUpperCase().replace(/[^A-Z0-9]/g,"").replace(/(.{3})\s*(.*)/,"$1 $2").trim(),r}function U(r){return new RegExp(/^[ABCEGHJKLMNPRSTVXY][0-9][ABCEGHJKLMNPRSTVWXYZ] ?[0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]$/).test(r)}function T(r){return["transparent","currentColor"].includes(r)?!0:typeof r!="string"||!r.trim()?!1:typeof CSS=="object"&&typeof CSS.supports=="function"?CSS.supports("color",r):G(r)}function G(r){const e=new RegExp(/^rgba?\(\s*(\d{1,3}%?,\s*){2}\d{1,3}%?\s*(?:,\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),t=new RegExp(/^hsla?\(\s*\d+(deg|grad|rad|turn)?,\s*\d{1,3}%,\s*\s*\d{1,3}%(?:,\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),s=new RegExp(/^rgba?\(\s*(\d{1,3}%?\s+){2}\d{1,3}%?\s*(?:\s*\/\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),i=new RegExp(/^hsla?\(\s*\d+(deg|grad|rad|turn)?\s+\d{1,3}%\s+\s*\d{1,3}%(?:\s*\/\s*(\.\d+|0+(\.\d+)?|1(\.0+)?|0|1\.0|\d{1,2}(\.\d*)?%|100%))?\s*\)$/),a=new RegExp(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);let n="aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen";const l=new RegExp(`^(${n})$`,"i");return e.test(r)||t.test(r)||s.test(r)||i.test(r)||a.test(r)||l.test(r)}let R=null;const I=new Map;function q(r){if(r=r.trim().toLowerCase(),r==="transparent")return"transparent";if(I.has(r))return I.get(r);R===null&&(R=document.createElement("canvas"),R.willReadFrequently=!0);let e=R.getContext("2d");if(!e)throw new Error("Can't get context from colorCanvas");e.fillStyle=r,e.fillRect(0,0,1,1);let t=e.getImageData(0,0,1,1).data,s="#"+("000000"+(t[0]<<16|t[1]<<8|t[2]).toString(16)).slice(-6);return I.set(r,s),s}function z(r){let e={valid:!1,error:!1,messages:[]};return typeof r=="boolean"?{valid:r,error:!1,messages:[]}:typeof r=="string"?{valid:!1,error:!1,messages:[r]}:(typeof r.valid=="boolean"&&(e.valid=r.valid),typeof r.message=="string"&&(e.messages=[r.message]),typeof r.messages=="string"&&(e.messages=[r.messages]),Array.isArray(r.messages)&&(e.messages=r.messages),r.error===!0&&(e.error=!0),e)}const K=Object.freeze(Object.defineProperty({__proto__:null,formatDateTime:y,isColor:T,isDate:C,isDateInRange:x,isEmail:A,isFormControl:c,isInteger:k,isNANPTel:O,isNumber:_,isPostalCA:U,isTime:H,isType:f,isUrl:P,isZip:V,momentToFPFormat:Z,monthToNumber:L,normalizeValidationResult:z,parseColor:q,parseDate:p,parseDateToString:D,parseInteger:v,parseNANPTel:N,parseNumber:w,parsePostalCA:F,parseTime:E,parseTimeToString:S,parseUrl:$,parseZip:Y,yearToFull:b},Symbol.toStringTag,{value:"Module"}));class W extends Event{constructor(t){super("validationSuccess",{cancelable:!0});d(this,"submitEvent");this.submitEvent=t}}class B extends Event{constructor(t){super("validationError",{cancelable:!0});d(this,"submitEvent");this.submitEvent=t}}class J{constructor(e,t={}){d(this,"form");d(this,"inputs",[]);d(this,"inputErrors",{});d(this,"messages",{ERROR_MAIN:"There is a problem with your submission.",ERROR_GENERIC:"Enter a valid value.",ERROR_REQUIRED:"This field is required.",OPTION_REQUIRED:"An option must be selected.",CHECKED_REQUIRED:"This must be checked.",ERROR_MAXLENGTH:"This must be ${val} characters or fewer.",ERROR_MINLENGTH:"This must be at least ${val} characters.",ERROR_NUMBER:"This must be a number.",ERROR_INTEGER:"This must be a whole number.",ERROR_TEL:"This is not a valid telephone number.",ERROR_EMAIL:"This is not a valid email address.",ERROR_ZIP:"This is not a valid zip code.",ERROR_POSTAL:"This is not a valid postal code.",ERROR_DATE:"This is not a valid date.",ERROR_DATE_PAST:"The date must be in the past.",ERROR_DATE_FUTURE:"The date must be in the future.",ERROR_DATE_RANGE:"The date is outside the allowed range.",ERROR_TIME:"This is not a valid time.",ERROR_TIME_RANGE:"The time is outside the allowed range.",ERROR_URL:"This is not a valid URL.",ERROR_COLOR:"This is not a valid CSS colour.",ERROR_CUSTOM_VALIDATION:"There was a problem validating this field."});d(this,"debug");d(this,"autoInit");d(this,"preventSubmit",!1);d(this,"hiddenClasses");d(this,"errorMainClasses");d(this,"errorInputClasses");d(this,"dispatchTimeout",0);d(this,"originalNoValidate",!1);d(this,"validationSuccessCallback");d(this,"validationErrorCallback");d(this,"submitHandlerRef",this.submitHandler.bind(this));d(this,"inputInputHandlerRef",this.inputInputHandler.bind(this));d(this,"inputChangeHandlerRef",this.inputChangeHandler.bind(this));d(this,"inputKeydownHandlerRef",this.inputKeydownHandler.bind(this));d(this,"inputHandlers",{number:{parse:w,isValid:_,error:this.messages.ERROR_NUMBER},integer:{parse:v,isValid:k,error:this.messages.ERROR_INTEGER},tel:{parse:N,isValid:O,error:this.messages.ERROR_TEL},email:{parse:e=>e.trim(),isValid:A,error:this.messages.ERROR_EMAIL},zip:{parse:Y,isValid:V,error:this.messages.ERROR_ZIP},postal:{parse:F,isValid:U,error:this.messages.ERROR_POSTAL},url:{parse:$,isValid:P,error:this.messages.ERROR_URL},date:{parse:D,isValid:C,error:this.messages.ERROR_DATE},time:{parse:S,isValid:H,error:this.messages.ERROR_TIME},color:{parse:e=>e.trim().toLowerCase(),isValid:T,error:this.messages.ERROR_COLOR}});d(this,"isSubmitting",!1);if(!e)throw new Error("Validator requires a form to be passed as the first argument.");if(!(e instanceof HTMLFormElement))throw new Error("form argument must be an instance of HTMLFormElement");this.form=e,(e.dataset.preventSubmit===""||e.dataset.preventSubmit)&&(this.preventSubmit=!0),Object.assign(this.messages,t.messages||{}),this.debug=t.debug||!1,this.autoInit=t.autoInit!==!1,this.preventSubmit=t.preventSubmit===!1?!1:this.preventSubmit,this.hiddenClasses=t.hiddenClasses||"hidden opacity-0",this.errorMainClasses=t.errorMainClasses||"m-2 border border-red-500 bg-red-100 p-3 dark:bg-red-900/80 text-center",this.errorInputClasses=t.errorInputClasses||"border-red-600 dark:border-red-500",this.validationSuccessCallback=t.validationSuccessCallback||(()=>{}),this.validationErrorCallback=t.validationErrorCallback||(()=>{}),this.autoInit&&this.init(),new MutationObserver(()=>this.autoInit&&this.init()).observe(e,{childList:!0})}addEventListeners(){this.form.addEventListener("submit",this.submitHandlerRef),this.form.addEventListener("input",this.inputInputHandlerRef),this.form.addEventListener("change",this.inputChangeHandlerRef),this.form.addEventListener("keydown",this.inputKeydownHandlerRef),this.form.addEventListener("remove",this.destroy,{once:!0})}removeEventListeners(){this.form.removeEventListener("submit",this.submitHandlerRef),this.form.removeEventListener("input",this.inputInputHandlerRef),this.form.removeEventListener("change",this.inputChangeHandlerRef),this.form.removeEventListener("keydown",this.inputKeydownHandlerRef),this.form.removeEventListener("remove",this.destroy)}init(){this.inputs=Array.from(this.form.elements),this.inputs.forEach(e=>{!e.name&&!e.id&&(e.id=`vl-input-${Math.random().toString(36).slice(2)}`),this.inputErrors[e.name||e.id]=[]}),this.originalNoValidate=this.form.hasAttribute("novalidate"),this.form.setAttribute("novalidate","novalidate"),this.removeEventListeners(),this.addEventListeners()}getErrorEl(e){const t=document.getElementById(e.name+"-error");return t||document.getElementById(e.id+"-error")||null}addErrorMain(e){const t=document.createElement("div");t.id="form-error-main",this.errorMainClasses.split(" ").forEach(s=>{t.classList.add(s)}),e?t.innerHTML=e:t.innerHTML=this.messages.ERROR_MAIN,this.form.appendChild(t)}addInputError(e,t=e.dataset.errorDefault||this.messages.ERROR_GENERIC){const s=e.name||e.id;this.debug&&console.log("Invalid value for "+s+": "+t),s in this.inputErrors||(this.inputErrors[s]=[]),this.inputErrors[s].includes(t)||this.inputErrors[s].push(t)}showInputErrors(e){if(!e||!e.name&&!e.id)return;const t=e.name||e.id,s=t in this.inputErrors?this.inputErrors[t]:[];if(!s.length)return;e.setAttribute("aria-invalid","true"),this.errorInputClasses.split(" ").forEach(a=>{e.classList.add(a)});let i=this.getErrorEl(e);i&&(i.innerHTML=s.join("<br>"),this.hiddenClasses.split(" ").forEach(a=>{i&&i.classList.remove(a)}))}showFormErrors(){if(this.inputs.forEach(e=>this.showInputErrors(e)),Object.values(this.inputErrors).some(e=>Array.isArray(e)&&e.length)){const e=this.form.querySelectorAll("#form-error-main");e.length?e.forEach(t=>{t.innerHTML||(t.innerHTML=this.messages.ERROR_MAIN),this.hiddenClasses.split(" ").forEach(s=>{t.classList.remove(s)})}):this.addErrorMain()}}clearInputErrors(e){this.inputErrors[e.name||e.id]=[],e.removeAttribute("aria-invalid");let t=this.getErrorEl(e);t&&(this.errorInputClasses.split(" ").forEach(s=>{e.classList.remove(s)}),this.hiddenClasses.split(" ").forEach(s=>{t&&t.classList.add(s)}),t.textContent="")}clearFormErrors(){this.form.querySelectorAll("#form-error-main").forEach(e=>{this.hiddenClasses.split(" ").forEach(t=>{e.classList.add(t)})}),this.inputs.forEach(e=>this.clearInputErrors(e))}validateRequired(e){let t=!0;if(e.required&&(e.value===""||e instanceof HTMLInputElement&&["checkbox","radio"].includes(e.type)&&!e.checked))if(e instanceof HTMLInputElement&&["checkbox","radio"].includes(e.type)){let s=!1,i=e.name;const a=this.form.querySelectorAll(`input[name="${i}"]`);if(a.forEach(n=>{if(n instanceof HTMLInputElement&&n.checked===!0){s=!0;return}}),s===!1){t=!1;let n=a.length>1?this.messages.OPTION_REQUIRED:this.messages.CHECKED_REQUIRED;e.dataset.errorDefault&&(n=e.dataset.errorDefault),this.addInputError(e,n)}}else c(e)&&(t=!1,this.addInputError(e,e.dataset.errorDefault||this.messages.ERROR_REQUIRED));return t}validateLength(e){let t=!0;if((e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&e.value.length){let s=e.minLength>0?e.minLength:e.dataset.minLength?parseInt(e.dataset.minLength):0,i=e.maxLength>0&&e.maxLength<5e5?e.maxLength:e.dataset.maxLength?parseInt(e.dataset.maxLength):1/0;s>0&&e.value.length<s&&(t=!1,this.addInputError(e,this.messages.ERROR_MINLENGTH.replace("${val}",s.toString()))),e.value.length>i&&(t=!1,this.addInputError(e,this.messages.ERROR_MAXLENGTH.replace("${val}",i.toString())))}return t}validateInputType(e){const t=e.dataset.type||e.type,s=this.inputHandlers[e.type]||this.inputHandlers[t];if(s){const i=e.dataset.dateFormat||e.dataset.timeFormat,a=s.parse(e.value,i),n=["date","time","datetime-local","month","week"];if(a.length&&!n.includes(e.type)&&(e.value=a),!s.isValid(e.value))return this.addInputError(e,s.error),!1}return!0}validateDateRange(e){if(e.dataset.dateRange){const t=e.dataset.dateRange,s=p(e.value);if(!isNaN(s.getTime())&&!x(s,t)){let i=e.dataset.errorDefault||this.messages.ERROR_DATE_RANGE;return t==="past"?i=this.messages.ERROR_DATE_PAST:t==="future"&&(i=this.messages.ERROR_DATE_FUTURE),this.addInputError(e,i),!1}}return!0}validatePattern(e){const t=e.dataset.pattern||e instanceof HTMLInputElement&&e.pattern||null;return t&&!new RegExp(t).test(e.value)?(this.addInputError(e),!1):!0}async validateCustom(e){const t=e.dataset.validation;if(!t||typeof t!="string")return!0;const s=window[t];if(!s||typeof s!="function")return!0;let i;try{i=await Promise.resolve(s(e.value)),i=z(i)}catch{return this.addInputError(e,this.messages.ERROR_CUSTOM_VALIDATION),!1}const a=i.messages.join("<br>")||this.messages.ERROR_CUSTOM_VALIDATION;return i.valid||this.addInputError(e,a),i.valid}async validateInput(e){if(!(e instanceof HTMLInputElement)||!e.value.length)return!0;let t=!0;return t=this.validateInputType(e)&&t,t=this.validateDateRange(e)&&t,t=this.validatePattern(e)&&t,t=await this.validateCustom(e)&&t,t}async validate(e){let t=!0;for(const s of this.inputs)t=this.validateRequired(s)&&t,t=this.validateLength(s)&&t,t=await this.validateInput(s)&&t;return t}async submitHandler(e){if(this.isSubmitting)return;e.preventDefault(),this.clearFormErrors();let t=await this.validate(e);this.showFormErrors();const s=new W(e),i=new B(e);t?(this.form.dispatchEvent(s),this.validationSuccessCallback&&this.validationSuccessCallback(e)):(this.form.dispatchEvent(i),this.validationErrorCallback&&this.validationErrorCallback(e)),t&&!this.preventSubmit&&(this.isSubmitting=!0,s.defaultPrevented||this.form.submit(),this.isSubmitting=!1)}async inputChangeHandler(e){e.target instanceof HTMLInputElement&&(this.clearInputErrors(e.target),await this.validateInput(e.target),this.showInputErrors(e.target))}inputInputHandler(e){const t=e.target;f(t,"integer")&&(t.value=v(t.value)),t.type!=="number"&&f(t,["number","float","decimal"])&&(t.value=w(t.value)),f(t,"color")&&this.syncColorInput(e)}syncColorInput(e){let t=e.target,s=t;t.type==="color"&&(s=this.form.querySelector(`#${t.id.replace(/-color/,"")}`));let i=this.form.querySelector(`#${s.id}-color-label`);if((t.dataset.type||"")==="color"){let a=this.form.querySelector(`input#${t.id}-color`);if(!a||!T(t.value))return;a.value=q(t.value)}t.type==="color"&&(s.value=t.value),i&&(i.style.backgroundColor=t.value),clearTimeout(this.dispatchTimeout),this.dispatchTimeout=window.setTimeout(()=>{s.dispatchEvent(new Event("change",{bubbles:!0}))},200)}inputKeydownHandler(e){e.target instanceof HTMLInputElement&&f(e.target,"integer")&&(e.key==="ArrowUp"?(e.preventDefault(),e.target.value===""&&(e.target.value="0"),e.target.value=(parseInt(e.target.value)+1).toString()):e.key==="ArrowDown"&&(parseInt(e.target.value)>0?e.target.value=(parseInt(e.target.value)-1).toString():e.target.value="0"))}destroy(){this.removeEventListeners(),this.originalNoValidate||this.form.removeAttribute("novalidate")}}m.default=J,m.utils=K,Object.defineProperties(m,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|