backbone-validation-rails 0.6.2 → 0.6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  module Backbone
2
2
  module Validation
3
3
  module Rails
4
- VERSION = "0.6.2"
4
+ VERSION = "0.6.2.1"
5
5
  end
6
6
  end
7
7
  end
@@ -5,4 +5,542 @@
5
5
  //
6
6
  // Documentation and full license available at:
7
7
  // http://thedersen.com/projects/backbone-validation
8
- Backbone.Validation=function(a){"use strict";var b={forceUpdate:!1,selector:"name",labelFormatter:"sentenceCase",valid:Function.prototype,invalid:Function.prototype},c=function(){var c=function(b){return a.reduce(a.keys(b.validation||{}),function(a,b){return a[b]=void 0,a},{})},e=function(b,c){var d=b.validation?b.validation[c]||{}:{};if(a.isFunction(d)||a.isString(d))d={fn:d};return a.isArray(d)||(d=[d]),a.reduce(d,function(b,c){return a.each(a.without(a.keys(c),"msg"),function(a){b.push({fn:h[a],val:c[a],msg:c.msg})}),b},[])},f=function(b,c,d,f){return a.reduce(e(b,c),function(a,e){var g=e.fn.call(h,d,c,e.val,b,f);return g===!1||a===!1?!1:g&&!a?e.msg||g:a},"")},g=function(b,c){var d,e,g={},h=!0,i=a.clone(c);for(e in c)d=f(b,e,c[e],i),d&&(g[e]=d,h=!1);return{invalidAttrs:g,isValid:h}},i=function(b,d){return{preValidate:function(b,c){return f(this,b,c,a.extend({},this.attributes))},isValid:function(b){if(a.isString(b))return!f(this,b,this.get(b),a.extend({},this.attributes));if(a.isArray(b)){for(var c=0;c<b.length;c++)if(f(this,b[c],this.get(b[c]),a.extend({},this.attributes)))return!1;return!0}return b===!0&&this.validate(),this.validation?this._isValid:!0},validate:function(e,f){var h=this,i=!e,j=a.extend({},d,f),k=a.extend(c(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(b,n,m.invalidAttrs[n],j.selector),o||j.valid(b,n,j.selector)}a.defer(function(){h.trigger("validated",h._isValid,h,m.invalidAttrs),h.trigger("validated:"+(h._isValid?"valid":"invalid"),h,m.invalidAttrs)});if(!j.forceUpdate&&a.intersection(a.keys(m.invalidAttrs),a.keys(l)).length>0)return m.invalidAttrs}}},j=function(b,c,d){a.extend(c,i(b,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.2",configure:function(c){a.extend(b,c)},bind:function(c,e){var f=c.model,g=c.collection;e=a.extend({},b,d,e);if(typeof f=="undefined"&&typeof g=="undefined")throw"Before you execute the binding your view must have a model or a collection.\nSee http://thedersen.com/projects/backbone-validation/#using-form-model-validation for more information.";f&&j(c,f,e),g&&(g.each(function(a){j(c,a,e)}),g.bind("add",l,{view:c,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,b)}}(),d=c.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=c.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=c.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=c.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=c.validators=function(){var c=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,c){return g[b.labelFormatter](a,c)},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(b){return a.isNumber(b)||a.isString(b)&&b.match(e.number)},j=function(b){return!(a.isNull(b)||a.isUndefined(b)||a.isString(b)&&c(b)==="")};return{fn:function(b,c,d,e,f){return a.isString(d)&&(d=e[d]),d.call(e,b,c,f)},required:function(b,c,e,g,i){var k=a.isFunction(e)?e.call(g,b,c,i):e;if(!k&&!j(b))return!1;if(k&&!j(b))return h(f.required,d(c,g))},acceptance:function(b,c,e,g){if(b!=="true"&&(!a.isBoolean(b)||b===!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(a,b,e,g){if(!j(a)||c(a).length!==e)return h(f.length,d(b,g),e)},minLength:function(a,b,e,g){if(!j(a)||c(a).length<e)return h(f.minLength,d(b,g),e)},maxLength:function(a,b,e,g){if(!j(a)||c(a).length>e)return h(f.maxLength,d(b,g),e)},rangeLength:function(a,b,e,g){if(!j(a)||c(a).length<e[0]||c(a).length>e[1])return h(f.rangeLength,d(b,g),e[0],e[1])},oneOf:function(b,c,e,g){if(!a.include(e,b))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)}}}();return c}(_);
8
+
9
+ Backbone.Validation = (function(_){
10
+ 'use strict';
11
+
12
+ // Default options
13
+ // ---------------
14
+
15
+ var defaultOptions = {
16
+ forceUpdate: false,
17
+ selector: 'name',
18
+ labelFormatter: 'sentenceCase',
19
+ valid: Function.prototype,
20
+ invalid: Function.prototype
21
+ };
22
+
23
+
24
+ // Validation
25
+ // ----------
26
+
27
+ var Validation = (function(){
28
+
29
+ // Returns an object with undefined properties for all
30
+ // attributes on the model that has defined one or more
31
+ // validation rules.
32
+ var getValidatedAttrs = function(model) {
33
+ return _.reduce(_.keys(model.validation || {}), function(memo, key) {
34
+ memo[key] = void 0;
35
+ return memo;
36
+ }, {});
37
+ };
38
+
39
+ // Looks on the model for validations for a specified
40
+ // attribute. Returns an array of any validators defined,
41
+ // or an empty array if none is defined.
42
+ var getValidators = function(model, attr) {
43
+ var attrValidationSet = model.validation ? model.validation[attr] || {} : {};
44
+
45
+ // If the validator is a function or a string, wrap it in a function validator
46
+ if (_.isFunction(attrValidationSet) || _.isString(attrValidationSet)) {
47
+ attrValidationSet = {
48
+ fn: attrValidationSet
49
+ };
50
+ }
51
+
52
+ // Stick the validator object into an array
53
+ if(!_.isArray(attrValidationSet)) {
54
+ attrValidationSet = [attrValidationSet];
55
+ }
56
+
57
+ // Reduces the array of validators into a new array with objects
58
+ // with a validation method to call, the value to validate against
59
+ // and the specified error message, if any
60
+ return _.reduce(attrValidationSet, function(memo, attrValidation) {
61
+ _.each(_.without(_.keys(attrValidation), 'msg'), function(validator) {
62
+ memo.push({
63
+ fn: defaultValidators[validator],
64
+ val: attrValidation[validator],
65
+ msg: attrValidation.msg
66
+ });
67
+ });
68
+ return memo;
69
+ }, []);
70
+ };
71
+
72
+ // Validates an attribute against all validators defined
73
+ // for that attribute. If one or more errors are found,
74
+ // the first error message is returned.
75
+ // If the attribute is valid, an empty string is returned.
76
+ var validateAttr = function(model, attr, value, computed) {
77
+ // Reduces the array of validators to an error message by
78
+ // applying all the validators and returning the first error
79
+ // message, if any.
80
+ return _.reduce(getValidators(model, attr), function(memo, validator){
81
+ var result = validator.fn.call(defaultValidators, value, attr, validator.val, model, computed);
82
+ if(result === false || memo === false) {
83
+ return false;
84
+ }
85
+ if (result && !memo) {
86
+ return validator.msg || result;
87
+ }
88
+ return memo;
89
+ }, '');
90
+ };
91
+
92
+ // Loops through the model's attributes and validates them all.
93
+ // Returns and object containing names of invalid attributes
94
+ // as well as error messages.
95
+ var validateModel = function(model, attrs) {
96
+ var error, attr,
97
+ invalidAttrs = {},
98
+ isValid = true,
99
+ computed = _.clone(attrs);
100
+
101
+ for (attr in attrs) {
102
+ error = validateAttr(model, attr, attrs[attr], computed);
103
+ if (error) {
104
+ invalidAttrs[attr] = error;
105
+ isValid = false;
106
+ }
107
+ }
108
+
109
+ return {
110
+ invalidAttrs: invalidAttrs,
111
+ isValid: isValid
112
+ };
113
+ };
114
+
115
+ // Contains the methods that are mixed in on the model when binding
116
+ var mixin = function(view, options) {
117
+ return {
118
+
119
+ // Check whether or not a value passes validation
120
+ // without updating the model
121
+ preValidate: function(attr, value) {
122
+ return validateAttr(this, attr, value, _.extend({}, this.attributes));
123
+ },
124
+
125
+ // Check to see if an attribute, an array of attributes or the
126
+ // entire model is valid. Passing true will force a validation
127
+ // of the model.
128
+ isValid: function(option) {
129
+ if(_.isString(option)){
130
+ return !validateAttr(this, option, this.get(option), _.extend({}, this.attributes));
131
+ }
132
+ if(_.isArray(option)){
133
+ for (var i = 0; i < option.length; i++) {
134
+ if(validateAttr(this, option[i], this.get(option[i]), _.extend({}, this.attributes))){
135
+ return false;
136
+ }
137
+ }
138
+ return true;
139
+ }
140
+ if(option === true) {
141
+ this.validate();
142
+ }
143
+ return this.validation ? this._isValid : true;
144
+ },
145
+
146
+ // This is called by Backbone when it needs to perform validation.
147
+ // You can call it manually without any parameters to validate the
148
+ // entire model.
149
+ validate: function(attrs, setOptions){
150
+ var model = this,
151
+ validateAll = !attrs,
152
+ opt = _.extend({}, options, setOptions),
153
+ allAttrs = _.extend(getValidatedAttrs(model), model.attributes, attrs),
154
+ changedAttrs = attrs || allAttrs,
155
+ result = validateModel(model, allAttrs);
156
+
157
+ model._isValid = result.isValid;
158
+
159
+ // After validation is performed, loop through all changed attributes
160
+ // and call either the valid or invalid callback so the view is updated.
161
+ for(var attr in allAttrs) {
162
+ var invalid = result.invalidAttrs.hasOwnProperty(attr),
163
+ changed = changedAttrs.hasOwnProperty(attr);
164
+ if(invalid && (changed || validateAll)){
165
+ opt.invalid(view, attr, result.invalidAttrs[attr], opt.selector);
166
+ }
167
+ if(!invalid){
168
+ opt.valid(view, attr, opt.selector);
169
+ }
170
+ }
171
+
172
+ // Trigger validated events.
173
+ // Need to defer this so the model is actually updated before
174
+ // the event is triggered.
175
+ _.defer(function() {
176
+ model.trigger('validated', model._isValid, model, result.invalidAttrs);
177
+ model.trigger('validated:' + (model._isValid ? 'valid' : 'invalid'), model, result.invalidAttrs);
178
+ });
179
+
180
+ // Return any error messages to Backbone, unless the forceUpdate flag is set.
181
+ // Then we do not return anything and fools Backbone to believe the validation was
182
+ // a success. That way Backbone will update the model regardless.
183
+ if (!opt.forceUpdate && _.intersection(_.keys(result.invalidAttrs), _.keys(changedAttrs)).length > 0) {
184
+ return result.invalidAttrs;
185
+ }
186
+ }
187
+ };
188
+ };
189
+
190
+ // Helper to mix in validation on a model
191
+ var bindModel = function(view, model, options) {
192
+ _.extend(model, mixin(view, options));
193
+ };
194
+
195
+ // Removes the methods added to a model
196
+ var unbindModel = function(model) {
197
+ delete model.validate;
198
+ delete model.preValidate;
199
+ delete model.isValid;
200
+ };
201
+
202
+ // Mix in validation on a model whenever a model is
203
+ // added to a collection
204
+ var collectionAdd = function(model) {
205
+ bindModel(this.view, model, this.options);
206
+ };
207
+
208
+ // Remove validation from a model whenever a model is
209
+ // removed from a collection
210
+ var collectionRemove = function(model) {
211
+ unbindModel(model);
212
+ };
213
+
214
+ // Returns the public methods on Backbone.Validation
215
+ return {
216
+
217
+ // Current version of the library
218
+ version: '0.6.2',
219
+
220
+ // Called to configure the default options
221
+ configure: function(options) {
222
+ _.extend(defaultOptions, options);
223
+ },
224
+
225
+ // Hooks up validation on a view with a model
226
+ // or collection
227
+ bind: function(view, options) {
228
+ var model = view.model,
229
+ collection = view.collection;
230
+ options = _.extend({}, defaultOptions, defaultCallbacks, options);
231
+
232
+ if(typeof model === 'undefined' && typeof collection === 'undefined'){
233
+ throw 'Before you execute the binding your view must have a model or a collection.\n' +
234
+ 'See http://thedersen.com/projects/backbone-validation/#using-form-model-validation for more information.';
235
+ }
236
+
237
+ if(model) {
238
+ bindModel(view, model, options);
239
+ }
240
+ if(collection) {
241
+ collection.each(function(model){
242
+ bindModel(view, model, options);
243
+ });
244
+ collection.bind('add', collectionAdd, {view: view, options: options});
245
+ collection.bind('remove', collectionRemove);
246
+ }
247
+ },
248
+
249
+ // Removes validation from a view with a model
250
+ // or collection
251
+ unbind: function(view) {
252
+ var model = view.model,
253
+ collection = view.collection;
254
+
255
+ if(model) {
256
+ unbindModel(view.model);
257
+ }
258
+ if(collection) {
259
+ collection.each(function(model){
260
+ unbindModel(model);
261
+ });
262
+ collection.unbind('add', collectionAdd);
263
+ collection.unbind('remove', collectionRemove);
264
+ }
265
+ },
266
+
267
+ // Used to extend the Backbone.Model.prototype
268
+ // with validation
269
+ mixin: mixin(null, defaultOptions)
270
+ };
271
+ }());
272
+
273
+
274
+ // Callbacks
275
+ // ---------
276
+
277
+ var defaultCallbacks = Validation.callbacks = {
278
+
279
+ // Gets called when a previously invalid field in the
280
+ // view becomes valid. Removes any error message.
281
+ // Should be overridden with custom functionality.
282
+ valid: function(view, attr, selector) {
283
+ view.$('[' + selector + '~=' + attr + ']')
284
+ .removeClass('invalid')
285
+ .removeAttr('data-error');
286
+ },
287
+
288
+ // Gets called when a field in the view becomes invalid.
289
+ // Adds a error message.
290
+ // Should be overridden with custom functionality.
291
+ invalid: function(view, attr, error, selector) {
292
+ view.$('[' + selector + '~=' + attr + ']')
293
+ .addClass('invalid')
294
+ .attr('data-error', error);
295
+ }
296
+ };
297
+
298
+
299
+ // Patterns
300
+ // --------
301
+
302
+ var defaultPatterns = Validation.patterns = {
303
+ // Matches any digit(s) (i.e. 0-9)
304
+ digits: /^\d+$/,
305
+
306
+ // Matched any number (e.g. 100.000)
307
+ number: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
308
+
309
+ // Matches a valid email address (e.g. mail@example.com)
310
+ 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,
311
+
312
+ // Mathes any valid url (e.g. http://www.xample.com)
313
+ 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
314
+ };
315
+
316
+
317
+ // Error messages
318
+ // --------------
319
+
320
+ // Error message for the build in validators.
321
+ // {x} gets swapped out with arguments form the validator.
322
+ var defaultMessages = Validation.messages = {
323
+ required: '{0} is required',
324
+ acceptance: '{0} must be accepted',
325
+ min: '{0} must be greater than or equal to {1}',
326
+ max: '{0} must be less than or equal to {1}',
327
+ range: '{0} must be between {1} and {2}',
328
+ length: '{0} must be {1} characters',
329
+ minLength: '{0} must be at least {1} characters',
330
+ maxLength: '{0} must be at most {1} characters',
331
+ rangeLength: '{0} must be between {1} and {2} characters',
332
+ oneOf: '{0} must be one of: {1}',
333
+ equalTo: '{0} must be the same as {1}',
334
+ pattern: '{0} must be a valid {1}'
335
+ };
336
+
337
+ // Label formatters
338
+ // ----------------
339
+
340
+ // Label formatters are used to convert the attribute name
341
+ // to a more human friendly label when using the built in
342
+ // error messages.
343
+ // Configure which one to use with a call to
344
+ //
345
+ // Backbone.Validation.configure({
346
+ // labelFormatter: 'label'
347
+ // });
348
+ var defaultLabelFormatters = Validation.labelFormatters = {
349
+
350
+ // Returns the attribute name with applying any formatting
351
+ none: function(attrName) {
352
+ return attrName;
353
+ },
354
+
355
+ // Converts attributeName or attribute_name to Attribute name
356
+ sentenceCase: function(attrName) {
357
+ return attrName.replace(/(?:^\w|[A-Z]|\b\w)/g, function(match, index) {
358
+ return index === 0 ? match.toUpperCase() : ' ' + match.toLowerCase();
359
+ }).replace('_', ' ');
360
+ },
361
+
362
+ // Looks for a label configured on the model and returns it
363
+ //
364
+ // var Model = Backbone.Model.extend({
365
+ // validation: {
366
+ // someAttribute: {
367
+ // required: true
368
+ // }
369
+ // },
370
+ //
371
+ // labels: {
372
+ // someAttribute: 'Custom label'
373
+ // }
374
+ // });
375
+ label: function(attrName, model) {
376
+ return model.labels[attrName] || defaultLabelFormatters.sentenceCase(attrName, model);
377
+ }
378
+ };
379
+
380
+ // Built in validators
381
+ // -------------------
382
+
383
+ var defaultValidators = Validation.validators = (function(){
384
+ // Use native trim when defined
385
+ var trim = String.prototype.trim ?
386
+ function(text) {
387
+ return text === null ? '' : String.prototype.trim.call(text);
388
+ } :
389
+ function(text) {
390
+ var trimLeft = /^\s+/,
391
+ trimRight = /\s+$/;
392
+
393
+ return text === null ? '' : text.toString().replace(trimLeft, '').replace(trimRight, '');
394
+ };
395
+
396
+ // Uses the configured label formatter to format the attribute name
397
+ // to make it more readable for the user
398
+ var formatLabel = function(attrName, model) {
399
+ return defaultLabelFormatters[defaultOptions.labelFormatter](attrName, model);
400
+ };
401
+
402
+ // Replaces nummeric placeholders like {0} in a string with arguments
403
+ // passed to the function
404
+ var format = function() {
405
+ var args = Array.prototype.slice.call(arguments);
406
+ var text = args.shift();
407
+ return text.replace(/\{(\d+)\}/g, function(match, number) {
408
+ return typeof args[number] !== 'undefined' ? args[number] : match;
409
+ });
410
+ };
411
+
412
+ // Determines whether or not a value is a number
413
+ var isNumber = function(value){
414
+ return _.isNumber(value) || (_.isString(value) && value.match(defaultPatterns.number));
415
+ };
416
+
417
+ // Determines whether or not not a value is empty
418
+ var hasValue = function(value) {
419
+ return !(_.isNull(value) || _.isUndefined(value) || (_.isString(value) && trim(value) === ''));
420
+ };
421
+
422
+ return {
423
+ // Function validator
424
+ // Lets you implement a custom function used for validation
425
+ fn: function(value, attr, fn, model, computed) {
426
+ if(_.isString(fn)){
427
+ fn = model[fn];
428
+ }
429
+ return fn.call(model, value, attr, computed);
430
+ },
431
+
432
+ // Required validator
433
+ // Validates if the attribute is required or not
434
+ required: function(value, attr, required, model, computed) {
435
+ var isRequired = _.isFunction(required) ? required.call(model, value, attr, computed) : required;
436
+ if(!isRequired && !hasValue(value)) {
437
+ return false; // overrides all other validators
438
+ }
439
+ if (isRequired && !hasValue(value)) {
440
+ return format(defaultMessages.required, formatLabel(attr, model));
441
+ }
442
+ },
443
+
444
+ // Acceptance validator
445
+ // Validates that something has to be accepted, e.g. terms of use
446
+ // `true` or 'true' are valid
447
+ acceptance: function(value, attr, accept, model) {
448
+ if(value !== 'true' && (!_.isBoolean(value) || value === false)) {
449
+ return format(defaultMessages.acceptance, formatLabel(attr, model));
450
+ }
451
+ },
452
+
453
+ // Min validator
454
+ // Validates that the value has to be a number and equal to or greater than
455
+ // the min value specified
456
+ min: function(value, attr, minValue, model) {
457
+ if (!isNumber(value) || value < minValue) {
458
+ return format(defaultMessages.min, formatLabel(attr, model), minValue);
459
+ }
460
+ },
461
+
462
+ // Max validator
463
+ // Validates that the value has to be a number and equal to or less than
464
+ // the max value specified
465
+ max: function(value, attr, maxValue, model) {
466
+ if (!isNumber(value) || value > maxValue) {
467
+ return format(defaultMessages.max, formatLabel(attr, model), maxValue);
468
+ }
469
+ },
470
+
471
+ // Range validator
472
+ // Validates that the value has to be a number and equal to or between
473
+ // the two numbers specified
474
+ range: function(value, attr, range, model) {
475
+ if(!isNumber(value) || value < range[0] || value > range[1]) {
476
+ return format(defaultMessages.range, formatLabel(attr, model), range[0], range[1]);
477
+ }
478
+ },
479
+
480
+ // Length validator
481
+ // Validates that the value has to be a string with length equal to
482
+ // the length value specified
483
+ length: function(value, attr, length, model) {
484
+ if (!hasValue(value) || trim(value).length !== length) {
485
+ return format(defaultMessages.length, formatLabel(attr, model), length);
486
+ }
487
+ },
488
+
489
+ // Min length validator
490
+ // Validates that the value has to be a string with length equal to or greater than
491
+ // the min length value specified
492
+ minLength: function(value, attr, minLength, model) {
493
+ if (!hasValue(value) || trim(value).length < minLength) {
494
+ return format(defaultMessages.minLength, formatLabel(attr, model), minLength);
495
+ }
496
+ },
497
+
498
+ // Max length validator
499
+ // Validates that the value has to be a string with length equal to or less than
500
+ // the max length value specified
501
+ maxLength: function(value, attr, maxLength, model) {
502
+ if (!hasValue(value) || trim(value).length > maxLength) {
503
+ return format(defaultMessages.maxLength, formatLabel(attr, model), maxLength);
504
+ }
505
+ },
506
+
507
+ // Range length validator
508
+ // Validates that the value has to be a string and equal to or between
509
+ // the two numbers specified
510
+ rangeLength: function(value, attr, range, model) {
511
+ if(!hasValue(value) || trim(value).length < range[0] || trim(value).length > range[1]) {
512
+ return format(defaultMessages.rangeLength, formatLabel(attr, model), range[0], range[1]);
513
+ }
514
+ },
515
+
516
+ // One of validator
517
+ // Validates that the value has to be equal to one of the elements in
518
+ // the specified array. Case sensitive matching
519
+ oneOf: function(value, attr, values, model) {
520
+ if(!_.include(values, value)){
521
+ return format(defaultMessages.oneOf, formatLabel(attr, model), values.join(', '));
522
+ }
523
+ },
524
+
525
+ // Equal to validator
526
+ // Validates that the value has to be equal to the value of the attribute
527
+ // with the name specified
528
+ equalTo: function(value, attr, equalTo, model, computed) {
529
+ if(value !== computed[equalTo]) {
530
+ return format(defaultMessages.equalTo, formatLabel(attr, model), formatLabel(equalTo, model));
531
+ }
532
+ },
533
+
534
+ // Pattern validator
535
+ // Validates that the value has to match the pattern specified.
536
+ // Can be a regular expression or the name of one of the built in patterns
537
+ pattern: function(value, attr, pattern, model) {
538
+ if (!hasValue(value) || !value.toString().match(defaultPatterns[pattern] || pattern)) {
539
+ return format(defaultMessages.pattern, formatLabel(attr, model), pattern);
540
+ }
541
+ }
542
+ };
543
+ }());
544
+
545
+ return Validation;
546
+ }(_));
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backbone-validation-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: