diff --git a/app.js b/app.js index 98e923d..1d238f4 100644 --- a/app.js +++ b/app.js @@ -38,7 +38,7 @@ myApp.controller('Ctrl', ['$location', '$route', '$scope', '$translate', functio // -- Controller to use Angular-Validation Directive // ----------------------------------------------- myApp.controller('CtrlValidationDirective', ['$q', '$scope', 'validationService', function ($q, $scope, validationService) { - $scope.$validationOptions = { debounce: 1500 }; // you can change default debounce globally + $scope.$validationOptions = { debounce: 1500, preValidateFormElements: false }; // you can change default debounce globally $scope.submitForm = function() { if(new validationService().checkFormValidity($scope.form1)) { @@ -67,6 +67,9 @@ myApp.controller('CtrlValidationDirective', ['$q', '$scope', 'validationService' // -- Controller to use Angular-Validation Directive with 2 forms // --------------------------------------------------------------- myApp.controller('Ctrl2forms', ['$scope', 'validationService', function ($scope, validationService) { + // on this page we will pre-validate the form and show all errors on page load + $scope.$validationOptions = { debounce: 500, preValidateFormElements: true }; + $scope.submitForm = function() { if(new validationService().checkFormValidity($scope.form01)) { alert('All good, proceed with submit...'); @@ -103,7 +106,7 @@ myApp.controller('CtrlValidationService', ['$q', '$scope', '$translate', 'valida // #3 .addValidator({ elmName: 'inputX', rules: 'myRules'}) // the available object properties are the exact same set as the directive except that they are camelCase myValidation - .setGlobalOptions({ debounce: 1500, scope: $scope }) + .setGlobalOptions({ debounce: 1500, scope: $scope, isolatedScope: $scope, preValidateFormElements: false }) .addValidator({ elmName: 'input2', debounce: 3000, rules: 'numeric_signed|required'}) .addValidator('input3', 'float_signed|between_num:-0.6,99.5|required') .addValidator('input4', 'exact_len:4|regex:YYWW:=^(0[9]|1[0-9]|2[0-9]|3[0-9])(5[0-2]|[0-4][0-9])$:regex|required|integer') diff --git a/bower.json b/bower.json index 8dabda4..d412731 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-validation-ghiscoding", - "version": "1.3.26", + "version": "1.3.27", "author": "Ghislain B.", "description": "Angular-Validation Directive and Service (ghiscoding)", "main": [ diff --git a/changelog.txt b/changelog.txt index 857196d..4c70d01 100644 --- a/changelog.txt +++ b/changelog.txt @@ -27,4 +27,5 @@ Angular-Validation change logs 1.3.23 (2015-05-05) Added option to display only last error message instead of all messages at once. Fixed a bug where changing route on View/Controller would make the ValidationSummary fail when coming back to original View/Controller, this bug was associated to the fact that the ValidationSummary kept growing from Controller to Controller, now this ValidationSummary is wipe out as soon as we detect a route change. 1.3.24 (2015-05-17) Replaced all `:param` inside each locale translations with a better standard of {0}, {1}, etc.. like C# `String.Format()`. Added a full Protractor End-to-End test suite (1000+ asserts). Fixed a few minor bugs found with Protractor test cases. Fixed issue #36, bower.json scripts in wrong order. 1.3.25 (2015-05-19) Enhancement #34 to add Remote Validation and updated Protractor to cover this new feature. -1.3.26 (2015-05-30) Added enhancement #35 - PreValidate the Form, display all errors on page load. \ No newline at end of file +1.3.26 (2015-05-30) Added enhancement #35 - PreValidate the Form, display all errors on page load. +1.3.27 (2015-06-02) Added possibility to use own isolated scope (issue #37 and #26). Fixed an implementation issue found from last revision (issue #35). Fixed email validation (issue #38). Fixed a performance issue found with onBlur which would run as much validations as there was characters inside the input when onBlur was called (abcdef => would make 6 validations) and this was extremely costly with a Remote validation call. Revisited the Remote validation to accept also the "As" alias "remote:vm.customRemoteValidation". Finally added and updated a few Protractor tests to cover all of the above and more. \ No newline at end of file diff --git a/dist/angular-validation.min.js b/dist/angular-validation.min.js index c96d6b2..e25b6e1 100644 --- a/dist/angular-validation.min.js +++ b/dist/angular-validation.min.js @@ -2,11 +2,11 @@ * Angular-Validation Directive and Service (ghiscoding) * http://github.com/ghiscoding/angular-validation * @author: Ghislain B. - * @version: 1.3.26 + * @version: 1.3.27 * @license: MIT - * @build: Sat May 30 2015 15:44:05 GMT-0400 (Eastern Daylight Time) + * @build: Tue Jun 02 2015 22:16:00 GMT-0400 (Eastern Daylight Time) */ -angular.module("ghiscoding.validation",["pascalprecht.translate"]).directive("validation",["$timeout","validationCommon","validationRules",function(i,t){return{restrict:"A",require:"ngModel",link:function(a,e,n,d){function l(){v=!0,i.cancel(r),s.updateErrorMsg(""),d.$setValidity("validation",!0),"undefined"!=typeof u&&e.unbind("blur",u)}function o(t){return s.validate(t,!1),s.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?(v=!1,(t||s.isFieldRequired())&&d.$setValidity("validation",!1),e.bind("blur",u=function(){v||a.$evalAsync(d.$setValidity("validation",s.validate(t,!0)))}),""!==t&&"undefined"!=typeof t||"NUMBER"!==e.prop("type").toUpperCase()?"SELECT"===e.prop("tagName").toUpperCase()?(d.$setValidity("validation",s.validate(t,!0)),t):("undefined"!=typeof t&&(s.updateErrorMsg(""),i.cancel(r),r=i(function(){a.$evalAsync(d.$setValidity("validation",s.validate(t,!0)))},s.typingLimit)),t):(i.cancel(r),d.$setValidity("validation",s.validate(t,!0)),t)):(l(),t)}var r,u,s=new t(a,e,n,d),v=!1;d.$formatters.unshift(o),d.$parsers.unshift(o),n.$observe("disabled",function(i){i?d.$setValidity("validation",!0):d.$setValidity("validation",s.validate(d.$viewValue,!0))})}}}]); -angular.module("ghiscoding.validation").factory("validationCommon",["$rootScope","$timeout","$translate","validationRules",function(t,e,a,r){function i(){var t=this,e={};t.validators=[],t.typingLimit=N,t.validatorAttrs.hasOwnProperty("debounce")?t.typingLimit=parseInt(t.validatorAttrs.debounce,10):t.validatorAttrs.hasOwnProperty("typingLimit")?t.typingLimit=parseInt(t.validatorAttrs.typingLimit,10):t.scope.$validationOptions&&t.scope.$validationOptions.hasOwnProperty("debounce")&&(t.typingLimit=parseInt(t.scope.$validationOptions.debounce,10));var a=t.validatorAttrs.hasOwnProperty("rules")?t.validatorAttrs.rules:t.validatorAttrs.validation;if(a.indexOf("regex:")>=0){var i=a.match("regex:(.*?):regex");if(i.length<2)throw'Regex validator within the validation needs to be define with an opening "regex:" and a closing ":regex", please review your validator.';var n=i[1].split(":=");e={message:n[0],pattern:n[1]},a=a.replace(i[0],"regex:")}var s=a.split("|");if(s){t.bFieldRequired=a.indexOf("required")>=0?!0:!1;for(var o=0,l=s.length;l>o;o++){var d=s[o].split(":"),u=s[o].indexOf("alt=")>=0?!0:!1;t.validators[o]=r.getElementValidators({altText:u===!0?2===d.length?d[1]:d[2]:"",customRegEx:e,rule:d[0],ruleParams:u&&2===d.length?null:d[1]})}}return t}function n(t){return y(F,"fieldName",t)}function s(){return F}function o(t,e,a,r){this.scope=t,this.elm=e,this.ctrl=r,this.validatorAttrs=a,g(e,a,r,t),this.defineValidation()}function l(){var t=this;return t.bFieldRequired}function d(t){var e=A(F,"fieldName",t);e>=0&&F.splice(e,1)}function u(t,e){var a=A(t,"field",e);a>=0&&t.splice(a,1),a=A(U,"field",e),a>=0&&U.splice(a,1)}function p(t){T=t}function m(t){V=t}function c(t,e){var r=this;e&&e.obj&&(r=e.obj,r.validatorAttrs=e.obj.attrs);var i=e&&e.elm?e.elm:r.elm,n=i&&i.attr("name")?i.attr("name"):null;if("undefined"==typeof n||null===n){var s=i?i.attr("ng-model"):"unknown";throw'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="'+s+'"'}var o=e&&e.translate?a.instant(t):t,l=n.replace(/[|&;$%@"<>()+,\[\]\{\}]/g,""),d=null;if(r.validatorAttrs&&r.validatorAttrs.hasOwnProperty("validationErrorTo")){var u=r.validatorAttrs.validationErrorTo.charAt(0),p="."===u||"#"===u?r.validatorAttrs.validationErrorTo:"#"+r.validatorAttrs.validationErrorTo;d=angular.element(document.querySelector(p))}d&&0!==d.length||(d=angular.element(document.querySelector(".validation-"+l)));var m=e&&e.isSubmitted?e.isSubmitted:!1;e&&!e.isValid&&(m||r.ctrl.$dirty||r.ctrl.$touched)?d.length>0?d.text(o):i.after(''+o+""):d.text("")}function f(t,e){var r,i,s=this,o=!0,l=!0,d="";"undefined"==typeof t&&(t="");for(var u=s.validatorAttrs&&s.validatorAttrs.name?s.validatorAttrs.name:s.attrs&&s.attrs.name?s.attrs.name:s.elm.attr("name"),p=n(u),m=s.validatorAttrs.hasOwnProperty("rules")?s.validatorAttrs.rules:s.validatorAttrs.validation,c=0,f=s.validators.length;f>c;c++){if(i=s.validators[c],"conditionalDate"===i.type){if(r=new RegExp(i.pattern,"i"),o=("\\S+"===i.pattern||m&&"required"===i.pattern)&&null===t?!1:r.test(t)){var g=i.dateType,y=S(t,g).getTime();if(2==i.params.length){var b=S(i.params[0],g).getTime(),A=S(i.params[1],g).getTime(),O=$(i.condition[0],y,b),E=$(i.condition[1],y,A);o=O&&E?!0:!1}else{var R=S(i.params[0],g).getTime();o=$(i.condition,y,R)}}}else if("conditionalNumber"===i.type)if(2==i.params.length){var O=$(i.condition[0],parseFloat(t),parseFloat(i.params[0])),E=$(i.condition[1],parseFloat(t),parseFloat(i.params[1]));o=O&&E?!0:!1}else o=$(i.condition,parseFloat(t),parseFloat(i.params[0]));else if("match"===i.type){var x=i.params[0],w=s.scope.$eval(x);o=w===t&&!!t}else if("remote"===i.type&&""!==t){s.ctrl.$processing=!0;var T=i.params[0],N=s.scope[T],F="function"==typeof N?N():null;if(!F||"function"!=typeof F.then)throw"Remote Validation requires a declared function (in your Controller) which also returns a promise, please review your code.";s.ctrl.$setValidity("remote",!1),function(t){F.then(function(a){s.ctrl.$processing=!1;var r=d+" ";"boolean"==typeof a?o=a?!0:!1:"object"==typeof a&&(o=a.isValid?!0:!1),o===!1&&(p.isValid=!1,r+=a.message?a.message:t,v(s,p,r,!1,e)),o===!0&&l===!0&&(p.isValid=!0,s.ctrl.$setValidity("remote",!0),v(s,p,"",!0,e))})}(i.altText)}else s.elm.prop("disabled")||1==parseInt(s.elm.attr("ng-disabled"))||"true"===s.elm.attr("ng-disabled")?o=!0:"string"==typeof t&&""===t&&"NUMBER"===s.elm.prop("type").toUpperCase()?o=!1:(r=new RegExp(i.pattern,"i"),o=("\\S+"===i.pattern||m&&"required"===i.pattern)&&null===t?!1:r.test(t));(!s.bFieldRequired&&!t||s.elm.prop("disabled")||1==parseInt(s.elm.attr("ng-disabled"))||"true"===s.elm.attr("ng-disabled"))&&(o=!0),o||(l=!1,function(t,r,i){var n=i.message;i.altText&&i.altText.length>0&&(n=i.altText.replace("alt=","")),a(n).then(function(a){d.length>0&&V?d=" "+(i&&i.params?String.format(a,i.params):a):d+=" "+(i&&i.params?String.format(a,i.params):a),v(s,t,d,l,e)})["catch"](function(){i.altText&&i.altText.length>0&&(d.length>0&&V?d=" "+n:d+=" "+n,v(s,t,d,l,e))})}(p,o,i))}return o&&(h(s,""),s.updateErrorMsg("",{isValid:o})),p&&(p.isValid=l,l&&(p.message="")),l}function g(t,e,r,i){var n=e.name?e.name:t.attr("name"),s=e&&e.friendlyName?a.instant(e.friendlyName):"",o={fieldName:n,friendlyName:s,elm:t,attrs:e,ctrl:r,scope:i,isValid:!1,message:""},l=A(F,"fieldName",t.attr("name"));return l>=0?F[l]=o:F.push(o),F}function v(t,e,a,r,i){a=a.trim(),h(e,a),e&&(e.message=a),(t.validatorAttrs.preValidateFormElements||t.scope.$validationOptions&&t.scope.$validationOptions.hasOwnProperty("preValidateFormElements"))&&(e&&"function"==typeof t.ctrl.$setTouched&&e.ctrl.$setTouched(),c(a,{isSubmitted:!0,isValid:r,obj:e})),i&&e&&!e.isValid?t.updateErrorMsg(a,{isValid:r}):e&&e.isValid&&h(e,"")}function h(t,e){if("undefined"!=typeof t&&null!=t){var r=t.validatorAttrs&&t.validatorAttrs.name?t.validatorAttrs.name:t.attrs&&t.attrs.name?t.attrs.name:t.elm.attr("name"),i=O(r,t),n=A(U,"field",r);if(n>=0&&""===e)U.splice(n,1);else if(""!==e){var s=t.attrs&&t.friendlyName?a.instant(t.friendlyName):"",o={field:r,friendlyName:s,message:e,formName:i?i.$name:null};n>=0?U[n]=o:U.push(o)}return t.scope.$validationSummary=U,i&&(i.$validationSummary=b(U,"formName",i.$name)),U}}function y(t,e,a){for(var r=0;r8?t.substring(9).split(":"):null;break;case"UK":case"EURO":case"EURO_SHORT":case"EURO-SHORT":case"EUROPE":a=t.substring(0,8),r=t.substring(2,3),i=E(a,r),l=i[0],o=i[1],s=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],n=t.length>8?t.substring(9).split(":"):null;break;case"US_LONG":case"US-LONG":a=t.substring(0,10),r=t.substring(2,3),i=E(a,r),o=i[0],l=i[1],s=i[2],n=t.length>8?t.substring(9).split(":"):null;break;case"US":case"US_SHORT":case"US-SHORT":a=t.substring(0,8),r=t.substring(2,3),i=E(a,r),o=i[0],l=i[1],s=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],n=t.length>8?t.substring(9).split(":"):null;break;case"ISO":default:a=t.substring(0,10),r=t.substring(4,5),i=E(a,r),s=i[0],o=i[1],l=i[2],n=t.length>10?t.substring(11).split(":"):null}var d=n&&3===n.length?n[0]:0,u=n&&3===n.length?n[1]:0,p=n&&3===n.length?n[2]:0;return new Date(s,o-1,l,d,u,p)}function E(t,e){var a=[];switch(e){case"/":a=t.split("/");break;case".":a=t.split(".");break;case"-":default:a=t.split("-")}return a}function $(t,e,a){var r=!1;switch(t){case"<":r=a>e?!0:!1;break;case"<=":r=a>=e?!0:!1;break;case">":r=e>a?!0:!1;break;case">=":r=e>=a?!0:!1;break;case"!=":case"<>":r=e!=a?!0:!1;break;case"=":case"==":r=e==a?!0:!1;break;default:r=!1}return r}function R(){return this.replace(/^\s+|\s+$/g,"")}function x(){var t=Array.isArray(arguments[0])?arguments[0]:arguments;return this.replace(/{(\d+)}/g,function(e,a){return"undefined"!=typeof t[a]?t[a]:e})}function w(t){var e=Array.isArray(arguments[1])?arguments[1]:Array.prototype.slice.call(arguments,1);return t.replace(/{(\d+)}/g,function(t,a){return"undefined"!=typeof e[a]?e[a]:t})}var T=!1,V=!1,N=1e3,F=[],U=[];t.$on("$routeChangeStart",function(){T||(V=!1,F=[],U=[])});var k=function(t,e,a,r){this.timer=null,this.bFieldRequired=!1,this.validators=[],this.typingLimit=N,this.scope=t,this.elm=e,this.ctrl=r,this.validatorAttrs=a,t&&e&&a&&r&&(g(e,a,r,t),this.defineValidation())};return k.prototype.defineValidation=i,k.prototype.getFormElementByName=n,k.prototype.getFormElements=s,k.prototype.isFieldRequired=l,k.prototype.initialize=o,k.prototype.removeFromValidationSummary=u,k.prototype.removeFromFormElementObjectList=d,k.prototype.setBypassRootScopeReset=p,k.prototype.setDisplayOnlyLastErrorMsg=m,k.prototype.updateErrorMsg=c,k.prototype.validate=f,String.prototype.trim=R,String.prototype.format=x,String.format=w,k}]); -angular.module("ghiscoding.validation").factory("validationRules",[function(){function e(e){var a="undefined"!=typeof e.altText?e.altText.replace("alt=",""):null,t=e.hasOwnProperty("customRegEx")?e.customRegEx:null,s=e.hasOwnProperty("rule")?e.rule:null,r=e.hasOwnProperty("ruleParams")?e.ruleParams:null,n={};switch(s){case"alpha":n={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA",type:"regex"};break;case"alphaSpaces":case"alpha_spaces":n={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_SPACE",type:"regex"};break;case"alphaNum":case"alpha_num":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA_NUM",type:"regex"};break;case"alphaNumSpaces":case"alpha_num_spaces":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_NUM_SPACE",type:"regex"};break;case"alphaDash":case"alpha_dash":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$",message:"INVALID_ALPHA_DASH",type:"regex"};break;case"alphaDashSpaces":case"alpha_dash_spaces":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s_-])+$",message:"INVALID_ALPHA_DASH_SPACE",type:"regex"};break;case"betweenLen":case"between_len":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_len:1,5";n={pattern:"^.{"+_[0]+","+_[1]+"}$",message:"INVALID_BETWEEN_CHAR",params:[_[0],_[1]],type:"regex"};break;case"betweenNum":case"between_num":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_num:1,5";n={condition:[">=","<="],message:"INVALID_BETWEEN_NUM",params:[_[0],_[1]],type:"conditionalNumber"};break;case"boolean":n={pattern:"^(true|True|false|False|0|1)$",message:"INVALID_BOOLEAN",type:"regex"};break;case"checked":n={pattern:"^true$",message:"INVALID_CHECKBOX_SELECTED",type:"regex"};break;case"creditCard":case"credit_card":n={pattern:"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11})$",message:"INVALID_CREDIT_CARD",type:"regex"};break;case"dateEuroLong":case"date_euro_long":n={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG",type:"regex"};break;case"dateEuroLongBetween":case"date_euro_long_between":case"betweenDateEuroLong":case"between_date_euro_long":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_long:01-01-1990,31-12-2015";n={condition:[">=","<="],dateType:"EURO_LONG",params:[_[0],_[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_BETWEEN",type:"conditionalDate"};break;case"dateEuroLongMax":case"date_euro_long_max":case"maxDateEuroLong":case"max_date_euro_long":n={condition:"<=",dateType:"EURO_LONG",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MAX",type:"conditionalDate"};break;case"dateEuroLongMin":case"date_euro_long_min":case"minDateEuroLong":case"min_date_euro_long":n={condition:">=",dateType:"EURO_LONG",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MIN",type:"conditionalDate"};break;case"dateEuroShort":case"date_euro_short":n={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT",type:"regex"};break;case"dateEuroShortBetween":case"date_euro_short_between":case"betweenDateEuroShort":case"between_date_euro_short":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_short:01-01-90,31-12-15";n={condition:[">=","<="],dateType:"EURO_SHORT",params:[_[0],_[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateEuroShortMax":case"date_euro_short_max":case"maxDateEuroShort":case"max_date_euro_short":n={condition:"<=",dateType:"EURO_SHORT",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MAX",type:"conditionalDate"};break;case"dateEuroShortMin":case"date_euro_short_min":case"minDateEuroShort":case"min_date_euro_short":n={condition:">=",dateType:"EURO_SHORT",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MIN",type:"conditionalDate"};break;case"dateIso":case"date_iso":n={pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO",type:"regex"};break;case"dateIsoBetween":case"date_iso_between":case"betweenDateIso":case"between_date_iso":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_iso:1990-01-01,2000-12-31";n={condition:[">=","<="],dateType:"ISO",params:[_[0],_[1]],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_BETWEEN",type:"conditionalDate"};break;case"dateIsoMax":case"date_iso_max":case"maxDateIso":case"max_date_iso":n={condition:"<=",dateType:"ISO",params:[r],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MAX",type:"conditionalDate"};break;case"dateIsoMin":case"date_iso_min":case"minDateIso":case"min_date_iso":n={condition:">=",dateType:"ISO",params:[r],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MIN",type:"conditionalDate"};break;case"dateUsLong":case"date_us_long":n={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG",type:"regex"};break;case"dateUsLongBetween":case"date_us_long_between":case"betweenDateUsLong":case"between_date_us_long":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_long:01/01/1990,12/31/2015";n={condition:[">=","<="],dateType:"US_LONG",params:[_[0],_[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_BETWEEN",type:"conditionalDate"};break;case"dateUsLongMax":case"date_us_long_max":case"maxDateUsLong":case"max_date_us_long":n={condition:"<=",dateType:"US_LONG",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MAX",type:"conditionalDate"};break;case"dateUsLongMin":case"date_us_long_min":case"minDateUsLong":case"min_date_us_long":n={condition:">=",dateType:"US_LONG",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MIN",type:"conditionalDate"};break;case"dateUsShort":case"date_us_short":n={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT",type:"regex"};break;case"dateUsShortBetween":case"date_us_short_between":case"betweenDateUsShort":case"between_date_us_short":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_short:01/01/90,12/31/15";n={condition:[">=","<="],dateType:"US_SHORT",params:[_[0],_[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateUsShortMax":case"date_us_short_max":case"maxDateUsShort":case"max_date_us_short":n={condition:"<=",dateType:"US_SHORT",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MAX",type:"conditionalDate"};break;case"dateUsShortMin":case"date_us_short_min":case"minDateUsShort":case"min_date_us_short":n={condition:">=",dateType:"US_SHORT",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MIN",type:"conditionalDate"};break;case"email":n={pattern:"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",message:"INVALID_EMAIL",type:"regex"};break;case"exactLen":case"exact_len":n={pattern:"^.{"+r+"}$",message:"INVALID_EXACT_LEN",params:[r],type:"regex"};break;case"float":n={pattern:"^\\d*\\.{1}\\d+$",message:"INVALID_FLOAT",type:"regex"};break;case"floatSigned":case"float_signed":n={pattern:"^[-+]?\\d*\\.{1}\\d+$",message:"INVALID_FLOAT_SIGNED",type:"regex"};break;case"iban":n={pattern:"^[a-zA-Z]{2}\\d{2}\\s?([0-9a-zA-Z]{4}\\s?){4}[0-9a-zA-Z]{2}$",message:"INVALID_IBAN",type:"regex"};break;case"int":case"integer":n={pattern:"^\\d+$",message:"INVALID_INTEGER",type:"regex"};break;case"intSigned":case"integerSigned":case"int_signed":case"integer_signed":n={pattern:"^[+-]?\\d+$",message:"INVALID_INTEGER_SIGNED",type:"regex"};break;case"ipv4":n={pattern:"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$",message:"INVALID_IPV4",type:"regex"};break;case"ipv6":n={pattern:"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$",message:"INVALID_IPV6",type:"regex"};break;case"match":var e=r.split(",");n={message:"INVALID_INPUT_MATCH",params:e,type:"match"};break;case"maxLen":case"max_len":n={pattern:"^.{0,"+r+"}$",message:"INVALID_MAX_CHAR",params:[r],type:"regex"};break;case"maxNum":case"max_num":n={condition:"<=",message:"INVALID_MAX_NUM",params:[r],type:"conditionalNumber"};break;case"minLen":case"min_len":n={pattern:"^.{"+r+",}$",message:"INVALID_MIN_CHAR",params:[r],type:"regex"};break;case"minNum":case"min_num":n={condition:">=",message:"INVALID_MIN_NUM",params:[r],type:"conditionalNumber"};break;case"numeric":n={pattern:"^\\d*\\.?\\d+$",message:"INVALID_NUMERIC",type:"regex"};break;case"numericSigned":case"numeric_signed":n={pattern:"^[-+]?\\d*\\.?\\d+$",message:"INVALID_NUMERIC_SIGNED",type:"regex"};break;case"regex":n={pattern:t.pattern,message:"INVALID_PATTERN",params:[t.message],type:"regex"};break;case"remote":n={message:"",params:[r],type:"remote"};break;case"required":n={pattern:"\\S+",message:"INVALID_REQUIRED",type:"regex"};break;case"url":n={pattern:"(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?",message:"INVALID_URL",type:"regex"};break;case"time":n={pattern:"^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$",message:"INVALID_TIME",type:"regex"}}return n.altText=a,n}var a={getElementValidators:e};return a}]); -angular.module("ghiscoding.validation").service("validationService",["$timeout","validationCommon",function(e,o){function t(o,t,i){var n=this,a={};if("string"==typeof o&&"string"==typeof t?(a.elmName=o,a.rules=t,a.friendlyName="string"==typeof i?i:""):a=o,"object"!=typeof a||!a.hasOwnProperty("elmName")||!a.hasOwnProperty("rules")||!a.hasOwnProperty("scope")&&"undefined"==typeof n.validationAttrs.scope)throw"Angular-Validation-Service requires at least the following 3 attributes: {elmName, rules, scope}";return a.elm=angular.element(document.querySelector('[name="'+a.elmName+'"]:not([disabled]):not([ng-disabled]')),"object"!=typeof a.elm||0===a.elm.length?n:(new RegExp("{{(.*?)}}").test(a.elmName)&&(a.elmName=$interpolate(a.elmName)(a.scope)),a.name=a.elmName,a.elm.bind("blur",p=function(e){f||(n.commonObj.initialize(a.scope,a.elm,a,a.ctrl),n.commonObj.typingLimit=0,c(n,e.target.value))}),a=s(n.validationAttrs,a),a.scope.$watch(a.elmName,function(o,t){return void 0===o&&void 0!==t?(e.cancel(n.timer),void n.commonObj.ctrl.$setValidity("validation",n.commonObj.validate("",!0))):(a.ctrl=angular.element(a.elm).controller("ngModel"),a.value=o,n.commonObj.initialize(a.scope,a.elm,a,a.ctrl),void c(n,o))},!0),n)}function i(e){var o=this,t="",i=!0;if("undefined"==typeof e||"undefined"==typeof e.$validationSummary)throw"checkFormValidity() requires a valid Angular Form or $scope object passed as argument to function properly (ex.: $scope.form1 OR $scope).";for(var n=0,a=e.$validationSummary.length;a>n;n++)if(i=!1,t=e.$validationSummary[n].field){var r=o.commonObj.getFormElementByName(t);r.elm&&r.elm.length>0&&("function"==typeof r.ctrl.$setTouched&&r.ctrl.$setTouched(),o.commonObj.updateErrorMsg(e.$validationSummary[n].message,{isSubmitted:!0,isValid:r.isValid,obj:r}))}return i}function n(e){var o=this;if("undefined"==typeof e||"undefined"==typeof e.$validationSummary)throw"clearInvalidValidatorsInSummary() requires a valid Angular Form or $scope object passed as argument to function properly (ex.: $scope.form1 OR $scope).";for(var t=[],i=0,n=e.$validationSummary.length;n>i;i++)t.push(e.$validationSummary[i].field);for(i=0,n=t.length;n>i;i++)t[i]&&(o.commonObj.removeFromFormElementObjectList(t[i]),o.commonObj.removeFromValidationSummary(e.$validationSummary,t[i]))}function a(e,o){var t,i=this;if("undefined"==typeof e||"undefined"==typeof e.$validationSummary)throw"removeValidator() only works with Validation that were defined by the Service (not by the Directive) and requires a valid Angular Form or $scope object passed as argument to function properly (ex.: $scope.form1 OR $scope).";if("undefined"==typeof i.commonObj.scope&&"undefined"!=typeof i.validationAttrs.scope&&(i.commonObj.scope=i.validationAttrs.scope),o instanceof Array)for(var n=0,a=o.length;a>n;n++)t=i.commonObj.getFormElementByName(o[n]),t.elm.removeAttr("validation"),u(i,t,e.$validationSummary);else t=i.commonObj.getFormElementByName(o),t.elm.removeAttr("validation"),u(i,t,e.$validationSummary);return i}function r(e){var o=this,t="boolean"==typeof e?e:!0;o.commonObj.setBypassRootScopeReset(t)}function m(e){var o=this,t="boolean"==typeof e?e:!0;o.commonObj.setDisplayOnlyLastErrorMsg(t)}function l(e){var o=this;return o.validationAttrs=e,o}function c(o,t){return o.commonObj.validate(t,!1),o.commonObj.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?(f=!1,(o.commonObj.isFieldRequired()||t)&&o.commonObj.ctrl.$setValidity("validation",!1),""!==t&&"undefined"!=typeof t||"NUMBER"!==o.commonObj.elm.prop("type").toUpperCase()?"SELECT"===o.commonObj.elm.prop("tagName").toUpperCase()?(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t):("undefined"!=typeof t&&(o.commonObj.updateErrorMsg(""),e.cancel(o.timer),o.timer=e(function(){o.commonObj.scope.$evalAsync(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)))},o.commonObj.typingLimit)),t):(e.cancel(o.timer),o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t)):(d(o),t)}function d(o){f=!0,e.cancel(self.timer),o.commonObj.updateErrorMsg(""),o.commonObj.ctrl.$setValidity("validation",!0),"undefined"!=typeof p&&o.commonObj.elm.unbind("blur",p)}function s(e,o){var t={};for(var i in e)t[i]=e[i];for(var i in o)t[i]=o[i];return t}function u(e,o,t){if("undefined"!=typeof e.commonObj.scope){var i=e.commonObj.scope.$watch(o.fieldName,function(){});i(),"function"==typeof o.ctrl.$setUntouched&&o.ctrl.$setUntouched(),o.ctrl.$setPristine(),o.ctrl.$setValidity("validation",!0),e.commonObj.removeFromValidationSummary(t,o.fieldName),e.commonObj.updateErrorMsg("",{isValid:!0,obj:o})}}var p,f=!1,y=function(){this.validationAttrs={},this.commonObj=new o};return y.prototype.addValidator=t,y.prototype.checkFormValidity=i,y.prototype.removeValidator=a,y.prototype.setBypassRootScopeReset=r,y.prototype.setDisplayOnlyLastErrorMsg=m,y.prototype.setGlobalOptions=l,y.prototype.clearInvalidValidatorsInSummary=n,y}]); \ No newline at end of file +angular.module("ghiscoding.validation",["pascalprecht.translate"]).directive("validation",["$timeout","validationCommon","validationRules",function(i,e){return{restrict:"A",require:"ngModel",link:function(t,a,n,d){function l(){v=!0,i.cancel(r),s.updateErrorMsg(""),d.$setValidity("validation",!0),"undefined"!=typeof u&&a.unbind("blur",u)}function o(e,n){var o="undefined"!=typeof n?n:s.typingLimit;return s.validate(e,!1),s.isFieldRequired()||""!==e&&null!==e&&"undefined"!=typeof e?(v=!1,(e||s.isFieldRequired())&&d.$setValidity("validation",!1),""!==e&&"undefined"!=typeof e||"NUMBER"!==a.prop("type").toUpperCase()?"SELECT"===a.prop("tagName").toUpperCase()?(d.$setValidity("validation",s.validate(e,!0)),e):("undefined"!=typeof e&&(s.updateErrorMsg(""),i.cancel(r),r=i(function(){t.$evalAsync(d.$setValidity("validation",s.validate(e,!0)))},o)),e):(i.cancel(r),d.$setValidity("validation",s.validate(e,!0)),e)):(l(),e)}var r,u,s=new e(t,a,n,d),v=!1;d.$formatters.unshift(o),d.$parsers.unshift(o),n.$observe("disabled",function(i){i?d.$setValidity("validation",!0):d.$setValidity("validation",s.validate(d.$viewValue,!0))}),a.bind("blur",u=function(i){v||o(i.target.value,0)})}}}]); +angular.module("ghiscoding.validation").factory("validationCommon",["$rootScope","$timeout","$translate","validationRules",function(t,e,a,r){function i(){var t=this,e={};t.validators=[],t.typingLimit=N,t.validatorAttrs.hasOwnProperty("debounce")?t.typingLimit=parseInt(t.validatorAttrs.debounce,10):t.validatorAttrs.hasOwnProperty("typingLimit")?t.typingLimit=parseInt(t.validatorAttrs.typingLimit,10):t.scope.$validationOptions&&t.scope.$validationOptions.hasOwnProperty("debounce")&&(t.typingLimit=parseInt(t.scope.$validationOptions.debounce,10));var a=t.validatorAttrs.hasOwnProperty("rules")?t.validatorAttrs.rules:t.validatorAttrs.validation;if(a.indexOf("regex:")>=0){var i=a.match("regex:(.*?):regex");if(i.length<2)throw'Regex validator within the validation needs to be define with an opening "regex:" and a closing ":regex", please review your validator.';var n=i[1].split(":=");e={message:n[0],pattern:n[1]},a=a.replace(i[0],"regex:")}var s=a.split("|");if(s){t.bFieldRequired=a.indexOf("required")>=0?!0:!1;for(var o=0,l=s.length;l>o;o++){var d=s[o].split(":"),p=s[o].indexOf("alt=")>=0?!0:!1;t.validators[o]=r.getElementValidators({altText:p===!0?2===d.length?d[1]:d[2]:"",customRegEx:e,rule:d[0],ruleParams:p&&2===d.length?null:d[1]})}}return t}function n(t){return y(F,"fieldName",t)}function s(){return F}function o(t,e,a,r){this.scope=t,this.elm=e,this.ctrl=r,this.validatorAttrs=a,g(e,a,r,t),this.defineValidation()}function l(){var t=this;return t.bFieldRequired}function d(t){var e=O(F,"fieldName",t);e>=0&&F.splice(e,1)}function p(t,e){var a=O(t,"field",e);a>=0&&t.splice(a,1),a=O(k,"field",e),a>=0&&k.splice(a,1)}function u(t){w=t}function m(t){T=t}function c(t,e){var r=this;e&&e.obj&&(r=e.obj,r.validatorAttrs=e.obj.attrs);var i=e&&e.elm?e.elm:r.elm,n=i&&i.attr("name")?i.attr("name"):null;if("undefined"==typeof n||null===n){var s=i?i.attr("ng-model"):"unknown";throw'Angular-Validation Service requires you to have a (name="") attribute on the element to validate... Your element is: ng-model="'+s+'"'}var o=e&&e.translate?a.instant(t):t,l=n.replace(/[|&;$%@"<>()+,\[\]\{\}]/g,""),d=null;if(r.validatorAttrs&&r.validatorAttrs.hasOwnProperty("validationErrorTo")){var p=r.validatorAttrs.validationErrorTo.charAt(0),u="."===p||"#"===p?r.validatorAttrs.validationErrorTo:"#"+r.validatorAttrs.validationErrorTo;d=angular.element(document.querySelector(u))}d&&0!==d.length||(d=angular.element(document.querySelector(".validation-"+l)));var m=e&&e.isSubmitted?e.isSubmitted:!1;e&&!e.isValid&&(m||r.ctrl.$dirty||r.ctrl.$touched)?d.length>0?d.text(o):i.after(''+o+""):d.text("")}function f(t,e){var r,i,s=this,o=!0,l=!0,d="";"undefined"==typeof t&&(t="");for(var p=s.validatorAttrs&&s.validatorAttrs.name?s.validatorAttrs.name:s.attrs&&s.attrs.name?s.attrs.name:s.elm.attr("name"),u=n(p),m=s.validatorAttrs.hasOwnProperty("rules")?s.validatorAttrs.rules:s.validatorAttrs.validation,c=0,f=s.validators.length;f>c;c++){if(i=s.validators[c],"conditionalDate"===i.type){if(r=new RegExp(i.pattern,"i"),o=("\\S+"===i.pattern||m&&"required"===i.pattern)&&null===t?!1:r.test(t)){var g=i.dateType,y=$(t,g).getTime();if(2==i.params.length){var b=$(i.params[0],g).getTime(),O=$(i.params[1],g).getTime(),A=E(i.condition[0],y,b),S=E(i.condition[1],y,O);o=A&&S?!0:!1}else{var x=$(i.params[0],g).getTime();o=E(i.condition,y,x)}}}else if("conditionalNumber"===i.type)if(2==i.params.length){var A=E(i.condition[0],parseFloat(t),parseFloat(i.params[0])),S=E(i.condition[1],parseFloat(t),parseFloat(i.params[1]));o=A&&S?!0:!1}else o=E(i.condition,parseFloat(t),parseFloat(i.params[0]));else if("match"===i.type){var R=i.params[0],V=s.scope.$eval(R);o=V===t&&!!t}else if("remote"===i.type&&""!==t&&e===!0){s.ctrl.$processing=!0;var w=null,N=i.params[0];if(-1===N.indexOf("."))w=s.scope[N];else{var F=N.split(".");w=s.scope;for(var k=0,L=F.length;L>k;k++)w=w[F[k]]}var q="function"==typeof w?w():null;if(U.length>1)for(;U.length>0;){var P=U.pop();"function"==typeof P.abort&&P.abort()}if(U.push(q),!q||"function"!=typeof q.then)throw"Remote Validation requires a declared function (in your Controller) which also needs to return a Promise, please review your code.";s.ctrl.$setValidity("remote",!1),function(t){q.then(function(a){a=a.data||a,U.pop(),s.ctrl.$processing=!1;var r=d+" ";"boolean"==typeof a?o=a?!0:!1:"object"==typeof a&&(o=a.isValid?!0:!1),o===!1&&(u.isValid=!1,r+=a.message?a.message:t,v(s,u,r,!1,e)),o===!0&&l===!0&&(u.isValid=!0,s.ctrl.$setValidity("remote",!0),v(s,u,"",!0,e))})}(i.altText)}else s.elm.prop("disabled")||1==parseInt(s.elm.attr("ng-disabled"))||"true"===s.elm.attr("ng-disabled")?o=!0:"string"==typeof t&&""===t&&"NUMBER"===s.elm.prop("type").toUpperCase()?o=!1:(r=new RegExp(i.pattern,"i"),o=("\\S+"===i.pattern||m&&"required"===i.pattern)&&null===t?!1:r.test(t));(!s.bFieldRequired&&!t||s.elm.prop("disabled")||1==parseInt(s.elm.attr("ng-disabled"))||"true"===s.elm.attr("ng-disabled"))&&(o=!0),o||(l=!1,function(t,r,i){var n=i.message;i.altText&&i.altText.length>0&&(n=i.altText.replace("alt=","")),a(n).then(function(a){d.length>0&&T?d=" "+(i&&i.params?String.format(a,i.params):a):d+=" "+(i&&i.params?String.format(a,i.params):a),v(s,t,d,l,e)})["catch"](function(){i.altText&&i.altText.length>0&&(d.length>0&&T?d=" "+n:d+=" "+n,v(s,t,d,l,e))})}(u,o,i))}return o&&(h(s,""),s.updateErrorMsg("",{isValid:o})),u&&(u.isValid=l,l&&(u.message="")),l}function g(t,e,r,i){var n=e.name?e.name:t.attr("name"),s=e&&e.friendlyName?a.instant(e.friendlyName):"",o={fieldName:n,friendlyName:s,elm:t,attrs:e,ctrl:r,scope:i,isValid:!1,message:""},l=O(F,"fieldName",t.attr("name"));return l>=0?F[l]=o:F.push(o),F}function v(t,e,a,r,i){a=a.trim(),h(e,a),e&&(e.message=a),(t.validatorAttrs.preValidateFormElements||t.scope.$validationOptions&&t.scope.$validationOptions.preValidateFormElements)&&(e&&"function"==typeof t.ctrl.$setTouched&&e.ctrl.$setTouched(),t.ctrl.$dirty===!1&&c(a,{isSubmitted:!0,isValid:r,obj:e})),i&&e&&!e.isValid?t.updateErrorMsg(a,{isValid:r}):e&&e.isValid&&h(e,"")}function h(t,e){if("undefined"!=typeof t&&null!=t){var r=t.validatorAttrs&&t.validatorAttrs.name?t.validatorAttrs.name:t.attrs&&t.attrs.name?t.attrs.name:t.elm.attr("name"),i=A(r,t),n=O(k,"field",r);if(n>=0&&""===e)k.splice(n,1);else if(""!==e){var s=t.attrs&&t.friendlyName?a.instant(t.friendlyName):"",o={field:r,friendlyName:s,message:e,formName:i?i.$name:null};n>=0?k[n]=o:k.push(o)}return t.scope.$validationSummary=k,i&&(i.$validationSummary=b(k,"formName",i.$name)),k}}function y(t,e,a){for(var r=0;r8?t.substring(9).split(":"):null;break;case"UK":case"EURO":case"EURO_SHORT":case"EURO-SHORT":case"EUROPE":a=t.substring(0,8),r=t.substring(2,3),i=S(a,r),l=i[0],o=i[1],s=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],n=t.length>8?t.substring(9).split(":"):null;break;case"US_LONG":case"US-LONG":a=t.substring(0,10),r=t.substring(2,3),i=S(a,r),o=i[0],l=i[1],s=i[2],n=t.length>8?t.substring(9).split(":"):null;break;case"US":case"US_SHORT":case"US-SHORT":a=t.substring(0,8),r=t.substring(2,3),i=S(a,r),o=i[0],l=i[1],s=parseInt(i[2])<50?"20"+i[2]:"19"+i[2],n=t.length>8?t.substring(9).split(":"):null;break;case"ISO":default:a=t.substring(0,10),r=t.substring(4,5),i=S(a,r),s=i[0],o=i[1],l=i[2],n=t.length>10?t.substring(11).split(":"):null}var d=n&&3===n.length?n[0]:0,p=n&&3===n.length?n[1]:0,u=n&&3===n.length?n[2]:0;return new Date(s,o-1,l,d,p,u)}function S(t,e){var a=[];switch(e){case"/":a=t.split("/");break;case".":a=t.split(".");break;case"-":default:a=t.split("-")}return a}function E(t,e,a){var r=!1;switch(t){case"<":r=a>e?!0:!1;break;case"<=":r=a>=e?!0:!1;break;case">":r=e>a?!0:!1;break;case">=":r=e>=a?!0:!1;break;case"!=":case"<>":r=e!=a?!0:!1;break;case"=":case"==":r=e==a?!0:!1;break;default:r=!1}return r}function x(){return this.replace(/^\s+|\s+$/g,"")}function R(){var t=Array.isArray(arguments[0])?arguments[0]:arguments;return this.replace(/{(\d+)}/g,function(e,a){return"undefined"!=typeof t[a]?t[a]:e})}function V(t){var e=Array.isArray(arguments[1])?arguments[1]:Array.prototype.slice.call(arguments,1);return t.replace(/{(\d+)}/g,function(t,a){return"undefined"!=typeof e[a]?e[a]:t})}var w=!1,T=!1,N=1e3,F=[],U=[],k=[];t.$on("$routeChangeStart",function(){w||(T=!1,F=[],k=[])});var L=function(t,e,a,r){this.timer=null,this.bFieldRequired=!1,this.validators=[],this.typingLimit=N,this.scope=t,this.elm=e,this.ctrl=r,this.validatorAttrs=a,t&&t.$validationOptions&&t.$validationOptions.hasOwnProperty("isolatedScope")&&(this.scope=t.$validationOptions.isolatedScope,this.scope.$validationOptions=t.$validationOptions),this.elm&&this.validatorAttrs&&this.ctrl&&this.scope&&(g(this.elm,this.validatorAttrs,this.ctrl,this.scope),this.defineValidation())};return L.prototype.defineValidation=i,L.prototype.getFormElementByName=n,L.prototype.getFormElements=s,L.prototype.isFieldRequired=l,L.prototype.initialize=o,L.prototype.removeFromValidationSummary=p,L.prototype.removeFromFormElementObjectList=d,L.prototype.setBypassRootScopeReset=u,L.prototype.setDisplayOnlyLastErrorMsg=m,L.prototype.updateErrorMsg=c,L.prototype.validate=f,String.prototype.trim=x,String.prototype.format=R,String.format=V,L}]); +angular.module("ghiscoding.validation").factory("validationRules",[function(){function e(e){var a="undefined"!=typeof e.altText?e.altText.replace("alt=",""):null,t=e.hasOwnProperty("customRegEx")?e.customRegEx:null,s=e.hasOwnProperty("rule")?e.rule:null,r=e.hasOwnProperty("ruleParams")?e.ruleParams:null,n={};switch(s){case"alpha":n={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA",type:"regex"};break;case"alphaSpaces":case"alpha_spaces":n={pattern:"^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_SPACE",type:"regex"};break;case"alphaNum":case"alpha_num":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$",message:"INVALID_ALPHA_NUM",type:"regex"};break;case"alphaNumSpaces":case"alpha_num_spaces":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s])+$",message:"INVALID_ALPHA_NUM_SPACE",type:"regex"};break;case"alphaDash":case"alpha_dash":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$",message:"INVALID_ALPHA_DASH",type:"regex"};break;case"alphaDashSpaces":case"alpha_dash_spaces":n={pattern:"^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\\s_-])+$",message:"INVALID_ALPHA_DASH_SPACE",type:"regex"};break;case"betweenLen":case"between_len":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_len:1,5";n={pattern:"^.{"+_[0]+","+_[1]+"}$",message:"INVALID_BETWEEN_CHAR",params:[_[0],_[1]],type:"regex"};break;case"betweenNum":case"between_num":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_num:1,5";n={condition:[">=","<="],message:"INVALID_BETWEEN_NUM",params:[_[0],_[1]],type:"conditionalNumber"};break;case"boolean":n={pattern:"^(true|True|false|False|0|1)$",message:"INVALID_BOOLEAN",type:"regex"};break;case"checked":n={pattern:"^true$",message:"INVALID_CHECKBOX_SELECTED",type:"regex"};break;case"creditCard":case"credit_card":n={pattern:"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11})$",message:"INVALID_CREDIT_CARD",type:"regex"};break;case"dateEuroLong":case"date_euro_long":n={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG",type:"regex"};break;case"dateEuroLongBetween":case"date_euro_long_between":case"betweenDateEuroLong":case"between_date_euro_long":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_long:01-01-1990,31-12-2015";n={condition:[">=","<="],dateType:"EURO_LONG",params:[_[0],_[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_BETWEEN",type:"conditionalDate"};break;case"dateEuroLongMax":case"date_euro_long_max":case"maxDateEuroLong":case"max_date_euro_long":n={condition:"<=",dateType:"EURO_LONG",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MAX",type:"conditionalDate"};break;case"dateEuroLongMin":case"date_euro_long_min":case"minDateEuroLong":case"min_date_euro_long":n={condition:">=",dateType:"EURO_LONG",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$",message:"INVALID_DATE_EURO_LONG_MIN",type:"conditionalDate"};break;case"dateEuroShort":case"date_euro_short":n={pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT",type:"regex"};break;case"dateEuroShortBetween":case"date_euro_short_between":case"betweenDateEuroShort":case"between_date_euro_short":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_euro_short:01-01-90,31-12-15";n={condition:[">=","<="],dateType:"EURO_SHORT",params:[_[0],_[1]],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateEuroShortMax":case"date_euro_short_max":case"maxDateEuroShort":case"max_date_euro_short":n={condition:"<=",dateType:"EURO_SHORT",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MAX",type:"conditionalDate"};break;case"dateEuroShortMin":case"date_euro_short_min":case"minDateEuroShort":case"min_date_euro_short":n={condition:">=",dateType:"EURO_SHORT",params:[r],pattern:"^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/]\\d\\d$",message:"INVALID_DATE_EURO_SHORT_MIN",type:"conditionalDate"};break;case"dateIso":case"date_iso":n={pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO",type:"regex"};break;case"dateIsoBetween":case"date_iso_between":case"betweenDateIso":case"between_date_iso":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_iso:1990-01-01,2000-12-31";n={condition:[">=","<="],dateType:"ISO",params:[_[0],_[1]],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_BETWEEN",type:"conditionalDate"};break;case"dateIsoMax":case"date_iso_max":case"maxDateIso":case"max_date_iso":n={condition:"<=",dateType:"ISO",params:[r],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MAX",type:"conditionalDate"};break;case"dateIsoMin":case"date_iso_min":case"minDateIso":case"min_date_iso":n={condition:">=",dateType:"ISO",params:[r],pattern:"^(19|20)\\d\\d([-])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])$",message:"INVALID_DATE_ISO_MIN",type:"conditionalDate"};break;case"dateUsLong":case"date_us_long":n={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG",type:"regex"};break;case"dateUsLongBetween":case"date_us_long_between":case"betweenDateUsLong":case"between_date_us_long":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_long:01/01/1990,12/31/2015";n={condition:[">=","<="],dateType:"US_LONG",params:[_[0],_[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_BETWEEN",type:"conditionalDate"};break;case"dateUsLongMax":case"date_us_long_max":case"maxDateUsLong":case"max_date_us_long":n={condition:"<=",dateType:"US_LONG",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MAX",type:"conditionalDate"};break;case"dateUsLongMin":case"date_us_long_min":case"minDateUsLong":case"min_date_us_long":n={condition:">=",dateType:"US_LONG",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/](19|20)\\d\\d$",message:"INVALID_DATE_US_LONG_MIN",type:"conditionalDate"};break;case"dateUsShort":case"date_us_short":n={pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT",type:"regex"};break;case"dateUsShortBetween":case"date_us_short_between":case"betweenDateUsShort":case"between_date_us_short":var _=r.split(",");if(2!==_.length)throw"This validation must include exactly 2 params separated by a comma (,) ex.: between_date_us_short:01/01/90,12/31/15";n={condition:[">=","<="],dateType:"US_SHORT",params:[_[0],_[1]],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_BETWEEN",type:"conditionalDate"};break;case"dateUsShortMax":case"date_us_short_max":case"maxDateUsShort":case"max_date_us_short":n={condition:"<=",dateType:"US_SHORT",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MAX",type:"conditionalDate"};break;case"dateUsShortMin":case"date_us_short_min":case"minDateUsShort":case"min_date_us_short":n={condition:">=",dateType:"US_SHORT",params:[r],pattern:"^(0[1-9]|1[012])[-/](0[1-9]|[12][0-9]|3[01])[-/]\\d\\d$",message:"INVALID_DATE_US_SHORT_MIN",type:"conditionalDate"};break;case"email":n={pattern:"(?:[\\wа-я0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.?[\\wа-я0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\wа-я0-9](?:[\\wа-я0-9-]*[\\wа-я0-9])?\\.?)+[\\wа-я0-9](?:[\\wа-я0-9-]*[\\wа-я0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.?){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[\\wа-я0-9-]*[\\wа-я0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])",message:"INVALID_EMAIL",type:"regex"};break;case"exactLen":case"exact_len":n={pattern:"^.{"+r+"}$",message:"INVALID_EXACT_LEN",params:[r],type:"regex"};break;case"float":n={pattern:"^\\d*\\.{1}\\d+$",message:"INVALID_FLOAT",type:"regex"};break;case"floatSigned":case"float_signed":n={pattern:"^[-+]?\\d*\\.{1}\\d+$",message:"INVALID_FLOAT_SIGNED",type:"regex"};break;case"iban":n={pattern:"^[a-zA-Z]{2}\\d{2}\\s?([0-9a-zA-Z]{4}\\s?){4}[0-9a-zA-Z]{2}$",message:"INVALID_IBAN",type:"regex"};break;case"int":case"integer":n={pattern:"^\\d+$",message:"INVALID_INTEGER",type:"regex"};break;case"intSigned":case"integerSigned":case"int_signed":case"integer_signed":n={pattern:"^[+-]?\\d+$",message:"INVALID_INTEGER_SIGNED",type:"regex"};break;case"ipv4":n={pattern:"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$",message:"INVALID_IPV4",type:"regex"};break;case"ipv6":n={pattern:"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$",message:"INVALID_IPV6",type:"regex"};break;case"match":var e=r.split(",");n={message:"INVALID_INPUT_MATCH",params:e,type:"match"};break;case"maxLen":case"max_len":n={pattern:"^.{0,"+r+"}$",message:"INVALID_MAX_CHAR",params:[r],type:"regex"};break;case"maxNum":case"max_num":n={condition:"<=",message:"INVALID_MAX_NUM",params:[r],type:"conditionalNumber"};break;case"minLen":case"min_len":n={pattern:"^.{"+r+",}$",message:"INVALID_MIN_CHAR",params:[r],type:"regex"};break;case"minNum":case"min_num":n={condition:">=",message:"INVALID_MIN_NUM",params:[r],type:"conditionalNumber"};break;case"numeric":n={pattern:"^\\d*\\.?\\d+$",message:"INVALID_NUMERIC",type:"regex"};break;case"numericSigned":case"numeric_signed":n={pattern:"^[-+]?\\d*\\.?\\d+$",message:"INVALID_NUMERIC_SIGNED",type:"regex"};break;case"regex":n={pattern:t.pattern,message:"INVALID_PATTERN",params:[t.message],type:"regex"};break;case"remote":n={message:"",params:[r],type:"remote"};break;case"required":n={pattern:"\\S+",message:"INVALID_REQUIRED",type:"regex"};break;case"url":n={pattern:"(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?",message:"INVALID_URL",type:"regex"};break;case"time":n={pattern:"^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$",message:"INVALID_TIME",type:"regex"}}return n.altText=a,n}var a={getElementValidators:e};return a}]); +angular.module("ghiscoding.validation").service("validationService",["$timeout","validationCommon",function(e,o){function t(o,t,i){var n=this,a={};if("string"==typeof o&&"string"==typeof t?(a.elmName=o,a.rules=t,a.friendlyName="string"==typeof i?i:""):a=o,"object"!=typeof a||!a.hasOwnProperty("elmName")||!a.hasOwnProperty("rules")||!a.hasOwnProperty("scope")&&"undefined"==typeof n.validationAttrs.scope)throw"Angular-Validation-Service requires at least the following 3 attributes: {elmName, rules, scope}";if(a.elm=angular.element(document.querySelector('[name="'+a.elmName+'"]:not([disabled]):not([ng-disabled]')),"object"!=typeof a.elm||0===a.elm.length)return n;new RegExp("{{(.*?)}}").test(a.elmName)&&(a.elmName=$interpolate(a.elmName)(a.scope)),a.name=a.elmName;var r=a.scope?a.scope:n.validationAttrs.scope;if(n.validationAttrs.isolatedScope){var m=r.$validationOptions||null;r=n.validationAttrs.isolatedScope,m&&(r.$validationOptions=m)}return a.elm.bind("blur",p=function(e){f||(n.commonObj.initialize(r,a.elm,a,a.ctrl),c(n,e.target.value,0))}),a=s(n.validationAttrs,a),r.$watch(a.elmName,function(o,t){return void 0===o&&void 0!==t?(e.cancel(n.timer),void n.commonObj.ctrl.$setValidity("validation",n.commonObj.validate("",!0))):(a.ctrl=angular.element(a.elm).controller("ngModel"),a.value=o,n.commonObj.initialize(r,a.elm,a,a.ctrl),void c(n,o))},!0),n}function i(e){var o=this,t="",i=!0;if("undefined"==typeof e||"undefined"==typeof e.$validationSummary)throw"checkFormValidity() requires a valid Angular Form or $scope object passed as argument to function properly (ex.: $scope.form1 OR $scope).";for(var n=0,a=e.$validationSummary.length;a>n;n++)if(i=!1,t=e.$validationSummary[n].field){var r=o.commonObj.getFormElementByName(t);r.elm&&r.elm.length>0&&("function"==typeof r.ctrl.$setTouched&&r.ctrl.$setTouched(),o.commonObj.updateErrorMsg(e.$validationSummary[n].message,{isSubmitted:!0,isValid:r.isValid,obj:r}))}return i}function n(e){var o=this;if("undefined"==typeof e||"undefined"==typeof e.$validationSummary)throw"clearInvalidValidatorsInSummary() requires a valid Angular Form or $scope object passed as argument to function properly (ex.: $scope.form1 OR $scope).";for(var t=[],i=0,n=e.$validationSummary.length;n>i;i++)t.push(e.$validationSummary[i].field);for(i=0,n=t.length;n>i;i++)t[i]&&(o.commonObj.removeFromFormElementObjectList(t[i]),o.commonObj.removeFromValidationSummary(e.$validationSummary,t[i]))}function a(e,o){var t,i=this;if("undefined"==typeof e||"undefined"==typeof e.$validationSummary)throw"removeValidator() only works with Validation that were defined by the Service (not by the Directive) and requires a valid Angular Form or $scope object passed as argument to function properly (ex.: $scope.form1 OR $scope).";if("undefined"==typeof i.commonObj.scope&&"undefined"!=typeof i.validationAttrs.scope&&(i.commonObj.scope=i.validationAttrs.scope),o instanceof Array)for(var n=0,a=o.length;a>n;n++)t=i.commonObj.getFormElementByName(o[n]),t.elm.removeAttr("validation"),u(i,t,e.$validationSummary);else t=i.commonObj.getFormElementByName(o),t.elm.removeAttr("validation"),u(i,t,e.$validationSummary);return i}function r(e){var o=this,t="boolean"==typeof e?e:!0;o.commonObj.setBypassRootScopeReset(t)}function m(e){var o=this,t="boolean"==typeof e?e:!0;o.commonObj.setDisplayOnlyLastErrorMsg(t)}function l(e){var o=this;return o.validationAttrs=e,o}function c(o,t,i){var n="undefined"!=typeof i?i:o.commonObj.typingLimit;return o.commonObj.validate(t,!1),o.commonObj.isFieldRequired()||""!==t&&null!==t&&"undefined"!=typeof t?(f=!1,(o.commonObj.isFieldRequired()||t)&&o.commonObj.ctrl.$setValidity("validation",!1),""!==t&&"undefined"!=typeof t||"NUMBER"!==o.commonObj.elm.prop("type").toUpperCase()?"SELECT"===o.commonObj.elm.prop("tagName").toUpperCase()?(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t):("undefined"!=typeof t&&(o.commonObj.updateErrorMsg(""),e.cancel(o.timer),o.timer=e(function(){o.commonObj.scope.$evalAsync(o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)))},n)),t):(e.cancel(o.timer),o.commonObj.ctrl.$setValidity("validation",o.commonObj.validate(t,!0)),t)):(d(o),t)}function d(o){f=!0,e.cancel(self.timer),o.commonObj.updateErrorMsg(""),o.commonObj.ctrl.$setValidity("validation",!0),"undefined"!=typeof p&&o.commonObj.elm.unbind("blur",p)}function s(e,o){var t={};for(var i in e)t[i]=e[i];for(var i in o)t[i]=o[i];return t}function u(e,o,t){if("undefined"!=typeof e.commonObj.scope){var i=e.commonObj.scope.$watch(o.fieldName,function(){});i(),"function"==typeof o.ctrl.$setUntouched&&o.ctrl.$setUntouched(),o.ctrl.$setPristine(),o.ctrl.$setValidity("validation",!0),e.commonObj.removeFromValidationSummary(t,o.fieldName),e.commonObj.updateErrorMsg("",{isValid:!0,obj:o})}}var p,f=!1,v=function(){this.validationAttrs={},this.commonObj=new o};return v.prototype.addValidator=t,v.prototype.checkFormValidity=i,v.prototype.removeValidator=a,v.prototype.setBypassRootScopeReset=r,v.prototype.setDisplayOnlyLastErrorMsg=m,v.prototype.setGlobalOptions=l,v.prototype.clearInvalidValidatorsInSummary=n,v}]); \ No newline at end of file diff --git a/full-tests/app.js b/full-tests/app.js index af5ef6b..a46b112 100644 --- a/full-tests/app.js +++ b/full-tests/app.js @@ -7,6 +7,9 @@ myApp.config(['$routeProvider', '$locationProvider', function ($routeProvider, $ .when('/validate-Directive', { templateUrl: 'Directive.html', controller: 'CtrlValidationDirective' }) .when('/validate-Service', { templateUrl: 'Service.html', controller: 'CtrlValidationService' }) .otherwise({ redirectTo: 'validate-Directive' }); + }]) + .config(['$compileProvider', function ($compileProvider) { + $compileProvider.debugInfoEnabled(false); }]) .config(['$translateProvider', function ($translateProvider) { $translateProvider.useStaticFilesLoader({ diff --git a/full-tests/index.html b/full-tests/index.html index 4b136f4..b66a093 100644 --- a/full-tests/index.html +++ b/full-tests/index.html @@ -45,7 +45,7 @@

Angular-Validation Directive|Service (ghiscoding)

- + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/more-examples/dynamicFormView.html b/more-examples/dynamic-form/template.html similarity index 100% rename from more-examples/dynamicFormView.html rename to more-examples/dynamic-form/template.html diff --git a/more-examples/dynamicInput-app.js b/more-examples/dynamic-input/app.js similarity index 94% rename from more-examples/dynamicInput-app.js rename to more-examples/dynamic-input/app.js index 0188476..aa69297 100644 --- a/more-examples/dynamicInput-app.js +++ b/more-examples/dynamic-input/app.js @@ -4,7 +4,7 @@ var myApp = angular.module('myApp', ['ngRoute', 'pascalprecht.translate', 'ghisc myApp.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) { $routeProvider.when('/dynamic', { - templateUrl: 'dynamicInputTemplate.html', + templateUrl: 'template.html', controller: 'CtrlDynamic' }); $routeProvider.otherwise({ @@ -13,7 +13,7 @@ myApp.config(['$routeProvider', '$locationProvider', function ($routeProvider, $ }]) .config(['$translateProvider', function ($translateProvider) { $translateProvider.useStaticFilesLoader({ - prefix: '../locales/validation/', + prefix: '../../locales/validation/', suffix: '.json' }); diff --git a/more-examples/dynamicInputIndex.html b/more-examples/dynamic-input/index.html similarity index 61% rename from more-examples/dynamicInputIndex.html rename to more-examples/dynamic-input/index.html index fbe6ecb..572b547 100644 --- a/more-examples/dynamicInputIndex.html +++ b/more-examples/dynamic-input/index.html @@ -4,7 +4,7 @@ Angular-Validation - + @@ -27,18 +27,19 @@

{{'CHANGE_LANGUAGE' | translate}}

- - + + - - + + + - + \ No newline at end of file diff --git a/more-examples/dynamicInputTemplate.html b/more-examples/dynamic-input/template.html similarity index 100% rename from more-examples/dynamicInputTemplate.html rename to more-examples/dynamic-input/template.html diff --git a/more-examples/dynamicFormIndex.html b/more-examples/dynamicFormIndex.html deleted file mode 100644 index 933a33a..0000000 --- a/more-examples/dynamicFormIndex.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - AngularJS Plunker - - - - - - -

Form Validation (with dynamic form and fields)

- - -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/package.json b/package.json index df17a12..84690f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-validation-ghiscoding", - "version": "1.3.26", + "version": "1.3.27", "author": "Ghislain B.", "description": "Angular-Validation Directive and Service (ghiscoding)", "main": "app.js", diff --git a/protractor/conf.js b/protractor/conf.js index d6354dd..38bf86b 100644 --- a/protractor/conf.js +++ b/protractor/conf.js @@ -20,7 +20,7 @@ }, // Spec patterns are relative to the current working directory when protractor is called - specs: ['mixed_validation_spec.js', 'full_tests_spec.js'], + specs: ['mixed_validation_spec.js', 'dynamic_spec.js', 'full_tests_spec.js'], jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 550000 @@ -30,6 +30,8 @@ // format the output when tests are run with Team City onPrepare: function () { + browser.driver.manage().window().setPosition(0, 0); + browser.driver.manage().window().setSize(1024, 1045); if (process.env.TEAMCITY_VERSION) { require('jasmine-reporters'); jasmine.getEnv().addReporter(new jasmine.TeamcityReporter()); diff --git a/protractor/dynamic_spec.js b/protractor/dynamic_spec.js new file mode 100644 index 0000000..838c59b --- /dev/null +++ b/protractor/dynamic_spec.js @@ -0,0 +1,74 @@ +describe('Angular-Validation Dynamic (isolated scope) Tests:', function () { + // global variables + + describe('When choosing `more-examples` Dynamic Form Input', function () { + it('Should navigate to Dynamic Form Input home page', function () { + browser.get('http://localhost/github/angular-validation/more-examples/dynamic-form/index.html'); + + // Find the title element + var titleElement = element(by.css('h3')); + + // Assert that the text element has the expected value. + // Protractor patches 'expect' to understand promises. + expect(titleElement.getText()).toEqual('Form Validation (with dynamic form and fields)'); + }); + + it('Should click on Validate Submit button and expect 2 invalid Forms', function () { + var validateBtn = $('[name=validateForms]'); + validateBtn.click(); + browser.waitForAngular(); + + var elmForm1isValid = element(by.binding('Form1.isValid')); + expect(elmForm1isValid.getText()).toEqual('Form1 isValid: false'); + + var elmForm2isValid = element(by.binding('Form2.isValid')); + expect(elmForm2isValid.getText()).toEqual('Form2 isValid: false'); + }); + + it('Should enter valid data in Form1 and expect valid Form1 but invalid Form2', function () { + var elmFirstName = $('[name=firstName]'); + elmFirstName.sendKeys('abc'); + elmFirstName.sendKeys(protractor.Key.TAB); + + var elmLastName = $('[name=lastName]'); + elmLastName.sendKeys('def'); + elmLastName.sendKeys(protractor.Key.TAB); + + var validateBtn = $('[name=validateForms]'); + validateBtn.click(); + browser.waitForAngular(); + + var elmForm1isValid = element(by.binding('Form1.isValid')); + expect(elmForm1isValid.getText()).toEqual('Form1 isValid: true'); + + var elmForm2isValid = element(by.binding('Form2.isValid')); + expect(elmForm2isValid.getText()).toEqual('Form2 isValid: false'); + }); + + it('Should click on second Tab and show second Form', function () { + var tab2 = element(by.id('Item2')); + tab2.click(); + browser.waitForAngular(); + }); + + it('Should enter valid data in Form2 and expect both Forms to be valid', function () { + var elmEmail = $('[name=email]'); + elmEmail.sendKeys('abc'); + elmEmail.sendKeys(protractor.Key.TAB); + + var elmPhone = $('[name=phoneNo]'); + elmPhone.sendKeys('def'); + elmPhone.sendKeys(protractor.Key.TAB); + + var validateBtn = $('[name=validateForms]'); + validateBtn.click(); + browser.waitForAngular(); + + var elmForm1isValid = element(by.binding('Form1.isValid')); + expect(elmForm1isValid.getText()).toEqual('Form1 isValid: true'); + + var elmForm2isValid = element(by.binding('Form2.isValid')); + expect(elmForm2isValid.getText()).toEqual('Form2 isValid: true'); + }); + }); +}); \ No newline at end of file diff --git a/protractor/full_tests_spec.js b/protractor/full_tests_spec.js index 4a53215..8aa4c9a 100644 --- a/protractor/full_tests_spec.js +++ b/protractor/full_tests_spec.js @@ -557,7 +557,7 @@ function loadData() { { 'validator': 'email', 'invalid_data': ['g$g.com', 'g@g,com', 'g@g.'], - 'valid_data': ['g@g.com', 'someone@hotmail.com'], + 'valid_data': ['someone@hotmail.com', 'nickname@zone', 'other.email-with-dash@some-company.com', 'кокер@спаниель.рф'], 'error_message': { 'en': "Must be a valid email address.", 'es': "Debe contener una dirección de correo electronico valida.", @@ -610,7 +610,7 @@ function loadData() { 'invalid_data': ['ABC1234567890', 'DEABCD12500105170648489890'], 'valid_data': ['DE12500105170648489890', 'AB12500105170648489890'], 'error_message': { - 'en': "Must a valid IBAN.", + 'en': "Must be a valid IBAN.", 'es': "Debe contener un IBAN valido.", 'fr': "Doit être un IBAN valide.", 'no': "Må være en gyldig IBAN.", @@ -779,7 +779,7 @@ function loadData() { 'error_message': { 'en': "Must be a valid time format (hh:mm) OR (hh:mm:ss).", 'es': "Debe contener un formato de tiempo valido (hh:mm) ó (hh:mm:ss).", - 'fr': "Doit être un format de date valide (hh:mm) OU (hh:mm:ss).", + 'fr': "Doit être un format de temps valide (hh:mm) OU (hh:mm:ss).", 'no': "Må være et gyldig tidsformat (tt:mm) OR (tt:mm:ss).", 'ru': "Должно быть допустимым форматом времени (hh:mm) или (hh:mm:ss)." } diff --git a/protractor/mixed_validation_spec.js b/protractor/mixed_validation_spec.js index 6f29b49..ad83da1 100644 --- a/protractor/mixed_validation_spec.js +++ b/protractor/mixed_validation_spec.js @@ -70,6 +70,22 @@ ]; var types = ['Directive', 'Service']; + // variables used on 2Forms web page + var formElement2FormsNames = ['input2', 'input3', 'select1', 'area1']; + var formElement2FormsSummaryNames = ['First Name', 'Last Name', 'area1', 'select1']; + var errorMessages2Forms = [ + 'May only contain letters, numbers and dashes. Must be at least 2 characters. Field is required.', + 'May only contain letters, numbers and dashes. Must be at least 2 characters. Field is required.', + 'Change language', + 'May only contain letters, numbers, dashes and spaces. Must be at least 15 characters. Field is required.' + ]; + var validInput2FormsTexts = [ + 'John', + 'Doe', + 'en', + 'This is a great tool' + ]; + for(var k = 0, kln = types.length; k < kln; k++) { // because we are dealing with promises, we better use closures to pass certain variables (function(types, k) { @@ -241,4 +257,87 @@ }); // Directive() })(types, k); // closure } // for() + + describe('When clicking on top menu Angular-Validation >> 2 Forms', function () { + it('Should navigate to Angular-Validation home page', function () { + browser.get('http://localhost/Github/angular-validation'); + + // Find the title element + var titleElement = element(by.css('h1')); + + // Assert that the text element has the expected value. + // Protractor patches 'expect' to understand promises. + expect(titleElement.getText()).toEqual('Angular-Validation Directive|Service (ghiscoding)'); + }); + + it('Should navigate to TestingForm 2Forms page', function () { + browser.get('http://localhost/Github/angular-validation'); + + var anchorLink = $('[name=btn_goto_2forms]'); + anchorLink.click(); + browser.waitForAngular(); + + // Find the sub-title element + var titleElement = element(by.css('h3')); + + // Assert that the text element has the expected value. + // Protractor patches 'expect' to understand promises. + expect(titleElement.getText()).toEqual('Directive - 2 Forms'); + }); + + it('Should show ValidationSummary and contain all error messages', function () { + // showValidation checkbox should false at first but true after + var elmCheckboxShowSummary = element(by.model('displayValidationSummary')); + expect(elmCheckboxShowSummary.isSelected()).toBeFalsy(); + + var btnShowSummary = $('button[name=btn_showValidation]'); + btnShowSummary.click(); + browser.waitForAngular(); + + elmCheckboxShowSummary = element(by.model('displayValidationSummary')); + expect(elmCheckboxShowSummary.isSelected()).toBeTruthy(); + + // scroll back to top + browser.executeScript('window.scrollTo(0,0);').then(function () { + var itemRows = element.all(by.binding('message')); + var inputName; + + for (var i = 0, j = 0, ln = itemRows.length; i < ln; i++) { + // since field after input13 is part of errorMessages and is empty string, we need to skip that one + if (formElement2FormsNames[i] === 'input13') { + j++; + } + expect(itemRows.get(i).getText()).toEqual(formElement2FormsSummaryNames[i] + ': ' + errorMessages2Forms[j++]); + } + }); + }); + + it('Should enter valid text and make error go away', function () { + for (var i = 0, ln = formElement2FormsNames.length; i < ln; i++) { + // some fields are not required or disabled so no error will show up, continue to next ones + if (formElement2FormsNames[i] === 'input12' || formElement2FormsNames[i] === 'input14') { + continue; + } + var elmInput = $('[name=' + formElement2FormsNames[i] + ']'); + elmInput.click(); + elmInput.sendKeys(validInput2FormsTexts[i]); + + if (formElement2FormsNames[i] === 'select1') { + element(by.cssContainingText('option', validInput2FormsTexts[i])).click(); // click on good option + elmInput.sendKeys(protractor.Key.TAB); + } + + var elmError = $('.validation-' + formElement2FormsNames[i]); + expect(elmError.getText()).toEqual(''); + } + }); + + it('Should check that both submit button are now enabled', function() { + var elmSubmit1 = $('[name=save_btn1]'); + expect(elmSubmit1.isEnabled()).toBe(true); + + var elmSubmit2 = $('[name=save_btn2]'); + expect(elmSubmit2.isEnabled()).toBe(true); + }); + }); }); \ No newline at end of file diff --git a/readme.md b/readme.md index b5a9ca0..7e078b6 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ #Angular Validation (Directive / Service) -`Version: 1.3.26` +`Version: 1.3.27` ### Form validation after user inactivity of default 1sec. (customizable timeout) Forms Validation with Angular made easy! Angular-Validation is an angular directive/service with locales (languages) with a very simple approach of defining your `validation=""` directly within your element to validate (input, textarea, etc) and...that's it!!! The directive/service will take care of the rest! @@ -73,6 +73,7 @@ All the documentation has been moved to the Wiki section, see the [github wiki]( * [Service Examples](https://github.com/ghiscoding/angular-validation/wiki/Working-Service-Examples) * [Alternate Text on Validators](https://github.com/ghiscoding/angular-validation/wiki/Alternate-Text-on-Validators) * [DisplayErrorTo](https://github.com/ghiscoding/angular-validation/wiki/Bootstrap-Input-Groups-Wrapping) + * [Isolated Scope](https://github.com/ghiscoding/angular-validation/wiki/Isolated-Scope) * [PreValidate Form (on page load)](https://github.com/ghiscoding/angular-validation/wiki/PreValidate-Form-(on-page-load)) * [Remote Validation (AJAX)](https://github.com/ghiscoding/angular-validation/wiki/Remote-Validation-(AJAX)) * [Remove a Validator](https://github.com/ghiscoding/angular-validation/wiki/Remove-Validator-from-Element) diff --git a/src/validation-common.js b/src/validation-common.js index 66b6e1e..9f26715 100644 --- a/src/validation-common.js +++ b/src/validation-common.js @@ -22,6 +22,7 @@ angular var timer; // timer of user inactivity time var typingLimit; // maximum user inactivity typing limit var formElements = []; // Array of all Form Elements, this is not a DOM Elements, these are custom objects defined as { fieldName, elm, attrs, ctrl, isValid, message } + var remotePromises = []; // keep track of promises called and running when using the Remote validator var validators = []; // Array of all Form Validators var validatorAttrs = {}; // Current Validator attributes var validationSummary = []; // Array Validation Error Summary @@ -46,10 +47,16 @@ angular this.ctrl = ctrl; this.validatorAttrs = attrs; + // user could pass his own scope, useful in a case of an isolate scope + if (!!scope && !!scope.$validationOptions && scope.$validationOptions.hasOwnProperty('isolatedScope')) { + this.scope = scope.$validationOptions.isolatedScope; // rewrite original scope + this.scope.$validationOptions = scope.$validationOptions; // reuse the validationOption from original scope + } + // only the angular-validation Directive can possibly reach this condition with all properties filled // on the other hand the angular-validation Service will `initialize()` function to initialize the same set of variables - if(!!scope && !!elm && !!attrs && !!ctrl) { - addToFormElementObjectList(elm, attrs, ctrl, scope); + if (!!this.elm && !!this.validatorAttrs && !!this.ctrl && !!this.scope) { + addToFormElementObjectList(this.elm, this.validatorAttrs, this.ctrl, this.scope); this.defineValidation(); } }; @@ -363,13 +370,34 @@ angular isValid = (otherNgModelVal === strValue && !!strValue); } // it might be a remote validation, this should return a promise with the result as a boolean or a { isValid: bool, message: msg } - else if(validator.type === "remote" && strValue !== '') { - //isValid = false; // make sure to invalidate the field so that the Directive/Service itself does not make it as true even though Remote might not be finished. + else if(validator.type === "remote" && strValue !== '' && showError === true) { self.ctrl.$processing = true; // $processing can be use in the DOM to display a remote processing message to the user + var fct = null; var fname = validator.params[0]; - var fn = self.scope[fname]; - var promise = (typeof fn === "function") ? fn() : null; + if (fname.indexOf(".") === -1) { + fct = self.scope[fname]; + } else { + // function name might also be declared with the Controller As alias, for example: vm.customRemote() + // split the name and flatten it to find it inside the scope + var split = fname.split('.'); + fct = self.scope; + for (var k = 0, kln = split.length; k < kln; k++) { + fct = fct[split[k]]; + } + } + var promise = (typeof fct === "function") ? fct() : null; + + // if we already have previous promises running, we might want to abort them (if user specified an abort function) + if (remotePromises.length > 1) { + while (remotePromises.length > 0) { + var previousPromise = remotePromises.pop(); + if (typeof previousPromise.abort === "function") { + previousPromise.abort(); // run the abort if user declared it + } + } + } + remotePromises.push(promise); // always add to beginning of array list of promises if(!!promise && typeof promise.then === "function") { self.ctrl.$setValidity('remote', false); // make the field invalid before processing it @@ -377,8 +405,11 @@ angular // process the promise (function(altText) { promise.then(function (result) { - self.ctrl.$processing = false; // finished resolving, no more pending - var errorMsg = message + ' '; // use the global error message + result = result.data || result; + remotePromises.pop(); // remove the last promise from array list of promises + + self.ctrl.$processing = false; // finished resolving, no more pending + var errorMsg = message + ' '; // use the global error message if (typeof result === "boolean") { isValid = (!!result) ? true : false; @@ -403,7 +434,7 @@ angular }); })(validator.altText); }else { - throw 'Remote Validation requires a declared function (in your Controller) which also returns a promise, please review your code.' + throw 'Remote Validation requires a declared function (in your Controller) which also needs to return a Promise, please review your code.' } } // or finally it might be a regular regex pattern checking @@ -432,7 +463,7 @@ angular if(!isValid) { isFieldValid = false; - // run $translate promise, use closures to keep access to all necessary variables + // run $translate promise, use closures to keep access to all necessary variables (function(formElmObj, isValid, validator) { var msgToTranslate = validator.message; if(!!validator.altText && validator.altText.length > 0) { @@ -522,12 +553,15 @@ angular } // if user is pre-validating all form elements, display error right away - if(self.validatorAttrs.preValidateFormElements || (!!self.scope.$validationOptions && self.scope.$validationOptions.hasOwnProperty('preValidateFormElements'))) { + if (!!self.validatorAttrs.preValidateFormElements || (!!self.scope.$validationOptions && !!self.scope.$validationOptions.preValidateFormElements)) { // make the element as it was touched for CSS, only works in AngularJS 1.3+ if (!!formElmObj && typeof self.ctrl.$setTouched === "function") { formElmObj.ctrl.$setTouched(); } - updateErrorMsg(message, { isSubmitted: true, isValid: isFieldValid, obj: formElmObj }); + // only display errors on page load, when elements are not yet dirty + if(self.ctrl.$dirty === false) { + updateErrorMsg(message, { isSubmitted: true, isValid: isFieldValid, obj: formElmObj }); + } } // error Display @@ -637,13 +671,24 @@ angular function getElementParentForm(elmName, self) { // from the element passed, get his parent form var forms = document.getElementsByName(elmName); + var parentForm = null; for (var i = 0; i < forms.length; i++) { var form = forms[i].form; if (!!form && form.name && self.scope[form.name]) { - return self.scope[form.name]; + parentForm = self.scope[form.name]; + if(typeof parentForm.$name === "undefined") { + parentForm.$name = form.name; // make sure it has a $name, since we use that variable later on + } + return parentForm; } } + + // falling here with a form name but without a form object found in the scope is often due to isolate scope + // we can hack it and define our own form inside this isolate scope, in that way we can still use something like: isolateScope.form1.$validationSummary + if(!!form && !!form.name) { + return self.scope[form.name] = { $name: form.name, specialNote: 'Created by Angular-Validation for Isolated Scope usage' }; + } return null; } diff --git a/src/validation-directive.js b/src/validation-directive.js index 0b38081..73a96b8 100644 --- a/src/validation-directive.js +++ b/src/validation-directive.js @@ -45,6 +45,14 @@ } }); + // onBlur make validation without waiting + elm.bind('blur', blurHandler = function(event) { + if(!isValidationCancelled) { + // validate without delay + attemptToValidate(event.target.value, 0); + } + }); + //---- // Private functions declaration //---------------------------------- @@ -66,7 +74,10 @@ * and is also customizable through the (typing-limit) for which inactivity this.timer will trigger validation. * @param string value: value of the input field */ - function attemptToValidate(value) { + function attemptToValidate(value, typingLimit) { + // get the waiting delay time if passed as argument or get it from common Object + var waitingLimit = (typeof typingLimit !== "undefined") ? typingLimit : commonObj.typingLimit; + // pre-validate without any events just to pre-fill our validationSummary with all field errors // passing false as 2nd argument for not showing any errors on screen commonObj.validate(value, false); @@ -84,15 +95,6 @@ ctrl.$setValidity('validation', false); } - // if field is not required and his value is empty, cancel validation and exit out - // onBlur make validation without waiting - elm.bind('blur', blurHandler = function() { - if(!isValidationCancelled) { - // make the regular validation of the field value - scope.$evalAsync( ctrl.$setValidity('validation', commonObj.validate(value, true)) ); - } - }); - // if a field holds invalid characters which are not numbers inside an `input type="number"`, then it's automatically invalid // we will still call the `.validate()` function so that it shows also the possible other error messages if((value === "" || typeof value === "undefined") && elm.prop('type').toUpperCase() === "NUMBER") { @@ -116,7 +118,7 @@ $timeout.cancel(timer); timer = $timeout(function() { scope.$evalAsync(ctrl.$setValidity('validation', commonObj.validate(value, true) )); - }, commonObj.typingLimit); + }, waitingLimit); } return value; diff --git a/src/validation-rules.js b/src/validation-rules.js index a8bf782..65b38a8 100644 --- a/src/validation-rules.js +++ b/src/validation-rules.js @@ -389,7 +389,9 @@ angular break; case "email" : validator = { - pattern: "^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", + // Email RFC 5322, pattern pulled from http://www.regular-expressions.info/email.html + // but removed necessity of a TLD (Top Level Domain) which makes this email valid: admin@mailserver1 + pattern: "(?:[\\wа-я0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.?[\\wа-я0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\wа-я0-9](?:[\\wа-я0-9-]*[\\wа-я0-9])?\\.?)+[\\wа-я0-9](?:[\\wа-я0-9-]*[\\wа-я0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.?){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[\\wа-я0-9-]*[\\wа-я0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", message: "INVALID_EMAIL", type: "regex" }; diff --git a/src/validation-service.js b/src/validation-service.js index de36f97..0607ea6 100644 --- a/src/validation-service.js +++ b/src/validation-service.js @@ -74,13 +74,24 @@ angular } attrs.name = attrs.elmName; + // get the scope from the validator or from the global options (validationAttrs) + var scope = (!!attrs.scope) ? attrs.scope : self.validationAttrs.scope; + + // user could pass his own scope, useful in a case of an isolate scope + if (!!self.validationAttrs.isolatedScope) { + var tempValidationOptions = scope.$validationOptions || null; // keep global validationOptions + scope = self.validationAttrs.isolatedScope; // rewrite original scope + if(!!tempValidationOptions) { + scope.$validationOptions = tempValidationOptions; // reuse the validationOption from original scope + } + } + // onBlur make validation without waiting attrs.elm.bind('blur', blurHandler = function(event) { if(!isValidationCancelled) { - // re-initialize to use current element & remove waiting time & validate - self.commonObj.initialize(attrs.scope, attrs.elm, attrs, attrs.ctrl); - self.commonObj.typingLimit = 0; - attemptToValidate(self, event.target.value); + // re-initialize to use current element & validate without delay + self.commonObj.initialize(scope, attrs.elm, attrs, attrs.ctrl); + attemptToValidate(self, event.target.value, 0); } }); @@ -89,7 +100,7 @@ angular attrs = mergeObjects(self.validationAttrs, attrs); // watch the element for any value change, validate it once that happen - attrs.scope.$watch(attrs.elmName, function (newVal, oldVal) { + scope.$watch(attrs.elmName, function (newVal, oldVal) { // when previous value was set and new value is not, this is most probably an invalid character entered in a type input="text" // we will still call the `.validate()` function so that it shows also the possible other error messages if(newVal === undefined && oldVal !== undefined) { @@ -101,7 +112,7 @@ angular attrs.ctrl = angular.element(attrs.elm).controller('ngModel'); attrs.value = newVal; - self.commonObj.initialize(attrs.scope, attrs.elm, attrs, attrs.ctrl); + self.commonObj.initialize(scope, attrs.elm, attrs, attrs.ctrl); attemptToValidate(self, newVal); }, true); // $watch() @@ -244,7 +255,10 @@ angular * @param object self * @param string value: value of the input field */ - function attemptToValidate(self, value) { + function attemptToValidate(self, value, typingLimit) { + // get the waiting delay time if passed as argument or get it from common Object + var waitingLimit = (typeof typingLimit !== "undefined") ? typingLimit : self.commonObj.typingLimit; + // pre-validate without any events just to pre-fill our validationSummary with all field errors // passing false as 2nd argument for not showing any errors on screen self.commonObj.validate(value, false); @@ -285,7 +299,7 @@ angular $timeout.cancel(self.timer); self.timer = $timeout(function() { self.commonObj.scope.$evalAsync(self.commonObj.ctrl.$setValidity('validation', self.commonObj.validate(value, true) )); - }, self.commonObj.typingLimit); + }, waitingLimit); } return value; diff --git a/templates/testing2Forms.html b/templates/testing2Forms.html index 221a4e9..d7146cc 100644 --- a/templates/testing2Forms.html +++ b/templates/testing2Forms.html @@ -1,3 +1,9 @@ +
+ {{ 'FORM_PREVALIDATED' | translate }}
+ + Wiki - PreValidate Form (on page load) +
+

Directive - 2 Forms

@@ -6,6 +12,7 @@

Directive - 2 Forms

+
@@ -30,7 +37,7 @@

{{ 'ERRORS' | translate }}!

- +
@@ -61,6 +68,8 @@

{{ 'ERRORS' | translate }}!

- +
- \ No newline at end of file + +
+ \ No newline at end of file diff --git a/templates/testingFormDirective.html b/templates/testingFormDirective.html index 75df1b0..dce5a60 100644 --- a/templates/testingFormDirective.html +++ b/templates/testingFormDirective.html @@ -1,3 +1,8 @@ + +

Directive

@@ -113,7 +118,7 @@

{{ 'ERRORS' | translate }}!

- +
diff --git a/templates/testingFormService.html b/templates/testingFormService.html index e2d4ca5..467cfd7 100644 --- a/templates/testingFormService.html +++ b/templates/testingFormService.html @@ -1,3 +1,8 @@ + +

Service

@@ -117,7 +122,7 @@

{{ 'ERRORS' | translate }}!

- +