# Iodine
@@ -23,7 +22,7 @@ Version 8+ of Iodine involved a major rewrite with numerous breaking changes. It
The easiest way to pull Iodine into your project is via a CDN (be sure to update the build number):
```html
-
+
```
You can also pull Iodine into your project via NPM:
diff --git a/dist/iodine.min.esm.js b/dist/iodine.min.esm.js
index 246a126..60465be 100644
--- a/dist/iodine.min.esm.js
+++ b/dist/iodine.min.esm.js
@@ -1,2 +1,2 @@
-class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}"undefined"!=typeof window&&(window.Iodine=new e);export{e as default};
+class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}"undefined"!=typeof window&&(window.Iodine=new e);export{e as default};
//# sourceMappingURL=iodine.min.esm.js.map
diff --git a/dist/iodine.min.esm.js.map b/dist/iodine.min.esm.js.map
index 19372b4..df591ac 100644
--- a/dist/iodine.min.esm.js.map
+++ b/dist/iodine.min.esm.js.map
@@ -1 +1 @@
-{"version":3,"file":"iodine.min.esm.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n return new RegExp(\"^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)$\").test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","param","field","chunks","split","shift","join","includes","key","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","result","fields","i","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","Number","isInteger","assertJson","JSON","parse","e","assertMax","limit","assertMin","parseFloat","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"iodine.min.esm.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n let regex = \"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","param","field","chunks","split","key","shift","join","includes","Date","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","fields","i","result","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","isInteger","assertJson","JSON","parse","e","assertMax","limit","parseFloat","assertMin","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
diff --git a/dist/iodine.min.js b/dist/iodine.min.js
index ab5fd4b..40dd446 100644
--- a/dist/iodine.min.js
+++ b/dist/iodine.min.js
@@ -1,2 +1,2 @@
-class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}"undefined"!=typeof window&&(window.Iodine=new e),module.exports=e;
+class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}"undefined"!=typeof window&&(window.Iodine=new e),module.exports=e;
//# sourceMappingURL=iodine.min.js.map
diff --git a/dist/iodine.min.js.map b/dist/iodine.min.js.map
index f552a2e..9ceaf04 100644
--- a/dist/iodine.min.js.map
+++ b/dist/iodine.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"iodine.min.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n return new RegExp(\"^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)$\").test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","param","field","chunks","split","shift","join","includes","key","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","result","fields","i","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","Number","isInteger","assertJson","JSON","parse","e","assertMax","limit","assertMin","parseFloat","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"iodine.min.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n let regex = \"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","param","field","chunks","split","key","shift","join","includes","Date","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","fields","i","result","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","isInteger","assertJson","JSON","parse","e","assertMax","limit","parseFloat","assertMin","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
diff --git a/dist/iodine.min.modern.js b/dist/iodine.min.modern.js
index e140247..2e9880a 100644
--- a/dist/iodine.min.modern.js
+++ b/dist/iodine.min.modern.js
@@ -1,2 +1,2 @@
-class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}"undefined"!=typeof window&&(window.Iodine=new e);export{e as default};
+class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}"undefined"!=typeof window&&(window.Iodine=new e);export{e as default};
//# sourceMappingURL=iodine.min.modern.js.map
diff --git a/dist/iodine.min.modern.js.map b/dist/iodine.min.modern.js.map
index b974a6b..de217c7 100644
--- a/dist/iodine.min.modern.js.map
+++ b/dist/iodine.min.modern.js.map
@@ -1 +1 @@
-{"version":3,"file":"iodine.min.modern.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n return new RegExp(\"^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)$\").test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","_this$default_field_n","param","field","chunks","split","shift","join","includes","key","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","result","fields","i","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","Number","isInteger","assertJson","JSON","parse","e","assertMax","limit","assertMin","parseFloat","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"iodine.min.modern.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n let regex = \"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","_this$default_field_n","param","field","chunks","split","key","shift","join","includes","Date","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","fields","i","result","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","isInteger","assertJson","JSON","parse","e","assertMax","limit","parseFloat","assertMin","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
diff --git a/dist/iodine.min.umd.js b/dist/iodine.min.umd.js
index 936941c..587b676 100644
--- a/dist/iodine.min.umd.js
+++ b/dist/iodine.min.umd.js
@@ -1,2 +1,2 @@
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).iodine=t()}(this,function(){class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}return"undefined"!=typeof window&&(window.Iodine=new e),e});
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).iodine=t()}(this,function(){class e{constructor(){this.locale=void 0,this.messages={after:"The date must be after: '[PARAM]'",afterOrEqual:"The date must be after or equal to: '[PARAM]'",array:"[FIELD] must be an array",before:"The date must be before: '[PARAM]'",beforeOrEqual:"The date must be before or equal to: '[PARAM]'",boolean:"[FIELD] must be true or false",date:"[FIELD] must be a date",different:"[FIELD] must be different to '[PARAM]'",endsWith:"[FIELD] must end with '[PARAM]'",email:"[FIELD] must be a valid email address",falsy:"[FIELD] must be a falsy value (false, 'false', 0 or '0')",in:"[FIELD] must be one of the following options: [PARAM]",integer:"[FIELD] must be an integer",json:"[FIELD] must be a parsable JSON object string",max:"[FIELD] must be less than or equal to [PARAM]",min:"[FIELD] must be greater than or equal to [PARAM]",maxLength:"[FIELD] must not be greater than '[PARAM]' in character length",minLength:"[FIELD] must not be less than '[PARAM]' character length",notIn:"[FIELD] must not be one of the following options: [PARAM]",numeric:"[FIELD] must be numeric",optional:"[FIELD] is optional",regexMatch:"[FIELD] must satisify the regular expression: [PARAM]",required:"[FIELD] must be present",same:"[FIELD] must be '[PARAM]'",startsWith:"[FIELD] must start with '[PARAM]'",string:"[FIELD] must be a string",truthy:"[FIELD] must be a truthy value (true, 'true', 1 or '1')",url:"[FIELD] must be a valid url",uuid:"[FIELD] must be a valid UUID"}}_compare(e,t,r,s=!1){return!!this.assertDate(e)&&!(!this.assertDate(t)&&!this.assertInteger(t))&&(t="number"==typeof t?t:t.getTime(),"less"===r&&s?e.getTime()<=t:"less"!==r||s?"more"===r&&s?e.getTime()>=t:"more"!==r||s?void 0:e.getTime()>t:e.getTime()"optional"!==e).map(e=>[e,this._title(e.split(":").shift()),e.split(":").slice(1)]):[]}_title(e){return`${e[0].toUpperCase()}${e.slice(1)}`}_validate(e,t){for(let r in t=this._prepare(e,t))if(!this[`assert${t[r][1]}`].apply(this,[e,t[r][2].join(":")]))return{valid:!1,rule:t[r][0],error:this._error(t[r][0])};return{valid:!0,rule:"",error:""}}assert(e,t){if(Array.isArray(t))return this._validate(e,t);let r=Object.keys(t),s={valid:!0,fields:{}};for(let a=0;a=t}assertMaxLength(e,t){return"string"==typeof e&&e.length<=t}assertMinLength(e,t){return"string"==typeof e&&e.length>=t}assertNotIn(e,t){return!this.assertIn(e,t)}assertNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}assertOptional(e){return[null,void 0,""].includes(e)}assertRegexMatch(e,t){return new RegExp(t).test(String(e))}assertRequired(e){return!this.assertOptional(e)}assertSame(e,t){return e==t}assertStartsWith(e,t){return this.assertString(e)&&e.startsWith(t)}assertString(e){return"string"==typeof e}assertTruthy(e){return[1,"1",!0,"true"].includes(e)}assertUrl(e){return new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$").test(String(e).toLowerCase())}assertUuid(e){return new RegExp("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$").test(String(e).toLowerCase())}rule(t,r){e.prototype[`assert${this._title(t)}`]=r}setErrorMessages(e){this.messages=e}setErrorMessage(e,t){this.messages[e]=t}setLocale(e){this.locale=e}setDefaultFieldName(e){this.default_field_name=e}}return"undefined"!=typeof window&&(window.Iodine=new e),e});
//# sourceMappingURL=iodine.min.umd.js.map
diff --git a/dist/iodine.min.umd.js.map b/dist/iodine.min.umd.js.map
index 8cceefe..b384ad8 100644
--- a/dist/iodine.min.umd.js.map
+++ b/dist/iodine.min.umd.js.map
@@ -1 +1 @@
-{"version":3,"file":"iodine.min.umd.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n return new RegExp(\"^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)$\").test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","param","field","chunks","split","shift","join","includes","key","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","result","fields","i","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","Number","isInteger","assertJson","JSON","parse","e","assertMax","limit","assertMin","parseFloat","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"iodine.min.umd.js","sources":["../src/iodine.js"],"sourcesContent":["/*\n|--------------------------------------------------------------------------\n| Iodine - JavaScript Library\n|--------------------------------------------------------------------------\n|\n| This library contains a collection of useful validation rules that can\n| be used to quickly verify whether items meet certain conditions.\n|\n*/\nexport default class Iodine\n{\n /**\n * Constructor.\n *\n */\n constructor()\n {\n this.locale = undefined;\n\n this.messages = {\n after : \"The date must be after: '[PARAM]'\",\n afterOrEqual : \"The date must be after or equal to: '[PARAM]'\",\n array : \"[FIELD] must be an array\",\n before : \"The date must be before: '[PARAM]'\",\n beforeOrEqual : \"The date must be before or equal to: '[PARAM]'\",\n boolean : \"[FIELD] must be true or false\",\n date : \"[FIELD] must be a date\",\n different : \"[FIELD] must be different to '[PARAM]'\",\n endsWith : \"[FIELD] must end with '[PARAM]'\",\n email : \"[FIELD] must be a valid email address\",\n falsy : \"[FIELD] must be a falsy value (false, 'false', 0 or '0')\",\n in : \"[FIELD] must be one of the following options: [PARAM]\",\n integer : \"[FIELD] must be an integer\",\n json : \"[FIELD] must be a parsable JSON object string\",\n max : \"[FIELD] must be less than or equal to [PARAM]\",\n min : \"[FIELD] must be greater than or equal to [PARAM]\",\n maxLength : \"[FIELD] must not be greater than '[PARAM]' in character length\",\n minLength : \"[FIELD] must not be less than '[PARAM]' character length\",\n notIn : \"[FIELD] must not be one of the following options: [PARAM]\",\n numeric : \"[FIELD] must be numeric\",\n optional : \"[FIELD] is optional\",\n regexMatch : \"[FIELD] must satisify the regular expression: [PARAM]\",\n required : \"[FIELD] must be present\",\n same : \"[FIELD] must be '[PARAM]'\",\n startsWith : \"[FIELD] must start with '[PARAM]'\",\n string : \"[FIELD] must be a string\",\n truthy : \"[FIELD] must be a truthy value (true, 'true', 1 or '1')\",\n url : \"[FIELD] must be a valid url\",\n uuid : \"[FIELD] must be a valid UUID\",\n };\n }\n\n /**\n * @internal.\n *\n */\n _compare(first, second, type, equals = false)\n {\n if (! this.assertDate(first)) return false;\n\n if (! this.assertDate(second) && ! this.assertInteger(second)) return false;\n\n second = typeof second === 'number' ? second : second.getTime();\n\n if (type === 'less' && equals) return first.getTime() <= second;\n if (type === 'less' && ! equals) return first.getTime() < second;\n if (type === 'more' && equals) return first.getTime() >= second;\n if (type === 'more' && ! equals) return first.getTime() > second;\n }\n\n /**\n * @internal.\n *\n */\n _error(rule, args = undefined)\n {\n let { param, field } = typeof args === 'object' ? args : { param : args, field : undefined };\n\n const chunks = rule.split(':');\n\n let key = chunks.shift();\n\n param = param || chunks.join(':');\n\n if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n param = new Date(parseInt(param)).toLocaleTimeString(this.locale, {\n year : 'numeric',\n month : 'short',\n day : 'numeric',\n hour : '2-digit',\n minute : 'numeric',\n hour12 : false,\n });\n }\n\n let message = [null, undefined, ''].includes(param)\n ? this.messages[key]\n : this.messages[key].replace('[PARAM]', param);\n\n return [null, undefined, ''].includes(field)\n ? message.replace('[FIELD]', this.default_field_name ?? 'Value')\n : message.replace('[FIELD]', field);\n }\n\n /**\n * @internal.\n *\n */\n _missing()\n {\n return {\n valid : false,\n rule : 'None',\n error : 'Rules exist, but no value was provided to check',\n };\n }\n\n /**\n * @internal.\n *\n */\n _prepare(value, rules = [])\n {\n if (! rules.length) return [];\n\n if (rules[0] === 'optional' && this.assertOptional(value)) return [];\n\n return rules.filter(rule => rule !== 'optional')\n .map(rule => [rule, this._title(rule.split(':').shift()), rule.split(':').slice(1)]);\n }\n\n /**\n * @internal.\n *\n */\n _title(value)\n {\n return `${value[0].toUpperCase()}${value.slice(1)}`;\n }\n\n /**\n * @internal.\n *\n */\n _validate(value, rules)\n {\n for (let index in rules = this._prepare(value, rules)) {\n if (! this[`assert${rules[index][1]}`].apply(this, [value, rules[index][2].join(':')])) {\n return {\n valid : false,\n rule : rules[index][0],\n error : this._error(rules[index][0]),\n };\n }\n }\n\n return {\n valid : true,\n rule : '',\n error : '',\n };\n }\n\n /**\n * Determine if the given content matches the given schema.\n *\n */\n assert(values, schema)\n {\n if (Array.isArray(schema)) {\n return this._validate(values, schema);\n }\n\n let keys = Object.keys(schema);\n\n let result = { valid : true, fields : { } };\n\n for (let i = 0; i < keys.length; i++) {\n result.fields[keys[i]] = values.hasOwnProperty(keys[i])\n ? this._validate(values[keys[i]], schema[keys[i]])\n : this._missing();\n\n if (! result.fields[keys[i]].valid) {\n result.valid = false;\n }\n }\n\n return result;\n }\n\n /**\n * Determine if the given date is after another given date.\n *\n */\n assertAfter(value, after)\n {\n return this._compare(value, after, 'more', false);\n }\n\n /**\n * Determine if the given date is after or equal to another given date.\n *\n */\n assertAfterOrEqual(value, after)\n {\n return this._compare(value, after, 'more', true);\n }\n\n /**\n * Determine if the given value is an array.\n *\n */\n assertArray(value)\n {\n return Array.isArray(value);\n }\n\n /**\n * Determine if the given date is before another given date.\n *\n */\n assertBefore(value, before)\n {\n return this._compare(value, before, 'less', false);\n }\n\n /**\n * Determine if the given date is before or equal to another given date.\n *\n */\n assertBeforeOrEqual(value, before)\n {\n return this._compare(value, before, 'less', true);\n }\n\n /**\n * Determine if the given value is a boolean.\n *\n */\n assertBoolean(value)\n {\n return [true, false].includes(value);\n }\n\n /**\n * Determine if the given value is a date object.\n *\n */\n assertDate(value)\n {\n return (value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value));\n }\n\n /**\n * Determine if the given value is different to another given value.\n *\n */\n assertDifferent(value, different)\n {\n return value != different;\n }\n\n /**\n * Determine if the given value ends with another given value.\n *\n */\n assertEndsWith(value, sub)\n {\n return this.assertString(value) && value.endsWith(sub);\n }\n\n /**\n * Determine if the given value is a valid email address.\n *\n */\n assertEmail(value)\n {\n let regex = \"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is falsy.\n *\n */\n assertFalsy(value)\n {\n return [0, '0', false, 'false'].includes(value);\n }\n\n /**\n * Determine if the given value is within the given array of options.\n *\n */\n assertIn(value, options)\n {\n return (typeof options === 'string' ? options.split(',') : options).includes(value);\n }\n\n /**\n * Determine if the given value is an integer.\n *\n */\n assertInteger(value)\n {\n return Number.isInteger(value) && parseInt(value).toString() === value.toString();\n }\n\n /**\n * Determine if the given value is a JSON string.\n *\n */\n assertJson(value)\n {\n try {\n return typeof JSON.parse(value) === 'object';\n } catch (e) {\n return false;\n }\n }\n\n /**\n * Determine if the given number is less than or equal to the maximum limit.\n *\n */\n assertMax(value, limit)\n {\n return parseFloat(value) <= limit;\n }\n\n /**\n * Determine if the given number is greater than or equal to the minimum limit.\n *\n */\n assertMin(value, limit)\n {\n return parseFloat(value) >= limit;\n }\n\n /**\n * Determine if the given value string length is less than or equal to the maximum limit.\n *\n */\n assertMaxLength(value, limit)\n {\n return typeof value === 'string' ? value.length <= limit : false;\n }\n\n /**\n * Determine if the given value string length is greater than or equal to the minimum limit.\n *\n */\n assertMinLength(value, limit)\n {\n return typeof value === 'string' ? value.length >= limit : false;\n }\n\n /**\n * Determine if the given value is not within the given array of options.\n *\n */\n assertNotIn(value, options)\n {\n return ! this.assertIn(value, options);\n }\n\n /**\n * Determine if the given value is numeric (an integer or a float).\n *\n */\n assertNumeric(value)\n {\n return ! isNaN(parseFloat(value)) && isFinite(value);\n }\n\n /**\n * Determine if the given value is optional.\n *\n */\n assertOptional(value)\n {\n return [null, undefined, ''].includes(value);\n }\n\n /**\n * Determine if the given value satisifies the given regular expression.\n *\n */\n assertRegexMatch(value, expression)\n {\n return new RegExp(expression).test(String(value));\n }\n\n /**\n * Determine if the given value is present.\n *\n */\n assertRequired(value)\n {\n return ! this.assertOptional(value);\n }\n\n /**\n * Determine if the given value is the same as another given value.\n *\n */\n assertSame(value, same)\n {\n return value == same;\n }\n\n /**\n * Determine if the given value starts with another given value.\n *\n */\n assertStartsWith(value, sub)\n {\n return this.assertString(value) && value.startsWith(sub);\n }\n\n /**\n * Determine if the given value is a string.\n *\n */\n assertString(value)\n {\n return typeof value === 'string';\n }\n\n /**\n * Determine if the given value is truthy.\n *\n */\n assertTruthy(value)\n {\n return [1, '1', true, 'true'].includes(value);\n }\n\n /**\n * Determine if the given value is a valid URL.\n *\n */\n assertUrl(value)\n {\n let regex = \"^(https?:\\\\/\\\\/)?((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*(\\\\?[;&a-z\\\\d%_.~+=-]*)?(\\\\#[-a-z\\\\d_]*)?$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Determine if the given value is a valid UUID.\n *\n */\n assertUuid(value)\n {\n let regex = \"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$\";\n\n return new RegExp(regex).test(String(value).toLowerCase());\n }\n\n /**\n * Attach a custom validation rule to the library.\n *\n */\n rule(name, closure)\n {\n Iodine.prototype[`assert${this._title(name)}`] = closure;\n }\n\n /**\n * Replace the default error messages with a new set.\n *\n */\n setErrorMessages(messages)\n {\n this.messages = messages;\n }\n\n /**\n * Add or replace an error message.\n *\n */\n setErrorMessage(key, message)\n {\n this.messages[key] = message;\n }\n\n /**\n * Replace the default locale with a new value.\n *\n */\n setLocale(locale)\n {\n this.locale = locale;\n }\n\n /**\n * Replace the default field name with a new value.\n *\n */\n setDefaultFieldName(fieldName)\n {\n this.default_field_name = fieldName;\n }\n}\n\n/**\n * Create an instance of the library.\n *\n */\nif (typeof window !== 'undefined') {\n window.Iodine = new Iodine();\n}\n"],"names":["Iodine","constructor","this","locale","undefined","messages","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endsWith","email","falsy","in","integer","json","max","min","maxLength","minLength","notIn","numeric","optional","regexMatch","required","same","startsWith","string","truthy","url","uuid","_compare","first","second","type","equals","assertDate","assertInteger","getTime","_error","rule","args","param","field","chunks","split","key","shift","join","includes","Date","parseInt","toLocaleTimeString","year","month","day","hour","minute","hour12","message","replace","default_field_name","_missing","valid","error","_prepare","value","rules","length","assertOptional","filter","map","_title","slice","toUpperCase","_validate","index","apply","assert","values","schema","Array","isArray","keys","Object","fields","i","result","hasOwnProperty","assertAfter","assertAfterOrEqual","assertArray","assertBefore","assertBeforeOrEqual","assertBoolean","prototype","toString","call","isNaN","assertDifferent","assertEndsWith","sub","assertString","assertEmail","RegExp","test","String","toLowerCase","assertFalsy","assertIn","options","isInteger","assertJson","JSON","parse","e","assertMax","limit","parseFloat","assertMin","assertMaxLength","assertMinLength","assertNotIn","assertNumeric","isFinite","assertRegexMatch","expression","assertRequired","assertSame","assertStartsWith","assertTruthy","assertUrl","assertUuid","name","closure","setErrorMessages","setErrorMessage","setLocale","setDefaultFieldName","fieldName","window"],"mappings":"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"}
\ No newline at end of file
diff --git a/package.json b/package.json
index f3d8228..dbf50d1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@kingshott/iodine",
- "version": "8.0.0",
+ "version": "8.1.0",
"description": "A micro client-side validation library",
"repository": {
"type": "git",
diff --git a/resources/build.svg b/resources/build.svg
deleted file mode 100644
index 1a93dc9..0000000
--- a/resources/build.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/license.svg b/resources/license.svg
deleted file mode 100644
index d43fcb9..0000000
--- a/resources/license.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/version.svg b/resources/version.svg
deleted file mode 100644
index e3358ca..0000000
--- a/resources/version.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
diff --git a/src/iodine.js b/src/iodine.js
index dd3d1b4..5412949 100644
--- a/src/iodine.js
+++ b/src/iodine.js
@@ -275,7 +275,9 @@ export default class Iodine
*/
assertEmail(value)
{
- return new RegExp("^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)$").test(String(value).toLowerCase());
+ let regex = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
+
+ return new RegExp(regex).test(String(value).toLowerCase());
}
/**
diff --git a/tests/test.js b/tests/test.js
index 80392d4..7b38ec0 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -149,6 +149,8 @@ test('email values', () =>
{
expect(window.Iodine.assertEmail('john@example.com')).toBe(true);
expect(window.Iodine.assertEmail('m@i.com')).toBe(true);
+ expect(window.Iodine.assertEmail('m@i.de')).toBe(true);
+ expect(window.Iodine.assertEmail('m@i.co.uk')).toBe(true);
expect(window.Iodine.assertEmail('😃@i.com')).toBe(false);
expect(window.Iodine.assertEmail('')).toBe(false);
expect(window.Iodine.assertEmail('45454.com')).toBe(false);