Skip to content
Browse files

Added grunt build script (and fixed some lint errors to make it pass)

  • Loading branch information...
1 parent aaf9d43 commit 7fd9344fdb60bad987b86860055311aed589b51a @thedersen committed Jul 23, 2012
View
8 backbone.validation.min.js
@@ -1,8 +0,0 @@
-// Backbone.Validation v0.6.0
-//
-// Copyright (C)2011-2012 Thomas Pedersen
-// Distributed under MIT License
-//
-// Documentation and full license available at:
-// http://thedersen.com/projects/backbone-validation
-(function(e,t){var n={forceUpdate:!1,selector:"name",labelFormatter:"sentenceCase",valid:Function.prototype,invalid:Function.prototype};e.Validation=function(){var e=function(e){return t.reduce(t.keys(e.validation||{}),function(e,t){return e[t]=void 0,e},{})},i=function(e,n){var r=e.validation?e.validation[n]||{}:{};return t.isFunction(r)?r:t.isString(r)?e[r]:(t.isArray(r)||(r=[r]),t.reduce(r,function(e,n){return t.each(t.without(t.keys(n),"msg"),function(t){e.push({fn:u[t],val:n[t],msg:n.msg})}),e},[]))},s=function(e,n,r,s){var o=i(e,n);return t.isFunction(o)?o.call(e,r,n,s):t.reduce(o,function(t,i){var o=i.fn.call(u,r,n,i.val,e,s);return o===!1||t===!1?!1:o&&!t?i.msg||o:t},"")},o=function(e,n){var r,i,o,u={},a=!0,f=t.clone(n);for(o in n)i=s(e,o,n[o],f),i&&(u[o]=i,a=!1);return{invalidAttrs:u,isValid:a}},a=function(n,r){return{preValidate:function(e,n){return s(this,e,n,t.extend({},this.attributes))},isValid:function(e){if(t.isString(e))return!s(this,e,this.get(e),t.extend({},this.attributes));if(t.isArray(e)){for(var n=0;n<e.length;n++)if(s(this,e[n],this.get(e[n]),t.extend({},this.attributes)))return!1;return!0}return e===!0&&this.validate(),this.validation?this._isValid:!0},validate:function(i,s){var u=this,a=!i,f=t.extend({},r,s),l=t.extend(e(u),u.attributes,i),c=i||l,h=o(u,l);u._isValid=h.isValid;for(var p in l){var d=h.invalidAttrs.hasOwnProperty(p),v=c.hasOwnProperty(p);d&&(v||a)&&f.invalid(n,p,h.invalidAttrs[p],f.selector),d||f.valid(n,p,f.selector)}t.defer(function(){u.trigger("validated",u._isValid,u,h.invalidAttrs),u.trigger("validated:"+(u._isValid?"valid":"invalid"),u,h.invalidAttrs)});if(!f.forceUpdate&&t.intersection(t.keys(h.invalidAttrs),t.keys(c)).length>0)return h.invalidAttrs}}},f=function(e,n,r){t.extend(n,a(e,r))},l=function(e){delete e.validate,delete e.preValidate,delete e.isValid},c=function(e){f(this.view,e,this.options)},h=function(e){l(e)};return{version:"0.6.0",configure:function(e){t.extend(n,e)},bind:function(e,i){var s=e.model,o=e.collection;i=t.extend({},n,r,i),s&&f(e,s,i),o&&(o.each(function(t){f(e,t,i)}),o.bind("add",c,{view:e,options:i}),o.bind("remove",h))},unbind:function(e){var t=e.model,n=e.collection;t&&l(e.model),n&&(n.each(function(e){l(e)}),n.unbind("add",c),n.unbind("remove",h))},mixin:a(null,n)}}();var r=e.Validation.callbacks={valid:function(e,t,n){e.$("["+n+"~="+t+"]").removeClass("invalid").removeAttr("data-error")},invalid:function(e,t,n,r){e.$("["+r+"~="+t+"]").addClass("invalid").attr("data-error",n)}},i=e.Validation.patterns={digits:/^\d+$/,number:/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,email:/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,url:/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i},s=e.Validation.messages={required:"{0} is required",acceptance:"{0} must be accepted",min:"{0} must be greater than or equal to {1}",max:"{0} must be less than or equal to {1}",range:"{0} must be between {1} and {2}",length:"{0} must be {1} characters",minLength:"{0} must be at least {1} characters",maxLength:"{0} must be at most {1} characters",rangeLength:"{0} must be between {1} and {2} characters",oneOf:"{0} must be one of: {1}",equalTo:"{0} must be the same as {1}",pattern:"{0} must be a valid {1}"},o=e.Validation.labelFormatters={none:function(e){return e},sentenceCase:function(e){return e.replace(/(?:^\w|[A-Z]|\b\w)/g,function(e,t){return t===0?e.toUpperCase():" "+e.toLowerCase()}).replace("_"," ")},label:function(e,t){return t.labels[e]||o.sentenceCase(e,t)}},u=e.Validation.validators=function(){var e=String.prototype.trim?function(e){return e===null?"":String.prototype.trim.call(e)}:function(e){var t=/^\s+/,n=/\s+$/;return e===null?"":e.toString().replace(t,"").replace(n,"")},r=function(e,t){return o[n.labelFormatter](e,t)},u=function(){var e=Array.prototype.slice.call(arguments),t=e.shift();return t.replace(/\{(\d+)\}/g,function(t,n){return typeof e[n]!="undefined"?e[n]:t})},a=function(e){return t.isNumber(e)||t.isString(e)&&e.match(i.number)},f=function(n){return!(t.isNull(n)||t.isUndefined(n)||t.isString(n)&&e(n)==="")};return{fn:function(e,n,r,i,s){return t.isString(r)&&(r=i[r]),r.call(i,e,n,s)},required:function(e,n,i,o,a){var l=t.isFunction(i)?i.call(o,e,n,a):i;if(!l&&!f(e))return!1;if(l&&!f(e))return u(s.required,r(n,o))},acceptance:function(e,n,i,o){if(e!=="true"&&(!t.isBoolean(e)||e===!1))return u(s.acceptance,r(n,o))},min:function(e,t,n,i){if(!a(e)||e<n)return u(s.min,r(t,i),n)},max:function(e,t,n,i){if(!a(e)||e>n)return u(s.max,r(t,i),n)},range:function(e,t,n,i){if(!a(e)||e<n[0]||e>n[1])return u(s.range,r(t,i),n[0],n[1])},length:function(t,n,i,o){if(!f(t)||e(t).length!==i)return u(s.length,r(n,o),i)},minLength:function(t,n,i,o){if(!f(t)||e(t).length<i)return u(s.minLength,r(n,o),i)},maxLength:function(t,n,i,o){if(!f(t)||e(t).length>i)return u(s.maxLength,r(n,o),i)},rangeLength:function(t,n,i,o){if(!f(t)||e(t).length<i[0]||e(t).length>i[1])return u(s.rangeLength,r(n,o),i[0],i[1])},oneOf:function(e,n,i,o){if(!t.include(i,e))return u(s.oneOf,r(n,o),i.join(", "))},equalTo:function(e,t,n,i,o){if(e!==o[n])return u(s.equalTo,r(t,i),r(n,i))},pattern:function(e,t,n,o){if(!f(e)||!e.toString().match(i[n]||n))return u(s.pattern,r(t,o),n)}}}()})(Backbone,_);
View
2 buster.js
@@ -10,7 +10,7 @@ for (var i = 0; i < versions.length; i++) {
'lib/jquery-1.6.2.js',
'lib/underscore.js',
'lib/backbone-' + ver + '.js',
- 'backbone.validation.js'
+ 'dist/backbone-validation.js'
],
tests: [
'tests/**/*.js'
View
2 buster.min.js
@@ -10,7 +10,7 @@ for (var i = 0; i < versions.length; i++) {
'lib/jquery-1.6.2.js',
'lib/underscore.js',
'lib/backbone-' + ver + '.js',
- 'backbone.validation.min.js'
+ 'dist/backbone-validation-min.js'
],
tests: [
'tests/**/*.js'
View
8 dist/backbone-validation-min.js
@@ -0,0 +1,8 @@
+// Backbone.Validation v0.6.0
+//
+// Copyright (c) 2011-2012 Thomas Pedersen
+// Distributed under MIT License
+//
+// Documentation and full license available at:
+// http://thedersen.com/projects/backbone-validation
+(function(a,b){"use strict";var c={forceUpdate:!1,selector:"name",labelFormatter:"sentenceCase",valid:Function.prototype,invalid:Function.prototype};a.Validation=function(){var a=function(a){return b.reduce(b.keys(a.validation||{}),function(a,b){return a[b]=void 0,a},{})},e=function(a,c){var d=a.validation?a.validation[c]||{}:{};if(b.isFunction(d)||b.isString(d))d={fn:d};return b.isArray(d)||(d=[d]),b.reduce(d,function(a,c){return b.each(b.without(b.keys(c),"msg"),function(b){a.push({fn:h[b],val:c[b],msg:c.msg})}),a},[])},f=function(a,c,d,f){return b.reduce(e(a,c),function(b,e){var g=e.fn.call(h,d,c,e.val,a,f);return g===!1||b===!1?!1:g&&!b?e.msg||g:b},"")},g=function(a,c){var d,e,g={},h=!0,i=b.clone(c);for(e in c)d=f(a,e,c[e],i),d&&(g[e]=d,h=!1);return{invalidAttrs:g,isValid:h}},i=function(c,d){return{preValidate:function(a,c){return f(this,a,c,b.extend({},this.attributes))},isValid:function(a){if(b.isString(a))return!f(this,a,this.get(a),b.extend({},this.attributes));if(b.isArray(a)){for(var c=0;c<a.length;c++)if(f(this,a[c],this.get(a[c]),b.extend({},this.attributes)))return!1;return!0}return a===!0&&this.validate(),this.validation?this._isValid:!0},validate:function(e,f){var h=this,i=!e,j=b.extend({},d,f),k=b.extend(a(h),h.attributes,e),l=e||k,m=g(h,k);h._isValid=m.isValid;for(var n in k){var o=m.invalidAttrs.hasOwnProperty(n),p=l.hasOwnProperty(n);o&&(p||i)&&j.invalid(c,n,m.invalidAttrs[n],j.selector),o||j.valid(c,n,j.selector)}b.defer(function(){h.trigger("validated",h._isValid,h,m.invalidAttrs),h.trigger("validated:"+(h._isValid?"valid":"invalid"),h,m.invalidAttrs)});if(!j.forceUpdate&&b.intersection(b.keys(m.invalidAttrs),b.keys(l)).length>0)return m.invalidAttrs}}},j=function(a,c,d){b.extend(c,i(a,d))},k=function(a){delete a.validate,delete a.preValidate,delete a.isValid},l=function(a){j(this.view,a,this.options)},m=function(a){k(a)};return{version:"0.6.0",configure:function(a){b.extend(c,a)},bind:function(a,e){var f=a.model,g=a.collection;e=b.extend({},c,d,e),f&&j(a,f,e),g&&(g.each(function(b){j(a,b,e)}),g.bind("add",l,{view:a,options:e}),g.bind("remove",m))},unbind:function(a){var b=a.model,c=a.collection;b&&k(a.model),c&&(c.each(function(a){k(a)}),c.unbind("add",l),c.unbind("remove",m))},mixin:i(null,c)}}();var d=a.Validation.callbacks={valid:function(a,b,c){a.$("["+c+"~="+b+"]").removeClass("invalid").removeAttr("data-error")},invalid:function(a,b,c,d){a.$("["+d+"~="+b+"]").addClass("invalid").attr("data-error",c)}},e=a.Validation.patterns={digits:/^\d+$/,number:/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,email:/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,url:/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i},f=a.Validation.messages={required:"{0} is required",acceptance:"{0} must be accepted",min:"{0} must be greater than or equal to {1}",max:"{0} must be less than or equal to {1}",range:"{0} must be between {1} and {2}",length:"{0} must be {1} characters",minLength:"{0} must be at least {1} characters",maxLength:"{0} must be at most {1} characters",rangeLength:"{0} must be between {1} and {2} characters",oneOf:"{0} must be one of: {1}",equalTo:"{0} must be the same as {1}",pattern:"{0} must be a valid {1}"},g=a.Validation.labelFormatters={none:function(a){return a},sentenceCase:function(a){return a.replace(/(?:^\w|[A-Z]|\b\w)/g,function(a,b){return b===0?a.toUpperCase():" "+a.toLowerCase()}).replace("_"," ")},label:function(a,b){return b.labels[a]||g.sentenceCase(a,b)}},h=a.Validation.validators=function(){var a=String.prototype.trim?function(a){return a===null?"":String.prototype.trim.call(a)}:function(a){var b=/^\s+/,c=/\s+$/;return a===null?"":a.toString().replace(b,"").replace(c,"")},d=function(a,b){return g[c.labelFormatter](a,b)},h=function(){var a=Array.prototype.slice.call(arguments),b=a.shift();return b.replace(/\{(\d+)\}/g,function(b,c){return typeof a[c]!="undefined"?a[c]:b})},i=function(a){return b.isNumber(a)||b.isString(a)&&a.match(e.number)},j=function(c){return!(b.isNull(c)||b.isUndefined(c)||b.isString(c)&&a(c)==="")};return{fn:function(a,c,d,e,f){return b.isString(d)&&(d=e[d]),d.call(e,a,c,f)},required:function(a,c,e,g,i){var k=b.isFunction(e)?e.call(g,a,c,i):e;if(!k&&!j(a))return!1;if(k&&!j(a))return h(f.required,d(c,g))},acceptance:function(a,c,e,g){if(a!=="true"&&(!b.isBoolean(a)||a===!1))return h(f.acceptance,d(c,g))},min:function(a,b,c,e){if(!i(a)||a<c)return h(f.min,d(b,e),c)},max:function(a,b,c,e){if(!i(a)||a>c)return h(f.max,d(b,e),c)},range:function(a,b,c,e){if(!i(a)||a<c[0]||a>c[1])return h(f.range,d(b,e),c[0],c[1])},length:function(b,c,e,g){if(!j(b)||a(b).length!==e)return h(f.length,d(c,g),e)},minLength:function(b,c,e,g){if(!j(b)||a(b).length<e)return h(f.minLength,d(c,g),e)},maxLength:function(b,c,e,g){if(!j(b)||a(b).length>e)return h(f.maxLength,d(c,g),e)},rangeLength:function(b,c,e,g){if(!j(b)||a(b).length<e[0]||a(b).length>e[1])return h(f.rangeLength,d(c,g),e[0],e[1])},oneOf:function(a,c,e,g){if(!b.include(e,a))return h(f.oneOf,d(c,g),e.join(", "))},equalTo:function(a,b,c,e,g){if(a!==g[c])return h(f.equalTo,d(b,e),d(c,e))},pattern:function(a,b,c,g){if(!j(a)||!a.toString().match(e[c]||c))return h(f.pattern,d(b,g),c)}}}()})(Backbone,_);
View
11 backbone.validation.js → dist/backbone-validation.js
@@ -1,12 +1,13 @@
// Backbone.Validation v0.6.0
//
-// Copyright (C)2011-2012 Thomas Pedersen
+// Copyright (c) 2011-2012 Thomas Pedersen
// Distributed under MIT License
//
// Documentation and full license available at:
// http://thedersen.com/projects/backbone-validation
(function(Backbone, _){
+ 'use strict';
// Default options
// ---------------
@@ -262,7 +263,7 @@
// with validation
mixin: mixin(null, defaultOptions)
};
- })();
+ }());
// Callbacks
@@ -399,7 +400,7 @@
var args = Array.prototype.slice.call(arguments);
var text = args.shift();
return text.replace(/\{(\d+)\}/g, function(match, number) {
- return typeof args[number] != 'undefined' ? args[number] : match;
+ return typeof args[number] !== 'undefined' ? args[number] : match;
});
};
@@ -510,6 +511,6 @@
}
}
};
- })();
+ }());
-})(Backbone, _);
+}(Backbone, _));
View
65 grunt.js
@@ -0,0 +1,65 @@
+module.exports = function(grunt) {
+
+ // Project configuration.
+ grunt.initConfig({
+ pkg: '<json:package.json>',
+ meta: {
+ banner: '// <%= pkg.title || pkg.name %> v<%= pkg.version %>\n' +
+ '//\n' +
+ '// Copyright (c) 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author.name %>\n' +
+ '// Distributed under MIT License\n' +
+ '//\n' +
+ '// Documentation and full license available at:\n' +
+ '// <%= pkg.homepage ? pkg.homepage : "" %>'
+ },
+ concat: {
+ browser: {
+ src: ['<banner:meta.banner>', '<file_strip_banner:src/<%= pkg.name %>.js>'],
+ dest: 'dist/<%= pkg.name %>.js'
+ }
+ },
+ min: {
+ browser: {
+ src: ['<banner:meta.banner>', '<config:concat.browser.dest>'],
+ dest: 'dist/<%= pkg.name %>-min.js'
+ }
+ },
+ watch: {
+ files: '<config:lint.files>',
+ tasks: 'lint'
+ },
+ lint: {
+ files: ['grunt.js', 'src/**/*.js', 'tests/**/*.js']
+ },
+ jshint: {
+ options: {
+ curly: true,
+ eqeqeq: true,
+ immed: true,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ sub: true,
+ undef: true,
+ boss: true,
+ eqnull: true,
+ browser: true,
+ node: true
+ },
+ globals: {
+ Backbone: true,
+ _: true,
+ jQuery: true,
+ $: true,
+ buster: true,
+ assert: true,
+ refute: true
+ }
+ },
+ uglify: {}
+ });
+
+ // Default task.
+ grunt.registerTask('default', 'concat lint min');
+
+};
View
54 package.json
@@ -1,18 +1,36 @@
-l{
- "author": "Thomas Pedersen (http://thedersen.com/)",
- "name": "backbone-validation",
- "version": "0.6.0",
- "url": "http://thedersen.github.com/backbone.validation",
- "repository": "git://github.com/thedersen/backbone.validation.git",
- "scripts": {
- "test": "buster test"
- },
- "engines": {
- "node": ">=0.6.3"
- },
- "dependencies": {},
- "devDependencies": {
- "buster": "0.4.3",
- "uglify-js": "1.2.5"
- }
-}
+{
+ "name": "backbone-validation",
+ "title": "Backbone.Validation",
+ "version": "0.6.0",
+ "author": {
+ "name": "Thomas Pedersen",
+ "url": "http://thedersen.com/"
+ },
+ "homepage": "http://thedersen.com/projects/backbone-validation",
+ "licenses": [{
+ "type": "MIT",
+ "url": "http://thedersen.mit-license.org/"
+ }],
+ "bugs": {
+ "url": "http://github.com/thedersen/backbone.validation/issues"
+ },
+ "directories": {
+ "lib": "./dist"
+ },
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/thedersen/backbone.validation.git"
+ },
+ "scripts": {
+ "test": "buster test",
+ "prepublish": "grunt"
+ },
+ "main": "./dest/backbone-validation-amd.js",
+ "dependencies": {
+ "backbone": ">=0.9.1",
+ "underscore": ">=1.3.1"
+ },
+ "devDependencies": {
+ "buster": ">=0.6.2"
+ }
+}
View
508 src/backbone-validation.js
@@ -0,0 +1,508 @@
+(function(Backbone, _){
+ 'use strict';
+
+ // Default options
+ // ---------------
+
+ var defaultOptions = {
+ forceUpdate: false,
+ selector: 'name',
+ labelFormatter: 'sentenceCase',
+ valid: Function.prototype,
+ invalid: Function.prototype
+ };
+
+
+ // Validation
+ // ----------
+
+ Backbone.Validation = (function(){
+
+ // Returns an object with undefined properties for all
+ // attributes on the model that has defined one or more
+ // validation rules.
+ var getValidatedAttrs = function(model) {
+ return _.reduce(_.keys(model.validation || {}), function(memo, key) {
+ memo[key] = void 0;
+ return memo;
+ }, {});
+ };
+
+ // Looks on the model for validations for a specified
+ // attribute. Returns an array of any validators defined,
+ // or an empty array if none is defined.
+ var getValidators = function(model, attr) {
+ var attrValidationSet = model.validation ? model.validation[attr] || {} : {};
+
+ // If the validator is a function or a string, wrap it in a function validator
+ if (_.isFunction(attrValidationSet) || _.isString(attrValidationSet)) {
+ attrValidationSet = {
+ fn: attrValidationSet
+ };
+ }
+
+ // Stick the validator object into an array
+ if(!_.isArray(attrValidationSet)) {
+ attrValidationSet = [attrValidationSet];
+ }
+
+ // Reduces the array of validators into a new array with objects
+ // with a validation method to call, the value to validate against
+ // and the specified error message, if any
+ return _.reduce(attrValidationSet, function(memo, attrValidation) {
+ _.each(_.without(_.keys(attrValidation), 'msg'), function(validator) {
+ memo.push({
+ fn: defaultValidators[validator],
+ val: attrValidation[validator],
+ msg: attrValidation.msg
+ });
+ });
+ return memo;
+ }, []);
+ };
+
+ // Validates an attribute against all validators defined
+ // for that attribute. If one or more errors are found,
+ // the first error message is returned.
+ // If the attribute is valid, an empty string is returned.
+ var validateAttr = function(model, attr, value, computed) {
+ // Reduces the array of validators to an error message by
+ // applying all the validators and returning the first error
+ // message, if any.
+ return _.reduce(getValidators(model, attr), function(memo, validator){
+ var result = validator.fn.call(defaultValidators, value, attr, validator.val, model, computed);
+ if(result === false || memo === false) {
+ return false;
+ }
+ if (result && !memo) {
+ return validator.msg || result;
+ }
+ return memo;
+ }, '');
+ };
+
+ // Loops through the model's attributes and validates them all.
+ // Returns and object containing names of invalid attributes
+ // as well as error messages.
+ var validateModel = function(model, attrs) {
+ var error, attr,
+ invalidAttrs = {},
+ isValid = true,
+ computed = _.clone(attrs);
+
+ for (attr in attrs) {
+ error = validateAttr(model, attr, attrs[attr], computed);
+ if (error) {
+ invalidAttrs[attr] = error;
+ isValid = false;
+ }
+ }
+
+ return {
+ invalidAttrs: invalidAttrs,
+ isValid: isValid
+ };
+ };
+
+ // Contains the methods that are mixed in on the model when binding
+ var mixin = function(view, options) {
+ return {
+
+ // Check whether or not a value passes validation
+ // without updating the model
+ preValidate: function(attr, value) {
+ return validateAttr(this, attr, value, _.extend({}, this.attributes));
+ },
+
+ // Check to see if an attribute, an array of attributes or the
+ // entire model is valid. Passing true will force a validation
+ // of the model.
+ isValid: function(option) {
+ if(_.isString(option)){
+ return !validateAttr(this, option, this.get(option), _.extend({}, this.attributes));
+ }
+ if(_.isArray(option)){
+ for (var i = 0; i < option.length; i++) {
+ if(validateAttr(this, option[i], this.get(option[i]), _.extend({}, this.attributes))){
+ return false;
+ }
+ }
+ return true;
+ }
+ if(option === true) {
+ this.validate();
+ }
+ return this.validation ? this._isValid : true;
+ },
+
+ // This is called by Backbone when it needs to perform validation.
+ // You can call it manually without any parameters to validate the
+ // entire model.
+ validate: function(attrs, setOptions){
+ var model = this,
+ validateAll = !attrs,
+ opt = _.extend({}, options, setOptions),
+ allAttrs = _.extend(getValidatedAttrs(model), model.attributes, attrs),
+ changedAttrs = attrs || allAttrs,
+ result = validateModel(model, allAttrs);
+
+ model._isValid = result.isValid;
+
+ // After validation is performed, loop through all changed attributes
+ // and call either the valid or invalid callback so the view is updated.
+ for(var attr in allAttrs) {
+ var invalid = result.invalidAttrs.hasOwnProperty(attr),
+ changed = changedAttrs.hasOwnProperty(attr);
+ if(invalid && (changed || validateAll)){
+ opt.invalid(view, attr, result.invalidAttrs[attr], opt.selector);
+ }
+ if(!invalid){
+ opt.valid(view, attr, opt.selector);
+ }
+ }
+
+ // Trigger validated events.
+ // Need to defer this so the model is actually updated before
+ // the event is triggered.
+ _.defer(function() {
+ model.trigger('validated', model._isValid, model, result.invalidAttrs);
+ model.trigger('validated:' + (model._isValid ? 'valid' : 'invalid'), model, result.invalidAttrs);
+ });
+
+ // Return any error messages to Backbone, unless the forceUpdate flag is set.
+ // Then we do not return anything and fools Backbone to believe the validation was
+ // a success. That way Backbone will update the model regardless.
+ if (!opt.forceUpdate && _.intersection(_.keys(result.invalidAttrs), _.keys(changedAttrs)).length > 0) {
+ return result.invalidAttrs;
+ }
+ }
+ };
+ };
+
+ // Helper to mix in validation on a model
+ var bindModel = function(view, model, options) {
+ _.extend(model, mixin(view, options));
+ };
+
+ // Removes the methods added to a model
+ var unbindModel = function(model) {
+ delete model.validate;
+ delete model.preValidate;
+ delete model.isValid;
+ };
+
+ // Mix in validation on a model whenever a model is
+ // added to a collection
+ var collectionAdd = function(model) {
+ bindModel(this.view, model, this.options);
+ };
+
+ // Remove validation from a model whenever a model is
+ // removed from a collection
+ var collectionRemove = function(model) {
+ unbindModel(model);
+ };
+
+ // Returns the public methods on Backbone.Validation
+ return {
+
+ // Current version of the library
+ version: '0.6.0',
+
+ // Called to configure the default options
+ configure: function(options) {
+ _.extend(defaultOptions, options);
+ },
+
+ // Hooks up validation on a view with a model
+ // or collection
+ bind: function(view, options) {
+ var model = view.model,
+ collection = view.collection;
+ options = _.extend({}, defaultOptions, defaultCallbacks, options);
+
+ if(model) {
+ bindModel(view, model, options);
+ }
+ if(collection) {
+ collection.each(function(model){
+ bindModel(view, model, options);
+ });
+ collection.bind('add', collectionAdd, {view: view, options: options});
+ collection.bind('remove', collectionRemove);
+ }
+ },
+
+ // Removes validation from a view with a model
+ // or collection
+ unbind: function(view) {
+ var model = view.model,
+ collection = view.collection;
+
+ if(model) {
+ unbindModel(view.model);
+ }
+ if(collection) {
+ collection.each(function(model){
+ unbindModel(model);
+ });
+ collection.unbind('add', collectionAdd);
+ collection.unbind('remove', collectionRemove);
+ }
+ },
+
+ // Used to extend the Backbone.Model.prototype
+ // with validation
+ mixin: mixin(null, defaultOptions)
+ };
+ }());
+
+
+ // Callbacks
+ // ---------
+
+ var defaultCallbacks = Backbone.Validation.callbacks = {
+
+ // Gets called when a previously invalid field in the
+ // view becomes valid. Removes any error message.
+ // Should be overridden with custom functionality.
+ valid: function(view, attr, selector) {
+ view.$('[' + selector + '~=' + attr + ']')
+ .removeClass('invalid')
+ .removeAttr('data-error');
+ },
+
+ // Gets called when a field in the view becomes invalid.
+ // Adds a error message.
+ // Should be overridden with custom functionality.
+ invalid: function(view, attr, error, selector) {
+ view.$('[' + selector + '~=' + attr + ']')
+ .addClass('invalid')
+ .attr('data-error', error);
+ }
+ };
+
+
+ // Patterns
+ // --------
+
+ var defaultPatterns = Backbone.Validation.patterns = {
+ // Matches any digit(s) (i.e. 0-9)
+ digits: /^\d+$/,
+
+ // Matched any number (e.g. 100.000)
+ number: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
+
+ // Matches a valid email address (e.g. mail@example.com)
+ email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,
+
+ // Mathes any valid url (e.g. http://www.xample.com)
+ url: /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
+ };
+
+
+ // Error messages
+ // --------------
+
+ // Error message for the build in validators.
+ // {x} gets swapped out with arguments form the validator.
+ var defaultMessages = Backbone.Validation.messages = {
+ required: '{0} is required',
+ acceptance: '{0} must be accepted',
+ min: '{0} must be greater than or equal to {1}',
+ max: '{0} must be less than or equal to {1}',
+ range: '{0} must be between {1} and {2}',
+ length: '{0} must be {1} characters',
+ minLength: '{0} must be at least {1} characters',
+ maxLength: '{0} must be at most {1} characters',
+ rangeLength: '{0} must be between {1} and {2} characters',
+ oneOf: '{0} must be one of: {1}',
+ equalTo: '{0} must be the same as {1}',
+ pattern: '{0} must be a valid {1}'
+ };
+
+ // Label formatters
+ // ----------------
+
+ // Label formatters are used to convert the attribute name
+ // to a more human friendly label when using the built in
+ // error messages.
+ // Configure which one to use with a call to
+ //
+ // Backbone.Validation.configure({
+ // labelFormatter: 'label'
+ // });
+ var defaultLabelFormatters = Backbone.Validation.labelFormatters = {
+
+ // Returns the attribute name with applying any formatting
+ none: function(attrName) {
+ return attrName;
+ },
+
+ // Converts attributeName or attribute_name to Attribute name
+ sentenceCase: function(attrName) {
+ return attrName.replace(/(?:^\w|[A-Z]|\b\w)/g, function(match, index) {
+ return index === 0 ? match.toUpperCase() : ' ' + match.toLowerCase();
+ }).replace('_', ' ');
+ },
+
+ // Looks for a label configured on the model and returns it
+ //
+ // var Model = Backbone.Model.extend({
+ // validation: {
+ // someAttribute: {
+ // required: true
+ // }
+ // },
+ //
+ // labels: {
+ // someAttribute: 'Custom label'
+ // }
+ // });
+ label: function(attrName, model) {
+ return model.labels[attrName] || defaultLabelFormatters.sentenceCase(attrName, model);
+ }
+ };
+
+ // Built in validators
+ // -------------------
+
+ var defaultValidators = Backbone.Validation.validators = (function(){
+ // Use native trim when defined
+ var trim = String.prototype.trim ?
+ function(text) {
+ return text === null ? '' : String.prototype.trim.call(text);
+ } :
+ function(text) {
+ var trimLeft = /^\s+/,
+ trimRight = /\s+$/;
+
+ return text === null ? '' : text.toString().replace(trimLeft, '').replace(trimRight, '');
+ };
+
+ // Uses the configured label formatter to format the attribute name
+ // to make it more readable for the user
+ var formatLabel = function(attrName, model) {
+ return defaultLabelFormatters[defaultOptions.labelFormatter](attrName, model);
+ };
+
+ // Replaces nummeric placeholders like {0} in a string with arguments
+ // passed to the function
+ var format = function() {
+ var args = Array.prototype.slice.call(arguments);
+ var text = args.shift();
+ return text.replace(/\{(\d+)\}/g, function(match, number) {
+ return typeof args[number] !== 'undefined' ? args[number] : match;
+ });
+ };
+
+ // Determines whether or not a value is a number
+ var isNumber = function(value){
+ return _.isNumber(value) || (_.isString(value) && value.match(defaultPatterns.number));
+ };
+
+ // Determines whether or not not a value is empty
+ var hasValue = function(value) {
+ return !(_.isNull(value) || _.isUndefined(value) || (_.isString(value) && trim(value) === ''));
+ };
+
+ return {
+ // Function validator
+ fn: function(value, attr, fn, model, computed) {
+ if(_.isString(fn)){
+ fn = model[fn];
+ }
+ return fn.call(model, value, attr, computed);
+ },
+
+ // Required validator
+ required: function(value, attr, required, model, computed) {
+ var isRequired = _.isFunction(required) ? required.call(model, value, attr, computed) : required;
+ if(!isRequired && !hasValue(value)) {
+ return false; // overrides all other validators
+ }
+ if (isRequired && !hasValue(value)) {
+ return format(defaultMessages.required, formatLabel(attr, model));
+ }
+ },
+
+ // Acceptance validator
+ acceptance: function(value, attr, accept, model) {
+ if(value !== 'true' && (!_.isBoolean(value) || value === false)) {
+ return format(defaultMessages.acceptance, formatLabel(attr, model));
+ }
+ },
+
+ // Min validator
+ min: function(value, attr, minValue, model) {
+ if (!isNumber(value) || value < minValue) {
+ return format(defaultMessages.min, formatLabel(attr, model), minValue);
+ }
+ },
+
+ // Max validator
+ max: function(value, attr, maxValue, model) {
+ if (!isNumber(value) || value > maxValue) {
+ return format(defaultMessages.max, formatLabel(attr, model), maxValue);
+ }
+ },
+
+ // Range validator
+ range: function(value, attr, range, model) {
+ if(!isNumber(value) || value < range[0] || value > range[1]) {
+ return format(defaultMessages.range, formatLabel(attr, model), range[0], range[1]);
+ }
+ },
+
+ // Length validator
+ length: function(value, attr, length, model) {
+ if (!hasValue(value) || trim(value).length !== length) {
+ return format(defaultMessages.length, formatLabel(attr, model), length);
+ }
+ },
+
+ // Min length validator
+ minLength: function(value, attr, minLength, model) {
+ if (!hasValue(value) || trim(value).length < minLength) {
+ return format(defaultMessages.minLength, formatLabel(attr, model), minLength);
+ }
+ },
+
+ // Max length validator
+ maxLength: function(value, attr, maxLength, model) {
+ if (!hasValue(value) || trim(value).length > maxLength) {
+ return format(defaultMessages.maxLength, formatLabel(attr, model), maxLength);
+ }
+ },
+
+ // Range length validator
+ rangeLength: function(value, attr, range, model) {
+ if(!hasValue(value) || trim(value).length < range[0] || trim(value).length > range[1]) {
+ return format(defaultMessages.rangeLength, formatLabel(attr, model), range[0], range[1]);
+ }
+ },
+
+ // One of validator
+ oneOf: function(value, attr, values, model) {
+ if(!_.include(values, value)){
+ return format(defaultMessages.oneOf, formatLabel(attr, model), values.join(', '));
+ }
+ },
+
+ // Equal to validator
+ equalTo: function(value, attr, equalTo, model, computed) {
+ if(value !== computed[equalTo]) {
+ return format(defaultMessages.equalTo, formatLabel(attr, model), formatLabel(equalTo, model));
+ }
+ },
+
+ // Pattern validator
+ pattern: function(value, attr, pattern, model) {
+ if (!hasValue(value) || !value.toString().match(defaultPatterns[pattern] || pattern)) {
+ return format(defaultMessages.pattern, formatLabel(attr, model), pattern);
+ }
+ }
+ };
+ }());
+
+}(Backbone, _));
View
8 tests/events.js
@@ -3,10 +3,14 @@ buster.testCase("Backbone.Validation events", {
var Model = Backbone.Model.extend({
validation: {
age: function(val){
- if(!val) return 'age';
+ if(!val) {
+ return 'age';
+ }
},
name: function(val){
- if(!val) return 'name';
+ if(!val) {
+ return 'name';
+ }
}
}
});
View
4 tests/mixin.js
@@ -7,7 +7,9 @@ buster.testCase("Mixin validation", {
var Model = Backbone.Model.extend({
validation: {
name: function(val) {
- if(!val) return 'error';
+ if(!val) {
+ return 'error';
+ }
}
}
});

0 comments on commit 7fd9344

Please sign in to comment.
Something went wrong with that request. Please try again.