diff --git a/Pages/Demos/DelayedValidation.cshtml b/Pages/Demos/DelayedValidation.cshtml deleted file mode 100644 index 358ea2c..0000000 --- a/Pages/Demos/DelayedValidation.cshtml +++ /dev/null @@ -1,28 +0,0 @@ -@page -@model DemoWeb.Pages.Demos.DelayedValidation -@{ - Layout = "Shared/_Layout"; -} - -
- Please correct the following errors -
- -
- Required Email input with delayed validation. -
-
- - - -
- -
-
- -@section Scripts { - -} \ No newline at end of file diff --git a/Pages/Demos/ImmediateValidation.cshtml b/Pages/Demos/ImmediateValidation.cshtml new file mode 100644 index 0000000..036f033 --- /dev/null +++ b/Pages/Demos/ImmediateValidation.cshtml @@ -0,0 +1,35 @@ +@page +@model DemoWeb.Pages.Demos.ImmediateValidation +@{ + Layout = "Shared/_Layout"; +} + +
+ Please correct the following errors +
+ +
+ Required Email input with data-val-event specified for immediate validation as you type. +
+ Input with immediate validation (data-val-event="input") +
+ + + +
+ Input with default behavior +
+ + + +
+ +
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/Pages/Demos/DelayedValidation.cshtml.cs b/Pages/Demos/ImmediateValidation.cshtml.cs similarity index 62% rename from Pages/Demos/DelayedValidation.cshtml.cs rename to Pages/Demos/ImmediateValidation.cshtml.cs index 6e5aac2..081673d 100644 --- a/Pages/Demos/DelayedValidation.cshtml.cs +++ b/Pages/Demos/ImmediateValidation.cshtml.cs @@ -1,14 +1,18 @@ -using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.Mvc.Rendering; +using System.ComponentModel.DataAnnotations; namespace DemoWeb.Pages.Demos; -public class DelayedValidation : PageModel +public class ImmediateValidation : PageModel { [BindProperty] [Required] [EmailAddress] public string? EmailAddress { get; set; } + + [BindProperty] + [Required] + [EmailAddress] + public string? AnotherEmailAddress { get; set; } } \ No newline at end of file diff --git a/Pages/Index.cshtml b/Pages/Index.cshtml index 746587f..d091bf8 100644 --- a/Pages/Index.cshtml +++ b/Pages/Index.cshtml @@ -14,7 +14,7 @@
  • Form Action
  • Disabled inputs ({watch: true})
  • Disabled inputs (no watch)
  • -
  • Delayed Validation
  • +
  • Immediate Validation (data-val-event="input")
  • @if (Model.StatusMessage != null) { diff --git a/dist/aspnet-validation.js b/dist/aspnet-validation.js index 8372f15..8d09291 100644 --- a/dist/aspnet-validation.js +++ b/dist/aspnet-validation.js @@ -690,7 +690,6 @@ var ValidationService = /** @class */ (function () { root: document.body, watch: false, addNoValidate: true, - delayedValidation: false, }; /** * Override CSS class name for input validation error. Default: 'input-validation-error' @@ -1080,6 +1079,7 @@ var ValidationService = /** @class */ (function () { */ ValidationService.prototype.addInput = function (input) { var _this = this; + var _a; var uid = this.getElementUID(input); var directives = this.parseDirectives(input.attributes); this.validators[uid] = this.createValidator(input, directives); @@ -1097,10 +1097,10 @@ var ValidationService = /** @class */ (function () { validate = this.validators[uid]; if (!validate) return [2 /*return*/, true]; - if (this.options.delayedValidation && + if (!input.dataset.valEvent && event && event.type === 'input' && !input.classList.contains(this.ValidationInputCssClassName)) { - // When delayedValidation=true, "input" only takes it back to valid. "Change" can make it invalid. + // When no data-val-event specified on a field, "input" event only takes it back to valid. "Change" event can make it invalid. return [2 /*return*/, true]; } this.logger.log('Validating', { event: event }); @@ -1127,8 +1127,8 @@ var ValidationService = /** @class */ (function () { cb(event, callback); }, _this.debounce); }; - var validateEvent = input.dataset.valEvent || input instanceof HTMLSelectElement ? 'change' : - (this.options.delayedValidation ? 'input change' : 'input'); + var defaultEvent = input instanceof HTMLSelectElement ? 'change' : 'input change'; + var validateEvent = (_a = input.dataset.valEvent) !== null && _a !== void 0 ? _a : defaultEvent; var events = validateEvent.split(' '); events.forEach(function (eventName) { input.addEventListener(eventName, cb.debounced); @@ -1393,7 +1393,6 @@ var ValidationService = /** @class */ (function () { * Load default validation providers and scans the entire document when ready. * @param options.watch If set to true, a MutationObserver will be used to continuously watch for new elements that provide validation directives. * @param options.addNoValidate If set to true (the default), a novalidate attribute will be added to the containing form in validate elements. - * @param options.delayedValidation If set to false (the default), validation happens while user inputs. If set to true, validation happens on blur, unless input is already invalid, in which case it will validate on input to indicate the value is valid as soon as possible. */ ValidationService.prototype.bootstrap = function (options) { var _this = this; diff --git a/dist/aspnet-validation.min.js b/dist/aspnet-validation.min.js index 5f7108c..3adfac0 100644 --- a/dist/aspnet-validation.min.js +++ b/dist/aspnet-validation.min.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.aspnetValidation=t():e.aspnetValidation=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t),n.d(t,"isValidatable",(function(){return s})),n.d(t,"MvcValidationProviders",(function(){return d})),n.d(t,"ValidationService",(function(){return c}));var r=function(e,t,n,r){return new(n||(n=Promise))((function(i,a){function s(e){try{l(r.next(e))}catch(e){a(e)}}function o(e){try{l(r.throw(e))}catch(e){a(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,o)}l((r=r.apply(e,t||[])).next())}))},i=function(e,t){var n,r,i,a,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:o(0),throw:o(1),return:o(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function o(o){return function(l){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,o[0]&&(s=0)),s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]-1){var a=n.substring(0,i)+"."+r,o=document.getElementsByName(a)[0];if(s(o))return o}return e.form.querySelector(l("[name=".concat(r,"]")))}var d=function(){this.required=function(e,t,n){var r=t.type.toLowerCase();if("checkbox"===r||"radio"===r){for(var i=0,a=Array.from(t.form.querySelectorAll(l("[name='".concat(t.name,"'][type='").concat(r,"']"))));ii)return!1}return!0},this.compare=function(e,t,n){if(!n.other)return!0;var r=u(t,n.other);return!r||r.value===e},this.range=function(e,t,n){if(!e)return!0;var r=parseFloat(e);return!isNaN(r)&&(!(n.min&&rparseFloat(n.max)))},this.regex=function(e,t,n){return!e||!n.pattern||new RegExp(n.pattern).test(e)},this.email=function(e,t,n){return!e||/^([^\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,})+$/.test(e)},this.creditcard=function(e,t,n){if(!e)return!0;if(/[^0-9 \-]+/.test(e))return!1;var r,i,a=0,s=0,o=!1;if((e=e.replace(/\D/g,"")).length<13||e.length>19)return!1;for(r=e.length-1;r>=0;r--)i=e.charAt(r),s=parseInt(i,10),o&&(s*=2)>9&&(s-=9),a+=s,o=!o;return a%10==0},this.url=function(e,t,n){if(!e)return!0;var r=e.toLowerCase();return r.indexOf("http://")>-1||r.indexOf("https://")>-1||r.indexOf("ftp://")>-1},this.phone=function(e,t,n){return!e||!/[\+\-\s][\-\s]/g.test(e)&&/^\+?[0-9\-\s]+$/.test(e)},this.remote=function(e,t,n){if(!e)return!0;for(var r=n.additionalfields.split(","),i={},a=0,s=r;a=200&&r.status<300){var i=JSON.parse(r.responseText);e(i)}else t({status:r.status,statusText:r.statusText,data:r.responseText})},r.onerror=function(e){t({status:r.status,statusText:r.statusText,data:r.responseText})}}))}},c=function(){function e(e){var t=this;this.providers={},this.messageFor={},this.elementUIDs=[],this.elementByUID={},this.formInputs={},this.validators={},this.formEvents={},this.inputEvents={},this.summary={},this.debounce=300,this.allowHiddenFields=!1,this.validateForm=function(e,n){return r(t,void 0,void 0,(function(){var t,r,a;return i(this,(function(i){switch(i.label){case 0:if(!(e instanceof HTMLFormElement))throw new Error("validateForm() can only be called on
    elements");return t=this.getElementUID(e),r=this.formEvents[t],(a=!r)?[3,2]:[4,r(void 0,n)];case 1:a=i.sent(),i.label=2;case 2:return[2,a]}}))}))},this.validateField=function(e,n){return r(t,void 0,void 0,(function(){var t,r,a;return i(this,(function(i){switch(i.label){case 0:return t=this.getElementUID(e),r=this.inputEvents[t],(a=!r)?[3,2]:[4,r(void 0,n)];case 1:a=i.sent(),i.label=2;case 2:return[2,a]}}))}))},this.preValidate=function(e){e.preventDefault(),e.stopImmediatePropagation()},this.handleValidated=function(e,n,r){if(!(e instanceof HTMLFormElement))throw new Error("handleValidated() can only be called on elements");n?r&&t.submitValidForm(e,r):t.focusFirstInvalid(e)},this.submitValidForm=function(e,t){if(!(e instanceof HTMLFormElement))throw new Error("submitValidForm() can only be called on elements");var n=new SubmitEvent("submit",t);if(e.dispatchEvent(n)){var r=t.submitter,i=null,a=e.action;if(r){var s=r.getAttribute("name");s&&((i=document.createElement("input")).type="hidden",i.name=s,i.value=r.getAttribute("value"),e.appendChild(i));var o=r.getAttribute("formaction");o&&(e.action=o)}try{e.submit()}finally{i&&e.removeChild(i),e.action=a}}},this.focusFirstInvalid=function(e){if(!(e instanceof HTMLFormElement))throw new Error("focusFirstInvalid() can only be called on elements");var n=t.getElementUID(e),r=t.formInputs[n],i=null==r?void 0:r.find((function(e){return t.summary[e]}));if(i){var a=t.elementByUID[i];a instanceof HTMLElement&&a.focus()}},this.isValid=function(e,n,r){if(void 0===n&&(n=!0),!(e instanceof HTMLFormElement))throw new Error("isValid() can only be called on elements");n&&t.validateForm(e,r);var i=t.getElementUID(e),a=t.formInputs[i];return!(!0===(null==a?void 0:a.some((function(e){return t.summary[e]}))))},this.isFieldValid=function(e,n,r){void 0===n&&(n=!0),n&&t.validateField(e,r);var i=t.getElementUID(e);return void 0===t.summary[i]},this.options={root:document.body,watch:!1,addNoValidate:!0,delayedValidation:!1},this.ValidationInputCssClassName="input-validation-error",this.ValidationInputValidCssClassName="input-validation-valid",this.ValidationMessageCssClassName="field-validation-error",this.ValidationMessageValidCssClassName="field-validation-valid",this.ValidationSummaryCssClassName="validation-summary-errors",this.ValidationSummaryValidCssClassName="validation-summary-valid",this.logger=e||a}return e.prototype.addProvider=function(e,t){this.providers[e]||(this.logger.log("Registered provider: %s",e),this.providers[e]=t)},e.prototype.addMvcProviders=function(){var e=new d;this.addProvider("required",e.required),this.addProvider("length",e.stringLength),this.addProvider("maxlength",e.stringLength),this.addProvider("minlength",e.stringLength),this.addProvider("equalto",e.compare),this.addProvider("range",e.range),this.addProvider("regex",e.regex),this.addProvider("creditcard",e.creditcard),this.addProvider("email",e.email),this.addProvider("url",e.url),this.addProvider("phone",e.phone),this.addProvider("remote",e.remote)},e.prototype.scanMessages=function(e,t){for(var n=0,r=Array.from(e.querySelectorAll("span[form]"));n=0?a.splice(s,1):this.logger.log("Validation element for '%s' was already removed",name,t)}}}},e.prototype.parseDirectives=function(e){for(var t={},n={},r="data-val-".length,i=0;i=0?(i.splice(a,1),i.length||(null===(n=this.formEvents[r])||void 0===n||n.remove(),delete this.formEvents[r],delete this.formInputs[r],delete this.messageFor[r])):this.logger.log("Form input for UID '%s' was already removed",t)}},e.prototype.addInput=function(e){var t=this,n=this.getElementUID(e),a=this.parseDirectives(e.attributes);if(this.validators[n]=this.createValidator(e,a),e.form&&this.trackFormInput(e.form,n),!this.inputEvents[n]){var s=function(a,s){return r(t,void 0,void 0,(function(){var t,r,o;return i(this,(function(i){switch(i.label){case 0:if(!(t=this.validators[n]))return[2,!0];if(this.options.delayedValidation&&a&&"input"===a.type&&!e.classList.contains(this.ValidationInputCssClassName))return[2,!0];this.logger.log("Validating",{event:a}),i.label=1;case 1:return i.trys.push([1,3,,4]),[4,t()];case 2:return r=i.sent(),s(r),[2,r];case 3:return o=i.sent(),this.logger.log("Validation error",o),[2,!1];case 4:return[2]}}))}))},o=0;s.debounced=function(e,n){clearTimeout(o),o=setTimeout((function(){s(e,n)}),t.debounce)};var l=(e.dataset.valEvent||e instanceof HTMLSelectElement?"change":this.options.delayedValidation?"input change":"input").split(" ");l.forEach((function(t){e.addEventListener(t,s.debounced)})),s.remove=function(){l.forEach((function(t){e.removeEventListener(t,s.debounced)}))},this.inputEvents[n]=s}},e.prototype.removeInput=function(e){var t=this.getElementUID(e),n=this.inputEvents[t];(null==n?void 0:n.remove)&&(n.remove(),delete n.remove),delete this.summary[t],delete this.inputEvents[t],delete this.validators[t],e.form&&this.untrackFormInput(e.form,t)},e.prototype.scanInputs=function(e,t){var n=Array.from(e.querySelectorAll(l('[data-val="true"]')));s(e)&&"true"===e.getAttribute("data-val")&&n.push(e);for(var r=0;r-1)){var i=document.createElement("li");i.innerHTML=this.summary[n],t.appendChild(i),e.push(this.summary[n])}}return t},e.prototype.renderSummary=function(){var e=document.querySelectorAll('[data-valmsg-summary="true"]');if(e.length){var t=JSON.stringify(this.summary,Object.keys(this.summary).sort());if(t!==this.renderedSummaryJSON){this.renderedSummaryJSON=t;for(var n=this.createSummaryDOM(),r=0;r0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]-1){var a=n.substring(0,i)+"."+r,o=document.getElementsByName(a)[0];if(s(o))return o}return e.form.querySelector(l("[name=".concat(r,"]")))}var d=function(){this.required=function(e,t,n){var r=t.type.toLowerCase();if("checkbox"===r||"radio"===r){for(var i=0,a=Array.from(t.form.querySelectorAll(l("[name='".concat(t.name,"'][type='").concat(r,"']"))));ii)return!1}return!0},this.compare=function(e,t,n){if(!n.other)return!0;var r=u(t,n.other);return!r||r.value===e},this.range=function(e,t,n){if(!e)return!0;var r=parseFloat(e);return!isNaN(r)&&(!(n.min&&rparseFloat(n.max)))},this.regex=function(e,t,n){return!e||!n.pattern||new RegExp(n.pattern).test(e)},this.email=function(e,t,n){return!e||/^([^\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,})+$/.test(e)},this.creditcard=function(e,t,n){if(!e)return!0;if(/[^0-9 \-]+/.test(e))return!1;var r,i,a=0,s=0,o=!1;if((e=e.replace(/\D/g,"")).length<13||e.length>19)return!1;for(r=e.length-1;r>=0;r--)i=e.charAt(r),s=parseInt(i,10),o&&(s*=2)>9&&(s-=9),a+=s,o=!o;return a%10==0},this.url=function(e,t,n){if(!e)return!0;var r=e.toLowerCase();return r.indexOf("http://")>-1||r.indexOf("https://")>-1||r.indexOf("ftp://")>-1},this.phone=function(e,t,n){return!e||!/[\+\-\s][\-\s]/g.test(e)&&/^\+?[0-9\-\s]+$/.test(e)},this.remote=function(e,t,n){if(!e)return!0;for(var r=n.additionalfields.split(","),i={},a=0,s=r;a=200&&r.status<300){var i=JSON.parse(r.responseText);e(i)}else t({status:r.status,statusText:r.statusText,data:r.responseText})},r.onerror=function(e){t({status:r.status,statusText:r.statusText,data:r.responseText})}}))}},c=function(){function e(e){var t=this;this.providers={},this.messageFor={},this.elementUIDs=[],this.elementByUID={},this.formInputs={},this.validators={},this.formEvents={},this.inputEvents={},this.summary={},this.debounce=300,this.allowHiddenFields=!1,this.validateForm=function(e,n){return r(t,void 0,void 0,(function(){var t,r,a;return i(this,(function(i){switch(i.label){case 0:if(!(e instanceof HTMLFormElement))throw new Error("validateForm() can only be called on elements");return t=this.getElementUID(e),r=this.formEvents[t],(a=!r)?[3,2]:[4,r(void 0,n)];case 1:a=i.sent(),i.label=2;case 2:return[2,a]}}))}))},this.validateField=function(e,n){return r(t,void 0,void 0,(function(){var t,r,a;return i(this,(function(i){switch(i.label){case 0:return t=this.getElementUID(e),r=this.inputEvents[t],(a=!r)?[3,2]:[4,r(void 0,n)];case 1:a=i.sent(),i.label=2;case 2:return[2,a]}}))}))},this.preValidate=function(e){e.preventDefault(),e.stopImmediatePropagation()},this.handleValidated=function(e,n,r){if(!(e instanceof HTMLFormElement))throw new Error("handleValidated() can only be called on elements");n?r&&t.submitValidForm(e,r):t.focusFirstInvalid(e)},this.submitValidForm=function(e,t){if(!(e instanceof HTMLFormElement))throw new Error("submitValidForm() can only be called on elements");var n=new SubmitEvent("submit",t);if(e.dispatchEvent(n)){var r=t.submitter,i=null,a=e.action;if(r){var s=r.getAttribute("name");s&&((i=document.createElement("input")).type="hidden",i.name=s,i.value=r.getAttribute("value"),e.appendChild(i));var o=r.getAttribute("formaction");o&&(e.action=o)}try{e.submit()}finally{i&&e.removeChild(i),e.action=a}}},this.focusFirstInvalid=function(e){if(!(e instanceof HTMLFormElement))throw new Error("focusFirstInvalid() can only be called on elements");var n=t.getElementUID(e),r=t.formInputs[n],i=null==r?void 0:r.find((function(e){return t.summary[e]}));if(i){var a=t.elementByUID[i];a instanceof HTMLElement&&a.focus()}},this.isValid=function(e,n,r){if(void 0===n&&(n=!0),!(e instanceof HTMLFormElement))throw new Error("isValid() can only be called on elements");n&&t.validateForm(e,r);var i=t.getElementUID(e),a=t.formInputs[i];return!(!0===(null==a?void 0:a.some((function(e){return t.summary[e]}))))},this.isFieldValid=function(e,n,r){void 0===n&&(n=!0),n&&t.validateField(e,r);var i=t.getElementUID(e);return void 0===t.summary[i]},this.options={root:document.body,watch:!1,addNoValidate:!0},this.ValidationInputCssClassName="input-validation-error",this.ValidationInputValidCssClassName="input-validation-valid",this.ValidationMessageCssClassName="field-validation-error",this.ValidationMessageValidCssClassName="field-validation-valid",this.ValidationSummaryCssClassName="validation-summary-errors",this.ValidationSummaryValidCssClassName="validation-summary-valid",this.logger=e||a}return e.prototype.addProvider=function(e,t){this.providers[e]||(this.logger.log("Registered provider: %s",e),this.providers[e]=t)},e.prototype.addMvcProviders=function(){var e=new d;this.addProvider("required",e.required),this.addProvider("length",e.stringLength),this.addProvider("maxlength",e.stringLength),this.addProvider("minlength",e.stringLength),this.addProvider("equalto",e.compare),this.addProvider("range",e.range),this.addProvider("regex",e.regex),this.addProvider("creditcard",e.creditcard),this.addProvider("email",e.email),this.addProvider("url",e.url),this.addProvider("phone",e.phone),this.addProvider("remote",e.remote)},e.prototype.scanMessages=function(e,t){for(var n=0,r=Array.from(e.querySelectorAll("span[form]"));n=0?a.splice(s,1):this.logger.log("Validation element for '%s' was already removed",name,t)}}}},e.prototype.parseDirectives=function(e){for(var t={},n={},r="data-val-".length,i=0;i=0?(i.splice(a,1),i.length||(null===(n=this.formEvents[r])||void 0===n||n.remove(),delete this.formEvents[r],delete this.formInputs[r],delete this.messageFor[r])):this.logger.log("Form input for UID '%s' was already removed",t)}},e.prototype.addInput=function(e){var t,n=this,a=this.getElementUID(e),s=this.parseDirectives(e.attributes);if(this.validators[a]=this.createValidator(e,s),e.form&&this.trackFormInput(e.form,a),!this.inputEvents[a]){var o=function(t,s){return r(n,void 0,void 0,(function(){var n,r,o;return i(this,(function(i){switch(i.label){case 0:if(!(n=this.validators[a]))return[2,!0];if(!e.dataset.valEvent&&t&&"input"===t.type&&!e.classList.contains(this.ValidationInputCssClassName))return[2,!0];this.logger.log("Validating",{event:t}),i.label=1;case 1:return i.trys.push([1,3,,4]),[4,n()];case 2:return r=i.sent(),s(r),[2,r];case 3:return o=i.sent(),this.logger.log("Validation error",o),[2,!1];case 4:return[2]}}))}))},l=0;o.debounced=function(e,t){clearTimeout(l),l=setTimeout((function(){o(e,t)}),n.debounce)};var u=e instanceof HTMLSelectElement?"change":"input change",d=(null!==(t=e.dataset.valEvent)&&void 0!==t?t:u).split(" ");d.forEach((function(t){e.addEventListener(t,o.debounced)})),o.remove=function(){d.forEach((function(t){e.removeEventListener(t,o.debounced)}))},this.inputEvents[a]=o}},e.prototype.removeInput=function(e){var t=this.getElementUID(e),n=this.inputEvents[t];(null==n?void 0:n.remove)&&(n.remove(),delete n.remove),delete this.summary[t],delete this.inputEvents[t],delete this.validators[t],e.form&&this.untrackFormInput(e.form,t)},e.prototype.scanInputs=function(e,t){var n=Array.from(e.querySelectorAll(l('[data-val="true"]')));s(e)&&"true"===e.getAttribute("data-val")&&n.push(e);for(var r=0;r-1)){var i=document.createElement("li");i.innerHTML=this.summary[n],t.appendChild(i),e.push(this.summary[n])}}return t},e.prototype.renderSummary=function(){var e=document.querySelectorAll('[data-valmsg-summary="true"]');if(e.length){var t=JSON.stringify(this.summary,Object.keys(this.summary).sort());if(t!==this.renderedSummaryJSON){this.renderedSummaryJSON=t;for(var n=this.createSummaryDOM(),r=0;r\n */\nexport interface StringKeyValuePair {\n [key: string]: string\n}\n\n/**\n * A duplex key-value pair for an element, by GUID or its DOM object reference.\n */\ninterface ElementUID {\n node: Element,\n uid: string;\n}\n\n/**\n * A simple logging interface that mirrors the Console object.\n */\nexport interface Logger {\n log(message: string, ...args: any[]): void;\n warn(message: string, ...args: any[]): void;\n}\n\nconst nullLogger = new (class implements Logger {\n log(_: string, ..._args: any[]): void { }\n warn = globalThis.console.warn;\n})();\n\n/**\n * An `HTMLElement` that can be validated (`input`, `select`, `textarea`).\n */\nexport type ValidatableElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n\n/**\n * Checks if `element` is validatable (`input`, `select`, `textarea`).\n * @param element The element to check.\n * @returns `true` if validatable, otherwise `false`.\n */\nexport const isValidatable = (element: Node): element is ValidatableElement =>\n element instanceof HTMLInputElement\n || element instanceof HTMLSelectElement\n || element instanceof HTMLTextAreaElement;\n\nconst validatableElementTypes = ['input', 'select', 'textarea'];\n\n/**\n * Generates a selector to match validatable elements (`input`, `select`, `textarea`).\n * @param selector An optional selector to apply to the valid input types, e.g. `[data-val=\"true\"]`.\n * @returns The validatable elements.\n */\nconst validatableSelector = (selector?: string) =>\n validatableElementTypes.map(t => `${t}${selector || ''}`).join(',');\n\n/**\n * Parameters passed into validation providers from the element attributes.\n * error property is read from data-val-[Provider Name] attribute.\n * params property is populated from data-val-[Provider Name]-[Parameter Name] attributes.\n */\nexport interface ValidationDirectiveBindings {\n error: string,\n params: StringKeyValuePair\n}\n\n/**\n * A key-value pair describing what validations to enforce to an input element, with respective parameters.\n */\nexport type ValidationDirective = {\n [key: string]: ValidationDirectiveBindings\n};\n\n/**\n * Validation plugin signature with multitype return.\n * Boolean return signifies the validation result, which uses the default validation error message read from the element attribute.\n * String return signifies failed validation, which then will be used as the validation error message.\n * Promise return signifies asynchronous plugin behavior, with same behavior as Boolean or String.\n */\nexport type ValidationProvider = (value: string, element: ValidatableElement, params: StringKeyValuePair) => boolean | string | Promise;\n\n/**\n * Callback to receive the result of validating a form.\n */\nexport type ValidatedCallback = (success: boolean) => void;\n\ninterface ValidationEventCallback {\n (e?: TEvent, callback?: ValidatedCallback): Promise;\n debounced?: (e?: TEvent, callback?: ValidatedCallback) => void;\n remove?: () => void;\n}\n\n/**\n * A callback method signature that kickstarts a new validation task for an input element, as a Boolean Promise.\n */\ntype Validator = () => Promise;\n\n/**\n * Resolves and returns the element referred by original element using ASP.NET selector logic.\n * @param element - The input to validate\n * @param selector - Used to find the field. Ex. *.Password where * replaces whatever prefixes asp.net might add.\n */\nfunction getRelativeFormElement(element: ValidatableElement, selector: string): ValidatableElement {\n // example elementName: Form.PasswordConfirm, Form.Email\n // example selector (dafuq): *.Password, *.__RequestVerificationToken\n // example result element name: Form.Password, __RequestVerificationToken\n\n let elementName = element.name;\n let selectedName = selector.substring(2); // Password, __RequestVerificationToken\n let objectName = '';\n\n let dotLocation = elementName.lastIndexOf('.');\n if (dotLocation > -1) {\n // Form\n objectName = elementName.substring(0, dotLocation);\n\n // Form.Password\n const relativeElementName = objectName + '.' + selectedName;\n const relativeElement = document.getElementsByName(relativeElementName)[0];\n if (isValidatable(relativeElement)) {\n return relativeElement;\n }\n }\n\n // __RequestVerificationToken\n return element.form.querySelector(validatableSelector(`[name=${selectedName}]`));\n}\n\n/**\n * Contains default implementations for ASP.NET Core MVC validation attributes.\n */\nexport class MvcValidationProviders {\n /**\n * Validates whether the input has a value.\n */\n required: ValidationProvider = (value, element, params) => {\n // Handle single and multiple checkboxes/radio buttons.\n const elementType = element.type.toLowerCase();\n if (elementType === \"checkbox\" || elementType === \"radio\") {\n const allElementsOfThisName = Array.from(element.form.querySelectorAll(validatableSelector(`[name='${element.name}'][type='${elementType}']`)));\n for (let element of allElementsOfThisName) {\n if (element instanceof HTMLInputElement && element.checked === true) {\n return true;\n }\n }\n\n // Checkboxes do not submit a value when unchecked. To work around this, platforms such as ASP.NET render a\n // hidden input with the same name as the checkbox so that a value (\"false\") is still submitted even when\n // the checkbox is not checked. We check this special case here.\n if (elementType === \"checkbox\") {\n const checkboxHiddenInput = element.form.querySelector(`input[name='${element.name}'][type='hidden']`);\n if (checkboxHiddenInput instanceof HTMLInputElement && checkboxHiddenInput.value === \"false\") {\n return true;\n }\n }\n\n return false;\n }\n\n // Default behavior otherwise (trim ensures whitespace only is not seen as valid).\n return Boolean(value?.trim());\n }\n\n /**\n * Validates whether the input value satisfies the length contstraint.\n */\n stringLength: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n if (params.min) {\n let min = parseInt(params.min);\n if (value.length < min) {\n return false;\n }\n }\n\n if (params.max) {\n let max = parseInt(params.max);\n if (value.length > max) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Validates whether the input value is equal to another input value.\n */\n compare: ValidationProvider = (value, element, params) => {\n if (!params.other) {\n return true;\n }\n\n let otherElement = getRelativeFormElement(element, params.other);\n if (!otherElement) {\n return true;\n }\n\n return (otherElement.value === value);\n }\n\n /**\n * Validates whether the input value is a number within a given range.\n */\n range: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n let val = parseFloat(value);\n if (isNaN(val)) {\n return false;\n }\n\n if (params.min) {\n let min = parseFloat(params.min);\n if (val < min) {\n return false;\n }\n }\n\n if (params.max) {\n let max = parseFloat(params.max);\n if (val > max) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Validates whether the input value satisfies a regular expression pattern.\n */\n regex: ValidationProvider = (value, element, params) => {\n if (!value || !params.pattern) {\n return true;\n }\n\n let r = new RegExp(params.pattern);\n return r.test(value);\n }\n\n /**\n * Validates whether the input value is an email in accordance to RFC822 specification, with a top level domain.\n */\n email: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n // RFC822 email address with .TLD validation\n // (c) Richard Willis, Chris Ferdinandi, MIT Licensed\n // https://gist.github.com/badsyntax/719800\n // https://gist.github.com/cferdinandi/d04aad4ce064b8da3edf21e26f8944c4\n\n let r = /^([^\\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 return r.test(value);\n }\n\n /**\n * Validates whether the input value is a credit card number, with Luhn's Algorithm.\n */\n creditcard: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n // (c) jquery-validation, MIT Licensed\n // https://github.com/jquery-validation/jquery-validation/blob/master/src/additional/creditcard.js\n // based on https://en.wikipedia.org/wiki/Luhn_algorithm\n\n // Accept only spaces, digits and dashes\n if (/[^0-9 \\-]+/.test(value)) {\n return false;\n }\n\n var nCheck = 0,\n nDigit = 0,\n bEven = false,\n n, cDigit;\n\n value = value.replace(/\\D/g, \"\");\n\n // Basing min and max length on https://developer.ean.com/general_info/Valid_Credit_Card_Types\n if (value.length < 13 || value.length > 19) {\n return false;\n }\n\n for (n = value.length - 1; n >= 0; n--) {\n cDigit = value.charAt(n);\n nDigit = parseInt(cDigit, 10);\n if (bEven) {\n if ((nDigit *= 2) > 9) {\n nDigit -= 9;\n }\n }\n\n nCheck += nDigit;\n bEven = !bEven;\n }\n\n return (nCheck % 10) === 0;\n }\n\n /**\n * Validates whether the input value is a URL.\n */\n url: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n let lowerCaseValue = value.toLowerCase();\n\n // Match the logic in `UrlAttribute`\n return lowerCaseValue.indexOf('http://') > -1\n || lowerCaseValue.indexOf('https://') > -1\n || lowerCaseValue.indexOf('ftp://') > -1;\n }\n\n /**\n * Validates whether the input value is a phone number.\n */\n phone: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n // Allows whitespace or dash as number separator because some people like to do that...\n let consecutiveSeparator = /[\\+\\-\\s][\\-\\s]/g;\n if (consecutiveSeparator.test(value)) {\n return false;\n }\n\n let r = /^\\+?[0-9\\-\\s]+$/;\n return r.test(value);\n }\n\n /**\n * Asynchronously validates the input value to a JSON GET API endpoint.\n */\n remote: ValidationProvider = (value, element, params) => {\n if (!value) {\n return true;\n }\n\n // params.additionalfields: *.Email,*.Username\n let fieldSelectors: string[] = (params.additionalfields as string).split(',');\n let fields: StringKeyValuePair = {};\n\n for (let fieldSelector of fieldSelectors) {\n let fieldName = fieldSelector.substr(2);\n let fieldElement = getRelativeFormElement(element, fieldSelector);\n\n let hasValue = Boolean(fieldElement && fieldElement.value);\n if (!hasValue) {\n continue;\n }\n\n if (fieldElement instanceof HTMLInputElement &&\n (fieldElement.type === 'checkbox' || fieldElement.type === 'radio')) {\n fields[fieldName] = fieldElement.checked ? fieldElement.value : '';\n } else {\n fields[fieldName] = fieldElement.value;\n }\n }\n\n let url: string = params['url'];\n\n let encodedParams: string[] = [];\n for (let fieldName in fields) {\n let encodedParam = encodeURIComponent(fieldName) + '=' + encodeURIComponent(fields[fieldName]);\n encodedParams.push(encodedParam);\n }\n let payload = encodedParams.join('&');\n\n return new Promise((ok, reject) => {\n let request = new XMLHttpRequest();\n\n if (params.type && params.type.toLowerCase() === 'post') {\n let postData = new FormData();\n for (let fieldName in fields) {\n postData.append(fieldName, fields[fieldName]);\n }\n request.open('post', url);\n request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n request.send(payload);\n } else {\n request.open('get', url + '?' + payload);\n request.send();\n }\n\n request.onload = e => {\n if (request.status >= 200 && request.status < 300) {\n let data = JSON.parse(request.responseText);\n ok(data);\n } else {\n reject({\n status: request.status,\n statusText: request.statusText,\n data: request.responseText\n });\n }\n };\n\n request.onerror = e => {\n reject({\n status: request.status,\n statusText: request.statusText,\n data: request.responseText\n });\n };\n });\n }\n}\n\n/**\n * Configuration for @type {ValidationService}.\n */\nexport interface ValidationServiceOptions {\n watch: boolean;\n root: ParentNode;\n addNoValidate: boolean;\n delayedValidation: boolean\n}\n\n/**\n * Responsible for managing the DOM elements and running the validation providers.\n */\nexport class ValidationService {\n /**\n * A key-value collection of loaded validation plugins.\n */\n private providers: { [name: string]: ValidationProvider } = {};\n\n /**\n * A key-value collection of form UIDs and their elements for displaying validation messages for an input (by DOM name).\n */\n private messageFor: { [formUID: string]: { [name: string]: Element[] } } = {};\n\n /**\n * A list of managed elements, each having a randomly assigned unique identifier (UID).\n */\n private elementUIDs: ElementUID[] = [];\n\n /**\n * A key-value collection of UID to Element for quick lookup.\n */\n private elementByUID: { [uid: string]: Element } = {};\n\n /**\n * A key-value collection of input UIDs for a UID.\n */\n private formInputs: { [formUID: string]: string[] } = {};\n\n /**\n * A key-value map for input UID to its validator factory.\n */\n private validators: { [inputUID: string]: Validator } = {};\n\n /**\n * A key-value map for form UID to its trigger element (submit event for ).\n */\n private formEvents: { [formUID: string]: ValidationEventCallback } = {};\n\n /**\n * A key-value map for element UID to its trigger element (input event for