From e70518f7686d0194dae8f2b7262108ea2189bf9f Mon Sep 17 00:00:00 2001
From: Matt Kingshott <51963402+mattkingshott@users.noreply.github.com>
Date: Thu, 2 Apr 2020 13:06:35 +0100
Subject: [PATCH] added error message support, added comma-separated string
array support to in and isNotIn rules.
---
README.md | 32 +++++++++++++-
dist/iodine.min.js | 2 +-
dist/iodine.min.js.map | 2 +-
dist/iodine.min.mjs | 2 +-
dist/iodine.min.mjs.map | 2 +-
dist/iodine.min.umd.js | 2 +-
dist/iodine.min.umd.js.map | 2 +-
resources/size.svg | 4 +-
resources/version.svg | 4 +-
src/iodine.js | 91 +++++++++++++++++++++++++++++++++++++-
tests/test.js | 36 ++++++++++++++-
11 files changed, 163 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index a91a70f..a7a53d3 100755
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Iodine.js is a micro client-side validation library. It has no dependencies and
The easiest way to pull Iodine into your project is via a CDN:
```html
-
+
```
## Usage
@@ -54,7 +54,9 @@ Iodine.is(item_2, ['required', 'integer']); // false
The `is` method will return `true` if the item passes every rule.
-Alternatively, the name of the first rule that failed will be returned e.g. `'integer'`. You are then free to interpret the rule name and display a (localised) error message.
+Alternatively, the name of the first rule that failed will be returned e.g. `'integer'`.
+
+> Version 1 of Iodine only returned the rule name e.g. 'minimum'. Version 2+ returns the rule name and any supplied parameter e.g. 'minimum:7'.
## Additional parameters
@@ -84,6 +86,32 @@ Iodine.is(item_3, ['optional', 'integer']); // false
**IMPORTANT**: If you wish to allow for optional values, then you must supply `'optional'` as the first rule in the list.
+## Error messages
+
+Iodine includes a default set of error messages for the English language. To retrieve an error message for a rule, use the `getErrorMessage` method:
+
+```js
+Iodine.getErrorMessage('array'); // string
+```
+
+When dealing with parameters, the `getErrorMessage` method allows you to supply the rule either as a single `string` or as two arguments (the rule and parameter) e.g.
+
+```js
+Iodine.getErrorMessage('minimum:7'); // string
+Iodine.getErrorMessage('minimum', 7); // string
+```
+
+## Custom messages (localisation)
+
+You can easily replace the default error messages with your own via the `setErrorMessages` method. This method requires a single parameter, which is an `object` containing the messages. See the [_defaultMessages](src/iodine.js) method for an example of this object.
+
+Iodine will automatically swap `[PARAM]` placeholders with the parameters supplied in the `getErrorMessage` method. As such, you should insert this placeholder at the appropriate position in your new error message e.g.
+
+```js
+Iodine.setErrorMessages({ same: `Field must be '[PARAM]'` }); // English
+Iodine.setErrorMessages({ same: `Champ doit ĂȘtre '[PARAM]'` }); // French
+```
+
## Available rules
The following validation rules are available:
diff --git a/dist/iodine.min.js b/dist/iodine.min.js
index 6e129b8..023adcc 100644
--- a/dist/iodine.min.js
+++ b/dist/iodine.min.js
@@ -1,2 +1,2 @@
-var t=function(){};t.prototype._dateCompare=function(t,e,r,n){return void 0===n&&(n=!1),!!this.isDate(t)&&!(!this.isDate(e)&&!this.isInteger(e))&&(e="number"==typeof e?e:e.getTime(),"less"===r&&n?t.getTime()<=e:"less"!==r||n?"more"===r&&n?t.getTime()>=e:"more"!==r||n?void 0:t.getTime()>e:t.getTime()=e},t.prototype.isNotIn=function(t,e){return!e.includes(t)},t.prototype.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.prototype.isOptional=function(t){return[null,void 0,""].includes(t)},t.prototype.isRegexMatch=function(t,e){return new RegExp(e).test(String(t).toLowerCase())},t.prototype.isRequired=function(t){return!this.isOptional(t)},t.prototype.isSame=function(t,e){return t===e},t.prototype.isStartingWith=function(t,e){return this.isString(t)&&t.startsWith(e)},t.prototype.isString=function(t){return"string"==typeof t},t.prototype.isTruthy=function(t){return[1,"1",!0,"true"].includes(t)},t.prototype.isUrl=function(t){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(t).toLowerCase())},t.prototype.isUuid=function(t){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(t).toLowerCase())},t.prototype.is=function(t,e){if(void 0===e&&(e=[]),0===e.length)return!0;if("optional"===e[0]&&this.isOptional(t))return!0;for(var r=0;r=e:"more"!==r||i?void 0:t.getTime()>e:t.getTime()=e},t.prototype.isNotIn=function(t,e){return!this.isIn(t,e)},t.prototype.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.prototype.isOptional=function(t){return[null,void 0,""].includes(t)},t.prototype.isRegexMatch=function(t,e){return new RegExp(e).test(String(t).toLowerCase())},t.prototype.isRequired=function(t){return!this.isOptional(t)},t.prototype.isSame=function(t,e){return t===e},t.prototype.isStartingWith=function(t,e){return this.isString(t)&&t.startsWith(e)},t.prototype.isString=function(t){return"string"==typeof t},t.prototype.isTruthy=function(t){return[1,"1",!0,"true"].includes(t)},t.prototype.isUrl=function(t){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(t).toLowerCase())},t.prototype.isUuid=function(t){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(t).toLowerCase())},t.prototype.is=function(t,e){if(void 0===e&&(e=[]),0===e.length)return!0;if("optional"===e[0]&&this.isOptional(t))return!0;for(var r=0;r= second;\n\t\tif (type === 'more' && ! equals) return first.getTime() > second;\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after another given date.\n\t *\n\t **/\n\tisAfter(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after or equal to another given date.\n\t *\n\t **/\n\tisAfterOrEqual(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an array.\n\t *\n\t **/\n\tisArray(value)\n\t{\n\t\treturn Array.isArray(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before another given date.\n\t *\n\t **/\n\tisBefore(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before or equal to another given date.\n\t *\n\t **/\n\tisBeforeOrEqual(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a boolean.\n\t *\n\t **/\n\tisBoolean(value)\n\t{\n\t\treturn [true, false].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a date object.\n\t *\n\t **/\n\tisDate(value)\n\t{\n\t\treturn value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is different to another given value.\n\t *\n\t **/\n\tisDifferent(value, different)\n\t{\n\t\treturn value !== different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value ends with another given value.\n\t *\n\t **/\n\tisEndingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.endsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid email address.\n\t *\n\t **/\n\tisEmail(value)\n\t{\n\t\treturn new RegExp('^\\\\S+@\\\\S+[\\\\.][0-9a-z]+$').test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is falsy.\n\t *\n\t **/\n\tisFalsy(value)\n\t{\n\t\treturn [0, '0', false, 'false'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is within the given array of options.\n\t *\n\t **/\n\tisIn(value, options)\n\t{\n\t\treturn options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an integer.\n\t *\n\t **/\n\tisInteger(value)\n\t{\n\t\treturn Number.isInteger(value) && parseInt(value).toString() === value.toString();\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a JSON string.\n\t *\n\t **/\n\tisJson(value)\n\t{\n\t\ttry {\n \treturn typeof JSON.parse(value) === 'object';\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given maximum limit.\n\t *\n\t **/\n\tisMaximum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) <= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given minimum limit.\n\t *\n\t **/\n\tisMinimum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) >= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is not within the given array of options.\n\t *\n\t **/\n\tisNotIn(value, options)\n\t{\n\t\treturn ! options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is numeric (an integer or a float).\n\t *\n\t **/\n\tisNumeric(value)\n\t{\n\t\treturn ! isNaN(parseFloat(value)) && isFinite(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is optional.\n\t *\n\t **/\n\tisOptional(value)\n\t{\n\t\treturn [null, undefined, ''].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value satisifies the given regular expression.\n\t *\n\t **/\n\tisRegexMatch(value, expression)\n\t{\n\t\treturn new RegExp(expression).test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is present.\n\t *\n\t **/\n\tisRequired(value)\n\t{\n\t\treturn ! this.isOptional(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is the same as another given value.\n\t *\n\t **/\n\tisSame(value, different)\n\t{\n\t\treturn value === different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value starts with another given value.\n\t *\n\t **/\n\tisStartingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.startsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a string.\n\t *\n\t **/\n\tisString(value)\n\t{\n\t\treturn typeof value === 'string';\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is truthy.\n\t *\n\t **/\n\tisTruthy(value)\n\t{\n\t\treturn [1, '1', true, 'true'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid URL.\n\t *\n\t **/\n\tisUrl(value)\n\t{\n\t\treturn 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_]*)?$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid UUID.\n\t *\n\t **/\n\tisUuid(value)\n\t{\n\t\treturn 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}$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine whether the given value meets the given rules.\n\t *\n\t **/\n\tis(value, rules = [])\n\t{\n\t\t// Check if no rules were specified\n\t\tif (rules.length === 0) return true;\n\n\t\t// Check for an optional value\n\t\tif (rules[0] === 'optional' && this.isOptional(value)) return true;\n\n\t\t// Iterate through the rules\n\t\tfor (let index = 0; index < rules.length; index++) {\n\n\t\t\t// Ignore optional rules\n\t\t\tif (rules[index] === 'optional') continue;\n\n\t\t\t// Determine the method to use\n\t\t\tlet rule = rules[index].split(':')[0][0].toUpperCase()\n\t\t\t\t\t + rules[index].split(':')[0].slice(1);\n\n\t\t\t// Validate the value against the method\n\t\t\tlet result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);\n\n\t\t\t// Check if the value failed validation\n\t\t\tif (! result) return rules[index].split(':')[0];\n\n\t\t}\n\n\t\t// Otherwise, the value is valid\n\t\treturn true;\n\t}\n\n}\n\n\n\n/**\n * Create an instance of the library.\n *\n **/\nwindow.Iodine = new Iodine();"],"names":["Iodine","_dateCompare","first","second","type","equals","this","isDate","isInteger","getTime","isAfter","value","after","isAfterOrEqual","isArray","Array","isBefore","before","isBeforeOrEqual","isBoolean","includes","Object","prototype","toString","call","isNaN","isDifferent","different","isEndingWith","sub","isString","endsWith","isEmail","RegExp","test","String","toLowerCase","isFalsy","isIn","options","Number","parseInt","isJson","JSON","parse","e","isMaximum","limit","length","parseFloat","isMinimum","isNotIn","isNumeric","isFinite","isOptional","undefined","isRegexMatch","expression","isRequired","isSame","isStartingWith","startsWith","isTruthy","isUrl","isUuid","is","rules","let","index","split","toUpperCase","slice","apply","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\t/**\n\t * Constructor.\n\t *\n\t **/\n\tconstructor()\n\t{\n\t\tthis.messages = this._defaultMessages();\n\t}\n\n\n\n\t/**\n\t * @internal.\n\t *\n\t **/\n\t_dateCompare(first, second, type, equals = false)\n\t{\n\t\tif (! this.isDate(first)) return false;\n\n\t\tif (! this.isDate(second) && ! this.isInteger(second)) return false;\n\n\t\tsecond = typeof second === 'number' ? second : second.getTime();\n\n\t\tif (type === 'less' && equals) return first.getTime() <= second;\n\t\tif (type === 'less' && ! equals) return first.getTime() < second;\n\t\tif (type === 'more' && equals) return first.getTime() >= second;\n\t\tif (type === 'more' && ! equals) return first.getTime() > second;\n\t}\n\n\n\n\t/**\n\t * @internal.\n\t *\n\t **/\n\t_defaultMessages()\n\t{\n\t\treturn {\n\t\t\tafter : `The date must be after: '[PARAM]'`,\n\t\t\tafterOrEqual : `The date must be after or equal to: '[PARAM]'`,\n\t\t\tarray : `Field must be an array`,\n\t\t\tbefore : `The date must be before: '[PARAM]'`,\n\t\t\tbeforeOrEqual : `The date must be before or equal to: '[PARAM]'`,\n\t\t\tboolean : `Field must be true or false`,\n\t\t\tdate : `Field must be a date`,\n\t\t\tdifferent : `Field must be different to '[PARAM]'`,\n\t\t\tendingWith : `Field must end with '[PARAM]'`,\n\t\t\temail : `Field must be a valid email address`,\n\t\t\tfalsy : `Field must be a falsy value (false, 'false', 0 or '0')`,\n\t\t\tin \t\t : `Field must be one of the following options: [PARAM]`,\n\t\t\tinteger : `Field must be an integer`,\n\t\t\tjson : `Field must be a parsable JSON object string`,\n\t\t\tmaximum : `Field must not be greater than '[PARAM]' in size or character length`,\n\t\t\tminimum : `Field must not be less than '[PARAM]' in size or character length`,\n\t\t\tnotIn : `Field must not be one of the following options: [PARAM]`,\n\t\t\tnumeric : `Field must be numeric`,\n\t\t\toptional : `Field is optional`,\n\t\t\tregexMatch : `Field must satisify the regular expression: [PARAM]`,\n\t\t\trequired : `Field must be present`,\n\t\t\tsame : `Field must be '[PARAM]'`,\n\t\t\tstartingWith : `Field must start with '[PARAM]'`,\n\t\t\tstring : `Field must be a string`,\n\t\t\ttruthy : `Field must be a truthy value (true, 'true', 1 or '1')`,\n\t\t\turl : `Field must be a valid url`,\n\t\t\tuuid : `Field must be a valid UUID`,\n\t\t};\n\t}\n\n\n\n\t/**\n\t * Retrieve an error message for the given rule.\n\t *\n\t **/\n\tgetErrorMessage(rule, arg = null)\n\t{\n\t\tlet key = rule.split(':')[0];\n\t\tlet param = arg || rule.split(':')[1];\n\n\t\tif (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n\t\t\tparam = new Date(parseInt(param)).toLocaleTimeString(undefined, {\n\t\t\t\tyear: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: 'numeric'\n\t\t\t});\n\t\t}\n\n\t\treturn param === undefined\n\t\t\t ? this.messages[key]\n\t\t\t : this.messages[key].replace(\"[PARAM]\", param);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after another given date.\n\t *\n\t **/\n\tisAfter(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after or equal to another given date.\n\t *\n\t **/\n\tisAfterOrEqual(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an array.\n\t *\n\t **/\n\tisArray(value)\n\t{\n\t\treturn Array.isArray(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before another given date.\n\t *\n\t **/\n\tisBefore(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before or equal to another given date.\n\t *\n\t **/\n\tisBeforeOrEqual(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a boolean.\n\t *\n\t **/\n\tisBoolean(value)\n\t{\n\t\treturn [true, false].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a date object.\n\t *\n\t **/\n\tisDate(value)\n\t{\n\t\treturn value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is different to another given value.\n\t *\n\t **/\n\tisDifferent(value, different)\n\t{\n\t\treturn value !== different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value ends with another given value.\n\t *\n\t **/\n\tisEndingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.endsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid email address.\n\t *\n\t **/\n\tisEmail(value)\n\t{\n\t\treturn new RegExp('^\\\\S+@\\\\S+[\\\\.][0-9a-z]+$').test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is falsy.\n\t *\n\t **/\n\tisFalsy(value)\n\t{\n\t\treturn [0, '0', false, 'false'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is within the given array of options.\n\t *\n\t **/\n\tisIn(value, options)\n\t{\n\t\toptions = typeof options === 'string'\n\t\t\t\t? options.split(\",\")\n\t\t\t\t: options;\n\n\t\treturn options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an integer.\n\t *\n\t **/\n\tisInteger(value)\n\t{\n\t\treturn Number.isInteger(value) && parseInt(value).toString() === value.toString();\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a JSON string.\n\t *\n\t **/\n\tisJson(value)\n\t{\n\t\ttry {\n \treturn typeof JSON.parse(value) === 'object';\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given maximum limit.\n\t *\n\t **/\n\tisMaximum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) <= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given minimum limit.\n\t *\n\t **/\n\tisMinimum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) >= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is not within the given array of options.\n\t *\n\t **/\n\tisNotIn(value, options)\n\t{\n\t\treturn ! this.isIn(value, options);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is numeric (an integer or a float).\n\t *\n\t **/\n\tisNumeric(value)\n\t{\n\t\treturn ! isNaN(parseFloat(value)) && isFinite(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is optional.\n\t *\n\t **/\n\tisOptional(value)\n\t{\n\t\treturn [null, undefined, ''].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value satisifies the given regular expression.\n\t *\n\t **/\n\tisRegexMatch(value, expression)\n\t{\n\t\treturn new RegExp(expression).test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is present.\n\t *\n\t **/\n\tisRequired(value)\n\t{\n\t\treturn ! this.isOptional(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is the same as another given value.\n\t *\n\t **/\n\tisSame(value, different)\n\t{\n\t\treturn value === different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value starts with another given value.\n\t *\n\t **/\n\tisStartingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.startsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a string.\n\t *\n\t **/\n\tisString(value)\n\t{\n\t\treturn typeof value === 'string';\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is truthy.\n\t *\n\t **/\n\tisTruthy(value)\n\t{\n\t\treturn [1, '1', true, 'true'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid URL.\n\t *\n\t **/\n\tisUrl(value)\n\t{\n\t\treturn 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_]*)?$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid UUID.\n\t *\n\t **/\n\tisUuid(value)\n\t{\n\t\treturn 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}$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine whether the given value meets the given rules.\n\t *\n\t **/\n\tis(value, rules = [])\n\t{\n\t\t// Check if no rules were specified\n\t\tif (rules.length === 0) return true;\n\n\t\t// Check for an optional value\n\t\tif (rules[0] === 'optional' && this.isOptional(value)) return true;\n\n\t\t// Iterate through the rules\n\t\tfor (let index = 0; index < rules.length; index++) {\n\n\t\t\t// Ignore optional rules\n\t\t\tif (rules[index] === 'optional') continue;\n\n\t\t\t// Determine the method to use\n\t\t\tlet rule = rules[index].split(':')[0][0].toUpperCase()\n\t\t\t\t\t + rules[index].split(':')[0].slice(1);\n\n\t\t\t// Validate the value against the method\n\t\t\tlet result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);\n\n\t\t\t// Check if the value failed validation\n\t\t\tif (! result) return rules[index];\n\n\t\t}\n\n\t\t// Otherwise, the value is valid\n\t\treturn true;\n\t}\n\n\n\n\t/**\n\t * Replace the default error messages with a new set.\n\t *\n\t **/\n\tsetErrorMessages(messages)\n\t{\n\t\tthis.messages = messages;\n\t}\n\n}\n\n\n\n/**\n * Create an instance of the library.\n *\n **/\nwindow.Iodine = new Iodine();"],"names":["Iodine","constructor","messages","this","_defaultMessages","_dateCompare","first","second","type","equals","isDate","isInteger","getTime","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endingWith","email","falsy","in","integer","json","maximum","minimum","notIn","numeric","optional","regexMatch","required","same","startingWith","string","truthy","url","uuid","getErrorMessage","rule","arg","key","split","param","includes","Date","parseInt","toLocaleTimeString","undefined","year","month","day","hour","minute","replace","isAfter","value","isAfterOrEqual","isArray","Array","isBefore","isBeforeOrEqual","isBoolean","Object","prototype","toString","call","isNaN","isDifferent","isEndingWith","sub","isString","endsWith","isEmail","RegExp","test","String","toLowerCase","isFalsy","isIn","options","Number","isJson","JSON","parse","e","isMaximum","limit","length","parseFloat","isMinimum","isNotIn","isNumeric","isFinite","isOptional","isRegexMatch","expression","isRequired","isSame","isStartingWith","startsWith","isTruthy","isUrl","isUuid","is","rules","let","index","toUpperCase","slice","apply","setErrorMessages","window"],"mappings":"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"}
\ No newline at end of file
diff --git a/dist/iodine.min.mjs b/dist/iodine.min.mjs
index e10b708..cd1e2b1 100644
--- a/dist/iodine.min.mjs
+++ b/dist/iodine.min.mjs
@@ -1,2 +1,2 @@
-var t=function(){};t.prototype._dateCompare=function(t,e,r,n){return void 0===n&&(n=!1),!!this.isDate(t)&&!(!this.isDate(e)&&!this.isInteger(e))&&(e="number"==typeof e?e:e.getTime(),"less"===r&&n?t.getTime()<=e:"less"!==r||n?"more"===r&&n?t.getTime()>=e:"more"!==r||n?void 0:t.getTime()>e:t.getTime()=e},t.prototype.isNotIn=function(t,e){return!e.includes(t)},t.prototype.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.prototype.isOptional=function(t){return[null,void 0,""].includes(t)},t.prototype.isRegexMatch=function(t,e){return new RegExp(e).test(String(t).toLowerCase())},t.prototype.isRequired=function(t){return!this.isOptional(t)},t.prototype.isSame=function(t,e){return t===e},t.prototype.isStartingWith=function(t,e){return this.isString(t)&&t.startsWith(e)},t.prototype.isString=function(t){return"string"==typeof t},t.prototype.isTruthy=function(t){return[1,"1",!0,"true"].includes(t)},t.prototype.isUrl=function(t){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(t).toLowerCase())},t.prototype.isUuid=function(t){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(t).toLowerCase())},t.prototype.is=function(t,e){if(void 0===e&&(e=[]),0===e.length)return!0;if("optional"===e[0]&&this.isOptional(t))return!0;for(var r=0;r=e:"more"!==r||i?void 0:t.getTime()>e:t.getTime()=e},t.prototype.isNotIn=function(t,e){return!this.isIn(t,e)},t.prototype.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.prototype.isOptional=function(t){return[null,void 0,""].includes(t)},t.prototype.isRegexMatch=function(t,e){return new RegExp(e).test(String(t).toLowerCase())},t.prototype.isRequired=function(t){return!this.isOptional(t)},t.prototype.isSame=function(t,e){return t===e},t.prototype.isStartingWith=function(t,e){return this.isString(t)&&t.startsWith(e)},t.prototype.isString=function(t){return"string"==typeof t},t.prototype.isTruthy=function(t){return[1,"1",!0,"true"].includes(t)},t.prototype.isUrl=function(t){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(t).toLowerCase())},t.prototype.isUuid=function(t){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(t).toLowerCase())},t.prototype.is=function(t,e){if(void 0===e&&(e=[]),0===e.length)return!0;if("optional"===e[0]&&this.isOptional(t))return!0;for(var r=0;r= second;\n\t\tif (type === 'more' && ! equals) return first.getTime() > second;\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after another given date.\n\t *\n\t **/\n\tisAfter(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after or equal to another given date.\n\t *\n\t **/\n\tisAfterOrEqual(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an array.\n\t *\n\t **/\n\tisArray(value)\n\t{\n\t\treturn Array.isArray(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before another given date.\n\t *\n\t **/\n\tisBefore(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before or equal to another given date.\n\t *\n\t **/\n\tisBeforeOrEqual(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a boolean.\n\t *\n\t **/\n\tisBoolean(value)\n\t{\n\t\treturn [true, false].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a date object.\n\t *\n\t **/\n\tisDate(value)\n\t{\n\t\treturn value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is different to another given value.\n\t *\n\t **/\n\tisDifferent(value, different)\n\t{\n\t\treturn value !== different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value ends with another given value.\n\t *\n\t **/\n\tisEndingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.endsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid email address.\n\t *\n\t **/\n\tisEmail(value)\n\t{\n\t\treturn new RegExp('^\\\\S+@\\\\S+[\\\\.][0-9a-z]+$').test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is falsy.\n\t *\n\t **/\n\tisFalsy(value)\n\t{\n\t\treturn [0, '0', false, 'false'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is within the given array of options.\n\t *\n\t **/\n\tisIn(value, options)\n\t{\n\t\treturn options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an integer.\n\t *\n\t **/\n\tisInteger(value)\n\t{\n\t\treturn Number.isInteger(value) && parseInt(value).toString() === value.toString();\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a JSON string.\n\t *\n\t **/\n\tisJson(value)\n\t{\n\t\ttry {\n \treturn typeof JSON.parse(value) === 'object';\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given maximum limit.\n\t *\n\t **/\n\tisMaximum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) <= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given minimum limit.\n\t *\n\t **/\n\tisMinimum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) >= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is not within the given array of options.\n\t *\n\t **/\n\tisNotIn(value, options)\n\t{\n\t\treturn ! options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is numeric (an integer or a float).\n\t *\n\t **/\n\tisNumeric(value)\n\t{\n\t\treturn ! isNaN(parseFloat(value)) && isFinite(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is optional.\n\t *\n\t **/\n\tisOptional(value)\n\t{\n\t\treturn [null, undefined, ''].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value satisifies the given regular expression.\n\t *\n\t **/\n\tisRegexMatch(value, expression)\n\t{\n\t\treturn new RegExp(expression).test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is present.\n\t *\n\t **/\n\tisRequired(value)\n\t{\n\t\treturn ! this.isOptional(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is the same as another given value.\n\t *\n\t **/\n\tisSame(value, different)\n\t{\n\t\treturn value === different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value starts with another given value.\n\t *\n\t **/\n\tisStartingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.startsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a string.\n\t *\n\t **/\n\tisString(value)\n\t{\n\t\treturn typeof value === 'string';\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is truthy.\n\t *\n\t **/\n\tisTruthy(value)\n\t{\n\t\treturn [1, '1', true, 'true'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid URL.\n\t *\n\t **/\n\tisUrl(value)\n\t{\n\t\treturn 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_]*)?$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid UUID.\n\t *\n\t **/\n\tisUuid(value)\n\t{\n\t\treturn 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}$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine whether the given value meets the given rules.\n\t *\n\t **/\n\tis(value, rules = [])\n\t{\n\t\t// Check if no rules were specified\n\t\tif (rules.length === 0) return true;\n\n\t\t// Check for an optional value\n\t\tif (rules[0] === 'optional' && this.isOptional(value)) return true;\n\n\t\t// Iterate through the rules\n\t\tfor (let index = 0; index < rules.length; index++) {\n\n\t\t\t// Ignore optional rules\n\t\t\tif (rules[index] === 'optional') continue;\n\n\t\t\t// Determine the method to use\n\t\t\tlet rule = rules[index].split(':')[0][0].toUpperCase()\n\t\t\t\t\t + rules[index].split(':')[0].slice(1);\n\n\t\t\t// Validate the value against the method\n\t\t\tlet result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);\n\n\t\t\t// Check if the value failed validation\n\t\t\tif (! result) return rules[index].split(':')[0];\n\n\t\t}\n\n\t\t// Otherwise, the value is valid\n\t\treturn true;\n\t}\n\n}\n\n\n\n/**\n * Create an instance of the library.\n *\n **/\nwindow.Iodine = new Iodine();"],"names":["Iodine","_dateCompare","first","second","type","equals","this","isDate","isInteger","getTime","isAfter","value","after","isAfterOrEqual","isArray","Array","isBefore","before","isBeforeOrEqual","isBoolean","includes","Object","prototype","toString","call","isNaN","isDifferent","different","isEndingWith","sub","isString","endsWith","isEmail","RegExp","test","String","toLowerCase","isFalsy","isIn","options","Number","parseInt","isJson","JSON","parse","e","isMaximum","limit","length","parseFloat","isMinimum","isNotIn","isNumeric","isFinite","isOptional","undefined","isRegexMatch","expression","isRequired","isSame","isStartingWith","startsWith","isTruthy","isUrl","isUuid","is","rules","let","index","split","toUpperCase","slice","apply","window"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"iodine.min.mjs","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\t/**\n\t * Constructor.\n\t *\n\t **/\n\tconstructor()\n\t{\n\t\tthis.messages = this._defaultMessages();\n\t}\n\n\n\n\t/**\n\t * @internal.\n\t *\n\t **/\n\t_dateCompare(first, second, type, equals = false)\n\t{\n\t\tif (! this.isDate(first)) return false;\n\n\t\tif (! this.isDate(second) && ! this.isInteger(second)) return false;\n\n\t\tsecond = typeof second === 'number' ? second : second.getTime();\n\n\t\tif (type === 'less' && equals) return first.getTime() <= second;\n\t\tif (type === 'less' && ! equals) return first.getTime() < second;\n\t\tif (type === 'more' && equals) return first.getTime() >= second;\n\t\tif (type === 'more' && ! equals) return first.getTime() > second;\n\t}\n\n\n\n\t/**\n\t * @internal.\n\t *\n\t **/\n\t_defaultMessages()\n\t{\n\t\treturn {\n\t\t\tafter : `The date must be after: '[PARAM]'`,\n\t\t\tafterOrEqual : `The date must be after or equal to: '[PARAM]'`,\n\t\t\tarray : `Field must be an array`,\n\t\t\tbefore : `The date must be before: '[PARAM]'`,\n\t\t\tbeforeOrEqual : `The date must be before or equal to: '[PARAM]'`,\n\t\t\tboolean : `Field must be true or false`,\n\t\t\tdate : `Field must be a date`,\n\t\t\tdifferent : `Field must be different to '[PARAM]'`,\n\t\t\tendingWith : `Field must end with '[PARAM]'`,\n\t\t\temail : `Field must be a valid email address`,\n\t\t\tfalsy : `Field must be a falsy value (false, 'false', 0 or '0')`,\n\t\t\tin \t\t : `Field must be one of the following options: [PARAM]`,\n\t\t\tinteger : `Field must be an integer`,\n\t\t\tjson : `Field must be a parsable JSON object string`,\n\t\t\tmaximum : `Field must not be greater than '[PARAM]' in size or character length`,\n\t\t\tminimum : `Field must not be less than '[PARAM]' in size or character length`,\n\t\t\tnotIn : `Field must not be one of the following options: [PARAM]`,\n\t\t\tnumeric : `Field must be numeric`,\n\t\t\toptional : `Field is optional`,\n\t\t\tregexMatch : `Field must satisify the regular expression: [PARAM]`,\n\t\t\trequired : `Field must be present`,\n\t\t\tsame : `Field must be '[PARAM]'`,\n\t\t\tstartingWith : `Field must start with '[PARAM]'`,\n\t\t\tstring : `Field must be a string`,\n\t\t\ttruthy : `Field must be a truthy value (true, 'true', 1 or '1')`,\n\t\t\turl : `Field must be a valid url`,\n\t\t\tuuid : `Field must be a valid UUID`,\n\t\t};\n\t}\n\n\n\n\t/**\n\t * Retrieve an error message for the given rule.\n\t *\n\t **/\n\tgetErrorMessage(rule, arg = null)\n\t{\n\t\tlet key = rule.split(':')[0];\n\t\tlet param = arg || rule.split(':')[1];\n\n\t\tif (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n\t\t\tparam = new Date(parseInt(param)).toLocaleTimeString(undefined, {\n\t\t\t\tyear: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: 'numeric'\n\t\t\t});\n\t\t}\n\n\t\treturn param === undefined\n\t\t\t ? this.messages[key]\n\t\t\t : this.messages[key].replace(\"[PARAM]\", param);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after another given date.\n\t *\n\t **/\n\tisAfter(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after or equal to another given date.\n\t *\n\t **/\n\tisAfterOrEqual(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an array.\n\t *\n\t **/\n\tisArray(value)\n\t{\n\t\treturn Array.isArray(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before another given date.\n\t *\n\t **/\n\tisBefore(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before or equal to another given date.\n\t *\n\t **/\n\tisBeforeOrEqual(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a boolean.\n\t *\n\t **/\n\tisBoolean(value)\n\t{\n\t\treturn [true, false].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a date object.\n\t *\n\t **/\n\tisDate(value)\n\t{\n\t\treturn value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is different to another given value.\n\t *\n\t **/\n\tisDifferent(value, different)\n\t{\n\t\treturn value !== different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value ends with another given value.\n\t *\n\t **/\n\tisEndingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.endsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid email address.\n\t *\n\t **/\n\tisEmail(value)\n\t{\n\t\treturn new RegExp('^\\\\S+@\\\\S+[\\\\.][0-9a-z]+$').test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is falsy.\n\t *\n\t **/\n\tisFalsy(value)\n\t{\n\t\treturn [0, '0', false, 'false'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is within the given array of options.\n\t *\n\t **/\n\tisIn(value, options)\n\t{\n\t\toptions = typeof options === 'string'\n\t\t\t\t? options.split(\",\")\n\t\t\t\t: options;\n\n\t\treturn options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an integer.\n\t *\n\t **/\n\tisInteger(value)\n\t{\n\t\treturn Number.isInteger(value) && parseInt(value).toString() === value.toString();\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a JSON string.\n\t *\n\t **/\n\tisJson(value)\n\t{\n\t\ttry {\n \treturn typeof JSON.parse(value) === 'object';\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given maximum limit.\n\t *\n\t **/\n\tisMaximum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) <= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given minimum limit.\n\t *\n\t **/\n\tisMinimum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) >= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is not within the given array of options.\n\t *\n\t **/\n\tisNotIn(value, options)\n\t{\n\t\treturn ! this.isIn(value, options);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is numeric (an integer or a float).\n\t *\n\t **/\n\tisNumeric(value)\n\t{\n\t\treturn ! isNaN(parseFloat(value)) && isFinite(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is optional.\n\t *\n\t **/\n\tisOptional(value)\n\t{\n\t\treturn [null, undefined, ''].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value satisifies the given regular expression.\n\t *\n\t **/\n\tisRegexMatch(value, expression)\n\t{\n\t\treturn new RegExp(expression).test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is present.\n\t *\n\t **/\n\tisRequired(value)\n\t{\n\t\treturn ! this.isOptional(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is the same as another given value.\n\t *\n\t **/\n\tisSame(value, different)\n\t{\n\t\treturn value === different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value starts with another given value.\n\t *\n\t **/\n\tisStartingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.startsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a string.\n\t *\n\t **/\n\tisString(value)\n\t{\n\t\treturn typeof value === 'string';\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is truthy.\n\t *\n\t **/\n\tisTruthy(value)\n\t{\n\t\treturn [1, '1', true, 'true'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid URL.\n\t *\n\t **/\n\tisUrl(value)\n\t{\n\t\treturn 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_]*)?$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid UUID.\n\t *\n\t **/\n\tisUuid(value)\n\t{\n\t\treturn 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}$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine whether the given value meets the given rules.\n\t *\n\t **/\n\tis(value, rules = [])\n\t{\n\t\t// Check if no rules were specified\n\t\tif (rules.length === 0) return true;\n\n\t\t// Check for an optional value\n\t\tif (rules[0] === 'optional' && this.isOptional(value)) return true;\n\n\t\t// Iterate through the rules\n\t\tfor (let index = 0; index < rules.length; index++) {\n\n\t\t\t// Ignore optional rules\n\t\t\tif (rules[index] === 'optional') continue;\n\n\t\t\t// Determine the method to use\n\t\t\tlet rule = rules[index].split(':')[0][0].toUpperCase()\n\t\t\t\t\t + rules[index].split(':')[0].slice(1);\n\n\t\t\t// Validate the value against the method\n\t\t\tlet result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);\n\n\t\t\t// Check if the value failed validation\n\t\t\tif (! result) return rules[index];\n\n\t\t}\n\n\t\t// Otherwise, the value is valid\n\t\treturn true;\n\t}\n\n\n\n\t/**\n\t * Replace the default error messages with a new set.\n\t *\n\t **/\n\tsetErrorMessages(messages)\n\t{\n\t\tthis.messages = messages;\n\t}\n\n}\n\n\n\n/**\n * Create an instance of the library.\n *\n **/\nwindow.Iodine = new Iodine();"],"names":["Iodine","constructor","messages","this","_defaultMessages","_dateCompare","first","second","type","equals","isDate","isInteger","getTime","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endingWith","email","falsy","in","integer","json","maximum","minimum","notIn","numeric","optional","regexMatch","required","same","startingWith","string","truthy","url","uuid","getErrorMessage","rule","arg","key","split","param","includes","Date","parseInt","toLocaleTimeString","undefined","year","month","day","hour","minute","replace","isAfter","value","isAfterOrEqual","isArray","Array","isBefore","isBeforeOrEqual","isBoolean","Object","prototype","toString","call","isNaN","isDifferent","isEndingWith","sub","isString","endsWith","isEmail","RegExp","test","String","toLowerCase","isFalsy","isIn","options","Number","isJson","JSON","parse","e","isMaximum","limit","length","parseFloat","isMinimum","isNotIn","isNumeric","isFinite","isOptional","isRegexMatch","expression","isRequired","isSame","isStartingWith","startsWith","isTruthy","isUrl","isUuid","is","rules","let","index","toUpperCase","slice","apply","setErrorMessages","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 c25a438..1c0c1be 100644
--- a/dist/iodine.min.umd.js
+++ b/dist/iodine.min.umd.js
@@ -1,2 +1,2 @@
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.iodine=e()}(this,function(){var t=function(){};return t.prototype._dateCompare=function(t,e,r,n){return void 0===n&&(n=!1),!!this.isDate(t)&&!(!this.isDate(e)&&!this.isInteger(e))&&(e="number"==typeof e?e:e.getTime(),"less"===r&&n?t.getTime()<=e:"less"!==r||n?"more"===r&&n?t.getTime()>=e:"more"!==r||n?void 0:t.getTime()>e:t.getTime()=e},t.prototype.isNotIn=function(t,e){return!e.includes(t)},t.prototype.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.prototype.isOptional=function(t){return[null,void 0,""].includes(t)},t.prototype.isRegexMatch=function(t,e){return new RegExp(e).test(String(t).toLowerCase())},t.prototype.isRequired=function(t){return!this.isOptional(t)},t.prototype.isSame=function(t,e){return t===e},t.prototype.isStartingWith=function(t,e){return this.isString(t)&&t.startsWith(e)},t.prototype.isString=function(t){return"string"==typeof t},t.prototype.isTruthy=function(t){return[1,"1",!0,"true"].includes(t)},t.prototype.isUrl=function(t){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(t).toLowerCase())},t.prototype.isUuid=function(t){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(t).toLowerCase())},t.prototype.is=function(t,e){if(void 0===e&&(e=[]),0===e.length)return!0;if("optional"===e[0]&&this.isOptional(t))return!0;for(var r=0;r=e:"more"!==r||i?void 0:t.getTime()>e:t.getTime()=e},t.prototype.isNotIn=function(t,e){return!this.isIn(t,e)},t.prototype.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.prototype.isOptional=function(t){return[null,void 0,""].includes(t)},t.prototype.isRegexMatch=function(t,e){return new RegExp(e).test(String(t).toLowerCase())},t.prototype.isRequired=function(t){return!this.isOptional(t)},t.prototype.isSame=function(t,e){return t===e},t.prototype.isStartingWith=function(t,e){return this.isString(t)&&t.startsWith(e)},t.prototype.isString=function(t){return"string"==typeof t},t.prototype.isTruthy=function(t){return[1,"1",!0,"true"].includes(t)},t.prototype.isUrl=function(t){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(t).toLowerCase())},t.prototype.isUuid=function(t){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(t).toLowerCase())},t.prototype.is=function(t,e){if(void 0===e&&(e=[]),0===e.length)return!0;if("optional"===e[0]&&this.isOptional(t))return!0;for(var r=0;r= second;\n\t\tif (type === 'more' && ! equals) return first.getTime() > second;\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after another given date.\n\t *\n\t **/\n\tisAfter(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after or equal to another given date.\n\t *\n\t **/\n\tisAfterOrEqual(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an array.\n\t *\n\t **/\n\tisArray(value)\n\t{\n\t\treturn Array.isArray(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before another given date.\n\t *\n\t **/\n\tisBefore(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before or equal to another given date.\n\t *\n\t **/\n\tisBeforeOrEqual(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a boolean.\n\t *\n\t **/\n\tisBoolean(value)\n\t{\n\t\treturn [true, false].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a date object.\n\t *\n\t **/\n\tisDate(value)\n\t{\n\t\treturn value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is different to another given value.\n\t *\n\t **/\n\tisDifferent(value, different)\n\t{\n\t\treturn value !== different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value ends with another given value.\n\t *\n\t **/\n\tisEndingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.endsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid email address.\n\t *\n\t **/\n\tisEmail(value)\n\t{\n\t\treturn new RegExp('^\\\\S+@\\\\S+[\\\\.][0-9a-z]+$').test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is falsy.\n\t *\n\t **/\n\tisFalsy(value)\n\t{\n\t\treturn [0, '0', false, 'false'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is within the given array of options.\n\t *\n\t **/\n\tisIn(value, options)\n\t{\n\t\treturn options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an integer.\n\t *\n\t **/\n\tisInteger(value)\n\t{\n\t\treturn Number.isInteger(value) && parseInt(value).toString() === value.toString();\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a JSON string.\n\t *\n\t **/\n\tisJson(value)\n\t{\n\t\ttry {\n \treturn typeof JSON.parse(value) === 'object';\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given maximum limit.\n\t *\n\t **/\n\tisMaximum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) <= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given minimum limit.\n\t *\n\t **/\n\tisMinimum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) >= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is not within the given array of options.\n\t *\n\t **/\n\tisNotIn(value, options)\n\t{\n\t\treturn ! options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is numeric (an integer or a float).\n\t *\n\t **/\n\tisNumeric(value)\n\t{\n\t\treturn ! isNaN(parseFloat(value)) && isFinite(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is optional.\n\t *\n\t **/\n\tisOptional(value)\n\t{\n\t\treturn [null, undefined, ''].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value satisifies the given regular expression.\n\t *\n\t **/\n\tisRegexMatch(value, expression)\n\t{\n\t\treturn new RegExp(expression).test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is present.\n\t *\n\t **/\n\tisRequired(value)\n\t{\n\t\treturn ! this.isOptional(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is the same as another given value.\n\t *\n\t **/\n\tisSame(value, different)\n\t{\n\t\treturn value === different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value starts with another given value.\n\t *\n\t **/\n\tisStartingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.startsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a string.\n\t *\n\t **/\n\tisString(value)\n\t{\n\t\treturn typeof value === 'string';\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is truthy.\n\t *\n\t **/\n\tisTruthy(value)\n\t{\n\t\treturn [1, '1', true, 'true'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid URL.\n\t *\n\t **/\n\tisUrl(value)\n\t{\n\t\treturn 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_]*)?$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid UUID.\n\t *\n\t **/\n\tisUuid(value)\n\t{\n\t\treturn 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}$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine whether the given value meets the given rules.\n\t *\n\t **/\n\tis(value, rules = [])\n\t{\n\t\t// Check if no rules were specified\n\t\tif (rules.length === 0) return true;\n\n\t\t// Check for an optional value\n\t\tif (rules[0] === 'optional' && this.isOptional(value)) return true;\n\n\t\t// Iterate through the rules\n\t\tfor (let index = 0; index < rules.length; index++) {\n\n\t\t\t// Ignore optional rules\n\t\t\tif (rules[index] === 'optional') continue;\n\n\t\t\t// Determine the method to use\n\t\t\tlet rule = rules[index].split(':')[0][0].toUpperCase()\n\t\t\t\t\t + rules[index].split(':')[0].slice(1);\n\n\t\t\t// Validate the value against the method\n\t\t\tlet result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);\n\n\t\t\t// Check if the value failed validation\n\t\t\tif (! result) return rules[index].split(':')[0];\n\n\t\t}\n\n\t\t// Otherwise, the value is valid\n\t\treturn true;\n\t}\n\n}\n\n\n\n/**\n * Create an instance of the library.\n *\n **/\nwindow.Iodine = new Iodine();"],"names":["Iodine","_dateCompare","first","second","type","equals","this","isDate","isInteger","getTime","isAfter","value","after","isAfterOrEqual","isArray","Array","isBefore","before","isBeforeOrEqual","isBoolean","includes","Object","prototype","toString","call","isNaN","isDifferent","different","isEndingWith","sub","isString","endsWith","isEmail","RegExp","test","String","toLowerCase","isFalsy","isIn","options","Number","parseInt","isJson","JSON","parse","e","isMaximum","limit","length","parseFloat","isMinimum","isNotIn","isNumeric","isFinite","isOptional","undefined","isRegexMatch","expression","isRequired","isSame","isStartingWith","startsWith","isTruthy","isUrl","isUuid","is","rules","let","index","split","toUpperCase","slice","apply","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\t/**\n\t * Constructor.\n\t *\n\t **/\n\tconstructor()\n\t{\n\t\tthis.messages = this._defaultMessages();\n\t}\n\n\n\n\t/**\n\t * @internal.\n\t *\n\t **/\n\t_dateCompare(first, second, type, equals = false)\n\t{\n\t\tif (! this.isDate(first)) return false;\n\n\t\tif (! this.isDate(second) && ! this.isInteger(second)) return false;\n\n\t\tsecond = typeof second === 'number' ? second : second.getTime();\n\n\t\tif (type === 'less' && equals) return first.getTime() <= second;\n\t\tif (type === 'less' && ! equals) return first.getTime() < second;\n\t\tif (type === 'more' && equals) return first.getTime() >= second;\n\t\tif (type === 'more' && ! equals) return first.getTime() > second;\n\t}\n\n\n\n\t/**\n\t * @internal.\n\t *\n\t **/\n\t_defaultMessages()\n\t{\n\t\treturn {\n\t\t\tafter : `The date must be after: '[PARAM]'`,\n\t\t\tafterOrEqual : `The date must be after or equal to: '[PARAM]'`,\n\t\t\tarray : `Field must be an array`,\n\t\t\tbefore : `The date must be before: '[PARAM]'`,\n\t\t\tbeforeOrEqual : `The date must be before or equal to: '[PARAM]'`,\n\t\t\tboolean : `Field must be true or false`,\n\t\t\tdate : `Field must be a date`,\n\t\t\tdifferent : `Field must be different to '[PARAM]'`,\n\t\t\tendingWith : `Field must end with '[PARAM]'`,\n\t\t\temail : `Field must be a valid email address`,\n\t\t\tfalsy : `Field must be a falsy value (false, 'false', 0 or '0')`,\n\t\t\tin \t\t : `Field must be one of the following options: [PARAM]`,\n\t\t\tinteger : `Field must be an integer`,\n\t\t\tjson : `Field must be a parsable JSON object string`,\n\t\t\tmaximum : `Field must not be greater than '[PARAM]' in size or character length`,\n\t\t\tminimum : `Field must not be less than '[PARAM]' in size or character length`,\n\t\t\tnotIn : `Field must not be one of the following options: [PARAM]`,\n\t\t\tnumeric : `Field must be numeric`,\n\t\t\toptional : `Field is optional`,\n\t\t\tregexMatch : `Field must satisify the regular expression: [PARAM]`,\n\t\t\trequired : `Field must be present`,\n\t\t\tsame : `Field must be '[PARAM]'`,\n\t\t\tstartingWith : `Field must start with '[PARAM]'`,\n\t\t\tstring : `Field must be a string`,\n\t\t\ttruthy : `Field must be a truthy value (true, 'true', 1 or '1')`,\n\t\t\turl : `Field must be a valid url`,\n\t\t\tuuid : `Field must be a valid UUID`,\n\t\t};\n\t}\n\n\n\n\t/**\n\t * Retrieve an error message for the given rule.\n\t *\n\t **/\n\tgetErrorMessage(rule, arg = null)\n\t{\n\t\tlet key = rule.split(':')[0];\n\t\tlet param = arg || rule.split(':')[1];\n\n\t\tif (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {\n\t\t\tparam = new Date(parseInt(param)).toLocaleTimeString(undefined, {\n\t\t\t\tyear: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: 'numeric'\n\t\t\t});\n\t\t}\n\n\t\treturn param === undefined\n\t\t\t ? this.messages[key]\n\t\t\t : this.messages[key].replace(\"[PARAM]\", param);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after another given date.\n\t *\n\t **/\n\tisAfter(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is after or equal to another given date.\n\t *\n\t **/\n\tisAfterOrEqual(value, after)\n\t{\n\t\treturn this._dateCompare(value, after, 'more', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an array.\n\t *\n\t **/\n\tisArray(value)\n\t{\n\t\treturn Array.isArray(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before another given date.\n\t *\n\t **/\n\tisBefore(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', false);\n\t}\n\n\n\n\t/**\n\t * Determine if the given date is before or equal to another given date.\n\t *\n\t **/\n\tisBeforeOrEqual(value, before)\n\t{\n\t\treturn this._dateCompare(value, before, 'less', true);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a boolean.\n\t *\n\t **/\n\tisBoolean(value)\n\t{\n\t\treturn [true, false].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a date object.\n\t *\n\t **/\n\tisDate(value)\n\t{\n\t\treturn value && Object.prototype.toString.call(value) === '[object Date]' && ! isNaN(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is different to another given value.\n\t *\n\t **/\n\tisDifferent(value, different)\n\t{\n\t\treturn value !== different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value ends with another given value.\n\t *\n\t **/\n\tisEndingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.endsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid email address.\n\t *\n\t **/\n\tisEmail(value)\n\t{\n\t\treturn new RegExp('^\\\\S+@\\\\S+[\\\\.][0-9a-z]+$').test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is falsy.\n\t *\n\t **/\n\tisFalsy(value)\n\t{\n\t\treturn [0, '0', false, 'false'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is within the given array of options.\n\t *\n\t **/\n\tisIn(value, options)\n\t{\n\t\toptions = typeof options === 'string'\n\t\t\t\t? options.split(\",\")\n\t\t\t\t: options;\n\n\t\treturn options.includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is an integer.\n\t *\n\t **/\n\tisInteger(value)\n\t{\n\t\treturn Number.isInteger(value) && parseInt(value).toString() === value.toString();\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a JSON string.\n\t *\n\t **/\n\tisJson(value)\n\t{\n\t\ttry {\n \treturn typeof JSON.parse(value) === 'object';\n\t\t} catch (e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given maximum limit.\n\t *\n\t **/\n\tisMaximum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) <= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value meets the given minimum limit.\n\t *\n\t **/\n\tisMinimum(value, limit)\n\t{\n\t\tvalue = typeof value === 'string' ? value.length : value;\n\n\t\treturn parseFloat(value) >= limit;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is not within the given array of options.\n\t *\n\t **/\n\tisNotIn(value, options)\n\t{\n\t\treturn ! this.isIn(value, options);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is numeric (an integer or a float).\n\t *\n\t **/\n\tisNumeric(value)\n\t{\n\t\treturn ! isNaN(parseFloat(value)) && isFinite(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is optional.\n\t *\n\t **/\n\tisOptional(value)\n\t{\n\t\treturn [null, undefined, ''].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value satisifies the given regular expression.\n\t *\n\t **/\n\tisRegexMatch(value, expression)\n\t{\n\t\treturn new RegExp(expression).test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is present.\n\t *\n\t **/\n\tisRequired(value)\n\t{\n\t\treturn ! this.isOptional(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is the same as another given value.\n\t *\n\t **/\n\tisSame(value, different)\n\t{\n\t\treturn value === different;\n\t}\n\n\n\n\t/**\n\t * Determine if the given value starts with another given value.\n\t *\n\t **/\n\tisStartingWith(value, sub)\n\t{\n\t\treturn this.isString(value) && value.startsWith(sub);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a string.\n\t *\n\t **/\n\tisString(value)\n\t{\n\t\treturn typeof value === 'string';\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is truthy.\n\t *\n\t **/\n\tisTruthy(value)\n\t{\n\t\treturn [1, '1', true, 'true'].includes(value);\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid URL.\n\t *\n\t **/\n\tisUrl(value)\n\t{\n\t\treturn 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_]*)?$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine if the given value is a valid UUID.\n\t *\n\t **/\n\tisUuid(value)\n\t{\n\t\treturn 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}$')\n\t\t\t .test(String(value).toLowerCase());\n\t}\n\n\n\n\t/**\n\t * Determine whether the given value meets the given rules.\n\t *\n\t **/\n\tis(value, rules = [])\n\t{\n\t\t// Check if no rules were specified\n\t\tif (rules.length === 0) return true;\n\n\t\t// Check for an optional value\n\t\tif (rules[0] === 'optional' && this.isOptional(value)) return true;\n\n\t\t// Iterate through the rules\n\t\tfor (let index = 0; index < rules.length; index++) {\n\n\t\t\t// Ignore optional rules\n\t\t\tif (rules[index] === 'optional') continue;\n\n\t\t\t// Determine the method to use\n\t\t\tlet rule = rules[index].split(':')[0][0].toUpperCase()\n\t\t\t\t\t + rules[index].split(':')[0].slice(1);\n\n\t\t\t// Validate the value against the method\n\t\t\tlet result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);\n\n\t\t\t// Check if the value failed validation\n\t\t\tif (! result) return rules[index];\n\n\t\t}\n\n\t\t// Otherwise, the value is valid\n\t\treturn true;\n\t}\n\n\n\n\t/**\n\t * Replace the default error messages with a new set.\n\t *\n\t **/\n\tsetErrorMessages(messages)\n\t{\n\t\tthis.messages = messages;\n\t}\n\n}\n\n\n\n/**\n * Create an instance of the library.\n *\n **/\nwindow.Iodine = new Iodine();"],"names":["Iodine","constructor","messages","this","_defaultMessages","_dateCompare","first","second","type","equals","isDate","isInteger","getTime","after","afterOrEqual","array","before","beforeOrEqual","boolean","date","different","endingWith","email","falsy","in","integer","json","maximum","minimum","notIn","numeric","optional","regexMatch","required","same","startingWith","string","truthy","url","uuid","getErrorMessage","rule","arg","key","split","param","includes","Date","parseInt","toLocaleTimeString","undefined","year","month","day","hour","minute","replace","isAfter","value","isAfterOrEqual","isArray","Array","isBefore","isBeforeOrEqual","isBoolean","Object","prototype","toString","call","isNaN","isDifferent","isEndingWith","sub","isString","endsWith","isEmail","RegExp","test","String","toLowerCase","isFalsy","isIn","options","Number","isJson","JSON","parse","e","isMaximum","limit","length","parseFloat","isMinimum","isNotIn","isNumeric","isFinite","isOptional","isRegexMatch","expression","isRequired","isSame","isStartingWith","startsWith","isTruthy","isUrl","isUuid","is","rules","let","index","toUpperCase","slice","apply","setErrorMessages","window"],"mappings":"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"}
\ No newline at end of file
diff --git a/resources/size.svg b/resources/size.svg
index 4985920..99c73f1 100644
--- a/resources/size.svg
+++ b/resources/size.svg
@@ -14,7 +14,7 @@
size
size
- 1.1 kB
- 1.1 kB
+ 1.6 kB
+ 1.6 kB
\ No newline at end of file
diff --git a/resources/version.svg b/resources/version.svg
index 901c7e4..edd22e6 100644
--- a/resources/version.svg
+++ b/resources/version.svg
@@ -14,7 +14,7 @@
stable
stable
- v1.0.0
- v1.0.0
+ v2.0.0
+ v2.0.0
\ No newline at end of file
diff --git a/src/iodine.js b/src/iodine.js
index 9f2c9d1..ab50e17 100644
--- a/src/iodine.js
+++ b/src/iodine.js
@@ -10,6 +10,17 @@
export default class Iodine
{
+ /**
+ * Constructor.
+ *
+ **/
+ constructor()
+ {
+ this.messages = this._defaultMessages();
+ }
+
+
+
/**
* @internal.
*
@@ -30,6 +41,67 @@ export default class Iodine
+ /**
+ * @internal.
+ *
+ **/
+ _defaultMessages()
+ {
+ return {
+ 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]'`,
+ endingWith : `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`,
+ maximum : `Field must not be greater than '[PARAM]' in size or character length`,
+ minimum : `Field must not be less than '[PARAM]' in size or 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]'`,
+ startingWith : `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`,
+ };
+ }
+
+
+
+ /**
+ * Retrieve an error message for the given rule.
+ *
+ **/
+ getErrorMessage(rule, arg = null)
+ {
+ let key = rule.split(':')[0];
+ let param = arg || rule.split(':')[1];
+
+ if (['after', 'afterOrEqual', 'before', 'beforeOrEqual'].includes(key)) {
+ param = new Date(parseInt(param)).toLocaleTimeString(undefined, {
+ year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: 'numeric'
+ });
+ }
+
+ return param === undefined
+ ? this.messages[key]
+ : this.messages[key].replace("[PARAM]", param);
+ }
+
+
+
/**
* Determine if the given date is after another given date.
*
@@ -157,6 +229,10 @@ export default class Iodine
**/
isIn(value, options)
{
+ options = typeof options === 'string'
+ ? options.split(",")
+ : options;
+
return options.includes(value);
}
@@ -220,7 +296,7 @@ export default class Iodine
**/
isNotIn(value, options)
{
- return ! options.includes(value);
+ return ! this.isIn(value, options);
}
@@ -363,7 +439,7 @@ export default class Iodine
let result = this[`is${rule}`].apply(this, [value, rules[index].split(':')[1]]);
// Check if the value failed validation
- if (! result) return rules[index].split(':')[0];
+ if (! result) return rules[index];
}
@@ -371,6 +447,17 @@ export default class Iodine
return true;
}
+
+
+ /**
+ * Replace the default error messages with a new set.
+ *
+ **/
+ setErrorMessages(messages)
+ {
+ this.messages = messages;
+ }
+
}
diff --git a/tests/test.js b/tests/test.js
index 79c4928..0e0654e 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -173,7 +173,9 @@ test('it validates falsy values', () => {
*
**/
test('it validates in list values', () => {
+ expect(Iodine.isIn('a', 'a,b,c')).toBe(true);
expect(Iodine.isIn('a', ['a', 'b', 'c'])).toBe(true);
+ expect(Iodine.isIn('d', 'a,b,c')).toBe(false);
expect(Iodine.isIn('d', ['a', 'b', 'c'])).toBe(false);
});
@@ -239,7 +241,9 @@ test('it validates minimum values', () => {
*
**/
test('it validates not in list values', () => {
+ expect(Iodine.isNotIn('d', 'a,b,c')).toBe(true);
expect(Iodine.isNotIn('d', ['a', 'b', 'c'])).toBe(true);
+ expect(Iodine.isNotIn('a', 'a,b,c')).toBe(false);
expect(Iodine.isNotIn('a', ['a', 'b', 'c'])).toBe(false);
});
@@ -380,9 +384,37 @@ test('it validates UUID values', () => {
**/
test('it validates values against multiple rules', () => {
expect(Iodine.is('5', ['required', 'string', 'minimum:1', 'maximum:5'])).toBe(true);
- expect(Iodine.is(5, ['required', 'integer', 'minimum:7', 'maximum:10'])).toBe('minimum');
- expect(Iodine.is(5, ['optional', 'integer', 'minimum:7', 'maximum:10'])).toBe('minimum');
+ expect(Iodine.is(5, ['required', 'integer', 'minimum:7', 'maximum:10'])).toBe('minimum:7');
+ expect(Iodine.is(5, ['optional', 'integer', 'minimum:7', 'maximum:10'])).toBe('minimum:7');
expect(Iodine.is('', ['optional', 'integer', 'minimum:7', 'maximum:10'])).toBe(true);
expect(Iodine.is(null, ['optional', 'integer', 'minimum:7', 'maximum:10'])).toBe(true);
expect(Iodine.is(undefined, ['optional', 'integer', 'minimum:7', 'maximum:10'])).toBe(true);
+});
+
+
+
+/**
+ * Confirm that the 'getErrorMessage' method works correctly.
+ *
+ **/
+test('it retrieves formatted error messages for rules', () => {
+ let time = Date.UTC(2020, 4, 2, 3, 17, 0);
+ expect(Iodine.getErrorMessage('array')).toBe('Field must be an array');
+ expect(Iodine.getErrorMessage('endingWith:world')).toBe(`Field must end with 'world'`);
+ expect(Iodine.getErrorMessage('endingWith', 'world')).toBe(`Field must end with 'world'`);
+ expect(Iodine.getErrorMessage(`after:${time}`)).toBe(`The date must be after: '2 May 2020, 04:17'`);
+ expect(Iodine.getErrorMessage(`after`, time)).toBe(`The date must be after: '2 May 2020, 04:17'`);
+});
+
+
+
+/**
+ * Confirm that the default error messages can be replaced.
+ *
+ **/
+test('it can replace the default error messages', () => {
+ Iodine.setErrorMessages({ array : 'Hello world', endingWith : 'Hello, [PARAM]' })
+ expect(Iodine.getErrorMessage('array')).toBe('Hello world');
+ expect(Iodine.getErrorMessage('endingWith:John')).toBe('Hello, John');
+ expect(Iodine.getErrorMessage('endingWith', "John")).toBe('Hello, John');
});
\ No newline at end of file