@jdlien/validator 1.0.6 → 1.1.3

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
@@ -299,4 +299,4 @@ Module did not self-register: '...\node_modules\canvas\build\Release\canvas.node
299
299
  ```
300
300
 
301
301
  If that happens, you
302
- need to install the canvas module manually: `bash npm rebuild canvas --update-binary `
302
+ need to install the canvas module manually: `bash npm rebuild canvas --update-binary`
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Form Validator used by EPL apps and www2.
3
+ * © 2023 JD Lien
4
+ *
5
+ * @format
6
+ */
7
+ export type FormControl = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
8
+ export interface ValidatorOptions {
9
+ messages?: object;
10
+ debug?: boolean;
11
+ autoInit?: boolean;
12
+ preventSubmit?: boolean;
13
+ hiddenClasses?: string;
14
+ errorMainClasses?: string;
15
+ errorInputClasses?: string;
16
+ validationSuccessCallback?: (event: Event) => void;
17
+ validationErrorCallback?: (event: Event) => void;
18
+ }
19
+ export interface InputHandlers {
20
+ [key: string]: {
21
+ parse: (value: string, dateFormat?: string) => string;
22
+ isValid: (value: string) => boolean;
23
+ error: string;
24
+ };
25
+ }
26
+ export declare class ValidationSuccessEvent extends Event {
27
+ submitEvent: Event;
28
+ constructor(submitEvent: Event);
29
+ }
30
+ export declare class ValidationErrorEvent extends Event {
31
+ submitEvent: Event;
32
+ constructor(submitEvent: Event);
33
+ }
34
+ export default class Validator {
35
+ form: HTMLFormElement;
36
+ inputs: FormControl[];
37
+ inputErrors: {
38
+ [key: string]: string[];
39
+ };
40
+ messages: {
41
+ ERROR_MAIN: string;
42
+ ERROR_GENERIC: string;
43
+ ERROR_REQUIRED: string;
44
+ OPTION_REQUIRED: string;
45
+ CHECKED_REQUIRED: string;
46
+ ERROR_MAXLENGTH: string;
47
+ ERROR_MINLENGTH: string;
48
+ ERROR_NUMBER: string;
49
+ ERROR_INTEGER: string;
50
+ ERROR_TEL: string;
51
+ ERROR_EMAIL: string;
52
+ ERROR_ZIP: string;
53
+ ERROR_POSTAL: string;
54
+ ERROR_DATE: string;
55
+ ERROR_DATE_PAST: string;
56
+ ERROR_DATE_FUTURE: string;
57
+ ERROR_DATE_RANGE: string;
58
+ ERROR_TIME: string;
59
+ ERROR_TIME_RANGE: string;
60
+ ERROR_URL: string;
61
+ ERROR_COLOR: string;
62
+ ERROR_CUSTOM_VALIDATION: string;
63
+ };
64
+ debug: boolean;
65
+ autoInit: boolean;
66
+ preventSubmit: boolean;
67
+ hiddenClasses: string;
68
+ errorMainClasses: string;
69
+ errorInputClasses: string;
70
+ private dispatchTimeout;
71
+ private originalNoValidate;
72
+ private validationSuccessCallback;
73
+ private validationErrorCallback;
74
+ constructor(form: HTMLFormElement, options?: ValidatorOptions);
75
+ private submitHandlerRef;
76
+ private inputInputHandlerRef;
77
+ private inputChangeHandlerRef;
78
+ private inputKeydownHandlerRef;
79
+ addEventListeners(): void;
80
+ removeEventListeners(): void;
81
+ init(): void;
82
+ private getErrorEl;
83
+ private addErrorMain;
84
+ private addInputError;
85
+ private showInputErrors;
86
+ private showFormErrors;
87
+ private clearInputErrors;
88
+ private clearFormErrors;
89
+ private validateRequired;
90
+ private validateLength;
91
+ private inputHandlers;
92
+ private validateInputType;
93
+ private validateDateRange;
94
+ private validatePattern;
95
+ /**
96
+ * Specify a custom function in data-validation and it gets called to validate the input
97
+ * The custom function can return
98
+ * - a boolean
99
+ * - a Promise that resolves to a boolean
100
+ * - an object with a valid property that is a boolean
101
+ * - a Promise that resolves to an object with a valid property that is a boolean
102
+ * - and optionally a messages property that is a string or array of strings
103
+ * - OR optionally, a message property that is a string
104
+ * - optionaly, a boolean error property that is true if something went wrong
105
+ */
106
+ private validateCustom;
107
+ private validateInput;
108
+ validate(_e?: Event): Promise<boolean>;
109
+ private isSubmitting;
110
+ private submitHandler;
111
+ private inputChangeHandler;
112
+ private inputInputHandler;
113
+ private syncColorInput;
114
+ private inputKeydownHandler;
115
+ destroy(): void;
116
+ }
@@ -0,0 +1 @@
1
+ (function(f,i){typeof exports=="object"&&typeof module<"u"?module.exports=i():typeof define=="function"&&define.amd?define(i):(f=typeof globalThis<"u"?globalThis:f||self,f.Validator=i())})(this,function(){"use strict";var ae=Object.defineProperty;var se=(f,i,v)=>i in f?ae(f,i,{enumerable:!0,configurable:!0,writable:!0,value:v}):f[i]=v;var m=(f,i,v)=>(se(f,typeof i!="symbol"?i+"":i,v),v);var f=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},i={},v={get exports(){return i},set exports(w){i=w}};(function(w,t){(function(r,n){n(t)})(f,function(r){function n(e){return e instanceof HTMLInputElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement}function l(e,s){typeof s=="string"&&(s=[s]);const a=e.dataset.type||"",o=e.type;return!!(s.includes(a)||s.includes(o))}function g(e){return e.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 E(e){const s=parseInt(e);if(typeof e=="number"||!isNaN(s))return s-1;const a=new Date(`1 ${e} 2000`).getMonth();if(!isNaN(a))return a;const o={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 h in o)if(e.toLowerCase().startsWith(h))return o[h];throw new Error("Invalid month name: "+e)}function L(e){return typeof e=="string"&&(e=parseInt(e.replace(/\D/g,""))),e>99?e:e<(new Date().getFullYear()+20)%100?e+2e3:e+1900}function T(e){if(e instanceof Date)return e;e=e.trim().toLowerCase();let s=0,a=0,o=0,h=0,d=0,u=0;const p=new RegExp(/\d{1,2}\:\d\d(?:\:\d\ds?)?\s?(?:[a|p]m?)?/gi);if(p.test(e)){const R=e.match(p)[0];e=e.replace(R,"").trim();const y=I(R);if(y!==null&&({hour:h,minute:d,second:u}=y),e.length<=2){const S=new Date;return new Date(S.getFullYear(),S.getMonth(),S.getDate(),h,d,u)}}const b=/(^|\b)(mo|tu|we|th|fr|sa|su|lu|mard|mer|jeu|ve|dom)[\w]*\.?/gi;e=e.replace(b,"").trim();const c=new Date(new Date().setHours(0,0,0,0));if(/(now|today)/.test(e))return c;if(e.includes("tomorrow"))return new Date(c.setDate(c.getDate()+1));e.length===8&&(e=e.replace(/(\d\d\d\d)(\d\d)(\d\d)/,"$1-$2-$3")),e.length===6&&(e=e.replace(/(\d\d)(\d\d)(\d\d)/,L(e.slice(0,2))+"-$2-$3"));try{({year:s,month:a,day:o}=k(e))}catch{return new Date("")}return new Date(s,a-1,o,h,d,u)}function O(e,s=[null,null,null]){const a=o=>o.filter(h=>!s.includes(h));return e===0||e>31?a(["y"]):e>12?a(["d","y"]):e>=1&&e<=12?a(["m","d","y"]):[]}function k(e){const s=e.split(/[\s-/:.,]+/).filter(d=>d!=="");if(s.length<3){if(e.match(/\d{4}/)!==null)throw new Error("Invalid Date");s.unshift(String(new Date().getFullYear()))}const a={year:0,month:0,day:0};function o(d,u){d==="year"?a.year=L(u):a[d]=u}let h=0;for(;!(a.year&&a.month&&a.day);){e:for(const d of s){if(h++,/^[a-zA-Zé]+$/.test(d)){a.month||o("month",E(d)+1);continue}if(/^'\d\d$/.test(d)||/^\d{3,5}$/.test(d)){a.year||o("year",parseInt(d.replace(/'/,"")));continue}const u=parseInt(d);if(isNaN(u))throw console.error(`not date because ${d} isNaN`),new Error("Invalid Date");const p=O(u,[a.year?"y":null,a.month?"m":null,a.day?"d":null]);if(p.length==1){if(p[0]==="m"&&!a.month){o("month",u);continue e}if(p[0]==="d"&&!a.day){o("day",u);continue e}if(p[0]==="y"&&!a.year){o("year",u);continue e}}h>3&&(!a.month&&p.includes("m")?o("month",u):!a.day&&p.includes("d")&&o("day",u))}if(h>6)throw new Error("Invalid Date")}if(a.year&&a.month&&a.day)return a;throw new Error("Invalid Date")}function I(e){if(e=e.trim().toLowerCase(),e==="now"){const c=new Date;return{hour:c.getHours(),minute:c.getMinutes(),second:c.getSeconds()}}const s=e.match(/(\d{3,4})/);if(s){const c=s[1].length,R=s[1].slice(0,c==3?1:2),y=s[1].slice(-2);e=e.replace(s[1],R+":"+y)}const a=new RegExp(/^(\d{1,2})(?::(\d{1,2}))?\s*(?:(a|p)m?)?$/i);if(a.test(e)){const c=e.match(a);if(c===null)return null;e=c[1]+":"+(c[2]||"00")+(c[3]||"")}const o=new RegExp(/^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?\s*(?:(a|p)m?)?$/i);if(!o.test(e))return null;const h=e.match(o);if(h===null)return null;const d=parseInt(h[1]),u=parseInt(h[2]),p=h[3]?parseInt(h[3]):0,b=h[4];return isNaN(d)||isNaN(u)||isNaN(p)?null:b==="p"&&d<12?{hour:d+12,minute:u,second:p}:b==="a"&&d===12?{hour:0,minute:u,second:p}:d<0||d>23||u<0||u>59||p<0||p>59?null:{hour:d,minute:u,second:p}}function _(e,s="h:mm A"){const a=I(e);if(a){const o=new Date;return o.setHours(a.hour),o.setMinutes(a.minute),o.setSeconds(a.second),o.setMilliseconds(0),D(o,s)}return""}function D(e,s="YYYY-MM-DD"){if(e=T(e),isNaN(e.getTime()))return"";const a={y:e.getFullYear(),M:e.getMonth(),D:e.getDate(),W:e.getDay(),H:e.getHours(),m:e.getMinutes(),s:e.getSeconds(),ms:e.getMilliseconds()},o=(c,R=2)=>(c+"").padStart(R,"0"),h=()=>a.H%12||12,d=c=>c<12?"AM":"PM",u=c=>"January|February|March|April|May|June|July|August|September|October|November|December".split("|")[c];function p(c,R=0){const y="Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday".split("|");return R?y[c].slice(0,R):y[c]}const b={YY:String(a.y).slice(-2),YYYY:a.y,M:a.M+1,MM:o(a.M+1),MMMM:u(a.M),MMM:u(a.M).slice(0,3),D:String(a.D),DD:o(a.D),d:String(a.W),dd:p(a.W,2),ddd:p(a.W,3),dddd:p(a.W),H:String(a.H),HH:o(a.H),h:h(),hh:o(h()),A:d(a.H),a:d(a.H).toLowerCase(),m:String(a.m),mm:o(a.m),s:String(a.s),ss:o(a.s),SSS:o(a.ms,3)};return s.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,(c,R)=>R||b[c])}function x(e,s){const a=T(e);return isNaN(a.getTime())?"":((!s||s.length===0)&&(s="YYYY-MMM-DD"),D(a,s))}function $(e){if(typeof e!="string"&&!(e instanceof Date))return!1;let s=T(e);return s==null?!1:!isNaN(s.getTime())}function Y(e,s){return!(s==="past"&&e>new Date||s==="future"&&e.getTime()<new Date().setHours(0,0,0,0))}function V(e){let s=I(e);return s===null?!1:!isNaN(s.hour)&&!isNaN(s.minute)&&!isNaN(s.second)}function P(e){if(e.length>255||!new RegExp(/^.+@.+\.[a-zA-Z0-9]{2,}$/).test(e))return!1;let s="";return s+="^([a-zA-Z0-9!#$%'*+/=?^_`{|}~-]+",s+="(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*",s+="|",s+='"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*"',s+=")@(",s+="(",s+="(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+",s+="[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?",s+=")",s+=")$",new RegExp(s).test(e)}function U(e){return e=e.replace(/^[^2-90]+/g,""),e=e.replace(/(\d\d\d).*?(\d\d\d).*?(\d\d\d\d)(.*)/,"$1-$2-$3$4"),e}function F(e){return/^\d\d\d-\d\d\d-\d\d\d\d$/.test(e)}function q(e){return e.replace(/[^0-9]/g,"")}function Z(e){return/^\-?\d*\.?\d*$/.test(e)}function z(e){return e.replace(/[^\-0-9.]/g,"").replace(/(^-)|(-)/g,(s,a)=>a?"-":"").replace(/(\..*)\./g,"$1")}function G(e){return/^\-?\d*$/.test(e)}function j(e){return e=e.trim(),new RegExp("^(?:[a-z+]+:)?//","i").test(e)?e:"https://"+e}function K(e){return new RegExp("^(?:[-a-z+]+:)?//","i").test(e)}function W(e){return e=e.replace(/[^0-9]/g,"").replace(/(.{5})(.*)/,"$1-$2").trim(),e.length===6&&(e=e.replace(/-/,"")),e}function B(e){return new RegExp(/^\d{5}(-\d{4})?$/).test(e)}function J(e){return e=e.toUpperCase().replace(/[^A-Z0-9]/g,"").replace(/(.{3})\s*(.*)/,"$1 $2").trim(),e}function Q(e){return new RegExp(/^[ABCEGHJKLMNPRSTVXY][0-9][ABCEGHJKLMNPRSTVWXYZ] ?[0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]$/).test(e)}function X(e){return["transparent","currentColor"].includes(e)?!0:typeof e!="string"||!e.trim()?!1:typeof CSS=="object"&&typeof CSS.supports=="function"?CSS.supports("color",e):ee(e)}function ee(e){const s=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*\)$/),a=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*\)$/),o=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*\)$/),h=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*\)$/),d=new RegExp(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);let u="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 p=new RegExp(`^(${u})$`,"i");return s.test(e)||a.test(e)||o.test(e)||h.test(e)||d.test(e)||p.test(e)}let M=null;const C=new Map;function te(e){if(e=e.trim().toLowerCase(),["transparent","currentcolor"].includes(e))return e;if(C.has(e))return C.get(e);M===null&&(M=document.createElement("canvas"),M.willReadFrequently=!0);let s=M.getContext("2d");if(!s)throw new Error("Can't get context from colorCanvas");s.fillStyle=e,s.fillRect(0,0,1,1);let a=s.getImageData(0,0,1,1).data,o="#"+("000000"+(a[0]<<16|a[1]<<8|a[2]).toString(16)).slice(-6);return C.set(e,o),o}function re(e){let s={valid:!1,error:!1,messages:[]};return typeof e=="boolean"?{valid:e,error:!1,messages:[]}:typeof e=="string"?{valid:!1,error:!1,messages:[e]}:(typeof e.valid=="boolean"&&(s.valid=e.valid),typeof e.message=="string"&&(s.messages=[e.message]),typeof e.messages=="string"&&(s.messages=[e.messages]),Array.isArray(e.messages)&&(s.messages=e.messages),e.error===!0&&(s.error=!0),s)}r.formatDateTime=D,r.isColor=X,r.isDate=$,r.isDateInRange=Y,r.isEmail=P,r.isFormControl=n,r.isInteger=G,r.isNANPTel=F,r.isNumber=Z,r.isPostalCA=Q,r.isTime=V,r.isType=l,r.isUrl=K,r.isZip=B,r.momentToFPFormat=g,r.monthToNumber=E,r.normalizeValidationResult=re,r.parseColor=te,r.parseDate=T,r.parseDateToString=x,r.parseInteger=q,r.parseNANPTel=U,r.parseNumber=z,r.parsePostalCA=J,r.parseTime=I,r.parseTimeToString=_,r.parseUrl=j,r.parseZip=W,r.yearToFull=L,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})})})(v,i);class A extends Event{constructor(r){super("validationSuccess",{cancelable:!0});m(this,"submitEvent");this.submitEvent=r}}class N extends Event{constructor(r){super("validationError",{cancelable:!0});m(this,"submitEvent");this.submitEvent=r}}class H{constructor(t,r={}){m(this,"form");m(this,"inputs",[]);m(this,"inputErrors",{});m(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."});m(this,"debug");m(this,"autoInit");m(this,"preventSubmit",!1);m(this,"hiddenClasses");m(this,"errorMainClasses");m(this,"errorInputClasses");m(this,"dispatchTimeout",0);m(this,"originalNoValidate",!1);m(this,"validationSuccessCallback");m(this,"validationErrorCallback");m(this,"submitHandlerRef",this.submitHandler.bind(this));m(this,"inputInputHandlerRef",this.inputInputHandler.bind(this));m(this,"inputChangeHandlerRef",this.inputChangeHandler.bind(this));m(this,"inputKeydownHandlerRef",this.inputKeydownHandler.bind(this));m(this,"inputHandlers",{number:{parse:i.parseNumber,isValid:i.isNumber,error:this.messages.ERROR_NUMBER},integer:{parse:i.parseInteger,isValid:i.isInteger,error:this.messages.ERROR_INTEGER},tel:{parse:i.parseNANPTel,isValid:i.isNANPTel,error:this.messages.ERROR_TEL},email:{parse:t=>t.trim(),isValid:i.isEmail,error:this.messages.ERROR_EMAIL},zip:{parse:i.parseZip,isValid:i.isZip,error:this.messages.ERROR_ZIP},postal:{parse:i.parsePostalCA,isValid:i.isPostalCA,error:this.messages.ERROR_POSTAL},url:{parse:i.parseUrl,isValid:i.isUrl,error:this.messages.ERROR_URL},date:{parse:i.parseDateToString,isValid:i.isDate,error:this.messages.ERROR_DATE},time:{parse:i.parseTimeToString,isValid:i.isTime,error:this.messages.ERROR_TIME},color:{parse:t=>t.trim().toLowerCase(),isValid:i.isColor,error:this.messages.ERROR_COLOR}});m(this,"isSubmitting",!1);if(!t)throw new Error("Validator requires a form to be passed as the first argument.");if(!(t instanceof HTMLFormElement))throw new Error("form argument must be an instance of HTMLFormElement");this.form=t,(t.dataset.preventSubmit===""||t.dataset.preventSubmit)&&(this.preventSubmit=!0),Object.assign(this.messages,r.messages||{}),this.debug=r.debug||!1,this.autoInit=r.autoInit!==!1,this.preventSubmit=r.preventSubmit===!1?!1:this.preventSubmit,this.hiddenClasses=r.hiddenClasses||"hidden opacity-0",this.errorMainClasses=r.errorMainClasses||"m-2 border border-red-500 bg-red-100 p-3 dark:bg-red-900/80 text-center",this.errorInputClasses=r.errorInputClasses||"border-red-600 dark:border-red-500",this.validationSuccessCallback=r.validationSuccessCallback||(()=>{}),this.validationErrorCallback=r.validationErrorCallback||(()=>{}),this.autoInit&&this.init(),new MutationObserver(()=>this.autoInit&&this.init()).observe(t,{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(t=>{!t.name&&!t.id&&(t.id=`vl-input-${Math.random().toString(36).slice(2)}`),this.inputErrors[t.name||t.id]=[]}),this.originalNoValidate=this.form.hasAttribute("novalidate"),this.form.setAttribute("novalidate","novalidate"),this.removeEventListeners(),this.addEventListeners()}getErrorEl(t){const r=document.getElementById(t.name+"-error");return r||document.getElementById(t.id+"-error")||null}addErrorMain(t){const r=document.createElement("div");r.id="form-error-main",this.errorMainClasses.split(" ").forEach(n=>{r.classList.add(n)}),t?r.innerHTML=t:r.innerHTML=this.messages.ERROR_MAIN,this.form.appendChild(r)}addInputError(t,r=t.dataset.errorDefault||this.messages.ERROR_GENERIC){const n=t.name||t.id;this.debug&&console.log("Invalid value for "+n+": "+r),n in this.inputErrors||(this.inputErrors[n]=[]),this.inputErrors[n].includes(r)||this.inputErrors[n].push(r)}showInputErrors(t){if(!t||!t.name&&!t.id)return;const r=t.name||t.id,n=r in this.inputErrors?this.inputErrors[r]:[];if(!n.length)return;t.setAttribute("aria-invalid","true"),this.errorInputClasses.split(" ").forEach(g=>{t.classList.add(g)});let l=this.getErrorEl(t);l&&(l.innerHTML=n.join("<br>"),this.hiddenClasses.split(" ").forEach(g=>{l&&l.classList.remove(g)}))}showFormErrors(){if(this.inputs.forEach(t=>this.showInputErrors(t)),Object.values(this.inputErrors).some(t=>Array.isArray(t)&&t.length)){const t=this.form.querySelectorAll("#form-error-main");t.length?t.forEach(r=>{r.innerHTML||(r.innerHTML=this.messages.ERROR_MAIN),this.hiddenClasses.split(" ").forEach(n=>{r.classList.remove(n)})}):this.addErrorMain()}}clearInputErrors(t){this.inputErrors[t.name||t.id]=[],t.removeAttribute("aria-invalid");let r=this.getErrorEl(t);r&&(this.errorInputClasses.split(" ").forEach(n=>{t.classList.remove(n)}),this.hiddenClasses.split(" ").forEach(n=>{r&&r.classList.add(n)}),r.textContent="")}clearFormErrors(){this.form.querySelectorAll("#form-error-main").forEach(t=>{this.hiddenClasses.split(" ").forEach(r=>{t.classList.add(r)})}),this.inputs.forEach(t=>this.clearInputErrors(t))}validateRequired(t){let r=!0;if(t.required&&(t.value===""||t instanceof HTMLInputElement&&["checkbox","radio"].includes(t.type)&&!t.checked))if(t instanceof HTMLInputElement&&["checkbox","radio"].includes(t.type)){let n=!1,l=t.name;const g=this.form.querySelectorAll(`input[name="${l}"]`);if(g.forEach(E=>{if(E instanceof HTMLInputElement&&E.checked===!0){n=!0;return}}),n===!1){r=!1;let E=g.length>1?this.messages.OPTION_REQUIRED:this.messages.CHECKED_REQUIRED;t.dataset.errorDefault&&(E=t.dataset.errorDefault),this.addInputError(t,E)}}else i.isFormControl(t)&&(r=!1,this.addInputError(t,t.dataset.errorDefault||this.messages.ERROR_REQUIRED));return r}validateLength(t){let r=!0;if((t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length){let n=t.minLength>0?t.minLength:t.dataset.minLength?parseInt(t.dataset.minLength):0,l=t.maxLength>0&&t.maxLength<5e5?t.maxLength:t.dataset.maxLength?parseInt(t.dataset.maxLength):1/0;n>0&&t.value.length<n&&(r=!1,this.addInputError(t,this.messages.ERROR_MINLENGTH.replace("${val}",n.toString()))),t.value.length>l&&(r=!1,this.addInputError(t,this.messages.ERROR_MAXLENGTH.replace("${val}",l.toString())))}return r}validateInputType(t){const r=t.dataset.type||t.type,n=this.inputHandlers[t.type]||this.inputHandlers[r];if(n){const l=t.dataset.dateFormat||t.dataset.timeFormat,g=n.parse(t.value,l),E=["date","time","datetime-local","month","week"];if(g.length&&!E.includes(t.type)&&(t.value=g),!n.isValid(t.value))return this.addInputError(t,n.error),!1}return!0}validateDateRange(t){if(t.dataset.dateRange){const r=t.dataset.dateRange,n=i.parseDate(t.value);if(!isNaN(n.getTime())&&!i.isDateInRange(n,r)){let l=t.dataset.errorDefault||this.messages.ERROR_DATE_RANGE;return r==="past"?l=this.messages.ERROR_DATE_PAST:r==="future"&&(l=this.messages.ERROR_DATE_FUTURE),this.addInputError(t,l),!1}}return!0}validatePattern(t){const r=t.dataset.pattern||t instanceof HTMLInputElement&&t.pattern||null;return r&&!new RegExp(r).test(t.value)?(this.addInputError(t),!1):!0}async validateCustom(t){const r=t.dataset.validation;if(!r||typeof r!="string")return!0;const n=window[r];if(!n||typeof n!="function")return!0;let l;try{l=await Promise.resolve(n(t.value)),l=i.normalizeValidationResult(l)}catch{return this.addInputError(t,this.messages.ERROR_CUSTOM_VALIDATION),!1}const g=l.messages.join("<br>")||this.messages.ERROR_CUSTOM_VALIDATION;return l.valid||this.addInputError(t,g),l.valid}async validateInput(t){if(!(t instanceof HTMLInputElement)||!t.value.length)return!0;let r=!0;return r=this.validateInputType(t)&&r,r=this.validateDateRange(t)&&r,r=this.validatePattern(t)&&r,r=await this.validateCustom(t)&&r,r}async validate(t){let r=!0;for(const n of this.inputs)r=this.validateRequired(n)&&r,r=this.validateLength(n)&&r,r=await this.validateInput(n)&&r;return r}async submitHandler(t){if(this.isSubmitting)return;t.preventDefault(),this.clearFormErrors();let r=await this.validate(t);this.showFormErrors();const n=new A(t),l=new N(t);r?(this.form.dispatchEvent(n),this.validationSuccessCallback&&this.validationSuccessCallback(t)):(this.form.dispatchEvent(l),this.validationErrorCallback&&this.validationErrorCallback(t)),r&&!this.preventSubmit&&(this.isSubmitting=!0,n.defaultPrevented||this.form.submit(),this.isSubmitting=!1)}async inputChangeHandler(t){t.target instanceof HTMLInputElement&&(this.clearInputErrors(t.target),await this.validateInput(t.target),this.showInputErrors(t.target))}inputInputHandler(t){const r=t.target;i.isType(r,"integer")&&(r.value=i.parseInteger(r.value)),r.type!=="number"&&i.isType(r,["number","float","decimal"])&&(r.value=i.parseNumber(r.value)),i.isType(r,"color")&&this.syncColorInput(t)}syncColorInput(t){let r=t.target,n=r;r.type==="color"&&(n=this.form.querySelector(`#${r.id.replace(/-color/,"")}`));let l=this.form.querySelector(`#${n.id}-color-label`);if((r.dataset.type||"")==="color"){let g=this.form.querySelector(`input#${r.id}-color`);if(!g||!i.isColor(r.value))return;g.value=i.parseColor(r.value)}r.type==="color"&&(n.value=r.value),l&&(l.style.backgroundColor=r.value),clearTimeout(this.dispatchTimeout),this.dispatchTimeout=window.setTimeout(()=>{n.dispatchEvent(new Event("change",{bubbles:!0}))},200)}inputKeydownHandler(t){t.target instanceof HTMLInputElement&&i.isType(t.target,"integer")&&(t.key==="ArrowUp"?(t.preventDefault(),t.target.value===""&&(t.target.value="0"),t.target.value=(parseInt(t.target.value)+1).toString()):t.key==="ArrowDown"&&(parseInt(t.target.value)>0?t.target.value=(parseInt(t.target.value)-1).toString():t.target.value="0"))}destroy(){this.removeEventListeners(),this.originalNoValidate||this.form.removeAttribute("novalidate")}}return H});
package/package.json CHANGED
@@ -1,16 +1,20 @@
1
1
  {
2
2
  "name": "@jdlien/validator",
3
- "version": "1.0.6",
3
+ "version": "1.1.3",
4
4
  "type": "module",
5
5
  "module": "dist/validator.js",
6
- "description": "Validates and sanitizes the inputs in a form using native html attributes.",
6
+ "types": "dist/Validator.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "description": "Validates and sanitizes the inputs in a form using native HTML attributes.",
7
11
  "scripts": {
8
12
  "dev": "vite",
9
- "build": "tsc && vite build",
13
+ "build": "vite build && tsc --emitDeclarationOnly",
10
14
  "preview": "vite preview",
11
15
  "test": "vitest",
12
16
  "coverage": "vitest --coverage",
13
- "tw": "tailwindcss -i demo-src.css -o demo.css -w -m"
17
+ "tw": "tailwindcss -i demo/demo-src.css -o demo/demo.css -w -m"
14
18
  },
15
19
  "repository": {
16
20
  "type": "git",
@@ -23,19 +27,14 @@
23
27
  "form",
24
28
  "validation",
25
29
  "front-end",
26
- "validation",
27
30
  "better",
28
31
  "HTML",
29
32
  "inputs",
30
33
  "date",
31
- "validation",
32
34
  "time",
33
- "validation",
34
35
  "color",
35
- "validation",
36
36
  "required",
37
- "field",
38
- "validation"
37
+ "field"
39
38
  ],
40
39
  "author": "JD Lien",
41
40
  "license": "ISC",
@@ -47,14 +46,17 @@
47
46
  "@sheerun/mutationobserver-shim": "^0.3.3",
48
47
  "@tailwindcss/forms": "^0.5.3",
49
48
  "@types/jsdom": "^21.1.0",
50
- "@vitest/coverage-c8": "^0.28.5",
49
+ "@vitest/coverage-c8": "^0.29.1",
51
50
  "jsdom": "^21.1.0",
52
51
  "jsdom-global": "^3.0.2",
53
52
  "prettier": "^2.8.4",
54
53
  "tailwindcss": "^3.2.7",
55
54
  "typescript": "^4.9.3",
56
55
  "vite": "^4.1.0",
57
- "vitest": "^0.28.5"
56
+ "vitest": "^0.29.1"
58
57
  },
59
- "sideEffects": false
58
+ "sideEffects": false,
59
+ "dependencies": {
60
+ "@jdlien/validator-utils": "^1.1.4"
61
+ }
60
62
  }
package/.prettierrc DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "tabWidth": 2,
3
- "useTabs": false,
4
- "trailingComma": "es5",
5
- "semi": false,
6
- "singleQuote": true,
7
- "printWidth": 100
8
- }
package/demo-src.css DELETED
@@ -1,106 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- @layer base {
6
- [type='text'],
7
- [type='email'],
8
- [type='url'],
9
- [type='password'],
10
- [type='number'],
11
- [type='date'],
12
- [type='datetime-local'],
13
- [type='month'],
14
- [type='search'],
15
- [type='tel'],
16
- [type='time'],
17
- [type='week'],
18
- [type='color'],
19
- [multiple],
20
- textarea,
21
- select {
22
- @apply border border-zinc-300 rounded bg-zinc-50 px-2 py-1.5 text-zinc-700
23
- placeholder-zinc-500 placeholder-opacity-50 shadow-inner
24
- focus:bg-white focus:outline-none focus:ring-2 focus:ring-blue-400/50
25
- focus:ring-offset-0 focus:ring-offset-transparent;
26
-
27
- @apply dark:border-zinc-500 dark:bg-zinc-900 dark:text-white dark:focus:bg-zinc-800;
28
-
29
- @apply disabled:bg-zinc-200 disabled:text-zinc-500 dark:disabled:bg-zinc-800 dark:disabled:text-zinc-300;
30
- }
31
-
32
- /* Form input variants (do not apply when disabled) */
33
- [type='text']:not([disabled]),
34
- [type='email']:not([disabled]),
35
- [type='url']:not([disabled]),
36
- [type='password']:not([disabled]),
37
- [type='number']:not([disabled]),
38
- [type='date']:not([disabled]),
39
- [type='datetime-local']:not([disabled]),
40
- [type='month']:not([disabled]),
41
- [type='search']:not([disabled]),
42
- [type='tel']:not([disabled]),
43
- [type='time']:not([disabled]),
44
- [type='week']:not([disabled]),
45
- [type='color']:not([disabled]),
46
- [multiple]:not([disabled]),
47
- textarea:not([disabled]),
48
- select:not([disabled]) {
49
- @apply hover:bg-white dark:hover:bg-zinc-800;
50
- }
51
-
52
- [type='checkbox'],
53
- [type='radio'] {
54
- @apply border border-zinc-300 bg-zinc-50 text-blue-600 shadow-sm
55
- checked:border checked:border-zinc-400 checked:bg-blue-600 focus:outline-none
56
- focus:ring-2 focus:ring-blue-400/50 focus:ring-offset-0 focus:ring-offset-transparent focus:checked:bg-blue-500;
57
-
58
- @apply dark:border-zinc-500 dark:bg-zinc-400 dark:checked:bg-blue-600 dark:focus:checked:bg-blue-400;
59
-
60
- @apply disabled:opacity-60 disabled:checked:hover:bg-blue-600;
61
-
62
- /* Prevents border change when disabled and hovered */
63
- @apply disabled:checked:hover:border-zinc-400 disabled:dark:checked:bg-blue-600 dark:disabled:checked:hover:border-zinc-500;
64
- }
65
-
66
- [type='checkbox']:not([disabled]),
67
- [type='radio']:not([disabled]) {
68
- @apply hover:bg-zinc-200 checked:hover:bg-blue-500
69
- dark:hover:bg-zinc-300 dark:hover:checked:bg-blue-400 dark:hover:focus:checked:bg-blue-400;
70
- }
71
-
72
- [type='radio'] {
73
- @apply rounded-full;
74
- }
75
-
76
- select {
77
- @apply pr-6;
78
- /* background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='rgb(120 113 108)' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e") !important; */
79
- background-image: url('data:image/svg+xml,%3Csvg xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 fill=%22none%22 viewBox=%220 0 24 24%22 stroke-width=%221.5%22 stroke=%22rgb(120 113 108)%22%3E%3Cpath stroke-linecap=%22round%22 stroke-linejoin=%22round%22 d=%22M19.5 8.25l-7.5 7.5-7.5-7.5%22 %2F%3E%3C%2Fsvg%3E') !important;
80
- background-size: 1.2rem;
81
- background-position: right 0.1rem center;
82
- }
83
- }
84
-
85
- @layer components {
86
- .checked-label {
87
- @apply ml-1.5 mr-1;
88
- /* no styles are directly applied unless this is used with a checked input */
89
- }
90
-
91
- input:checked + .checked-label {
92
- @apply text-black dark:text-white;
93
- /* This is only really visible in dark mode, giving the text a subtle glow */
94
- text-shadow: 0 0 5px hsl(0 0% 100%/25%);
95
- }
96
-
97
- /* Apply checked-border to labels for checkboxes and radio buttons for a label that colors up automatically when checked */
98
- .checked-border {
99
- @apply inline-flex items-center border border-zinc-300/30 bg-zinc-50 px-1 text-zinc-700 shadow-inner;
100
- @apply hover:bg-white dark:border-zinc-500/10 dark:bg-zinc-900 dark:text-zinc-100 dark:hover:bg-zinc-800 dark:focus:bg-zinc-800;
101
- }
102
-
103
- .checked-border:has([type='checkbox']:checked, [type='radio']:checked) {
104
- @apply border-blue-600 text-black hover:border-blue-500 dark:text-white;
105
- }
106
- }