From 5e4f1ee5ec858776572c0c1b659839a2cf953145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Augustin=20=C5=A0ulc?= Date: Mon, 8 Jun 2015 15:41:34 +0200 Subject: [PATCH] Client validation updated to support Bootstrap styling. #27 --- .../Web/Scripts/_references.js | Bin 566 -> 700 bytes .../Web/Scripts/jquery-bundle.min.js | 212 +++++++++++++++++- .../jquery.validate.unobtrusive.bootstrap.js | 24 ++ src/Server/DeploymentFramework/Web/Web.csproj | 1 + .../DeploymentFramework/Web/gulpfile.js | 4 +- 5 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 src/Server/DeploymentFramework/Web/Scripts/jquery.validate.unobtrusive.bootstrap.js diff --git a/src/Server/DeploymentFramework/Web/Scripts/_references.js b/src/Server/DeploymentFramework/Web/Scripts/_references.js index 75593dfcee7ede2c7f07369ab239fd57256e89fa..6392b9c0d4d600d788095f4d8e4a5c13994ed993 100644 GIT binary patch delta 48 zcmdnSvWInp8Pntqj7cg<4EaD?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this);b.data("validator").resetForm();b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){return a(b.form).find(":input").filter("[name='"+f(c)+"']").val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); \ No newline at end of file +(function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this);b.data("validator").resetForm();b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){return a(b.form).find(":input").filter("[name='"+f(c)+"']").val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); +(function(jQuery) { + + var isValidDate = function(value, format) { + var getDateParser = function (regex, dayIndex, monthIndex, yearIndex) { + return { + regex: regex, + dayIndex: dayIndex, + monthIndex: monthIndex, + yearIndex: yearIndex + }; + }; + + var dateParser; + switch (format) { + case "d/M/yyyy": + dateParser = getDateParser(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/, 1, 2, 3); + break; + case "d-M-yyyy": + dateParser = getDateParser(/^(\d{1,2})-(\d{1,2})-(\d{4})$/, 1, 2, 3); + break; + case "d/M/yy": + dateParser = getDateParser(/^(\d{1,2})\/(\d{1,2})\/(\d{2})$/, 1, 2, 3); + break; + case "d-M-yy": + dateParser = getDateParser(/^(\d{1,2})-(\d{1,2})-(\d{2})$/, 1, 2, 3); + break; + case "dd/MM/yyyy": + dateParser = getDateParser(/^(\d{2})\/(\d{2})\/(\d{4})$/, 1, 2, 3); + break; + case "dd-MM-yyyy": + dateParser = getDateParser(/^(\d{2})-(\d{2})-(\d{4})$/, 1, 2, 3); + break; + case "dd/MM/yy": + dateParser = getDateParser(/^(\d{2})\/(\d{2})\/(\d{2})$/, 1, 2, 3); + break; + case "dd-MM-yy": + dateParser = getDateParser(/^(\d{2})-(\d{2})-(\d{2})$/, 1, 2, 3); + break; + case "M/d/yyyy": + dateParser = getDateParser(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/, 2, 1, 3); + break; + case "M-d-yyyy": + dateParser = getDateParser(/^(\d{1,2})-(\d{1,2})-(\d{4})$/, 2, 1, 3); + break; + case "M/d/yy": + dateParser = getDateParser(/^(\d{1,2})\/(\d{1,2})\/(\d{2})$/, 2, 1, 3); + break; + case "M-d-yy": + dateParser = getDateParser(/^(\d{1,2})-(\d{1,2})-(\d{2})$/, 2, 1, 3); + break; + case "MM/dd/yyyy": + dateParser = getDateParser(/^(\d{2})\/(\d{2})\/(\d{4})$/, 2, 1, 3); + break; + case "MM-dd-yyyy": + dateParser = getDateParser(/^(\d{2})-(\d{2})-(\d{4})$/, 2, 1, 3); + break; + case "MM/dd/yy": + dateParser = getDateParser(/^(\d{2})\/(\d{2})\/(\d{2})$/, 2, 1, 3); + break; + case "MM-dd-yy": + dateParser = getDateParser(/^(\d{2})-(\d{2})-(\d{2})$/, 2, 1, 3); + break; + case "yyyy/MM/dd": + dateParser = getDateParser(/^(\d{4})\/(\d{2})\/(\d{2})$/, 3, 2, 1); + break; + case "yyyy-MM-dd": + dateParser = getDateParser(/^(\d{4})-(\d{2})-(\d{2})$/, 3, 2, 1); + break; + case "yyyy/M/d": + dateParser = getDateParser(/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/, 3, 2, 1); + break; + case "yyyy-M-d": + dateParser = getDateParser(/^(\d{4})-(\d{1,2})-(\d{1,2})$/, 3, 2, 1); + break; + default: + return true; + } + + var match = value.match(dateParser.regex); + if (match == null) + return false; + + var year = match[dateParser.yearIndex] * 1; + var month = match[dateParser.monthIndex] * 1; + var day = match[dateParser.dayIndex] * 1; + if (year < 50) + year += 2000; + if (year < 100) + year += 1900; + + var date = new Date(year, month - 1, day); + + // http://stackoverflow.com/questions/8098202/javascript-detecting-valid-dates + if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) + return false; + + return true; + }; + + var isValidTime = function (value, format) { + + var getTimeParser = function (regex, hourIndex, minuteIndex, secondIndex, is12HourTime) { + return { + regex: regex, + hourIndex: hourIndex, + minuteIndex: minuteIndex, + secondIndex: secondIndex, + is12HourTime: is12HourTime + }; + }; + + var timeParser; + switch (format) { + case "h:mmtt": + timeParser = getTimeParser(/(\d{1,2}):(\d{2})(am|pm)/i, 1, 2, -1, true); + break; + case "h:mm:sstt": + timeParser = getTimeParser(/(\d{1,2}):(\d{2}):(\d{2})(am|pm)/i, 1, 2, 3, true); + break; + case "hh:mmtt": + timeParser = getTimeParser(/(\d{2}):(\d{2})(am|pm)/i, 1, 2, -1, true); + break; + case "hh:mm:sstt": + timeParser = getTimeParser(/(\d{2}):(\d{2}):(\d{2})(am|pm)/i, 1, 2, 3, true); + break; + case "H:mm": + timeParser = getTimeParser(/(\d{1,2}):(\d{2})/, 1, 2, -1, false); + break; + case "H:mm:ss": + timeParser = getTimeParser(/(\d{1,2}):(\d{2}):(\d{2})/, 1, 2, 3, false); + break; + case "HH:mm": + timeParser = getTimeParser(/(\d{2}):(\d{2})/, 1, 2, -1, false); + break; + case "HH:mm:ss": + timeParser = getTimeParser(/(\d{2}):(\d{2}):(\d{2})/, 1, 2, 3, false); + break; + default: + return true; + } + + var match = value.match(timeParser.regex); + if (match == null) + return false; + + var hour = match[timeParser.hourIndex] * 1; + var minute = match[timeParser.minuteIndex] * 1; + var is12Hour = timeParser.is12HourTime; + + if (is12Hour && (hour < 1 || hour > 12)) + return false; + if (!is12Hour && (hour < 0 || hour > 23)) + return false; + if (minute < 0 || minute > 59) + return false; + if (timeParser.secondIndex > 0) { + var second = match[timeParser.secondIndex] * 1; + if (second < 0 || second > 59) + return false; + } + + return true; + }; + + jQuery.validator.methods.date = function(value, element) { + if (this.optional(element)) + return true; + + var format = jQuery(element).data("val-format"); + if (format == "") + return !/Invalid|NaN/.test(new Date(value).toString()); + + var formatSplitBySpaces = format.split(" "); + if (formatSplitBySpaces.length > 2) + return true; + + var valueSplitBySpaces = value.split(" "); + if (formatSplitBySpaces.length != valueSplitBySpaces.length) + return false; + + return isValidDate(valueSplitBySpaces[0], formatSplitBySpaces[0]) + && (formatSplitBySpaces.length == 1 || isValidTime(valueSplitBySpaces[1], formatSplitBySpaces[1])); + }; + +})(jQuery); + +// from http://danielwertheim.se/2014/08/29/asp-net-mvc-5-quick-tip-for-unobtrusive-validation-and-bootstrap-styling/comment-page-1/ +(function ($) { + var defaultOptions = { + errorClass: 'has-error', + validClass: 'has-success', + highlight: function (element, errorClass, validClass) { + $(element).closest(".form-group") + .addClass(errorClass) + .removeClass(validClass); + }, + unhighlight: function (element, errorClass, validClass) { + $(element).closest(".form-group") + .removeClass(errorClass) + .addClass(validClass); + } + }; + + $.validator.setDefaults(defaultOptions); + + $.validator.unobtrusive.options = { + errorClass: defaultOptions.errorClass, + validClass: defaultOptions.validClass, + }; +})(jQuery); \ No newline at end of file diff --git a/src/Server/DeploymentFramework/Web/Scripts/jquery.validate.unobtrusive.bootstrap.js b/src/Server/DeploymentFramework/Web/Scripts/jquery.validate.unobtrusive.bootstrap.js new file mode 100644 index 0000000..c7ce25e --- /dev/null +++ b/src/Server/DeploymentFramework/Web/Scripts/jquery.validate.unobtrusive.bootstrap.js @@ -0,0 +1,24 @@ +// from http://danielwertheim.se/2014/08/29/asp-net-mvc-5-quick-tip-for-unobtrusive-validation-and-bootstrap-styling/comment-page-1/ +(function ($) { + var defaultOptions = { + errorClass: 'has-error', + validClass: 'has-success', + highlight: function (element, errorClass, validClass) { + $(element).closest(".form-group") + .addClass(errorClass) + .removeClass(validClass); + }, + unhighlight: function (element, errorClass, validClass) { + $(element).closest(".form-group") + .removeClass(errorClass) + .addClass(validClass); + } + }; + + $.validator.setDefaults(defaultOptions); + + $.validator.unobtrusive.options = { + errorClass: defaultOptions.errorClass, + validClass: defaultOptions.validClass, + }; +})(jQuery); \ No newline at end of file diff --git a/src/Server/DeploymentFramework/Web/Web.csproj b/src/Server/DeploymentFramework/Web/Web.csproj index 80ad2fb..de64745 100644 --- a/src/Server/DeploymentFramework/Web/Web.csproj +++ b/src/Server/DeploymentFramework/Web/Web.csproj @@ -367,6 +367,7 @@ + diff --git a/src/Server/DeploymentFramework/Web/gulpfile.js b/src/Server/DeploymentFramework/Web/gulpfile.js index 5edee51..56b4606 100644 --- a/src/Server/DeploymentFramework/Web/gulpfile.js +++ b/src/Server/DeploymentFramework/Web/gulpfile.js @@ -14,7 +14,9 @@ var config = { jquerysrc: [ 'bower_components/jquery/dist/jquery.min.js', 'bower_components/jquery-validation/dist/jquery.validate.min.js', - 'bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js' + 'bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js', + 'Scripts/jquery.validate.unobtrusive.chameleon.js', + 'Scripts/jquery.validate.unobtrusive.bootstrap.js' ], jquerybundle: 'Scripts/jquery-bundle.min.js',