From b7c8d1d17d195acbde87e83a6948572a2b872627 Mon Sep 17 00:00:00 2001 From: Josh Crawford Date: Fri, 12 Jun 2020 20:40:44 +1000 Subject: [PATCH 1/6] Update bouncer.js --- src/js/bouncer/bouncer.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/js/bouncer/bouncer.js b/src/js/bouncer/bouncer.js index 486e1e9..98a2813 100644 --- a/src/js/bouncer/bouncer.js +++ b/src/js/bouncer/bouncer.js @@ -74,6 +74,9 @@ // Form Submission disableSubmit: false, + + // Allow blur/click/input events to be opt-out + validateOnBlur: true, // Custom Events emitEvents: true @@ -789,9 +792,12 @@ publicAPIs.destroy = function () { // Remove event listeners - document.removeEventListener('blur', blurHandler, true); - document.removeEventListener('input', inputHandler, false); - document.removeEventListener('click', inputHandler, false); + if (settings.validateOnBlur) { + document.removeEventListener('blur', blurHandler, true); + document.removeEventListener('input', inputHandler, false); + document.removeEventListener('click', inputHandler, false); + } + document.removeEventListener('submit', submitHandler, false); // Remove all errors @@ -824,9 +830,12 @@ addNoValidate(selector); // Event Listeners - document.addEventListener('blur', blurHandler, true); - document.addEventListener('input', inputHandler, false); - document.addEventListener('click', inputHandler, false); + if (settings.validateOnBlur) { + document.addEventListener('blur', blurHandler, true); + document.addEventListener('input', inputHandler, false); + document.addEventListener('click', inputHandler, false); + } + document.addEventListener('submit', submitHandler, false); // Emit custom event @@ -854,4 +863,4 @@ return Constructor; -}); \ No newline at end of file +}); From b827f77150812e106ff49f17522c349247d4f634 Mon Sep 17 00:00:00 2001 From: Josh Crawford Date: Fri, 12 Jun 2020 20:54:53 +1000 Subject: [PATCH 2/6] Build --- dist/bouncer.js | 25 +++++++++++++++++-------- dist/bouncer.min.js | 4 ++-- dist/bouncer.polyfills.js | 25 +++++++++++++++++-------- dist/bouncer.polyfills.min.js | 4 ++-- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/dist/bouncer.js b/dist/bouncer.js index a7aa608..432544b 100644 --- a/dist/bouncer.js +++ b/dist/bouncer.js @@ -1,7 +1,7 @@ /*! * formbouncerjs v1.4.6 * A lightweight form validation script that augments native HTML5 form validation elements and attributes. - * (c) 2019 Chris Ferdinandi + * (c) 2020 Chris Ferdinandi * MIT License * http://github.com/cferdinandi/bouncer */ @@ -82,6 +82,9 @@ // Form Submission disableSubmit: false, + + // Allow blur/click/input events to be opt-out + validateOnBlur: true, // Custom Events emitEvents: true @@ -797,9 +800,12 @@ publicAPIs.destroy = function () { // Remove event listeners - document.removeEventListener('blur', blurHandler, true); - document.removeEventListener('input', inputHandler, false); - document.removeEventListener('click', inputHandler, false); + if (settings.validateOnBlur) { + document.removeEventListener('blur', blurHandler, true); + document.removeEventListener('input', inputHandler, false); + document.removeEventListener('click', inputHandler, false); + } + document.removeEventListener('submit', submitHandler, false); // Remove all errors @@ -832,9 +838,12 @@ addNoValidate(selector); // Event Listeners - document.addEventListener('blur', blurHandler, true); - document.addEventListener('input', inputHandler, false); - document.addEventListener('click', inputHandler, false); + if (settings.validateOnBlur) { + document.addEventListener('blur', blurHandler, true); + document.addEventListener('input', inputHandler, false); + document.addEventListener('click', inputHandler, false); + } + document.addEventListener('submit', submitHandler, false); // Emit custom event @@ -862,4 +871,4 @@ return Constructor; -})); \ No newline at end of file +})); diff --git a/dist/bouncer.min.js b/dist/bouncer.min.js index e498430..f58f852 100644 --- a/dist/bouncer.min.js +++ b/dist/bouncer.min.js @@ -1,2 +1,2 @@ -/*! formbouncerjs v1.4.6 | (c) 2019 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/bouncer */ -!(function(e,t){"function"==typeof define&&define.amd?define([],(function(){return t(e)})):"object"==typeof exports?module.exports=t(e):e.Bouncer=t(e)})("undefined"!=typeof global?global:"undefined"!=typeof window?window:this,(function(a){"use strict";var l={fieldClass:"error",errorClass:"error-message",fieldPrefix:"bouncer-field_",errorPrefix:"bouncer-error_",patterns:{email:/^([^\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,})+$/,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*)?$/,number:/^(?:[-+]?[0-9]*[.,]?[0-9]+)$/,color:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,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))/,time:/^(?:(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]))$/,month:/^(?:(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])))$/},customValidations:{},messageAfterField:!0,messageCustom:"data-bouncer-message",messageTarget:"data-bouncer-target",messages:{missingValue:{checkbox:"This field is required.",radio:"Please select a value.",select:"Please select a value.","select-multiple":"Please select at least one value.",default:"Please fill out this field."},patternMismatch:{email:"Please enter a valid email address.",url:"Please enter a URL.",number:"Please enter a number",color:"Please match the following format: #rrggbb",date:"Please use the YYYY-MM-DD format",time:"Please use the 24-hour time format. Ex. 23:00",month:"Please use the YYYY-MM format",default:"Please match the requested format."},outOfRange:{over:"Please select a value that is no more than {max}.",under:"Please select a value that is no less than {min}."},wrongLength:{over:"Please shorten this text to no more than {maxLength} characters. You are currently using {length} characters.",under:"Please lengthen this text to {minLength} characters or more. You are currently using {length} characters."},fallback:"There was an error with this field."},disableSubmit:!1,emitEvents:!0},c=function(e,t){Array.prototype.forEach.call(e,t)},s=function(){var r={};return c(arguments,(function(e){for(var t in e){if(!e.hasOwnProperty(t))return;"[object Object]"===Object.prototype.toString.call(e[t])?r[t]=s(r[t],e[t]):r[t]=e[t]}})),r},f=function(e,t,r){if("function"==typeof a.CustomEvent){var n=new CustomEvent(t,{bubbles:!0,detail:r||{}});e.dispatchEvent(n)}},d=function(e,t){return{missingValue:(function(e){if(!e.hasAttribute("required"))return!1;if("checkbox"===e.type)return!e.checked;var t=e.value.length;return"radio"===e.type&&(t=Array.prototype.filter.call(e.form.querySelectorAll('[name="'+m(e.name)+'"]'),(function(e){return e.checked})).length),t<1})(e),patternMismatch:(r=e,n=t,a=r.getAttribute("pattern"),!(!(a=a?new RegExp("^(?:"+a+")$"):n.patterns[r.type])||!r.value||r.value.length<1||r.value.match(a))),outOfRange:(function(e){if(!e.value||e.value.length<1)return!1;var t=e.getAttribute("max"),r=e.getAttribute("min"),n=parseFloat(e.value);return t&&t Date: Sat, 13 Jun 2020 13:57:50 +1000 Subject: [PATCH 3/6] Add `validateOnSubmit` I know this seems very counter-productive, but this can be used to essentially turn off validation. What this can be useful for is utilising Bouncer's API's for server-side errors, after the fact. Where you could do `bouncer.showError(field, { some: 'message' })` --- src/js/bouncer/bouncer.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/js/bouncer/bouncer.js b/src/js/bouncer/bouncer.js index 98a2813..1034ccb 100644 --- a/src/js/bouncer/bouncer.js +++ b/src/js/bouncer/bouncer.js @@ -78,6 +78,9 @@ // Allow blur/click/input events to be opt-out validateOnBlur: true, + // Allow validation to be turned off altogether. Useful for server-side validation use. + validateOnSubmit: true, + // Custom Events emitEvents: true @@ -798,7 +801,9 @@ document.removeEventListener('click', inputHandler, false); } - document.removeEventListener('submit', submitHandler, false); + if (settings.validateOnSubmit) { + document.removeEventListener('submit', submitHandler, false); + } // Remove all errors removeAllErrors(selector, settings); @@ -836,7 +841,9 @@ document.addEventListener('click', inputHandler, false); } - document.addEventListener('submit', submitHandler, false); + if (settings.validateOnSubmit) { + document.addEventListener('submit', submitHandler, false); + } // Emit custom event if (settings.emitEvents) { From a04cc34832761be26743ec71263a799a764dd69f Mon Sep 17 00:00:00 2001 From: Josh Crawford Date: Sat, 13 Jun 2020 14:02:25 +1000 Subject: [PATCH 4/6] Add `publicAPIs.showError` and modify `getErrorMessage` This adds `showError` to the public API, so we can trigger an error on-demand. Using: `bouncer.showError(field, { customMessage: 'Some error.' })` The second part is modifying `getErrorMessage()` to check to see if we're passing in a `customMessage` object, and to use that for the error, instead of looking at the messages settings. This is all particularly useful for server-side validation to make use of Bouncer's API's for handling error messages and showing them. --- src/js/bouncer/bouncer.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/js/bouncer/bouncer.js b/src/js/bouncer/bouncer.js index 1034ccb..8b82690 100644 --- a/src/js/bouncer/bouncer.js +++ b/src/js/bouncer/bouncer.js @@ -534,6 +534,11 @@ } } + // Custom message, passed directly in + if (errors.customMessage) { + return errors.customMessage; + } + // Fallback error message return messages.fallback; @@ -684,6 +689,18 @@ // // Methods // + + /** + * Show an error message in the DOM + * @param {Node} field The field to show an error message for + * @param {Object} errors The errors on the field + * @param {Object} options Additional plugin settings + */ + publicAPIs.showError = function (field, errors, options) { + var _settings = extend(settings, options || {}); + + return showError(field, errors, _settings) + }; /** * Validate a field From b6a083997394d5f1ed70080a2b82ce3d19fee0b0 Mon Sep 17 00:00:00 2001 From: Josh Crawford Date: Sat, 13 Jun 2020 14:10:34 +1000 Subject: [PATCH 5/6] Add `publicAPIs.removeError` --- src/js/bouncer/bouncer.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/js/bouncer/bouncer.js b/src/js/bouncer/bouncer.js index 8b82690..8a22c4d 100644 --- a/src/js/bouncer/bouncer.js +++ b/src/js/bouncer/bouncer.js @@ -702,6 +702,17 @@ return showError(field, errors, _settings) }; + /** + * Remove an error message from the DOM + * @param {Node} field The field with the error message + * @param {Object} settings The plugin settings + */ + publicAPIs.removeError = function (field, options) { + var _settings = extend(settings, options || {}); + + return removeError(field, _settings); + }; + /** * Validate a field * @param {Node} field The field to validate From 727f79785d6eec60227fb24ed9746c57bdfb776f Mon Sep 17 00:00:00 2001 From: Josh Crawford Date: Sat, 13 Jun 2020 14:13:10 +1000 Subject: [PATCH 6/6] Build + cleanup indentation --- dist/bouncer.js | 39 +++++++++++++++++++++++++++++++++-- dist/bouncer.min.js | 2 +- dist/bouncer.polyfills.js | 39 +++++++++++++++++++++++++++++++++-- dist/bouncer.polyfills.min.js | 2 +- src/js/bouncer/bouncer.js | 30 +++++++++++++-------------- 5 files changed, 91 insertions(+), 21 deletions(-) diff --git a/dist/bouncer.js b/dist/bouncer.js index 432544b..d8434d3 100644 --- a/dist/bouncer.js +++ b/dist/bouncer.js @@ -86,6 +86,9 @@ // Allow blur/click/input events to be opt-out validateOnBlur: true, + // Allow validation to be turned off altogether. Useful for server-side validation use. + validateOnSubmit: true, + // Custom Events emitEvents: true @@ -539,6 +542,11 @@ } } + // Custom message, passed directly in + if (errors.customMessage) { + return errors.customMessage; + } + // Fallback error message return messages.fallback; @@ -689,6 +697,29 @@ // // Methods // + + /** + * Show an error message in the DOM + * @param {Node} field The field to show an error message for + * @param {Object} errors The errors on the field + * @param {Object} options Additional plugin settings + */ + publicAPIs.showError = function (field, errors, options) { + var _settings = extend(settings, options || {}); + + return showError(field, errors, _settings) + }; + + /** + * Remove an error message from the DOM + * @param {Node} field The field with the error message + * @param {Object} settings The plugin settings + */ + publicAPIs.removeError = function (field, options) { + var _settings = extend(settings, options || {}); + + return removeError(field, _settings); + }; /** * Validate a field @@ -806,7 +837,9 @@ document.removeEventListener('click', inputHandler, false); } - document.removeEventListener('submit', submitHandler, false); + if (settings.validateOnSubmit) { + document.removeEventListener('submit', submitHandler, false); + } // Remove all errors removeAllErrors(selector, settings); @@ -844,7 +877,9 @@ document.addEventListener('click', inputHandler, false); } - document.addEventListener('submit', submitHandler, false); + if (settings.validateOnSubmit) { + document.addEventListener('submit', submitHandler, false); + } // Emit custom event if (settings.emitEvents) { diff --git a/dist/bouncer.min.js b/dist/bouncer.min.js index f58f852..f083299 100644 --- a/dist/bouncer.min.js +++ b/dist/bouncer.min.js @@ -1,2 +1,2 @@ /*! formbouncerjs v1.4.6 | (c) 2020 Chris Ferdinandi | MIT License | http://github.com/cferdinandi/bouncer */ -!(function(e,t){"function"==typeof define&&define.amd?define([],(function(){return t(e)})):"object"==typeof exports?module.exports=t(e):e.Bouncer=t(e)})("undefined"!=typeof global?global:"undefined"!=typeof window?window:this,(function(n){"use strict";var l={fieldClass:"error",errorClass:"error-message",fieldPrefix:"bouncer-field_",errorPrefix:"bouncer-error_",patterns:{email:/^([^\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,})+$/,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*)?$/,number:/^(?:[-+]?[0-9]*[.,]?[0-9]+)$/,color:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,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))/,time:/^(?:(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]))$/,month:/^(?:(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])))$/},customValidations:{},messageAfterField:!0,messageCustom:"data-bouncer-message",messageTarget:"data-bouncer-target",messages:{missingValue:{checkbox:"This field is required.",radio:"Please select a value.",select:"Please select a value.","select-multiple":"Please select at least one value.",default:"Please fill out this field."},patternMismatch:{email:"Please enter a valid email address.",url:"Please enter a URL.",number:"Please enter a number",color:"Please match the following format: #rrggbb",date:"Please use the YYYY-MM-DD format",time:"Please use the 24-hour time format. Ex. 23:00",month:"Please use the YYYY-MM format",default:"Please match the requested format."},outOfRange:{over:"Please select a value that is no more than {max}.",under:"Please select a value that is no less than {min}."},wrongLength:{over:"Please shorten this text to no more than {maxLength} characters. You are currently using {length} characters.",under:"Please lengthen this text to {minLength} characters or more. You are currently using {length} characters."},fallback:"There was an error with this field."},disableSubmit:!1,validateOnBlur:!0,emitEvents:!0},c=function(e,t){Array.prototype.forEach.call(e,t)},s=function(){var r={};return c(arguments,(function(e){for(var t in e){if(!e.hasOwnProperty(t))return;"[object Object]"===Object.prototype.toString.call(e[t])?r[t]=s(r[t],e[t]):r[t]=e[t]}})),r},f=function(e,t,r){if("function"==typeof n.CustomEvent){var a=new CustomEvent(t,{bubbles:!0,detail:r||{}});e.dispatchEvent(a)}},d=function(e,t){return{missingValue:(function(e){if(!e.hasAttribute("required"))return!1;if("checkbox"===e.type)return!e.checked;var t=e.value.length;return"radio"===e.type&&(t=Array.prototype.filter.call(e.form.querySelectorAll('[name="'+m(e.name)+'"]'),(function(e){return e.checked})).length),t<1})(e),patternMismatch:(r=e,a=t,n=r.getAttribute("pattern"),!(!(n=n?new RegExp("^(?:"+n+")$"):a.patterns[r.type])||!r.value||r.value.length<1||r.value.match(n))),outOfRange:(function(e){if(!e.value||e.value.length<1)return!1;var t=e.getAttribute("max"),r=e.getAttribute("min"),a=parseFloat(e.value);return t&&t