{"version":3,"sources":["bouncer.js"],"names":["root","define","amd","factory","exports","module","Bouncer","global","window","defaults","this","fieldClass","url","date","time","month","errorPrefix","patterns","messageHide","messageHideCustom","messageAfterField","messageMissingCustom","messagePatternCustom","messageOutOfRangeCustom","messageWrongLengthCustom","messageTarget","checkbox","default","patternMismatch","email","number","color","radio","outOfRange","over","under","wrongLength","fallback","disableSubmit","emitEvents","forEach","arr","callback","merged","key","extend","arguments","obj","hasOwnProperty","Object","prototype","toString","call","emitEvent","elem","querySelectorAll","selector","setAttribute","CustomEvent","event","type","bubbles","detail","details","dispatchEvent","runValidations","field","settings","missingValue","filter","length","hasAttribute","checked","value","Array","form","escapeCharacters","name","btn","pattern","min","getAttribute","num","parseFloat","max","match","id","index","String","codeUnit","string","result","firstCodeUnit","charCodeAt","InvalidCharacterError","charAt","getFieldID","create","fieldPrefix","Math","floor","random","getErrorLocation","target","createError","custom","document","error","errorClass","messages","replace","group","fieldTarget","nextSibling","closest","querySelector","hideMessage","location","firstChild","parentNode","appendChild","createTextNode","insertBefore","test","addErrorAttributes","removeAttributes","add","classList","remove","removeAttribute","removeError","removeChild","removeErrorAttributes","errors","showErrorAttributes","button","showError","customValidations","textContent","msg","isValid","validate","publicAPIs","options","disabled","readOnly","_settings","valid","hasErrors","validations","removeEventListener","blurHandler","matches","addEventListener","contains","submitHandler","Constructor","preventDefault","validateAll","focus","submit","destroy","inputHandler"],"mappings":"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","file":"../bouncer.min.js","sourcesContent":["/*!\n * formbouncerjs v1.4.6\n * A lightweight form validation script that augments native HTML5 form validation elements and attributes.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/bouncer\n */\n\n(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === 'object') {\n module.exports = factory(root);\n } else {\n root.Bouncer = factory(root);\n }\n})(\n typeof global !== 'undefined'\n ? global\n : typeof window !== 'undefined'\n ? window\n : this,\n function (window) {\n 'use strict';\n\n //\n // Variables\n //\n\n let defaults = {\n // Classes & IDs\n\n fieldClass: 'has-error',\n errorClass: 'error-message',\n fieldPrefix: 'bouncer-field_',\n errorPrefix: 'bouncer-error_',\n\n // Patterns\n patterns: {\n email:\n /^([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22))*\\x40([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d))*(\\.\\w{2,})+$/,\n url: /^(?:(?:https?|HTTPS?|ftp|FTP):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-zA-Z\\u00a1-\\uffff]{2,}))\\.?)(?::\\d{2,5})?(?:[/?#]\\S*)?$/,\n number: /^(?:[-+]?[0-9]*[.,]?[0-9]+)$/,\n color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,\n date: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/,\n time: /^(?:(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]))$/,\n month: /^(?:(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])))$/,\n },\n\n // Custom Validations\n customValidations: {},\n\n // Messages\n messageHide: false,\n messageHideCustom: 'data-hide-message',\n messageAfterField: true,\n messageMissingCustom: 'data-missing-message',\n messagePatternCustom: 'data-pattern-message',\n messageOutOfRangeCustom: 'data-outofrange-message',\n messageWrongLengthCustom: 'data-wronglength-message',\n messageTarget: 'data-bouncer-target',\n messageElement: 'span',\n messages: {\n missingValue: {\n checkbox: 'This field is required.',\n radio: 'Please select a value.',\n select: 'Please select a value.',\n 'select-multiple': 'Please select at least one value.',\n default: 'Please fill out this field.',\n },\n patternMismatch: {\n email: 'Please enter a valid email address.',\n url: 'Please enter a URL.',\n number: 'Please enter a number',\n color: 'Please match the following format: #rrggbb',\n date: 'Please use the YYYY-MM-DD format',\n time: 'Please use the 24-hour time format. Ex. 23:00',\n month: 'Please use the YYYY-MM format',\n default: 'Please match the requested format.',\n },\n outOfRange: {\n over: 'Please select a value that is no more than {max}.',\n under: 'Please select a value that is no less than {min}.',\n },\n wrongLength: {\n over: 'Please shorten this text to no more than {maxLength} characters. You are currently using {length} characters.',\n under:\n 'Please lengthen this text to {minLength} characters or more. You are currently using {length} characters.',\n },\n fallback: 'There was an error with this field.',\n },\n\n // Form Submission\n disableSubmit: false,\n\n // Custom Events\n emitEvents: true,\n };\n\n //\n // Methods\n //\n\n /**\n * A wrapper for Array.prototype.forEach() for non-arrays\n * @param {Array-like} arr The array-like object\n * @param {Function} callback The callback to run\n */\n let forEach = function (arr, callback) {\n Array.prototype.forEach.call(arr, callback);\n };\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n let extend = function () {\n let merged = {};\n forEach(arguments, function (obj) {\n for (let key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n if (Object.prototype.toString.call(obj[key]) === '[object Object]') {\n merged[key] = extend(merged[key], obj[key]);\n } else {\n merged[key] = obj[key];\n }\n // merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Object} options The settings object\n * @param {Node} anchor The anchor element\n * @param {Node} toggle The toggle element\n */\n let emitEvent = function (elem, type, details) {\n if (typeof window.CustomEvent !== 'function') return;\n let event = new CustomEvent(type, {\n bubbles: true,\n detail: details || {},\n });\n elem.dispatchEvent(event);\n };\n\n /**\n * Add the `novalidate` attribute to all forms\n * @param {Boolean} remove If true, remove the `novalidate` attribute\n */\n let addNoValidate = function (selector) {\n forEach(document.querySelectorAll(selector), function (form) {\n form.setAttribute('novalidate', true);\n });\n };\n\n /**\n * Remove the `novalidate` attribute to all forms\n */\n let removeNoValidate = function (selector) {\n forEach(document.querySelectorAll(selector), function (form) {\n form.removeAttribute('novalidate');\n });\n };\n\n /**\n * Check if a required field is missing its value\n * @param {Node} field The field to check\n * @return {Boolean} It true, field is missing it's value\n */\n let missingValue = function (field) {\n // If not required, bail\n if (!field.hasAttribute('required')) return false;\n\n // Handle checkboxes\n if (field.type === 'checkbox') {\n return !field.checked;\n }\n\n // Get the field value length\n let length = field.value.length;\n\n // Handle radio buttons\n if (field.type === 'radio') {\n length = Array.prototype.filter.call(\n field.form.querySelectorAll(\n '[name=\"' + escapeCharacters(field.name) + '\"]'\n ),\n function (btn) {\n return btn.checked;\n }\n ).length;\n }\n\n // Check for value\n return length < 1;\n };\n\n /**\n * Check if field value doesn't match a patter.\n * @param {Node} field The field to check\n * @param {Object} settings The plugin settings\n * @see https://www.w3.org/TR/html51/sec-forms.html#the-pattern-attribute\n * @return {Boolean} If true, there's a pattern mismatch\n */\n let patternMismatch = function (field, settings) {\n // Check if there's a pattern to match\n let pattern = field.getAttribute('pattern');\n pattern = pattern\n ? new RegExp('^(?:' + pattern + ')$')\n : settings.patterns[field.type];\n if (!pattern || !field.value || field.value.length < 1) return false;\n\n // Validate the pattern\n return field.value.match(pattern) ? false : true;\n };\n\n /**\n * Check if field value is out-of-range\n * @param {Node} field The field to check\n * @return {String} Returns 'over', 'under', or false\n */\n let outOfRange = function (field) {\n // Make sure field has value\n if (!field.value || field.value.length < 1) return false;\n\n // Check for range\n let max = field.getAttribute('max');\n let min = field.getAttribute('min');\n\n // Check validity\n let num = parseFloat(field.value);\n if (max && num > max) return 'over';\n if (min && num < min) return 'under';\n return false;\n };\n\n /**\n * Check if the field value is too long or too short\n * @param {Node} field The field to check\n * @return {String} Returns 'over', 'under', or false\n */\n let wrongLength = function (field) {\n // Make sure field has value\n if (!field.value || field.value.length < 1) return false;\n\n // Check for min/max length\n let max = field.getAttribute('maxlength');\n let min = field.getAttribute('minlength');\n\n // Check validity\n let length = field.value.length;\n if (max && length > max) return 'over';\n if (min && length < min) return 'under';\n return false;\n };\n\n /**\n * Test for standard field validations\n * @param {Node} field The field to test\n * @param {Object} settings The plugin settings\n * @return {Object} The tests and their results\n */\n let runValidations = function (field, settings) {\n return {\n missingValue: missingValue(field),\n patternMismatch: patternMismatch(field, settings),\n outOfRange: outOfRange(field),\n wrongLength: wrongLength(field),\n };\n };\n\n /**\n * Run any provided custom validations\n * @param {Node} field The field to test\n * @param {Object} errors The existing errors\n * @param {Object} validations The custom validations to run\n * @param {Object} settings The plugin settings\n * @return {Object} The tests and their results\n */\n let customValidations = function (field, errors, validations, settings) {\n for (let test in validations) {\n if (validations.hasOwnProperty(test)) {\n errors[test] = validations[test](field, settings);\n }\n }\n return errors;\n };\n\n /**\n * Check if a field has any errors\n * @param {Object} errors The validation test results\n * @return {Boolean} Returns true if there are errors\n */\n let hasErrors = function (errors) {\n for (let type in errors) {\n if (errors[type]) return true;\n }\n return false;\n };\n\n /**\n * Check a field for errors\n * @param {Node} field The field to test\n * @param {Object} settings The plugin settings\n * @return {Object} The field validity and errors\n */\n let getErrors = function (field, settings) {\n // Get standard validation errors\n let errors = runValidations(field, settings);\n\n // Check for custom validations\n errors = customValidations(\n field,\n errors,\n settings.customValidations,\n settings\n );\n\n return {\n valid: !hasErrors(errors),\n errors: errors,\n };\n };\n\n /**\n * Escape special characters for use with querySelector\n * @author Mathias Bynens\n * @link https://github.com/mathiasbynens/CSS.escape\n * @param {String} id The anchor ID to escape\n */\n let escapeCharacters = function (id) {\n let string = String(id);\n let length = string.length;\n let index = -1;\n let codeUnit;\n let result = '';\n let firstCodeUnit = string.charCodeAt(0);\n while (++index < length) {\n codeUnit = string.charCodeAt(index);\n // Note: there’s no need to special-case astral symbols, surrogate\n // pairs, or lone surrogates.\n\n // If the character is NULL (U+0000), then throw an\n // `InvalidCharacterError` exception and terminate these steps.\n if (codeUnit === 0x0000) {\n throw new InvalidCharacterError(\n 'Invalid character: the input contains U+0000.'\n );\n }\n\n if (\n // If the character is in the range [\\1-\\1F] (U+0001 to U+001F) or is\n // U+007F, […]\n (codeUnit >= 0x0001 && codeUnit <= 0x001f) ||\n codeUnit == 0x007f ||\n // If the character is the first character and is in the range [0-9]\n // (U+0030 to U+0039), […]\n (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||\n // If the character is the second character and is in the range [0-9]\n // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]\n (index === 1 &&\n codeUnit >= 0x0030 &&\n codeUnit <= 0x0039 &&\n firstCodeUnit === 0x002d)\n ) {\n // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point\n result += '\\\\' + codeUnit.toString(16) + ' ';\n continue;\n }\n\n // If the character is not handled by one of the above rules and is\n // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or\n // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to\n // U+005A), or [a-z] (U+0061 to U+007A), […]\n if (\n codeUnit >= 0x0080 ||\n codeUnit === 0x002d ||\n codeUnit === 0x005f ||\n (codeUnit >= 0x0030 && codeUnit <= 0x0039) ||\n (codeUnit >= 0x0041 && codeUnit <= 0x005a) ||\n (codeUnit >= 0x0061 && codeUnit <= 0x007a)\n ) {\n // the character itself\n result += string.charAt(index);\n continue;\n }\n\n // Otherwise, the escaped character.\n // http://dev.w3.org/csswg/cssom/#escape-a-character\n result += '\\\\' + string.charAt(index);\n }\n\n // Return sanitized hash\n return result;\n };\n\n /**\n * Get or create an ID for a field\n * @param {Node} field The field\n * @param {Object} settings The plugin settings\n * @param {Boolean} create If true, create an ID if there isn't one\n * @return {String} The field ID\n */\n let getFieldID = function (field, settings, create) {\n let id = field.name ? field.name : field.id;\n if (!id && create) {\n id = settings.fieldPrefix + Math.floor(Math.random() * 999);\n field.id = id;\n }\n if (field.type === 'checkbox') {\n id += '_' + (field.value || field.id);\n }\n return id;\n };\n\n /**\n * Special handling for radio buttons and checkboxes wrapped in labels.\n * @param {Node} field The field with the error\n * @return {Node} The field to show the error on\n */\n let getErrorField = function (field) {\n // If the field is a radio button, get the last item in the radio group\n // @todo if location is before, get first item\n if (field.type === 'radio' && field.name) {\n const group = field.form.querySelectorAll(\n '[name=\"' + escapeCharacters(field.name) + '\"]'\n );\n field = group[group.length - 1];\n }\n\n // Get the associated label for radio button or checkbox\n if (field.type === 'radio' || field.type === 'checkbox') {\n let label =\n field.closest('label') ||\n field.form.querySelector('[for=\"' + field.id + '\"]');\n field = label || field;\n }\n\n return field;\n };\n\n /**\n * Get the location for a field's error message\n * @param {Node} field The field\n * @param {Node} target The target for error message\n * @param {Object} settings The plugin settings\n * @return {Node} The error location\n */\n let getErrorLocation = function (field, target, settings) {\n // Check for a custom error message\n let selector = field.getAttribute(settings.messageTarget);\n if (selector) {\n const location = field.form.querySelector(selector);\n if (location) {\n // @bugfix by @HaroldPutman\n // https://github.com/cferdinandi/bouncer/pull/28\n return (\n location.firstChild ||\n location.appendChild(document.createTextNode(''))\n );\n }\n }\n\n // If the message should come after the field\n if (settings.messageAfterField) {\n // If there's no next sibling, create one\n if (!target.nextSibling) {\n target.parentNode.appendChild(document.createTextNode(''));\n }\n\n return target.nextSibling;\n }\n\n // If it should come before\n return target;\n };\n\n /**\n * Create a validation error message node\n * @param {Node} field The field\n * @param {Object} settings The plugin settings\n * @return {Node} The error message node\n */\n let createError = function (field, settings) {\n // Create the error message\n let error = document.createElement(settings.messageElement);\n error.className = settings.errorClass;\n error.id = settings.errorPrefix + getFieldID(field, settings, true);\n\n // If the field is a radio button or checkbox, grab the last field label\n let fieldTarget = getErrorField(field);\n\n // Check if message should be displayed\n let hideMessage = settings.messageHide;\n\n let custom = field.getAttribute(settings.messageHideCustom);\n if (custom && custom === 'true') hideMessage = true;\n else if (custom && custom === 'false') hideMessage = false;\n\n // Inject the error message into the DOM\n if (hideMessage === false) {\n let location = getErrorLocation(field, fieldTarget, settings);\n location.parentNode.insertBefore(error, location);\n }\n\n return error;\n };\n\n /**\n * Get the error message test\n * @param {Node} field The field to get an error message for\n * @param {Object} errors The errors on the field\n * @param {Object} settings The plugin settings\n * @return {String|Function} The error message\n */\n let getErrorMessage = function (field, errors, settings) {\n // Variables\n let messages = settings.messages;\n\n // Missing value error\n if (errors.missingValue) {\n let custom = field.getAttribute(settings.messageMissingCustom);\n if (custom) return custom;\n return (\n messages.missingValue[field.type] || messages.missingValue.default\n );\n }\n\n // Numbers that are out of range\n if (errors.outOfRange) {\n let custom = field.getAttribute(settings.messageOutOfRangeCustom);\n if (custom) return custom;\n return messages.outOfRange[errors.outOfRange]\n .replace('{max}', field.getAttribute('max'))\n .replace('{min}', field.getAttribute('min'))\n .replace('{length}', field.value.length);\n }\n\n // Values that are too long or short\n if (errors.wrongLength) {\n let custom = field.getAttribute(settings.messageWrongLengthCustom);\n if (custom) return custom;\n return messages.wrongLength[errors.wrongLength]\n .replace('{maxLength}', field.getAttribute('maxlength'))\n .replace('{minLength}', field.getAttribute('minlength'))\n .replace('{length}', field.value.length);\n }\n\n // Pattern mismatch error\n if (errors.patternMismatch) {\n let custom = field.getAttribute(settings.messagePatternCustom);\n if (custom) return custom;\n return (\n messages.patternMismatch[field.type] ||\n messages.patternMismatch.default\n );\n }\n\n // Custom validations\n for (let test in settings.customValidations) {\n if (settings.customValidations.hasOwnProperty(test)) {\n if (errors[test] && messages[test]) return messages[test];\n }\n }\n\n // Fallback error message\n return messages.fallback;\n };\n\n /**\n * Add error attributes to a field\n * @param {Node} field The field with the error message\n * @param {Node} error The error message\n * @param {Object} settings The plugin settings\n */\n let addErrorAttributes = function (field, error, settings) {\n field.classList.add(settings.fieldClass);\n field.setAttribute('aria-describedby', error.id);\n field.setAttribute('aria-invalid', true);\n };\n\n /**\n * Show error attributes on a field or radio/checkbox group\n * @param {Node} field The field with the error message\n * @param {Node} error The error message\n * @param {Object} settings The plugin settings\n */\n let showErrorAttributes = function (field, error, settings) {\n // If field is a radio button, add attributes to every button in the group\n if (field.type === 'radio' && field.name) {\n Array.prototype.forEach.call(\n document.querySelectorAll('[name=\"' + field.name + '\"]'),\n function (button) {\n addErrorAttributes(button, error, settings);\n }\n );\n }\n\n // Otherwise, add an error class and aria attribute to the field\n addErrorAttributes(field, error, settings);\n };\n\n /**\n * Show an error message in the DOM\n * @param {Node} field The field to show an error message for\n * @param {Object} errors The errors on the field\n * @param {Object} settings The plugin settings\n */\n let showError = function (field, errors, settings) {\n // Get/create an error message\n const error =\n field.form.querySelector(\n '#' +\n escapeCharacters(settings.errorPrefix + getFieldID(field, settings))\n ) || createError(field, settings);\n let msg = getErrorMessage(field, errors, settings);\n error.textContent =\n typeof msg === 'function' ? msg(field, settings) : msg;\n\n // Add error attributes\n showErrorAttributes(field, error, settings);\n\n // Emit custom event\n if (settings.emitEvents) {\n emitEvent(field, 'bouncerShowError', {\n errors: errors,\n });\n }\n };\n\n /**\n * Remove error attributes from a field\n * @param {Node} field The field with the error message\n * @param {Node} error The error message\n * @param {Object} settings The plugin settings\n */\n let removeAttributes = function (field, settings) {\n field.classList.remove(settings.fieldClass);\n field.removeAttribute('aria-describedby');\n field.removeAttribute('aria-invalid');\n };\n\n /**\n * Remove error attributes from the field or radio group\n * @param {Node} field The field with the error message\n * @param {Node} error The error message\n * @param {Object} settings The plugin settings\n */\n let removeErrorAttributes = function (field, settings) {\n // If field is a radio button, remove attributes from every button in the group\n if (field.type === 'radio' && field.name) {\n Array.prototype.forEach.call(\n document.querySelectorAll('[name=\"' + field.name + '\"]'),\n function (button) {\n removeAttributes(button, settings);\n }\n );\n return;\n }\n\n // Otherwise, add an error class and aria attribute to the field\n removeAttributes(field, settings);\n };\n\n /**\n * Remove an error message from the DOM\n * @param {Node} field The field with the error message\n * @param {Object} settings The plugin settings\n */\n let removeError = function (field, settings) {\n // Get the error message for this field\n const error = field.form.querySelector(\n '#' +\n escapeCharacters(settings.errorPrefix + getFieldID(field, settings))\n );\n if (!error) return;\n\n // Remove the error\n error.parentNode.removeChild(error);\n\n // Remove error and a11y from the field\n removeErrorAttributes(field, settings);\n\n // Emit custom event\n if (settings.emitEvents) {\n emitEvent(field, 'bouncerRemoveError');\n }\n };\n\n /**\n * Remove errors from all fields\n * @param {String} selector The selector for the form\n * @param {Object} settings The plugin settings\n */\n let removeAllErrors = function (selector, settings) {\n forEach(document.querySelectorAll(selector), function (form) {\n forEach(\n form.querySelectorAll('input, select, textarea'),\n function (field) {\n removeError(field, settings);\n }\n );\n });\n };\n\n /**\n * The plugin constructor\n * @param {String} selector The selector to use for forms to be validated\n * @param {Object} options User settings [optional]\n */\n let Constructor = function (selector, options) {\n //\n // Variables\n //\n\n let publicAPIs = {};\n let settings;\n\n //\n // Methods\n //\n\n /**\n * Validate a field\n * @param {Node} field The field to validate\n * @param {Object} options Validation options\n * @return {Object} The validity state and errors\n */\n publicAPIs.validate = function (field, options) {\n // Don't validate submits, buttons, file and reset inputs, and disabled and readonly fields\n if (\n field.disabled ||\n field.readOnly ||\n field.type === 'reset' ||\n field.type === 'submit' ||\n field.type === 'button'\n )\n return;\n\n // Local settings\n let _settings = extend(settings, options || {});\n\n // Check for errors\n let isValid = getErrors(field, _settings);\n\n // If valid, remove any error messages\n if (isValid.valid) {\n removeError(field, _settings);\n return;\n }\n\n // Otherwise, show an error message\n showError(field, isValid.errors, _settings);\n\n return isValid;\n };\n\n /**\n * Validate all fields in a form or section\n * @param {Node} target The form or section to validate fields in\n * @return {Array} An array of fields with errors\n */\n publicAPIs.validateAll = function (target) {\n return Array.prototype.filter.call(\n target.querySelectorAll('input, select, textarea'),\n function (field) {\n let validate = publicAPIs.validate(field);\n return validate && !validate.valid;\n }\n );\n };\n\n /**\n * Run a validation on field blur\n */\n let blurHandler = function (event) {\n // Only run if the field is in a form to be validated\n if (!event.target.form || !event.target.form.matches(selector)) return;\n\n // Validate the field\n publicAPIs.validate(event.target);\n };\n\n /**\n * Run a validation on a fields with errors when the value changes\n */\n let inputHandler = function (event) {\n // Only run if the field is in a form to be validated\n if (!event.target.form || !event.target.form.matches(selector)) return;\n\n // Only run on fields with errors\n if (!event.target.classList.contains(settings.fieldClass)) return;\n\n // Validate the field\n publicAPIs.validate(event.target);\n };\n\n /**\n * Validate an entire form when it's submitted\n */\n let submitHandler = function (event) {\n // Only run on matching elements\n if (!event.target.matches(selector)) return;\n\n // Prevent form submission\n event.preventDefault();\n\n // Validate each field\n let errors = publicAPIs.validateAll(event.target);\n\n // If there are errors, focus on the first one\n if (errors.length > 0) {\n errors[0].focus();\n emitEvent(event.target, 'bouncerFormInvalid', { errors: errors });\n return;\n }\n\n // Otherwise, submit if not disabled\n if (!settings.disableSubmit) {\n event.target.submit();\n }\n\n // Emit custom event\n if (settings.emitEvents) {\n emitEvent(event.target, 'bouncerFormValid');\n }\n };\n\n /**\n * Destroy the current plugin instantiation\n */\n publicAPIs.destroy = function () {\n // Remove event listeners\n document.removeEventListener('blur', blurHandler, true);\n document.removeEventListener('input', inputHandler, false);\n document.removeEventListener('click', inputHandler, false);\n document.removeEventListener('submit', submitHandler, false);\n\n // Remove all errors\n removeAllErrors(selector, settings);\n\n // Remove novalidate attribute\n removeNoValidate(selector);\n\n // Emit custom event\n if (settings.emitEvents) {\n emitEvent(document, 'bouncerDestroyed', {\n settings: settings,\n });\n }\n\n // Reset settings\n settings = null;\n };\n\n /**\n * Instantiate a new instance of the plugin\n */\n let init = function () {\n // Create settings\n settings = extend(defaults, options || {});\n\n // Add novalidate attribute\n addNoValidate(selector);\n\n // Event Listeners\n document.addEventListener('blur', blurHandler, true);\n document.addEventListener('input', inputHandler, false);\n document.addEventListener('click', inputHandler, false);\n document.addEventListener('submit', submitHandler, false);\n\n // Emit custom event\n if (settings.emitEvents) {\n emitEvent(document, 'bouncerInitialized', {\n settings: settings,\n });\n }\n };\n\n //\n // Inits & Event Listeners\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the constructor\n //\n\n return Constructor;\n }\n);\n"]}