parsley-rails 1.2.4.0 → 2.0.0.0

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -6
  3. data/lib/parsley-rails/version.rb +1 -1
  4. data/update.sh +6 -11
  5. data/vendor/assets/javascripts/parsley.i18n.bg.js +31 -41
  6. data/vendor/assets/javascripts/parsley.i18n.da.js +30 -40
  7. data/vendor/assets/javascripts/parsley.i18n.de.js +27 -35
  8. data/vendor/assets/javascripts/parsley.i18n.en.extra.js +6 -0
  9. data/vendor/assets/javascripts/parsley.i18n.en.js +30 -45
  10. data/vendor/assets/javascripts/parsley.i18n.es.js +29 -41
  11. data/vendor/assets/javascripts/parsley.i18n.fr.extra.js +6 -0
  12. data/vendor/assets/javascripts/parsley.i18n.fr.js +30 -41
  13. data/vendor/assets/javascripts/parsley.i18n.he.extra.js +6 -0
  14. data/vendor/assets/javascripts/parsley.i18n.he.js +31 -42
  15. data/vendor/assets/javascripts/parsley.i18n.id.js +30 -37
  16. data/vendor/assets/javascripts/parsley.i18n.it.extra.js +6 -0
  17. data/vendor/assets/javascripts/parsley.i18n.it.js +30 -37
  18. data/vendor/assets/javascripts/parsley.i18n.ja.js +30 -40
  19. data/vendor/assets/javascripts/parsley.i18n.nl.js +37 -41
  20. data/vendor/assets/javascripts/parsley.i18n.pl.js +33 -40
  21. data/vendor/assets/javascripts/parsley.i18n.pt-br.js +33 -0
  22. data/vendor/assets/javascripts/parsley.i18n.ru.js +38 -44
  23. data/vendor/assets/javascripts/parsley.i18n.sv.extra.js +6 -0
  24. data/vendor/assets/javascripts/parsley.i18n.sv.js +30 -42
  25. data/vendor/assets/javascripts/parsley.i18n.zh_cn.extra.js +6 -0
  26. data/vendor/assets/javascripts/parsley.i18n.zh_cn.js +30 -43
  27. data/vendor/assets/javascripts/parsley.js +2009 -1507
  28. data/vendor/assets/javascripts/parsley.remote.js +2351 -0
  29. metadata +16 -32
  30. data/vendor/assets/javascripts/parsley.extend.js +0 -167
  31. data/vendor/assets/javascripts/parsley.i18n.ar.js +0 -44
  32. data/vendor/assets/javascripts/parsley.i18n.ca.js +0 -40
  33. data/vendor/assets/javascripts/parsley.i18n.cs.js +0 -45
  34. data/vendor/assets/javascripts/parsley.i18n.cy.js +0 -38
  35. data/vendor/assets/javascripts/parsley.i18n.et.js +0 -46
  36. data/vendor/assets/javascripts/parsley.i18n.fa.js +0 -40
  37. data/vendor/assets/javascripts/parsley.i18n.fi.js +0 -40
  38. data/vendor/assets/javascripts/parsley.i18n.hr.js +0 -48
  39. data/vendor/assets/javascripts/parsley.i18n.hu.js +0 -44
  40. data/vendor/assets/javascripts/parsley.i18n.is.js +0 -32
  41. data/vendor/assets/javascripts/parsley.i18n.kr.js +0 -48
  42. data/vendor/assets/javascripts/parsley.i18n.lt.js +0 -40
  43. data/vendor/assets/javascripts/parsley.i18n.mn.js +0 -43
  44. data/vendor/assets/javascripts/parsley.i18n.no.js +0 -44
  45. data/vendor/assets/javascripts/parsley.i18n.pt_br.js +0 -42
  46. data/vendor/assets/javascripts/parsley.i18n.ro.js +0 -44
  47. data/vendor/assets/javascripts/parsley.i18n.th.js +0 -44
  48. data/vendor/assets/javascripts/parsley.i18n.tr.js +0 -48
  49. data/vendor/assets/javascripts/parsley.i18n.ua.js +0 -35
  50. data/vendor/assets/javascripts/parsley.i18n.vn.js +0 -35
  51. data/vendor/assets/javascripts/parsley.i18n.zh_tw.js +0 -39
  52. data/vendor/assets/javascripts/parsley.l10n.es.js +0 -183
@@ -0,0 +1,2351 @@
1
+ // `window.ParsleyExtend`, like `ParsleyAbstract`, is inherited by `ParsleyField` and `ParsleyForm`
2
+ // That way, we could add new methods or redefine some for these both classes. In particular case
3
+ // We are adding async validation methods that returns promises, bind them properly to triggered
4
+ // Events like onkeyup when field is invalid or on form submit. These validation methods adds an
5
+ // Extra `remote` validator which could not be simply added like other `ParsleyExtra` validators
6
+ // Because returns promises instead of booleans.
7
+ window.ParsleyExtend = window.ParsleyExtend || {};
8
+ window.ParsleyExtend = $.extend(window.ParsleyExtend, {
9
+ asyncSupport: true,
10
+
11
+ asyncValidators: $.extend({
12
+ default: {
13
+ fn: function (xhr) {
14
+ return 'resolved' === xhr.state();
15
+ },
16
+ url: false
17
+ },
18
+ reverse: {
19
+ fn: function (xhr) {
20
+ // If reverse option is set, a failing ajax request is considered successful
21
+ return 'rejected' === xhr.state();
22
+ },
23
+ url: false
24
+ }
25
+ }, window.ParsleyExtend.asyncValidators),
26
+
27
+ addAsyncValidator: function (name, fn, url) {
28
+ this.asyncValidators[name.toLowerCase()] = {
29
+ fn: fn,
30
+ url: url || false
31
+ };
32
+
33
+ return this;
34
+ },
35
+
36
+ asyncValidate: function () {
37
+ if ('ParsleyForm' === this.__class__)
38
+ return this._asyncValidateForm.apply(this, arguments);
39
+
40
+ return this._asyncValidateField.apply(this, arguments);
41
+ },
42
+
43
+ asyncIsValid: function () {
44
+ if ('ParsleyField' === this.__class__)
45
+ return this._asyncIsValidField.apply(this, arguments);
46
+
47
+ return this._asyncIsValidForm.apply(this, arguments);
48
+ },
49
+
50
+ onSubmitValidate: function (event) {
51
+ var that = this;
52
+
53
+ // This is a Parsley generated submit event, do not validate, do not prevent, simply exit and keep normal behavior
54
+ if (true === event.parsley)
55
+ return;
56
+
57
+ // Clone the event object
58
+ this.submitEvent = $.extend(true, {}, event);
59
+
60
+ // Prevent form submit and immediately stop its event propagation
61
+ if (event instanceof $.Event) {
62
+ event.stopImmediatePropagation();
63
+ event.preventDefault();
64
+ }
65
+
66
+ return this._asyncValidateForm(undefined, event)
67
+ .done(function () {
68
+ // If user do not have prevented the event, re-submit form
69
+ if (!that.submitEvent.isDefaultPrevented())
70
+ that.$element.trigger($.extend($.Event('submit'), { parsley: true }));
71
+ });
72
+ },
73
+
74
+ eventValidate: function (event) {
75
+ // For keyup, keypress, keydown.. events that could be a little bit obstrusive
76
+ // do not validate if val length < min threshold on first validation. Once field have been validated once and info
77
+ // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
78
+ if (new RegExp('key').test(event.type))
79
+ if (!this._ui.validationInformationVisible && this.getValue().length <= this.options.validationThreshold)
80
+ return;
81
+
82
+ this._ui.validatedOnce = true;
83
+ this.asyncValidate();
84
+ },
85
+
86
+ // Returns Promise
87
+ _asyncValidateForm: function (group, event) {
88
+ var
89
+ that = this,
90
+ promises = [];
91
+
92
+ this._refreshFields();
93
+
94
+ $.emit('parsley:form:validate', this);
95
+
96
+ for (var i = 0; i < this.fields.length; i++) {
97
+
98
+ // do not validate a field if not the same as given validation group
99
+ if (group && group !== this.fields[i].options.group)
100
+ continue;
101
+
102
+ promises.push(this.fields[i]._asyncValidateField());
103
+ }
104
+
105
+ return $.when.apply($, promises)
106
+ .always(function () {
107
+ $.emit('parsley:form:validated', that);
108
+ });
109
+ },
110
+
111
+ _asyncIsValidForm: function (group, force) {
112
+ var promises = [];
113
+ this._refreshFields();
114
+
115
+ for (var i = 0; i < this.fields.length; i++) {
116
+
117
+ // do not validate a field if not the same as given validation group
118
+ if (group && group !== this.fields[i].options.group)
119
+ continue;
120
+
121
+ promises.push(this.fields[i]._asyncIsValidField(force));
122
+ }
123
+
124
+ return $.when.apply($, promises);
125
+ },
126
+
127
+ _asyncValidateField: function (force) {
128
+ var that = this;
129
+
130
+ $.emit('parsley:field:validate', this);
131
+
132
+ return this._asyncIsValidField(force)
133
+ .done(function () {
134
+ $.emit('parsley:field:success', that);
135
+ })
136
+ .fail(function () {
137
+ $.emit('parsley:field:error', that);
138
+ })
139
+ .always(function () {
140
+ $.emit('parsley:field:validated', that);
141
+ });
142
+ },
143
+
144
+ _asyncIsValidField: function (force, value) {
145
+ var
146
+ deferred = $.Deferred(),
147
+ remoteConstraintIndex;
148
+
149
+ // If regular isValid (matching regular constraints) returns `false`, no need to go further
150
+ // Directly reject promise, do not run remote validator and save server load
151
+ if (false === this.isValid(force, value))
152
+ deferred.rejectWith(this);
153
+
154
+ // If regular constraints are valid, and there is a remote validator registered, run it
155
+ else if ('undefined' !== typeof this.constraintsByName.remote)
156
+ this._remote(deferred);
157
+
158
+ // Otherwise all is good, resolve promise
159
+ else
160
+ deferred.resolveWith(this);
161
+
162
+ // Return promise
163
+ return deferred.promise();
164
+ },
165
+
166
+ _remote: function (deferred) {
167
+ var
168
+ that = this,
169
+ data = {},
170
+ ajaxOptions,
171
+ csr,
172
+ validator = this.options.remoteValidator || (true === this.options.remoteReverse ? 'reverse' : 'default');
173
+
174
+ validator = validator.toLowerCase();
175
+
176
+ if ('undefined' === typeof this.asyncValidators[validator])
177
+ throw new Error('Calling an undefined async validator: `' + validator + '`');
178
+
179
+ // Fill data with current value
180
+ data[this.$element.attr('name') || this.$element.attr('id')] = this.getValue();
181
+
182
+ // All `$.ajax(options)` could be overridden or extended directly from DOM in `data-parsley-remote-options`
183
+ ajaxOptions = $.extend(true, {}, {
184
+ url: this.asyncValidators[validator].url || this.options.remote,
185
+ data: data,
186
+ type: 'GET'
187
+ }, this.options.remoteOptions || {});
188
+
189
+ // Generate store key based on ajax options
190
+ csr = $.param(ajaxOptions);
191
+
192
+ // Initialise querry cache
193
+ if ('undefined' === typeof this._remoteCache)
194
+ this._remoteCache = {};
195
+
196
+ // Try to retrieve stored xhr
197
+ if (!this._remoteCache[csr]) {
198
+ // Prevent multi burst xhr queries
199
+ if (this._xhr && 'pending' === this._xhr.state())
200
+ this._xhr.abort();
201
+
202
+ // Make ajax call
203
+ this._xhr = $.ajax(ajaxOptions)
204
+
205
+ // Store remote call result to avoid next calls with exact same parameters
206
+ this._remoteCache[csr] = this._xhr;
207
+ }
208
+
209
+ this._remoteCache[csr]
210
+ .done(function (data, textStatus, xhr) {
211
+ that._handleRemoteResult(validator, xhr, deferred);
212
+ })
213
+ .fail(function (xhr, status, message) {
214
+ // If we aborted the query, do not handle nothing for this value
215
+ if ('abort' === status)
216
+ return;
217
+
218
+ that._handleRemoteResult(validator, xhr, deferred);
219
+ });
220
+ },
221
+
222
+ _handleRemoteResult: function (validator, xhr, deferred) {
223
+ // If true, simply resolve and exit
224
+ if ('function' === typeof this.asyncValidators[validator].fn && this.asyncValidators[validator].fn(xhr)) {
225
+ deferred.resolveWith(this);
226
+
227
+ return;
228
+ }
229
+
230
+ // Else, create a proper remote validation Violation to trigger right UI
231
+ this.validationResult = [
232
+ new window.ParsleyValidator.Validator.Violation(
233
+ this.constraintsByName.remote,
234
+ this.getValue(),
235
+ null
236
+ )
237
+ ];
238
+
239
+ deferred.rejectWith(this);
240
+ }
241
+ });
242
+
243
+ // Remote validator is just an always true sync validator with lowest (-1) priority possible
244
+ // It will be overloaded in `validateThroughValidator()` that will do the heavy async work
245
+ // This 'hack' is needed not to mess up too much with error messages and stuff in `ParsleyUI`
246
+ window.ParsleyConfig = window.ParsleyConfig || {};
247
+ window.ParsleyConfig.validators = window.ParsleyConfig.validators || {};
248
+ window.ParsleyConfig.validators.remote = {
249
+ fn: function () {
250
+ return true;
251
+ },
252
+ priority: -1
253
+ };
254
+
255
+ /*!
256
+ * Parsleyjs
257
+ * Guillaume Potier - <guillaume@wisembly.com>
258
+ * Version 2.0.0 - built Sat Apr 19 2014 17:29:18
259
+ * MIT Licensed
260
+ *
261
+ */
262
+ !(function($) {
263
+ var ParsleyUtils = {
264
+ // Parsley DOM-API
265
+ // returns object from dom attributes and values
266
+ // if attr is given, returns bool if attr present in DOM or not
267
+ attr: function ($element, namespace, checkAttr) {
268
+ var
269
+ attribute,
270
+ obj = {},
271
+ regex = new RegExp('^' + namespace, 'i');
272
+ if ('undefined' === typeof $element || 'undefined' === typeof $element[0])
273
+ return {};
274
+ for (var i in $element[0].attributes) {
275
+ attribute = $element[0].attributes[i];
276
+ if ('undefined' !== typeof attribute && null !== attribute && attribute.specified && regex.test(attribute.name)) {
277
+ if ('undefined' !== typeof checkAttr && new RegExp(checkAttr + '$', 'i').test(attribute.name))
278
+ return true;
279
+ obj[this.camelize(attribute.name.replace(namespace, ''))] = this.deserializeValue(attribute.value);
280
+ }
281
+ }
282
+ return 'undefined' === typeof checkAttr ? obj : false;
283
+ },
284
+ setAttr: function ($element, namespace, attr, value) {
285
+ $element[0].setAttribute(this.dasherize(namespace + attr), String(value));
286
+ },
287
+ // Recursive object / array getter
288
+ get: function (obj, path) {
289
+ var
290
+ i = 0,
291
+ paths = (path || '').split('.');
292
+ while (this.isObject(obj) || this.isArray(obj)) {
293
+ obj = obj[paths[i++]];
294
+ if (i === paths.length)
295
+ return obj;
296
+ }
297
+ return undefined;
298
+ },
299
+ hash: function (length) {
300
+ return String(Math.random()).substring(2, length ? length + 2 : 9);
301
+ },
302
+ /** Third party functions **/
303
+ // Underscore isArray
304
+ isArray: function (mixed) {
305
+ return Object.prototype.toString.call(mixed) === '[object Array]';
306
+ },
307
+ // Underscore isObject
308
+ isObject: function (mixed) {
309
+ return mixed === Object(mixed);
310
+ },
311
+ // Zepto deserialize function
312
+ deserializeValue: function (value) {
313
+ var num;
314
+ try {
315
+ return value ?
316
+ value == "true" ||
317
+ (value == "false" ? false :
318
+ value == "null" ? null :
319
+ !isNaN(num = Number(value)) ? num :
320
+ /^[\[\{]/.test(value) ? $.parseJSON(value) :
321
+ value)
322
+ : value;
323
+ } catch (e) { return value; }
324
+ },
325
+ // Zepto camelize function
326
+ camelize: function (str) {
327
+ return str.replace(/-+(.)?/g, function(match, chr) {
328
+ return chr ? chr.toUpperCase() : '';
329
+ });
330
+ },
331
+ // Zepto dasherize function
332
+ dasherize: function (str) {
333
+ return str.replace(/::/g, '/')
334
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
335
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
336
+ .replace(/_/g, '-')
337
+ .toLowerCase();
338
+ }
339
+ };
340
+ // All these options could be overriden and specified directly in DOM using
341
+ // `data-parsley-` default DOM-API
342
+ // eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"`
343
+ // eg: `data-parsley-stop-on-first-failing-constraint="false"`
344
+ var ParsleyDefaults = {
345
+ // ### General
346
+ // Default data-namespace for DOM API
347
+ namespace: 'data-parsley-',
348
+ // Supported inputs by default
349
+ inputs: 'input, textarea, select',
350
+ // Excluded inputs by default
351
+ excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]',
352
+ // Stop validating field on highest priority failing constraint
353
+ priorityEnabled: true,
354
+ // ### UI
355
+ // Enable\Disable error messages
356
+ uiEnabled: true,
357
+ // Key events threshold before validation
358
+ validationThreshold: 3,
359
+ // Focused field on form validation error. 'fist'|'last'|'none'
360
+ focus: 'first',
361
+ // `$.Event()` that will trigger validation. eg: `keyup`, `change`..
362
+ trigger: false,
363
+ // Class that would be added on every failing validation Parsley field
364
+ errorClass: 'parsley-error',
365
+ // Same for success validation
366
+ successClass: 'parsley-success',
367
+ // Return the `$element` that will receive these above success or error classes
368
+ // Could also be (and given directly from DOM) a valid selector like `'#div'`
369
+ classHandler: function (ParsleyField) {},
370
+ // Return the `$element` where errors will be appended
371
+ // Could also be (and given directly from DOM) a valid selector like `'#div'`
372
+ errorsContainer: function (ParsleyField) {},
373
+ // ul elem that would receive errors' list
374
+ errorsWrapper: '<ul class="parsley-errors-list"></ul>',
375
+ // li elem that would receive error message
376
+ errorTemplate: '<li></li>'
377
+ };
378
+
379
+ var ParsleyAbstract = function() {};
380
+ ParsleyAbstract.prototype = {
381
+ asyncSupport: false,
382
+ actualizeOptions: function () {
383
+ this.options = this.OptionsFactory.get(this);
384
+ return this;
385
+ },
386
+ // ParsleyValidator validate proxy function . Could be replaced by third party scripts
387
+ validateThroughValidator: function (value, constraints, priority) {
388
+ return window.ParsleyValidator.validate.apply(window.ParsleyValidator, [value, constraints, priority]);
389
+ },
390
+ // Subscribe an event and a handler for a specific field or a specific form
391
+ // If on a ParsleyForm instance, it will be attached to form instance and also
392
+ // To every field instance for this form
393
+ subscribe: function (name, fn) {
394
+ $.listenTo(this, name.toLowerCase(), fn);
395
+ return this;
396
+ },
397
+ // Same as subscribe above. Unsubscribe an event for field, or form + its fields
398
+ unsubscribe: function (name) {
399
+ $.unsubscribeTo(this, name.toLowerCase());
400
+ return this;
401
+ },
402
+ // Reset UI
403
+ reset: function () {
404
+ // Field case: just emit a reset event for UI
405
+ if ('ParsleyForm' !== this.__class__)
406
+ return $.emit('parsley:field:reset', this);
407
+ // Form case: emit a reset event for each field
408
+ for (var i = 0; i < this.fields.length; i++)
409
+ $.emit('parsley:field:reset', this.fields[i]);
410
+ $.emit('parsley:form:reset', this);
411
+ },
412
+ // Destroy Parsley instance (+ UI)
413
+ destroy: function () {
414
+ // Field case: emit destroy event to clean UI and then destroy stored instance
415
+ if ('ParsleyForm' !== this.__class__) {
416
+ this.$element.removeData('Parsley');
417
+ this.$element.removeData('ParsleyFieldMultiple');
418
+ $.emit('parsley:field:destroy', this);
419
+ return;
420
+ }
421
+ // Form case: destroy all its fields and then destroy stored instance
422
+ for (var i = 0; i < this.fields.length; i++)
423
+ this.fields[i].destroy();
424
+ this.$element.removeData('Parsley');
425
+ $.emit('parsley:form:destroy', this);
426
+ }
427
+ };
428
+ /*!
429
+ * validator.js
430
+ * Guillaume Potier - <guillaume@wisembly.com>
431
+ * Version 0.5.8 - built Sun Mar 16 2014 17:18:21
432
+ * MIT Licensed
433
+ *
434
+ */
435
+ ( function ( exports ) {
436
+ /**
437
+ * Validator
438
+ */
439
+ var Validator = function ( options ) {
440
+ this.__class__ = 'Validator';
441
+ this.__version__ = '0.5.8';
442
+ this.options = options || {};
443
+ this.bindingKey = this.options.bindingKey || '_validatorjsConstraint';
444
+ return this;
445
+ };
446
+ Validator.prototype = {
447
+ constructor: Validator,
448
+ /*
449
+ * Validate string: validate( string, Assert, string ) || validate( string, [ Assert, Assert ], [ string, string ] )
450
+ * Validate object: validate( object, Constraint, string ) || validate( object, Constraint, [ string, string ] )
451
+ * Validate binded object: validate( object, string ) || validate( object, [ string, string ] )
452
+ */
453
+ validate: function ( objectOrString, AssertsOrConstraintOrGroup, group ) {
454
+ if ( 'string' !== typeof objectOrString && 'object' !== typeof objectOrString )
455
+ throw new Error( 'You must validate an object or a string' );
456
+ // string / array validation
457
+ if ( 'string' === typeof objectOrString || _isArray(objectOrString) )
458
+ return this._validateString( objectOrString, AssertsOrConstraintOrGroup, group );
459
+ // binded object validation
460
+ if ( this.isBinded( objectOrString ) )
461
+ return this._validateBindedObject( objectOrString, AssertsOrConstraintOrGroup );
462
+ // regular object validation
463
+ return this._validateObject( objectOrString, AssertsOrConstraintOrGroup, group );
464
+ },
465
+ bind: function ( object, constraint ) {
466
+ if ( 'object' !== typeof object )
467
+ throw new Error( 'Must bind a Constraint to an object' );
468
+ object[ this.bindingKey ] = new Constraint( constraint );
469
+ return this;
470
+ },
471
+ unbind: function ( object ) {
472
+ if ( 'undefined' === typeof object._validatorjsConstraint )
473
+ return this;
474
+ delete object[ this.bindingKey ];
475
+ return this;
476
+ },
477
+ isBinded: function ( object ) {
478
+ return 'undefined' !== typeof object[ this.bindingKey ];
479
+ },
480
+ getBinded: function ( object ) {
481
+ return this.isBinded( object ) ? object[ this.bindingKey ] : null;
482
+ },
483
+ _validateString: function ( string, assert, group ) {
484
+ var result, failures = [];
485
+ if ( !_isArray( assert ) )
486
+ assert = [ assert ];
487
+ for ( var i = 0; i < assert.length; i++ ) {
488
+ if ( ! ( assert[ i ] instanceof Assert) )
489
+ throw new Error( 'You must give an Assert or an Asserts array to validate a string' );
490
+ result = assert[ i ].check( string, group );
491
+ if ( result instanceof Violation )
492
+ failures.push( result );
493
+ }
494
+ return failures.length ? failures : true;
495
+ },
496
+ _validateObject: function ( object, constraint, group ) {
497
+ if ( 'object' !== typeof constraint )
498
+ throw new Error( 'You must give a constraint to validate an object' );
499
+ if ( constraint instanceof Constraint )
500
+ return constraint.check( object, group );
501
+ return new Constraint( constraint ).check( object, group );
502
+ },
503
+ _validateBindedObject: function ( object, group ) {
504
+ return object[ this.bindingKey ].check( object, group );
505
+ }
506
+ };
507
+ Validator.errorCode = {
508
+ must_be_a_string: 'must_be_a_string',
509
+ must_be_an_array: 'must_be_an_array',
510
+ must_be_a_number: 'must_be_a_number',
511
+ must_be_a_string_or_array: 'must_be_a_string_or_array'
512
+ };
513
+ /**
514
+ * Constraint
515
+ */
516
+ var Constraint = function ( data, options ) {
517
+ this.__class__ = 'Constraint';
518
+ this.options = options || {};
519
+ this.nodes = {};
520
+ if ( data ) {
521
+ try {
522
+ this._bootstrap( data );
523
+ } catch ( err ) {
524
+ throw new Error( 'Should give a valid mapping object to Constraint', err, data );
525
+ }
526
+ }
527
+ return this;
528
+ };
529
+ Constraint.prototype = {
530
+ constructor: Constraint,
531
+ check: function ( object, group ) {
532
+ var result, failures = {};
533
+ // check all constraint nodes if strict validation enabled. Else, only object nodes that have a constraint
534
+ for ( var property in this.options.strict ? this.nodes : object ) {
535
+ if ( this.options.strict ? this.has( property, object ) : this.has( property ) ) {
536
+ result = this._check( property, object[ property ], group );
537
+ // check returned an array of Violations or an object mapping Violations
538
+ if ( ( _isArray( result ) && result.length > 0 ) || ( !_isArray( result ) && !_isEmptyObject( result ) ) )
539
+ failures[ property ] = result;
540
+ // in strict mode, get a violation for each constraint node not in object
541
+ } else if ( this.options.strict ) {
542
+ try {
543
+ // we trigger here a HaveProperty Assert violation to have uniform Violation object in the end
544
+ new Assert().HaveProperty( property ).validate( object );
545
+ } catch ( violation ) {
546
+ failures[ property ] = violation;
547
+ }
548
+ }
549
+ }
550
+ return _isEmptyObject(failures) ? true : failures;
551
+ },
552
+ add: function ( node, object ) {
553
+ if ( object instanceof Assert || ( _isArray( object ) && object[ 0 ] instanceof Assert ) ) {
554
+ this.nodes[ node ] = object;
555
+ return this;
556
+ }
557
+ if ( 'object' === typeof object && !_isArray( object ) ) {
558
+ this.nodes[ node ] = object instanceof Constraint ? object : new Constraint( object );
559
+ return this;
560
+ }
561
+ throw new Error( 'Should give an Assert, an Asserts array, a Constraint', object );
562
+ },
563
+ has: function ( node, nodes ) {
564
+ nodes = 'undefined' !== typeof nodes ? nodes : this.nodes;
565
+ return 'undefined' !== typeof nodes[ node ];
566
+ },
567
+ get: function ( node, placeholder ) {
568
+ return this.has( node ) ? this.nodes[ node ] : placeholder || null;
569
+ },
570
+ remove: function ( node ) {
571
+ var _nodes = [];
572
+ for ( var i in this.nodes )
573
+ if ( i !== node )
574
+ _nodes[ i ] = this.nodes[ i ];
575
+ this.nodes = _nodes;
576
+ return this;
577
+ },
578
+ _bootstrap: function ( data ) {
579
+ if ( data instanceof Constraint )
580
+ return this.nodes = data.nodes;
581
+ for ( var node in data )
582
+ this.add( node, data[ node ] );
583
+ },
584
+ _check: function ( node, value, group ) {
585
+ // Assert
586
+ if ( this.nodes[ node ] instanceof Assert )
587
+ return this._checkAsserts( value, [ this.nodes[ node ] ], group );
588
+ // Asserts
589
+ if ( _isArray( this.nodes[ node ] ) )
590
+ return this._checkAsserts( value, this.nodes[ node ], group );
591
+ // Constraint -> check api
592
+ if ( this.nodes[ node ] instanceof Constraint )
593
+ return this.nodes[ node ].check( value, group );
594
+ throw new Error( 'Invalid node', this.nodes[ node ] );
595
+ },
596
+ _checkAsserts: function ( value, asserts, group ) {
597
+ var result, failures = [];
598
+ for ( var i = 0; i < asserts.length; i++ ) {
599
+ result = asserts[ i ].check( value, group );
600
+ if ( 'undefined' !== typeof result && true !== result )
601
+ failures.push( result );
602
+ // Some asserts (Collection for example) could return an object
603
+ // if ( result && ! ( result instanceof Violation ) )
604
+ // return result;
605
+ //
606
+ // // Vast assert majority return Violation
607
+ // if ( result instanceof Violation )
608
+ // failures.push( result );
609
+ }
610
+ return failures;
611
+ }
612
+ };
613
+ /**
614
+ * Violation
615
+ */
616
+ var Violation = function ( assert, value, violation ) {
617
+ this.__class__ = 'Violation';
618
+ if ( ! ( assert instanceof Assert ) )
619
+ throw new Error( 'Should give an assertion implementing the Assert interface' );
620
+ this.assert = assert;
621
+ this.value = value;
622
+ if ( 'undefined' !== typeof violation )
623
+ this.violation = violation;
624
+ };
625
+ Violation.prototype = {
626
+ show: function () {
627
+ var show = {
628
+ assert: this.assert.__class__,
629
+ value: this.value
630
+ };
631
+ if ( this.violation )
632
+ show.violation = this.violation;
633
+ return show;
634
+ },
635
+ __toString: function () {
636
+ if ( 'undefined' !== typeof this.violation )
637
+ this.violation = '", ' + this.getViolation().constraint + ' expected was ' + this.getViolation().expected;
638
+ return this.assert.__class__ + ' assert failed for "' + this.value + this.violation || '';
639
+ },
640
+ getViolation: function () {
641
+ var constraint, expected;
642
+ for ( constraint in this.violation )
643
+ expected = this.violation[ constraint ];
644
+ return { constraint: constraint, expected: expected };
645
+ }
646
+ };
647
+ /**
648
+ * Assert
649
+ */
650
+ var Assert = function ( group ) {
651
+ this.__class__ = 'Assert';
652
+ this.__parentClass__ = this.__class__;
653
+ this.groups = [];
654
+ if ( 'undefined' !== typeof group )
655
+ this.addGroup( group );
656
+ return this;
657
+ };
658
+ Assert.prototype = {
659
+ construct: Assert,
660
+ check: function ( value, group ) {
661
+ if ( group && !this.hasGroup( group ) )
662
+ return;
663
+ if ( !group && this.hasGroups() )
664
+ return;
665
+ try {
666
+ return this.validate( value, group );
667
+ } catch ( violation ) {
668
+ return violation;
669
+ }
670
+ },
671
+ hasGroup: function ( group ) {
672
+ if ( _isArray( group ) )
673
+ return this.hasOneOf( group );
674
+ // All Asserts respond to "Any" group
675
+ if ( 'Any' === group )
676
+ return true;
677
+ // Asserts with no group also respond to "Default" group. Else return false
678
+ if ( !this.hasGroups() )
679
+ return 'Default' === group;
680
+ return -1 !== this.groups.indexOf( group );
681
+ },
682
+ hasOneOf: function ( groups ) {
683
+ for ( var i = 0; i < groups.length; i++ )
684
+ if ( this.hasGroup( groups[ i ] ) )
685
+ return true;
686
+ return false;
687
+ },
688
+ hasGroups: function () {
689
+ return this.groups.length > 0;
690
+ },
691
+ addGroup: function ( group ) {
692
+ if ( _isArray( group ) )
693
+ return this.addGroups( group );
694
+ if ( !this.hasGroup( group ) )
695
+ this.groups.push( group );
696
+ return this;
697
+ },
698
+ removeGroup: function ( group ) {
699
+ var _groups = [];
700
+ for ( var i = 0; i < this.groups.length; i++ )
701
+ if ( group !== this.groups[ i ] )
702
+ _groups.push( this.groups[ i ] );
703
+ this.groups = _groups;
704
+ return this;
705
+ },
706
+ addGroups: function ( groups ) {
707
+ for ( var i = 0; i < groups.length; i++ )
708
+ this.addGroup( groups[ i ] );
709
+ return this;
710
+ },
711
+ /**
712
+ * Asserts definitions
713
+ */
714
+ HaveProperty: function ( node ) {
715
+ this.__class__ = 'HaveProperty';
716
+ this.node = node;
717
+ this.validate = function ( object ) {
718
+ if ( 'undefined' === typeof object[ this.node ] )
719
+ throw new Violation( this, object, { value: this.node } );
720
+ return true;
721
+ };
722
+ return this;
723
+ },
724
+ Blank: function () {
725
+ this.__class__ = 'Blank';
726
+ this.validate = function ( value ) {
727
+ if ( 'string' !== typeof value )
728
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } );
729
+ if ( '' !== value.replace( /^\s+/g, '' ).replace( /\s+$/g, '' ) )
730
+ throw new Violation( this, value );
731
+ return true;
732
+ };
733
+ return this;
734
+ },
735
+ Callback: function ( fn ) {
736
+ this.__class__ = 'Callback';
737
+ this.arguments = Array.prototype.slice.call( arguments );
738
+ if ( 1 === this.arguments.length )
739
+ this.arguments = [];
740
+ else
741
+ this.arguments.splice( 0, 1 );
742
+ if ( 'function' !== typeof fn )
743
+ throw new Error( 'Callback must be instanciated with a function' );
744
+ this.fn = fn;
745
+ this.validate = function ( value ) {
746
+ var result = this.fn.apply( this, [ value ].concat( this.arguments ) );
747
+ if ( true !== result )
748
+ throw new Violation( this, value, { result: result } );
749
+ return true;
750
+ };
751
+ return this;
752
+ },
753
+ Choice: function ( list ) {
754
+ this.__class__ = 'Choice';
755
+ if ( !_isArray( list ) && 'function' !== typeof list )
756
+ throw new Error( 'Choice must be instanciated with an array or a function' );
757
+ this.list = list;
758
+ this.validate = function ( value ) {
759
+ var list = 'function' === typeof this.list ? this.list() : this.list;
760
+ for ( var i = 0; i < list.length; i++ )
761
+ if ( value === list[ i ] )
762
+ return true;
763
+ throw new Violation( this, value, { choices: list } );
764
+ };
765
+ return this;
766
+ },
767
+ Collection: function ( constraint ) {
768
+ this.__class__ = 'Collection';
769
+ this.constraint = 'undefined' !== typeof constraint ? new Constraint( constraint ) : false;
770
+ this.validate = function ( collection, group ) {
771
+ var result, validator = new Validator(), count = 0, failures = {}, groups = this.groups.length ? this.groups : group;
772
+ if ( !_isArray( collection ) )
773
+ throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } );
774
+ for ( var i = 0; i < collection.length; i++ ) {
775
+ result = this.constraint ?
776
+ validator.validate( collection[ i ], this.constraint, groups ) :
777
+ validator.validate( collection[ i ], groups );
778
+ if ( !_isEmptyObject( result ) )
779
+ failures[ count ] = result;
780
+ count++;
781
+ }
782
+ return !_isEmptyObject( failures ) ? failures : true;
783
+ };
784
+ return this;
785
+ },
786
+ Count: function ( count ) {
787
+ this.__class__ = 'Count';
788
+ this.count = count;
789
+ this.validate = function ( array ) {
790
+ if ( !_isArray( array ) )
791
+ throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } );
792
+ var count = 'function' === typeof this.count ? this.count( array ) : this.count;
793
+ if ( isNaN( Number( count ) ) )
794
+ throw new Error( 'Count must be a valid interger', count );
795
+ if ( count !== array.length )
796
+ throw new Violation( this, array, { count: count } );
797
+ return true;
798
+ };
799
+ return this;
800
+ },
801
+ Email: function () {
802
+ this.__class__ = 'Email';
803
+ this.validate = function ( value ) {
804
+ var regExp = /^((([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;
805
+ if ( 'string' !== typeof value )
806
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } );
807
+ if ( !regExp.test( value ) )
808
+ throw new Violation( this, value );
809
+ return true;
810
+ };
811
+ return this;
812
+ },
813
+ Eql: function ( eql ) {
814
+ this.__class__ = 'Eql';
815
+ if ( 'undefined' === typeof eql )
816
+ throw new Error( 'Equal must be instanciated with an Array or an Object' );
817
+ this.eql = eql;
818
+ this.validate = function ( value ) {
819
+ var eql = 'function' === typeof this.eql ? this.eql( value ) : this.eql;
820
+ if ( !expect.eql( eql, value ) )
821
+ throw new Violation( this, value, { eql: eql } );
822
+ return true;
823
+ };
824
+ return this;
825
+ },
826
+ EqualTo: function ( reference ) {
827
+ this.__class__ = 'EqualTo';
828
+ if ( 'undefined' === typeof reference )
829
+ throw new Error( 'EqualTo must be instanciated with a value or a function' );
830
+ this.reference = reference;
831
+ this.validate = function ( value ) {
832
+ var reference = 'function' === typeof this.reference ? this.reference( value ) : this.reference;
833
+ if ( reference !== value )
834
+ throw new Violation( this, value, { value: reference } );
835
+ return true;
836
+ };
837
+ return this;
838
+ },
839
+ GreaterThan: function ( threshold ) {
840
+ this.__class__ = 'GreaterThan';
841
+ if ( 'undefined' === typeof threshold )
842
+ throw new Error( 'Should give a threshold value' );
843
+ this.threshold = threshold;
844
+ this.validate = function ( value ) {
845
+ if ( '' === value || isNaN( Number( value ) ) )
846
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } );
847
+ if ( this.threshold >= value )
848
+ throw new Violation( this, value, { threshold: this.threshold } );
849
+ return true;
850
+ };
851
+ return this;
852
+ },
853
+ GreaterThanOrEqual: function ( threshold ) {
854
+ this.__class__ = 'GreaterThanOrEqual';
855
+ if ( 'undefined' === typeof threshold )
856
+ throw new Error( 'Should give a threshold value' );
857
+ this.threshold = threshold;
858
+ this.validate = function ( value ) {
859
+ if ( '' === value || isNaN( Number( value ) ) )
860
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } );
861
+ if ( this.threshold > value )
862
+ throw new Violation( this, value, { threshold: this.threshold } );
863
+ return true;
864
+ };
865
+ return this;
866
+ },
867
+ InstanceOf: function ( classRef ) {
868
+ this.__class__ = 'InstanceOf';
869
+ if ( 'undefined' === typeof classRef )
870
+ throw new Error( 'InstanceOf must be instanciated with a value' );
871
+ this.classRef = classRef;
872
+ this.validate = function ( value ) {
873
+ if ( true !== (value instanceof this.classRef) )
874
+ throw new Violation( this, value, { classRef: this.classRef } );
875
+ return true;
876
+ };
877
+ return this;
878
+ },
879
+ IPv4: function () {
880
+ this.__class__ = 'IPv4';
881
+ this.validate = function ( value ) {
882
+ var regExp = /^(?:(?: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]?)$/;
883
+ if ( 'string' !== typeof value )
884
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } );
885
+ if ( !regExp.test( value ) )
886
+ throw new Violation( this, value );
887
+ return true;
888
+ };
889
+ return this;
890
+ },
891
+ Length: function ( boundaries ) {
892
+ this.__class__ = 'Length';
893
+ if ( !boundaries.min && !boundaries.max )
894
+ throw new Error( 'Lenth assert must be instanciated with a { min: x, max: y } object' );
895
+ this.min = boundaries.min;
896
+ this.max = boundaries.max;
897
+ this.validate = function ( value ) {
898
+ if ( 'string' !== typeof value && !_isArray( value ) )
899
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string_or_array } );
900
+ if ( 'undefined' !== typeof this.min && this.min === this.max && value.length !== this.min )
901
+ throw new Violation( this, value, { min: this.min, max: this.max } );
902
+ if ( 'undefined' !== typeof this.max && value.length > this.max )
903
+ throw new Violation( this, value, { max: this.max } );
904
+ if ( 'undefined' !== typeof this.min && value.length < this.min )
905
+ throw new Violation( this, value, { min: this.min } );
906
+ return true;
907
+ };
908
+ return this;
909
+ },
910
+ LessThan: function ( threshold ) {
911
+ this.__class__ = 'LessThan';
912
+ if ( 'undefined' === typeof threshold )
913
+ throw new Error( 'Should give a threshold value' );
914
+ this.threshold = threshold;
915
+ this.validate = function ( value ) {
916
+ if ( '' === value || isNaN( Number( value ) ) )
917
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } );
918
+ if ( this.threshold <= value )
919
+ throw new Violation( this, value, { threshold: this.threshold } );
920
+ return true;
921
+ };
922
+ return this;
923
+ },
924
+ LessThanOrEqual: function ( threshold ) {
925
+ this.__class__ = 'LessThanOrEqual';
926
+ if ( 'undefined' === typeof threshold )
927
+ throw new Error( 'Should give a threshold value' );
928
+ this.threshold = threshold;
929
+ this.validate = function ( value ) {
930
+ if ( '' === value || isNaN( Number( value ) ) )
931
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } );
932
+ if ( this.threshold < value )
933
+ throw new Violation( this, value, { threshold: this.threshold } );
934
+ return true;
935
+ };
936
+ return this;
937
+ },
938
+ Mac: function () {
939
+ this.__class__ = 'Mac';
940
+ this.validate = function ( value ) {
941
+ var regExp = /^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$/i;
942
+ if ( 'string' !== typeof value )
943
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } );
944
+ if ( !regExp.test( value ) )
945
+ throw new Violation( this, value );
946
+ return true;
947
+ };
948
+ return this;
949
+ },
950
+ NotNull: function () {
951
+ this.__class__ = 'NotNull';
952
+ this.validate = function ( value ) {
953
+ if ( null === value || 'undefined' === typeof value )
954
+ throw new Violation( this, value );
955
+ return true;
956
+ };
957
+ return this;
958
+ },
959
+ NotBlank: function () {
960
+ this.__class__ = 'NotBlank';
961
+ this.validate = function ( value ) {
962
+ if ( 'string' !== typeof value )
963
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } );
964
+ if ( '' === value.replace( /^\s+/g, '' ).replace( /\s+$/g, '' ) )
965
+ throw new Violation( this, value );
966
+ return true;
967
+ };
968
+ return this;
969
+ },
970
+ Null: function () {
971
+ this.__class__ = 'Null';
972
+ this.validate = function ( value ) {
973
+ if ( null !== value )
974
+ throw new Violation( this, value );
975
+ return true;
976
+ };
977
+ return this;
978
+ },
979
+ Range: function ( min, max ) {
980
+ this.__class__ = 'Range';
981
+ if ( 'undefined' === typeof min || 'undefined' === typeof max )
982
+ throw new Error( 'Range assert expects min and max values' );
983
+ this.min = min;
984
+ this.max = max;
985
+ this.validate = function ( value ) {
986
+ try {
987
+ // validate strings and objects with their Length
988
+ if ( ( 'string' === typeof value && isNaN( Number( value ) ) ) || _isArray( value ) )
989
+ new Assert().Length( { min: this.min, max: this.max } ).validate( value );
990
+ // validate numbers with their value
991
+ else
992
+ new Assert().GreaterThanOrEqual( this.min ).validate( value ) && new Assert().LessThanOrEqual( this.max ).validate( value );
993
+ return true;
994
+ } catch ( violation ) {
995
+ throw new Violation( this, value, violation.violation );
996
+ }
997
+ return true;
998
+ };
999
+ return this;
1000
+ },
1001
+ Regexp: function ( regexp, flag ) {
1002
+ this.__class__ = 'Regexp';
1003
+ if ( 'undefined' === typeof regexp )
1004
+ throw new Error( 'You must give a regexp' );
1005
+ this.regexp = regexp;
1006
+ this.flag = flag || '';
1007
+ this.validate = function ( value ) {
1008
+ if ( 'string' !== typeof value )
1009
+ throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } );
1010
+ if ( !new RegExp( this.regexp, this.flag ).test( value ) )
1011
+ throw new Violation( this, value, { regexp: this.regexp, flag: this.flag } );
1012
+ return true;
1013
+ };
1014
+ return this;
1015
+ },
1016
+ Required: function () {
1017
+ this.__class__ = 'Required';
1018
+ this.validate = function ( value ) {
1019
+ if ( 'undefined' === typeof value )
1020
+ throw new Violation( this, value );
1021
+ try {
1022
+ if ( 'string' === typeof value )
1023
+ new Assert().NotNull().validate( value ) && new Assert().NotBlank().validate( value );
1024
+ else if ( true === _isArray( value ) )
1025
+ new Assert().Length( { min: 1 } ).validate( value );
1026
+ } catch ( violation ) {
1027
+ throw new Violation( this, value );
1028
+ }
1029
+ return true;
1030
+ };
1031
+ return this;
1032
+ },
1033
+ // Unique() or Unique ( { key: foo } )
1034
+ Unique: function ( object ) {
1035
+ this.__class__ = 'Unique';
1036
+ if ( 'object' === typeof object )
1037
+ this.key = object.key;
1038
+ this.validate = function ( array ) {
1039
+ var value, store = [];
1040
+ if ( !_isArray( array ) )
1041
+ throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } );
1042
+ for ( var i = 0; i < array.length; i++ ) {
1043
+ value = 'object' === typeof array[ i ] ? array[ i ][ this.key ] : array[ i ];
1044
+ if ( 'undefined' === typeof value )
1045
+ continue;
1046
+ if ( -1 !== store.indexOf( value ) )
1047
+ throw new Violation( this, array, { value: value } );
1048
+ store.push( value );
1049
+ }
1050
+ return true;
1051
+ };
1052
+ return this;
1053
+ }
1054
+ };
1055
+ // expose to the world these awesome classes
1056
+ exports.Assert = Assert;
1057
+ exports.Validator = Validator;
1058
+ exports.Violation = Violation;
1059
+ exports.Constraint = Constraint;
1060
+ /**
1061
+ * Some useful object prototypes / functions here
1062
+ */
1063
+ // IE8<= compatibility
1064
+ // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
1065
+ if (!Array.prototype.indexOf)
1066
+ Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
1067
+
1068
+ if (this === null) {
1069
+ throw new TypeError();
1070
+ }
1071
+ var t = Object(this);
1072
+ var len = t.length >>> 0;
1073
+ if (len === 0) {
1074
+ return -1;
1075
+ }
1076
+ var n = 0;
1077
+ if (arguments.length > 1) {
1078
+ n = Number(arguments[1]);
1079
+ if (n != n) { // shortcut for verifying if it's NaN
1080
+ n = 0;
1081
+ } else if (n !== 0 && n != Infinity && n != -Infinity) {
1082
+ n = (n > 0 || -1) * Math.floor(Math.abs(n));
1083
+ }
1084
+ }
1085
+ if (n >= len) {
1086
+ return -1;
1087
+ }
1088
+ var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
1089
+ for (; k < len; k++) {
1090
+ if (k in t && t[k] === searchElement) {
1091
+ return k;
1092
+ }
1093
+ }
1094
+ return -1;
1095
+ };
1096
+ // Test if object is empty, useful for Constraint violations check
1097
+ var _isEmptyObject = function ( obj ) {
1098
+ for ( var property in obj )
1099
+ return false;
1100
+ return true;
1101
+ };
1102
+ var _isArray = function ( obj ) {
1103
+ return Object.prototype.toString.call( obj ) === '[object Array]';
1104
+ };
1105
+ // https://github.com/LearnBoost/expect.js/blob/master/expect.js
1106
+ var expect = {
1107
+ eql: function ( actual, expected ) {
1108
+ if ( actual === expected ) {
1109
+ return true;
1110
+ } else if ( 'undefined' !== typeof Buffer && Buffer.isBuffer( actual ) && Buffer.isBuffer( expected ) ) {
1111
+ if ( actual.length !== expected.length ) return false;
1112
+ for ( var i = 0; i < actual.length; i++ )
1113
+ if ( actual[i] !== expected[i] ) return false;
1114
+ return true;
1115
+ } else if ( actual instanceof Date && expected instanceof Date ) {
1116
+ return actual.getTime() === expected.getTime();
1117
+ } else if ( typeof actual !== 'object' && typeof expected !== 'object' ) {
1118
+ // loosy ==
1119
+ return actual == expected;
1120
+ } else {
1121
+ return this.objEquiv(actual, expected);
1122
+ }
1123
+ },
1124
+ isUndefinedOrNull: function ( value ) {
1125
+ return value === null || typeof value === 'undefined';
1126
+ },
1127
+ isArguments: function ( object ) {
1128
+ return Object.prototype.toString.call(object) == '[object Arguments]';
1129
+ },
1130
+ keys: function ( obj ) {
1131
+ if ( Object.keys )
1132
+ return Object.keys( obj );
1133
+ var keys = [];
1134
+ for ( var i in obj )
1135
+ if ( Object.prototype.hasOwnProperty.call( obj, i ) )
1136
+ keys.push(i);
1137
+ return keys;
1138
+ },
1139
+ objEquiv: function ( a, b ) {
1140
+ if ( this.isUndefinedOrNull( a ) || this.isUndefinedOrNull( b ) )
1141
+ return false;
1142
+ if ( a.prototype !== b.prototype ) return false;
1143
+ if ( this.isArguments( a ) ) {
1144
+ if ( !this.isArguments( b ) )
1145
+ return false;
1146
+ return eql( pSlice.call( a ) , pSlice.call( b ) );
1147
+ }
1148
+ try {
1149
+ var ka = this.keys( a ), kb = this.keys( b ), key, i;
1150
+ if ( ka.length !== kb.length )
1151
+ return false;
1152
+ ka.sort();
1153
+ kb.sort();
1154
+ for ( i = ka.length - 1; i >= 0; i-- )
1155
+ if ( ka[ i ] != kb[ i ] )
1156
+ return false;
1157
+ for ( i = ka.length - 1; i >= 0; i-- ) {
1158
+ key = ka[i];
1159
+ if ( !this.eql( a[ key ], b[ key ] ) )
1160
+ return false;
1161
+ }
1162
+ return true;
1163
+ } catch ( e ) {
1164
+ return false;
1165
+ }
1166
+ }
1167
+ };
1168
+ // AMD Compliance
1169
+ if ( "function" === typeof define && define.amd ) {
1170
+ define( 'validator', [],function() { return exports; } );
1171
+ }
1172
+ } )( 'undefined' === typeof exports ? this[ 'undefined' !== typeof validatorjs_ns ? validatorjs_ns : 'Validator' ] = {} : exports );
1173
+
1174
+
1175
+ var ParsleyValidator = function (validators, catalog) {
1176
+ this.__class__ = 'ParsleyValidator';
1177
+ this.Validator = Validator;
1178
+ // Default Parsley locale is en
1179
+ this.locale = 'en';
1180
+ this.init(validators || {}, catalog || {});
1181
+ };
1182
+ ParsleyValidator.prototype = {
1183
+ init: function (validators, catalog) {
1184
+ this.catalog = catalog;
1185
+ for (var name in validators)
1186
+ this.addValidator(name, validators[name].fn, validators[name].priority);
1187
+ $.emit('parsley:validator:init');
1188
+ },
1189
+ // Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
1190
+ setLocale: function (locale) {
1191
+ if ('undefined' === typeof this.catalog[locale])
1192
+ throw new Error(locale + ' is not available in the catalog');
1193
+ this.locale = locale;
1194
+ return this;
1195
+ },
1196
+ // Add a new messages catalog for a given locale. Set locale for this catalog if set === `true`
1197
+ addCatalog: function (locale, messages, set) {
1198
+ if ('object' === typeof messages)
1199
+ this.catalog[locale] = messages;
1200
+ if (true === set)
1201
+ return this.setLocale(locale);
1202
+ return this;
1203
+ },
1204
+ // Add a specific message for a given constraint in a given locale
1205
+ addMessage: function (locale, name, message) {
1206
+ if (undefined === typeof this.catalog[locale])
1207
+ this.catalog[locale] = {};
1208
+ this.catalog[locale][name.toLowerCase()] = message;
1209
+ return this;
1210
+ },
1211
+ validate: function (value, constraints, priority) {
1212
+ return new this.Validator.Validator().validate.apply(new Validator.Validator(), arguments);
1213
+ },
1214
+ // Add a new validator
1215
+ addValidator: function (name, fn, priority) {
1216
+ this.validators[name.toLowerCase()] = function (requirements) {
1217
+ return $.extend(new Validator.Assert().Callback(fn, requirements), { priority: priority });
1218
+ };
1219
+ return this;
1220
+ },
1221
+ updateValidator: function (name, fn, priority) {
1222
+ return addValidator(name, fn, priority);
1223
+ },
1224
+ removeValidator: function (name) {
1225
+ delete this.validators[name];
1226
+ return this;
1227
+ },
1228
+ getErrorMessage: function (constraint) {
1229
+ var message;
1230
+ // Type constraints are a bit different, we have to match their requirements too to find right error message
1231
+ if ('type' === constraint.name)
1232
+ message = this.catalog[this.locale][constraint.name][constraint.requirements];
1233
+ else
1234
+ message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);
1235
+ return '' !== message ? message : this.catalog[this.locale].defaultMessage;
1236
+ },
1237
+ // Kind of light `sprintf()` implementation
1238
+ formatMessage: function (string, parameters) {
1239
+ if ('object' === typeof parameters) {
1240
+ for (var i in parameters)
1241
+ string = this.formatMessage(string, parameters[i]);
1242
+ return string;
1243
+ }
1244
+ return 'string' === typeof string ? string.replace(new RegExp('%s', 'i'), parameters) : '';
1245
+ },
1246
+ // Here is the Parsley default validators list.
1247
+ // This is basically Validatorjs validators, with different API for some of them
1248
+ // and a Parsley priority set
1249
+ validators: {
1250
+ notblank: function () {
1251
+ return $.extend(new Validator.Assert().NotBlank(), { priority: 2 });
1252
+ },
1253
+ required: function () {
1254
+ return $.extend(new Validator.Assert().Required(), { priority: 512 });
1255
+ },
1256
+ type: function (type) {
1257
+ var assert;
1258
+ switch (type) {
1259
+ case 'email':
1260
+ assert = new Validator.Assert().Email();
1261
+ break;
1262
+ case 'number':
1263
+ assert = new Validator.Assert().Regexp('^-?(?:\\d+|\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$');
1264
+ break;
1265
+ case 'integer':
1266
+ assert = new Validator.Assert().Regexp('^-?\\d+$');
1267
+ break;
1268
+ case 'digits':
1269
+ assert = new Validator.Assert().Regexp('^\\d+$');
1270
+ break;
1271
+ case 'alphanum':
1272
+ assert = new Validator.Assert().Regexp('^\\w+$', 'i');
1273
+ break;
1274
+ case 'url':
1275
+ assert = new Validator.Assert().Regexp('(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)', 'i');
1276
+ break;
1277
+ default:
1278
+ throw new Error('validator type `' + type + '` is not supported');
1279
+ }
1280
+ return $.extend(assert, { priority: 256 });
1281
+ },
1282
+ pattern: function (regexp) {
1283
+ var flags = '';
1284
+ // Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern
1285
+ if (!!(/^\/.*\/(?:[gimy]*)$/.test(regexp))) {
1286
+ // Replace the regexp literal string with the first match group: ([gimy]*)
1287
+ // If no flag is present, this will be a blank string
1288
+ flags = regexp.replace(/.*\/([gimy]*)$/, '$1');
1289
+ // Again, replace the regexp literal string with the first match group:
1290
+ // everything excluding the opening and closing slashes and the flags
1291
+ regexp = regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1');
1292
+ }
1293
+ return $.extend(new Validator.Assert().Regexp(regexp, flags), { priority: 64 });
1294
+ },
1295
+ minlength: function (value) {
1296
+ return $.extend(new Validator.Assert().Length({ min: value }), {
1297
+ priority: 30,
1298
+ requirementsTransformer: function () {
1299
+ return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value;
1300
+ }
1301
+ });
1302
+ },
1303
+ maxlength: function (value) {
1304
+ return $.extend(new Validator.Assert().Length({ max: value }), {
1305
+ priority: 30,
1306
+ requirementsTransformer: function () {
1307
+ return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value;
1308
+ }
1309
+ });
1310
+ },
1311
+ length: function (array) {
1312
+ return $.extend(new Validator.Assert().Length({ min: array[0], max: array[1] }), { priority: 32 });
1313
+ },
1314
+ mincheck: function (length) {
1315
+ return this.minlength(length);
1316
+ },
1317
+ maxcheck: function (length) {
1318
+ return this.maxlength(length);
1319
+ },
1320
+ check: function (array) {
1321
+ return this.length(array);
1322
+ },
1323
+ min: function (value) {
1324
+ return $.extend(new Validator.Assert().GreaterThanOrEqual(value), {
1325
+ priority: 30,
1326
+ requirementsTransformer: function () {
1327
+ return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value;
1328
+ }
1329
+ });
1330
+ },
1331
+ max: function (value) {
1332
+ return $.extend(new Validator.Assert().LessThanOrEqual(value), {
1333
+ priority: 30,
1334
+ requirementsTransformer: function () {
1335
+ return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value;
1336
+ }
1337
+ });
1338
+ },
1339
+ range: function (array) {
1340
+ return $.extend(new Validator.Assert().Range(array[0], array[1]), {
1341
+ priority: 32,
1342
+ requirementsTransformer: function () {
1343
+ for (var i = 0; i < array.length; i++)
1344
+ array[i] = 'string' === typeof array[i] && !isNaN(array[i]) ? parseInt(array[i], 10) : array[i];
1345
+ return array;
1346
+ }
1347
+ });
1348
+ },
1349
+ equalto: function (value) {
1350
+ return $.extend(new Validator.Assert().EqualTo(value), {
1351
+ priority: 256,
1352
+ requirementsTransformer: function () {
1353
+ return $(value).length ? $(value).val() : value;
1354
+ }
1355
+ });
1356
+ }
1357
+ }
1358
+ };
1359
+
1360
+ var ParsleyUI = function (options) {
1361
+ this.__class__ = 'ParsleyUI';
1362
+ };
1363
+ ParsleyUI.prototype = {
1364
+ listen: function () {
1365
+ $.listen('parsley:form:init', this, this.setupForm);
1366
+ $.listen('parsley:field:init', this, this.setupField);
1367
+ $.listen('parsley:field:validated', this, this.reflow);
1368
+ $.listen('parsley:form:validated', this, this.focus);
1369
+ $.listen('parsley:field:reset', this, this.reset);
1370
+ $.listen('parsley:form:destroy', this, this.destroy);
1371
+ $.listen('parsley:field:destroy', this, this.destroy);
1372
+ return this;
1373
+ },
1374
+ reflow: function (fieldInstance) {
1375
+ // If this field has not an active UI (case for multiples) don't bother doing something
1376
+ if ('undefined' === typeof fieldInstance._ui || false === fieldInstance._ui.active)
1377
+ return;
1378
+ // Diff between two validation results
1379
+ var diff = this._diff(fieldInstance.validationResult, fieldInstance._ui.lastValidationResult);
1380
+ // Then store current validation result for next reflow
1381
+ fieldInstance._ui.lastValidationResult = fieldInstance.validationResult;
1382
+ // Field have been validated at least once if here. Useful for binded key events..
1383
+ fieldInstance._ui.validatedOnce = true;
1384
+ // Handle valid / invalid / none field class
1385
+ this.manageStatusClass(fieldInstance);
1386
+ // Add, remove, updated errors messages
1387
+ this.manageErrorsMessages(fieldInstance, diff);
1388
+ // Triggers impl
1389
+ this.actualizeTriggers(fieldInstance);
1390
+ // If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user
1391
+ if ((diff.kept.length || diff.added.length) && 'undefined' === typeof fieldInstance._ui.failedOnce)
1392
+ this.manageFailingFieldTrigger(fieldInstance);
1393
+ },
1394
+ // Returns an array of field's error message(s)
1395
+ getErrorsMessages: function (fieldInstance) {
1396
+ // No error message, field is valid
1397
+ if (true === fieldInstance.validationResult)
1398
+ return [];
1399
+ var messages = [];
1400
+ for (var i = 0; i < fieldInstance.validationResult.length; i++)
1401
+ messages.push(this._getErrorMessage(fieldInstance, fieldInstance.validationResult[i].assert));
1402
+ return messages;
1403
+ },
1404
+ manageStatusClass: function (fieldInstance) {
1405
+ if (true === fieldInstance.validationResult)
1406
+ this._successClass(fieldInstance);
1407
+ else if (fieldInstance.validationResult.length > 0)
1408
+ this._errorClass(fieldInstance);
1409
+ else
1410
+ this._resetClass(fieldInstance);
1411
+ },
1412
+ manageErrorsMessages: function (fieldInstance, diff) {
1413
+ if ('undefined' !== typeof fieldInstance.options.errorsMessagesDisabled)
1414
+ return;
1415
+ // Case where we have errorMessage option that configure an unique field error message, regardless failing validators
1416
+ if ('undefined' !== typeof fieldInstance.options.errorMessage) {
1417
+ if ((diff.added.length || diff.kept.length)) {
1418
+ if (0 === fieldInstance._ui.$errorsWrapper.find('.parsley-custom-error-message').length)
1419
+ fieldInstance._ui.$errorsWrapper
1420
+ .append($(fieldInstance.options.errorTemplate)
1421
+ .addClass('parsley-custom-error-message'));
1422
+ return fieldInstance._ui.$errorsWrapper
1423
+ .addClass('filled')
1424
+ .find('.parsley-custom-error-message')
1425
+ .html(fieldInstance.options.errorMessage);
1426
+ }
1427
+ return fieldInstance._ui.$errorsWrapper
1428
+ .removeClass('filled')
1429
+ .find('.parsley-custom-error-message')
1430
+ .remove();
1431
+ }
1432
+ // Show, hide, update failing constraints messages
1433
+ for (var i = 0; i < diff.removed.length; i++)
1434
+ this.removeError(fieldInstance, diff.removed[i].assert.name, true);
1435
+ for (i = 0; i < diff.added.length; i++)
1436
+ this.addError(fieldInstance, diff.added[i].assert.name, undefined, diff.added[i].assert, true);
1437
+ for (i = 0; i < diff.kept.length; i++)
1438
+ this.updateError(fieldInstance, diff.kept[i].assert.name, undefined, diff.kept[i].assert, true);
1439
+ },
1440
+ // TODO: strange API here, intuitive for manual usage with addError(pslyInstance, 'foo', 'bar')
1441
+ // but a little bit complex for above internal usage, with forced undefined parametter..
1442
+ addError: function (fieldInstance, name, message, assert, doNotUpdateClass) {
1443
+ fieldInstance._ui.$errorsWrapper
1444
+ .addClass('filled')
1445
+ .append($(fieldInstance.options.errorTemplate)
1446
+ .addClass('parsley-' + name)
1447
+ .html(message || this._getErrorMessage(fieldInstance, assert)));
1448
+ if (true !== doNotUpdateClass)
1449
+ this._errorClass(fieldInstance);
1450
+ },
1451
+ // Same as above
1452
+ updateError: function (fieldInstance, name, message, assert, doNotUpdateClass) {
1453
+ fieldInstance._ui.$errorsWrapper
1454
+ .addClass('filled')
1455
+ .find('.parsley-' + name)
1456
+ .html(message || this._getErrorMessage(fieldInstance, assert));
1457
+ if (true !== doNotUpdateClass)
1458
+ this._errorClass(fieldInstance);
1459
+ },
1460
+ // Same as above twice
1461
+ removeError: function (fieldInstance, name, doNotUpdateClass) {
1462
+ fieldInstance._ui.$errorsWrapper
1463
+ .removeClass('filled')
1464
+ .find('.parsley-' + name)
1465
+ .remove();
1466
+ // edge case possible here: remove a standard Parsley error that is still failing in fieldInstance.validationResult
1467
+ // but highly improbable cuz' manually removing a well Parsley handled error makes no sense.
1468
+ if (true !== doNotUpdateClass)
1469
+ this.manageStatusClass(fieldInstance);
1470
+ },
1471
+ focus: function (formInstance) {
1472
+ if (true === formInstance.validationResult || 'none' === formInstance.options.focus)
1473
+ return formInstance._focusedField = null;
1474
+ formInstance._focusedField = null;
1475
+ for (var i = 0; i < formInstance.fields.length; i++)
1476
+ if (true !== formInstance.fields[i].validationResult && formInstance.fields[i].validationResult.length > 0 && 'undefined' === typeof formInstance.fields[i].options.noFocus) {
1477
+ if ('first' === formInstance.options.focus) {
1478
+ formInstance._focusedField = formInstance.fields[i].$element;
1479
+ return formInstance._focusedField.focus();
1480
+ }
1481
+ formInstance._focusedField = formInstance.fields[i].$element;
1482
+ }
1483
+ if (null === formInstance._focusedField)
1484
+ return null;
1485
+ return formInstance._focusedField.focus();
1486
+ },
1487
+ _getErrorMessage: function (fieldInstance, constraint) {
1488
+ var customConstraintErrorMessage = constraint.name + 'Message';
1489
+ if ('undefined' !== typeof fieldInstance.options[customConstraintErrorMessage])
1490
+ return window.ParsleyValidator.formatMessage(fieldInstance.options[customConstraintErrorMessage], constraint.requirements);
1491
+ return window.ParsleyValidator.getErrorMessage(constraint);
1492
+ },
1493
+ _diff: function (newResult, oldResult, deep) {
1494
+ var
1495
+ added = [],
1496
+ kept = [];
1497
+ for (var i = 0; i < newResult.length; i++) {
1498
+ var found = false;
1499
+ for (var j = 0; j < oldResult.length; j++)
1500
+ if (newResult[i].assert.name === oldResult[j].assert.name) {
1501
+ found = true;
1502
+ break;
1503
+ }
1504
+ if (found)
1505
+ kept.push(newResult[i]);
1506
+ else
1507
+ added.push(newResult[i]);
1508
+ }
1509
+ return {
1510
+ kept: kept,
1511
+ added: added,
1512
+ removed: !deep ? this._diff(oldResult, newResult, true).added : []
1513
+ };
1514
+ },
1515
+ setupForm: function (formInstance) {
1516
+ formInstance.$element.on('submit.Parsley', false, $.proxy(formInstance.onSubmitValidate, formInstance));
1517
+ // UI could be disabled
1518
+ if (false === formInstance.options.uiEnabled)
1519
+ return;
1520
+ formInstance.$element.attr('novalidate', '');
1521
+ },
1522
+ setupField: function (fieldInstance) {
1523
+ var _ui = { active: false };
1524
+ // UI could be disabled
1525
+ if (false === fieldInstance.options.uiEnabled)
1526
+ return;
1527
+ _ui.active = true;
1528
+ // Give field its Parsley id in DOM
1529
+ fieldInstance.$element.attr(fieldInstance.options.namespace + 'id', fieldInstance.__id__);
1530
+ /** Generate important UI elements and store them in fieldInstance **/
1531
+ // $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
1532
+ _ui.$errorClassHandler = this._manageClassHandler(fieldInstance);
1533
+ // $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
1534
+ _ui.errorsWrapperId = 'parsley-id-' + ('undefined' !== typeof fieldInstance.options.multiple ? 'multiple-' + fieldInstance.options.multiple : fieldInstance.__id__);
1535
+ _ui.$errorsWrapper = $(fieldInstance.options.errorsWrapper).attr('id', _ui.errorsWrapperId);
1536
+ // ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
1537
+ _ui.lastValidationResult = [];
1538
+ _ui.validatedOnce = false;
1539
+ _ui.validationInformationVisible = false;
1540
+ // Store it in fieldInstance for later
1541
+ fieldInstance._ui = _ui;
1542
+ /** Mess with DOM now **/
1543
+ this._insertErrorWrapper(fieldInstance);
1544
+ // Bind triggers first time
1545
+ this.actualizeTriggers(fieldInstance);
1546
+ },
1547
+ // Determine which element will have `parsley-error` and `parsley-success` classes
1548
+ _manageClassHandler: function (fieldInstance) {
1549
+ // An element selector could be passed through DOM with `data-parsley-class-handler=#foo`
1550
+ if ('string' === typeof fieldInstance.options.classHandler && $(fieldInstance.options.classHandler).length)
1551
+ return $(fieldInstance.options.classHandler);
1552
+ // Class handled could also be determined by function given in Parsley options
1553
+ var $handler = fieldInstance.options.classHandler(fieldInstance);
1554
+ // If this function returned a valid existing DOM element, go for it
1555
+ if ('undefined' !== typeof $handler && $handler.length)
1556
+ return $handler;
1557
+ // Otherwise, if simple element (input, texatrea, select..) it will perfectly host the classes
1558
+ if ('undefined' === typeof fieldInstance.options.multiple || fieldInstance.$element.is('select'))
1559
+ return fieldInstance.$element;
1560
+ // But if multiple element (radio, checkbox), that would be their parent
1561
+ return fieldInstance.$element.parent();
1562
+ },
1563
+ _insertErrorWrapper: function (fieldInstance) {
1564
+ var $errorsContainer;
1565
+ if ('string' === typeof fieldInstance.options.errorsContainer )
1566
+ if ($(fieldInstance.options.errorsContainer + '').length)
1567
+ return $(fieldInstance.options.errorsContainer).append(fieldInstance._ui.$errorsWrapper);
1568
+ else if (window.console && window.console.warn)
1569
+ window.console.warn('The errors container `' + fieldInstance.options.errorsContainer + '` does not exist in DOM');
1570
+
1571
+ if ('function' === typeof fieldInstance.options.errorsContainer)
1572
+ $errorsContainer = fieldInstance.options.errorsContainer(fieldInstance);
1573
+ if ('undefined' !== typeof $errorsContainer && $errorsContainer.length)
1574
+ return $errorsContainer.append(fieldInstance._ui.$errorsWrapper);
1575
+ return 'undefined' === typeof fieldInstance.options.multiple ? fieldInstance.$element.after(fieldInstance._ui.$errorsWrapper) : fieldInstance.$element.parent().after(fieldInstance._ui.$errorsWrapper);
1576
+ },
1577
+ actualizeTriggers: function (fieldInstance) {
1578
+ var that = this;
1579
+ // Remove Parsley events already binded on this field
1580
+ if (fieldInstance.options.multiple)
1581
+ $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () {
1582
+ $(this).off('.Parsley');
1583
+ });
1584
+ else
1585
+ fieldInstance.$element.off('.Parsley');
1586
+ // If no trigger is set, all good
1587
+ if (false === fieldInstance.options.trigger)
1588
+ return;
1589
+ var triggers = fieldInstance.options.trigger.replace(/^\s+/g , '').replace(/\s+$/g , '');
1590
+ if ('' === triggers)
1591
+ return;
1592
+ // Bind fieldInstance.eventValidate if exists (for parsley.ajax for example), ParsleyUI.eventValidate otherwise
1593
+ if (fieldInstance.options.multiple)
1594
+ $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () {
1595
+ $(this).on(
1596
+ triggers.split(' ').join('.Parsley ') + '.Parsley',
1597
+ false,
1598
+ $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : that.eventValidate, fieldInstance));
1599
+ });
1600
+ else
1601
+ fieldInstance.$element
1602
+ .on(
1603
+ triggers.split(' ').join('.Parsley ') + '.Parsley',
1604
+ false,
1605
+ $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : this.eventValidate, fieldInstance));
1606
+ },
1607
+ // Called through $.proxy with fieldInstance. `this` context is ParsleyField
1608
+ eventValidate: function(event) {
1609
+ // For keyup, keypress, keydown.. events that could be a little bit obstrusive
1610
+ // do not validate if val length < min threshold on first validation. Once field have been validated once and info
1611
+ // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
1612
+ if (new RegExp('key').test(event.type))
1613
+ if (!this._ui.validationInformationVisible && this.getValue().length <= this.options.validationThreshold)
1614
+ return;
1615
+ this._ui.validatedOnce = true;
1616
+ this.validate();
1617
+ },
1618
+ manageFailingFieldTrigger: function (fieldInstance) {
1619
+ fieldInstance._ui.failedOnce = true;
1620
+ // Radio and checkboxes fields must bind every field multiple
1621
+ if (fieldInstance.options.multiple)
1622
+ $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () {
1623
+ if (!new RegExp('change', 'i').test($(this).parsley().options.trigger || ''))
1624
+ return $(this).on('change.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance));
1625
+ });
1626
+ // Select case
1627
+ if (fieldInstance.$element.is('select'))
1628
+ if (!new RegExp('change', 'i').test(fieldInstance.options.trigger || ''))
1629
+ return fieldInstance.$element.on('change.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance));
1630
+ // All other inputs fields
1631
+ if (!new RegExp('keyup', 'i').test(fieldInstance.options.trigger || ''))
1632
+ return fieldInstance.$element.on('keyup.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance));
1633
+ },
1634
+ reset: function (parsleyInstance) {
1635
+ // Reset all event listeners
1636
+ parsleyInstance.$element.off('.Parsley');
1637
+ parsleyInstance.$element.off('.ParsleyFailedOnce');
1638
+ // Nothing to do if UI never initialized for this field
1639
+ if ('undefined' === typeof parsleyInstance._ui)
1640
+ return;
1641
+ if ('ParsleyForm' === parsleyInstance.__class__)
1642
+ return;
1643
+ // Reset all errors' li
1644
+ parsleyInstance._ui.$errorsWrapper.children().each(function () {
1645
+ $(this).remove();
1646
+ });
1647
+ // Reset validation class
1648
+ this._resetClass(parsleyInstance);
1649
+ // Reset validation flags and last validation result
1650
+ parsleyInstance._ui.validatedOnce = false;
1651
+ parsleyInstance._ui.lastValidationResult = [];
1652
+ parsleyInstance._ui.validationInformationVisible = false;
1653
+ },
1654
+ destroy: function (parsleyInstance) {
1655
+ this.reset(parsleyInstance);
1656
+ if ('ParsleyForm' === parsleyInstance.__class__)
1657
+ return;
1658
+ parsleyInstance._ui.$errorsWrapper.remove();
1659
+ delete parsleyInstance._ui;
1660
+ },
1661
+ _successClass: function (fieldInstance) {
1662
+ fieldInstance._ui.validationInformationVisible = true;
1663
+ fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.errorClass).addClass(fieldInstance.options.successClass);
1664
+ },
1665
+ _errorClass: function (fieldInstance) {
1666
+ fieldInstance._ui.validationInformationVisible = true;
1667
+ fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).addClass(fieldInstance.options.errorClass);
1668
+ },
1669
+ _resetClass: function (fieldInstance) {
1670
+ fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).removeClass(fieldInstance.options.errorClass);
1671
+ }
1672
+ };
1673
+
1674
+ var ParsleyOptionsFactory = function (defaultOptions, globalOptions, userOptions, namespace) {
1675
+ this.__class__ = 'OptionsFactory';
1676
+ this.__id__ = ParsleyUtils.hash(4);
1677
+ this.formOptions = null;
1678
+ this.fieldOptions = null;
1679
+ this.staticOptions = $.extend(true, {}, defaultOptions, globalOptions, userOptions, { namespace: namespace });
1680
+ };
1681
+ ParsleyOptionsFactory.prototype = {
1682
+ get: function (parsleyInstance) {
1683
+ if ('undefined' === typeof parsleyInstance.__class__)
1684
+ throw new Error('Parsley Instance expected');
1685
+ switch (parsleyInstance.__class__) {
1686
+ case 'Parsley':
1687
+ return this.staticOptions;
1688
+ case 'ParsleyForm':
1689
+ return this.getFormOptions(parsleyInstance);
1690
+ case 'ParsleyField':
1691
+ case 'ParsleyFieldMultiple':
1692
+ return this.getFieldOptions(parsleyInstance);
1693
+ default:
1694
+ throw new Error('Instance ' + parsleyInstance.__class__ + ' is not supported');
1695
+ }
1696
+ },
1697
+ getFormOptions: function (formInstance) {
1698
+ this.formOptions = ParsleyUtils.attr(formInstance.$element, this.staticOptions.namespace);
1699
+ // not deep extend, since formOptions is a 1 level deep object
1700
+ return $.extend({}, this.staticOptions, this.formOptions);
1701
+ },
1702
+ getFieldOptions: function (fieldInstance) {
1703
+ this.fieldOptions = ParsleyUtils.attr(fieldInstance.$element, this.staticOptions.namespace);
1704
+ if (null === this.formOptions && 'undefined' !== typeof fieldInstance.parent)
1705
+ this.formOptions = this.getFormOptions(fieldInstance.parent);
1706
+ // not deep extend, since formOptions and fieldOptions is a 1 level deep object
1707
+ return $.extend({}, this.staticOptions, this.formOptions, this.fieldOptions);
1708
+ }
1709
+ };
1710
+
1711
+ var ParsleyForm = function (element, OptionsFactory) {
1712
+ this.__class__ = 'ParsleyForm';
1713
+ this.__id__ = ParsleyUtils.hash(4);
1714
+ if ('OptionsFactory' !== ParsleyUtils.get(OptionsFactory, '__class__'))
1715
+ throw new Error('You must give an OptionsFactory instance');
1716
+ this.OptionsFactory = OptionsFactory;
1717
+ this.$element = $(element);
1718
+ this.validationResult = null;
1719
+ this.options = this.OptionsFactory.get(this);
1720
+ };
1721
+ ParsleyForm.prototype = {
1722
+ onSubmitValidate: function (event) {
1723
+ this.validate(undefined, undefined, event);
1724
+ // prevent form submission if validation fails
1725
+ if (false === this.validationResult && event instanceof $.Event) {
1726
+ event.stopImmediatePropagation();
1727
+ event.preventDefault();
1728
+ }
1729
+ return this;
1730
+ },
1731
+ // @returns boolean
1732
+ validate: function (group, force, event) {
1733
+ this.submitEvent = event;
1734
+ this.validationResult = true;
1735
+ var fieldValidationResult = [];
1736
+ // Refresh form DOM options and form's fields that could have changed
1737
+ this._refreshFields();
1738
+ $.emit('parsley:form:validate', this);
1739
+ // loop through fields to validate them one by one
1740
+ for (var i = 0; i < this.fields.length; i++) {
1741
+ // do not validate a field if not the same as given validation group
1742
+ if (group && group !== this.fields[i].options.group)
1743
+ continue;
1744
+ fieldValidationResult = this.fields[i].validate(force);
1745
+ if (true !== fieldValidationResult && fieldValidationResult.length > 0 && this.validationResult)
1746
+ this.validationResult = false;
1747
+ }
1748
+ $.emit('parsley:form:validated', this);
1749
+ return this.validationResult;
1750
+ },
1751
+ // Iterate over refreshed fields, and stop on first failure
1752
+ isValid: function (group, force) {
1753
+ this._refreshFields();
1754
+ for (var i = 0; i < this.fields.length; i++) {
1755
+ // do not validate a field if not the same as given validation group
1756
+ if (group && group !== this.fields[i].options.group)
1757
+ continue;
1758
+ if (false === this.fields[i].isValid(force))
1759
+ return false;
1760
+ }
1761
+ return true;
1762
+ },
1763
+ _refreshFields: function () {
1764
+ return this.actualizeOptions()._bindFields();
1765
+ },
1766
+ _bindFields: function () {
1767
+ var self = this;
1768
+ this.fields = [];
1769
+ this.fieldsMappedById = {};
1770
+ this.$element.find(this.options.inputs).each(function () {
1771
+ var fieldInstance = new window.Parsley(this, {}, self);
1772
+ // Only add valid and not excluded `ParsleyField` and `ParsleyFieldMultiple` children
1773
+ if (('ParsleyField' === fieldInstance.__class__ || 'ParsleyFieldMultiple' === fieldInstance.__class__) && !fieldInstance.$element.is(fieldInstance.options.excluded))
1774
+ if ('undefined' === typeof self.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__]) {
1775
+ self.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__] = fieldInstance;
1776
+ self.fields.push(fieldInstance);
1777
+ }
1778
+ });
1779
+ return this;
1780
+ }
1781
+ };
1782
+
1783
+ var ConstraintFactory = function (parsleyField, name, requirements, priority, isDomConstraint) {
1784
+ if (!new RegExp('ParsleyField').test(ParsleyUtils.get(parsleyField, '__class__')))
1785
+ throw new Error('ParsleyField or ParsleyFieldMultiple instance expected');
1786
+ if ('function' !== typeof window.ParsleyValidator.validators[name] &&
1787
+ 'Assert' !== window.ParsleyValidator.validators[name](requirements).__parentClass__)
1788
+ throw new Error('Valid validator expected');
1789
+ var getPriority = function (parsleyField, name) {
1790
+ if ('undefined' !== typeof parsleyField.options[name + 'Priority'])
1791
+ return parsleyField.options[name + 'Priority'];
1792
+ return ParsleyUtils.get(window.ParsleyValidator.validators[name](requirements), 'priority') || 2;
1793
+ };
1794
+ priority = priority || getPriority(parsleyField, name);
1795
+ // If validator have a requirementsTransformer, execute it
1796
+ if ('function' === typeof window.ParsleyValidator.validators[name](requirements).requirementsTransformer)
1797
+ requirements = window.ParsleyValidator.validators[name](requirements).requirementsTransformer();
1798
+ return $.extend(window.ParsleyValidator.validators[name](requirements), {
1799
+ name: name,
1800
+ requirements: requirements,
1801
+ priority: priority,
1802
+ groups: [priority],
1803
+ isDomConstraint: isDomConstraint || ParsleyUtils.attr(parsleyField.$element, parsleyField.options.namespace, name)
1804
+ });
1805
+ };
1806
+
1807
+ var ParsleyField = function (field, OptionsFactory, parsleyFormInstance) {
1808
+ this.__class__ = 'ParsleyField';
1809
+ this.__id__ = ParsleyUtils.hash(4);
1810
+ this.$element = $(field);
1811
+ // If we have a parent `ParsleyForm` instance given, use its `OptionsFactory`, and save parent
1812
+ if ('undefined' !== typeof parsleyFormInstance) {
1813
+ this.parent = parsleyFormInstance;
1814
+ this.OptionsFactory = this.parent.OptionsFactory;
1815
+ this.options = this.OptionsFactory.get(this);
1816
+ // Else, take the `Parsley` one
1817
+ } else {
1818
+ this.OptionsFactory = OptionsFactory;
1819
+ this.options = this.OptionsFactory.get(this);
1820
+ }
1821
+ // Initialize some properties
1822
+ this.constraints = [];
1823
+ this.constraintsByName = {};
1824
+ this.validationResult = [];
1825
+ // Bind constraints
1826
+ this._bindConstraints();
1827
+ };
1828
+ ParsleyField.prototype = {
1829
+ // # Public API
1830
+ // Validate field and $.emit some events for mainly `ParsleyUI`
1831
+ // @returns validationResult:
1832
+ // - `true` if all constraint passes
1833
+ // - `[]` if not required field and empty (not validated)
1834
+ // - `[Violation, [Violation..]]` if there were validation errors
1835
+ validate: function (force) {
1836
+ this.value = this.getValue();
1837
+ // Field Validate event. `this.value` could be altered for custom needs
1838
+ $.emit('parsley:field:validate', this);
1839
+ $.emit('parsley:field:' + (this.isValid(force, this.value) ? 'success' : 'error'), this);
1840
+ // Field validated event. `this.validationResult` could be altered for custom needs too
1841
+ $.emit('parsley:field:validated', this);
1842
+ return this.validationResult;
1843
+ },
1844
+ // Just validate field. Do not trigger any event
1845
+ // Same @return as `validate()`
1846
+ isValid: function (force, value) {
1847
+ // Recompute options and rebind constraints to have latest changes
1848
+ this.refreshConstraints();
1849
+ // Sort priorities to validate more important first
1850
+ var priorities = this._getConstraintsSortedPriorities();
1851
+ // Value could be passed as argument, needed to add more power to 'parsley:field:validate'
1852
+ value = value || this.getValue();
1853
+ // If a field is empty and not required, leave it alone, it's just fine
1854
+ // Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators
1855
+ if (0 === value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty && true !== force)
1856
+ return this.validationResult = [];
1857
+ // If we want to validate field against all constraints, just call Validator and let it do the job
1858
+ if (false === this.options.priorityEnabled)
1859
+ return true === (this.validationResult = this.validateThroughValidator(value, this.constraints, 'Any'));
1860
+ // Else, iterate over priorities one by one, and validate related asserts one by one
1861
+ for (var i = 0; i < priorities.length; i++)
1862
+ if (true !== (this.validationResult = this.validateThroughValidator(value, this.constraints, priorities[i])))
1863
+ return false;
1864
+ return true;
1865
+ },
1866
+ // @returns Parsley field computed value that could be overrided or configured in DOM
1867
+ getValue: function () {
1868
+ var value;
1869
+ // Value could be overriden in DOM
1870
+ if ('undefined' !== typeof this.options.value)
1871
+ value = this.options.value;
1872
+ else
1873
+ value = this.$element.val();
1874
+ // Handle wrong DOM or configurations
1875
+ if ('undefined' === typeof value || null === value)
1876
+ return '';
1877
+ // Use `data-parsley-trim-value="true"` to auto trim inputs entry
1878
+ if (true === this.options.trimValue)
1879
+ return value.replace(/^\s+|\s+$/g, '');
1880
+ return value;
1881
+ },
1882
+ // Actualize options that could have change since previous validation
1883
+ // Re-bind accordingly constraints (could be some new, removed or updated)
1884
+ refreshConstraints: function () {
1885
+ return this.actualizeOptions()._bindConstraints();
1886
+ },
1887
+ /**
1888
+ * Add a new constraint to a field
1889
+ *
1890
+ * @method addConstraint
1891
+ * @param {String} name
1892
+ * @param {Mixed} requirements optional
1893
+ * @param {Number} priority optional
1894
+ * @param {Boolean} isDomConstraint optional
1895
+ */
1896
+ addConstraint: function (name, requirements, priority, isDomConstraint) {
1897
+ name = name.toLowerCase();
1898
+ if ('function' === typeof window.ParsleyValidator.validators[name]) {
1899
+ var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint);
1900
+ // if constraint already exist, delete it and push new version
1901
+ if ('undefined' !== this.constraintsByName[constraint.name])
1902
+ this.removeConstraint(constraint.name);
1903
+ this.constraints.push(constraint);
1904
+ this.constraintsByName[constraint.name] = constraint;
1905
+ }
1906
+ return this;
1907
+ },
1908
+ // Remove a constraint
1909
+ removeConstraint: function (name) {
1910
+ for (var i = 0; i < this.constraints.length; i++)
1911
+ if (name === this.constraints[i].name) {
1912
+ this.constraints.splice(i, 1);
1913
+ break;
1914
+ }
1915
+ return this;
1916
+ },
1917
+ // Update a constraint (Remove + re-add)
1918
+ updateConstraint: function (name, parameters, priority) {
1919
+ return this.removeConstraint(name)
1920
+ .addConstraint(name, parameters, priority);
1921
+ },
1922
+ // # Internals
1923
+ // Internal only.
1924
+ // Bind constraints from config + options + DOM
1925
+ _bindConstraints: function () {
1926
+ var constraints = [];
1927
+ // clean all existing DOM constraints to only keep javascript user constraints
1928
+ for (var i = 0; i < this.constraints.length; i++)
1929
+ if (false === this.constraints[i].isDomConstraint)
1930
+ constraints.push(this.constraints[i]);
1931
+ this.constraints = constraints;
1932
+ // then re-add Parsley DOM-API constraints
1933
+ for (var name in this.options)
1934
+ this.addConstraint(name, this.options[name]);
1935
+ // finally, bind special HTML5 constraints
1936
+ return this._bindHtml5Constraints();
1937
+ },
1938
+ // Internal only.
1939
+ // Bind specific HTML5 constraints to be HTML5 compliant
1940
+ _bindHtml5Constraints: function () {
1941
+ // html5 required
1942
+ if (this.$element.hasClass('required') || this.$element.attr('required'))
1943
+ this.addConstraint('required', true, undefined, true);
1944
+ // html5 pattern
1945
+ if ('string' === typeof this.$element.attr('pattern'))
1946
+ this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true);
1947
+ // range
1948
+ if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max'))
1949
+ this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true);
1950
+ // HTML5 min
1951
+ else if ('undefined' !== typeof this.$element.attr('min'))
1952
+ this.addConstraint('min', this.$element.attr('min'), undefined, true);
1953
+ // HTML5 max
1954
+ else if ('undefined' !== typeof this.$element.attr('max'))
1955
+ this.addConstraint('max', this.$element.attr('max'), undefined, true);
1956
+ // html5 types
1957
+ var type = this.$element.attr('type');
1958
+ if ('undefined' === typeof type)
1959
+ return this;
1960
+ // Small special case here for HTML5 number, that is in fact an integer validator
1961
+ if ('number' === type)
1962
+ return this.addConstraint('type', 'integer', undefined, true);
1963
+ // Regular other HTML5 supported types
1964
+ else if (new RegExp(type, 'i').test('email url range'))
1965
+ return this.addConstraint('type', type, undefined, true);
1966
+ return this;
1967
+ },
1968
+ // Internal only.
1969
+ // Field is required if have required constraint without `false` value
1970
+ _isRequired: function () {
1971
+ if ('undefined' === typeof this.constraintsByName.required)
1972
+ return false;
1973
+ return false !== this.constraintsByName.required.requirements;
1974
+ },
1975
+ // Internal only.
1976
+ // Sort constraints by priority DESC
1977
+ _getConstraintsSortedPriorities: function () {
1978
+ var priorities = [];
1979
+ // Create array unique of priorities
1980
+ for (var i = 0; i < this.constraints.length; i++)
1981
+ if (-1 === priorities.indexOf(this.constraints[i].priority))
1982
+ priorities.push(this.constraints[i].priority);
1983
+ // Sort them by priority DESC
1984
+ priorities.sort(function (a, b) { return b - a; });
1985
+ return priorities;
1986
+ }
1987
+ };
1988
+
1989
+ var ParsleyMultiple = function () {
1990
+ this.__class__ = 'ParsleyFieldMultiple';
1991
+ };
1992
+ ParsleyMultiple.prototype = {
1993
+ // Add new `$element` sibling for multiple field
1994
+ addElement: function ($element) {
1995
+ this.$elements.push($element);
1996
+ return this;
1997
+ },
1998
+ // See `ParsleyField.refreshConstraints()`
1999
+ refreshConstraints: function () {
2000
+ var fieldConstraints;
2001
+ this.constraints = [];
2002
+ // Select multiple special treatment
2003
+ if (this.$element.is('select')) {
2004
+ this.actualizeOptions()._bindConstraints();
2005
+ return this;
2006
+ }
2007
+ // Gather all constraints for each input in the multiple group
2008
+ for (var i = 0; i < this.$elements.length; i++) {
2009
+ fieldConstraints = this.$elements[i].data('ParsleyFieldMultiple').refreshConstraints().constraints;
2010
+ for (var j = 0; j < fieldConstraints.length; j++)
2011
+ this.addConstraint(fieldConstraints[j].name, fieldConstraints[j].requirements, fieldConstraints[j].priority, fieldConstraints[j].isDomConstraint);
2012
+ }
2013
+ return this;
2014
+ },
2015
+ // See `ParsleyField.getValue()`
2016
+ getValue: function () {
2017
+ // Value could be overriden in DOM
2018
+ if ('undefined' !== typeof this.options.value)
2019
+ return this.options.value;
2020
+ // Radio input case
2021
+ if (this.$element.is('input[type=radio]'))
2022
+ return $('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]:checked').val() || '';
2023
+ // checkbox input case
2024
+ if (this.$element.is('input[type=checkbox]')) {
2025
+ var values = [];
2026
+ $('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]:checked').each(function () {
2027
+ values.push($(this).val());
2028
+ });
2029
+ return values.length ? values : [];
2030
+ }
2031
+ // Select multiple case
2032
+ if (this.$element.is('select') && null === this.$element.val())
2033
+ return [];
2034
+ // Default case that should never happen
2035
+ return this.$element.val();
2036
+ },
2037
+ _init: function (multiple) {
2038
+ this.$elements = [this.$element];
2039
+ this.options.multiple = multiple;
2040
+ return this;
2041
+ }
2042
+ };
2043
+
2044
+ var
2045
+ o = $({}),
2046
+ subscribed = {};
2047
+ // $.listen(name, callback);
2048
+ // $.listen(name, context, callback);
2049
+ $.listen = function (name) {
2050
+ if ('undefined' === typeof subscribed[name])
2051
+ subscribed[name] = [];
2052
+ if ('function' === typeof arguments[1])
2053
+ return subscribed[name].push({ fn: arguments[1] });
2054
+ if ('object' === typeof arguments[1] && 'function' === typeof arguments[2])
2055
+ return subscribed[name].push({ fn: arguments[2], ctxt: arguments[1] });
2056
+ throw new Error('Wrong parameters');
2057
+ };
2058
+ $.listenTo = function (instance, name, fn) {
2059
+ if ('undefined' === typeof subscribed[name])
2060
+ subscribed[name] = [];
2061
+ if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm))
2062
+ throw new Error('Must give Parsley instance');
2063
+ if ('string' !== typeof name || 'function' !== typeof fn)
2064
+ throw new Error('Wrong parameters');
2065
+ subscribed[name].push({ instance: instance, fn: fn });
2066
+ };
2067
+ $.unsubscribe = function (name, fn) {
2068
+ if ('undefined' === typeof subscribed[name])
2069
+ return;
2070
+ if ('string' !== typeof name || 'function' !== typeof fn)
2071
+ throw new Error('Wrong arguments');
2072
+ for (var i = 0; i < subscribed[name].length; i++)
2073
+ if (subscribed[name][i].fn === fn)
2074
+ return subscribed[name].splice(i, 1);
2075
+ };
2076
+ $.unsubscribeTo = function (instance, name) {
2077
+ if ('undefined' === typeof subscribed[name])
2078
+ return;
2079
+ if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm))
2080
+ throw new Error('Must give Parsley instance');
2081
+ for (var i = 0; i < subscribed[name].length; i++)
2082
+ if ('undefined' !== typeof subscribed[name][i].instance && subscribed[name][i].instance.__id__ === instance.__id__)
2083
+ return subscribed[name].splice(i, 1);
2084
+ };
2085
+ $.unsubscribeAll = function (name) {
2086
+ if ('undefined' === typeof subscribed[name])
2087
+ return;
2088
+ delete subscribed[name];
2089
+ };
2090
+ // $.emit(name [, arguments...]);
2091
+ // $.emit(name, instance [, arguments..]);
2092
+ $.emit = function (name, instance) {
2093
+ if ('undefined' === typeof subscribed[name])
2094
+ return;
2095
+ // loop through registered callbacks for this event
2096
+ for (var i = 0; i < subscribed[name].length; i++) {
2097
+ // if instance is not registered, simple emit
2098
+ if ('undefined' === typeof subscribed[name][i].instance) {
2099
+ subscribed[name][i].fn.apply('undefined' !== typeof subscribed[name][i].ctxt ? subscribed[name][i].ctxt : o, Array.prototype.slice.call(arguments, 1));
2100
+ continue;
2101
+ }
2102
+ // if instance registered but no instance given for the emit, continue
2103
+ if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm))
2104
+ continue;
2105
+ // if instance is registered and same id, emit
2106
+ if (subscribed[name][i].instance.__id__ === instance.__id__) {
2107
+ subscribed[name][i].fn.apply(o, Array.prototype.slice.call(arguments, 1));
2108
+ continue;
2109
+ }
2110
+ // if registered instance is a Form and fired one is a Field, loop over all its fields and emit if field found
2111
+ if (subscribed[name][i].instance instanceof ParsleyForm && instance instanceof ParsleyField)
2112
+ for (var j = 0; j < subscribed[name][i].instance.fields.length; j++)
2113
+ if (subscribed[name][i].instance.fields[j].__id__ === instance.__id__) {
2114
+ subscribed[name][i].fn.apply(o, Array.prototype.slice.call(arguments, 1));
2115
+ continue;
2116
+ }
2117
+ }
2118
+ };
2119
+ $.subscribed = function () { return subscribed; };
2120
+
2121
+ // ParsleyConfig definition if not already set
2122
+ window.ParsleyConfig = window.ParsleyConfig || {};
2123
+ window.ParsleyConfig.i18n = window.ParsleyConfig.i18n || {};
2124
+ // Define then the messages
2125
+ window.ParsleyConfig.i18n.en = $.extend(window.ParsleyConfig.i18n.en || {}, {
2126
+ defaultMessage: "This value seems to be invalid.",
2127
+ type: {
2128
+ email: "This value should be a valid email.",
2129
+ url: "This value should be a valid url.",
2130
+ number: "This value should be a valid number.",
2131
+ integer: "This value should be a valid integer.",
2132
+ digits: "This value should be digits.",
2133
+ alphanum: "This value should be alphanumeric."
2134
+ },
2135
+ notblank: "This value should not be blank.",
2136
+ required: "This value is required.",
2137
+ pattern: "This value seems to be invalid.",
2138
+ min: "This value should be greater than or equal to %s.",
2139
+ max: "This value should be lower than or equal to %s.",
2140
+ range: "This value should be between %s and %s.",
2141
+ minlength: "This value is too short. It should have %s characters or more.",
2142
+ maxlength: "This value is too long. It should have %s characters or less.",
2143
+ length: "This value length is invalid. It should be between %s and %s characters long.",
2144
+ mincheck: "You must select at least %s choices.",
2145
+ maxcheck: "You must select %s choices or less.",
2146
+ check: "You must select between %s and %s choices.",
2147
+ equalto: "This value should be the same."
2148
+ });
2149
+ // If file is loaded after Parsley main file, auto-load locale
2150
+ if ('undefined' !== typeof window.ParsleyValidator)
2151
+ window.ParsleyValidator.addCatalog('en', window.ParsleyConfig.i18n.en, true);
2152
+
2153
+ // Parsley.js 2.0.0
2154
+ // http://parsleyjs.org
2155
+ // (c) 20012-2014 Guillaume Potier, Wisembly
2156
+ // Parsley may be freely distributed under the MIT license.
2157
+
2158
+ // ### Parsley factory
2159
+ var Parsley = function (element, options, parsleyFormInstance) {
2160
+ this.__class__ = 'Parsley';
2161
+ this.__version__ = '2.0.0';
2162
+ this.__id__ = ParsleyUtils.hash(4);
2163
+ // Parsley must be instanciated with a DOM element or jQuery $element
2164
+ if ('undefined' === typeof element)
2165
+ throw new Error('You must give an element');
2166
+ if ('undefined' !== typeof parsleyFormInstance && 'ParsleyForm' !== parsleyFormInstance.__class__)
2167
+ throw new Error('Parent instance must be a ParsleyForm instance');
2168
+ return this.init($(element), options, parsleyFormInstance);
2169
+ };
2170
+ Parsley.prototype = {
2171
+ init: function ($element, options, parsleyFormInstance) {
2172
+ if (!$element.length)
2173
+ throw new Error('You must bind Parsley on an existing element.');
2174
+ this.$element = $element;
2175
+ // If element have already been binded, returns its saved Parsley instance
2176
+ if (this.$element.data('Parsley')) {
2177
+ var savedparsleyFormInstance = this.$element.data('Parsley');
2178
+ // If saved instance have been binded without a ParsleyForm parent and there is one given in this call, add it
2179
+ if ('undefined' !== typeof parsleyFormInstance)
2180
+ savedparsleyFormInstance.parent = parsleyFormInstance;
2181
+ return savedparsleyFormInstance;
2182
+ }
2183
+ // Handle 'static' options
2184
+ this.OptionsFactory = new ParsleyOptionsFactory(ParsleyDefaults, ParsleyUtils.get(window, 'ParsleyConfig') || {}, options, this.getNamespace(options));
2185
+ this.options = this.OptionsFactory.get(this);
2186
+ // A ParsleyForm instance is obviously a `<form>` elem but also every node that is not an input and have `data-parsley-validate` attribute
2187
+ if (this.$element.is('form') || (ParsleyUtils.attr(this.$element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs)))
2188
+ return this.bind('parsleyForm');
2189
+ // Every other supported element and not excluded element is binded as a `ParsleyField` or `ParsleyFieldMultiple`
2190
+ else if (this.$element.is(this.options.inputs) && !this.$element.is(this.options.excluded))
2191
+ return this.isMultiple() ? this.handleMultiple(parsleyFormInstance) : this.bind('parsleyField', parsleyFormInstance);
2192
+ return this;
2193
+ },
2194
+ isMultiple: function () {
2195
+ return (this.$element.is('input[type=radio], input[type=checkbox]') && 'undefined' === typeof this.options.multiple) || (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple'));
2196
+ },
2197
+ // Multiples fields are a real nightmare :(
2198
+ // Maybe some refacto would be appreciated here..
2199
+ handleMultiple: function (parsleyFormInstance) {
2200
+ var
2201
+ that = this,
2202
+ name,
2203
+ multiple,
2204
+ parsleyMultipleInstance;
2205
+ // Get parsleyFormInstance options if exist, mixed with element attributes
2206
+ this.options = $.extend(this.options, parsleyFormInstance ? parsleyFormInstance.OptionsFactory.get(parsleyFormInstance) : {}, ParsleyUtils.attr(this.$element, this.options.namespace));
2207
+ // Handle multiple name
2208
+ if (this.options.multiple) {
2209
+ multiple = this.options.multiple;
2210
+ } else if ('undefined' !== typeof this.$element.attr('name') && this.$element.attr('name').length) {
2211
+ multiple = name = this.$element.attr('name');
2212
+ } else if ('undefined' !== typeof this.$element.attr('id') && this.$element.attr('id').length) {
2213
+ multiple = this.$element.attr('id');
2214
+ }
2215
+ // Special select multiple input
2216
+ if (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple')) {
2217
+ return this.bind('parsleyFieldMultiple', parsleyFormInstance, multiple || this.__id__);
2218
+ // Else for radio / checkboxes, we need a `name` or `data-parsley-multiple` to properly bind it
2219
+ } else if ('undefined' === typeof multiple) {
2220
+ if (window.console && window.console.warn)
2221
+ window.console.warn('To be binded by Parsley, a radio, a checkbox and a multiple select input must have either a name or a multiple option.', this.$element);
2222
+ return this;
2223
+ }
2224
+ // Remove special chars
2225
+ multiple = multiple.replace(/(:|\.|\[|\]|\$)/g, '');
2226
+ // Add proper `data-parsley-multiple` to siblings if we had a name
2227
+ if ('undefined' !== typeof name)
2228
+ $('input[name="' + name + '"]').each(function () {
2229
+ if ($(this).is('input[type=radio], input[type=checkbox]'))
2230
+ $(this).attr(that.options.namespace + 'multiple', multiple);
2231
+ });
2232
+ // Check here if we don't already have a related multiple instance saved
2233
+ if ($('[' + this.options.namespace + 'multiple=' + multiple +']').length)
2234
+ for (var i = 0; i < $('[' + this.options.namespace + 'multiple=' + multiple +']').length; i++)
2235
+ if ('undefined' !== typeof $($('[' + this.options.namespace + 'multiple=' + multiple +']').get(i)).data('Parsley')) {
2236
+ parsleyMultipleInstance = $($('[' + this.options.namespace + 'multiple=' + multiple +']').get(i)).data('Parsley');
2237
+ if (!this.$element.data('ParsleyFieldMultiple')) {
2238
+ parsleyMultipleInstance.addElement(this.$element);
2239
+ this.$element.attr(this.options.namespace + 'id', parsleyMultipleInstance.__id__);
2240
+ }
2241
+ break;
2242
+ }
2243
+ // Create a secret ParsleyField instance for every multiple field. It would be stored in `data('ParsleyFieldMultiple')`
2244
+ // And would be useful later to access classic `ParsleyField` stuff while being in a `ParsleyFieldMultiple` instance
2245
+ this.bind('parsleyField', parsleyFormInstance, multiple, true);
2246
+ return parsleyMultipleInstance || this.bind('parsleyFieldMultiple', parsleyFormInstance, multiple);
2247
+ },
2248
+ // Retrieve namespace used for DOM-API
2249
+ getNamespace: function (options) {
2250
+ // `data-parsley-namespace=<namespace>`
2251
+ if ('undefined' !== typeof this.$element.data('parsleyNamespace'))
2252
+ return this.$element.data('parsleyNamespace');
2253
+ if ('undefined' !== typeof ParsleyUtils.get(options, 'namespace'))
2254
+ return options.namespace;
2255
+ if ('undefined' !== typeof ParsleyUtils.get(window, 'ParsleyConfig.namespace'))
2256
+ return window.ParsleyConfig.namespace;
2257
+ return ParsleyDefaults.namespace;
2258
+ },
2259
+ // Return proper `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple`
2260
+ bind: function (type, parentParsleyFormInstance, multiple, doNotStore) {
2261
+ var parsleyInstance;
2262
+ switch (type) {
2263
+ case 'parsleyForm':
2264
+ parsleyInstance = $.extend(
2265
+ new ParsleyForm(this.$element, this.OptionsFactory),
2266
+ new ParsleyAbstract(),
2267
+ window.ParsleyExtend
2268
+ )._bindFields();
2269
+ break;
2270
+ case 'parsleyField':
2271
+ parsleyInstance = $.extend(
2272
+ new ParsleyField(this.$element, this.OptionsFactory, parentParsleyFormInstance),
2273
+ new ParsleyAbstract(),
2274
+ window.ParsleyExtend
2275
+ );
2276
+ break;
2277
+ case 'parsleyFieldMultiple':
2278
+ parsleyInstance = $.extend(
2279
+ new ParsleyField(this.$element, this.OptionsFactory, parentParsleyFormInstance),
2280
+ new ParsleyAbstract(),
2281
+ new ParsleyMultiple(),
2282
+ window.ParsleyExtend
2283
+ )._init(multiple);
2284
+ break;
2285
+ default:
2286
+ throw new Error(type + 'is not a supported Parsley type');
2287
+ }
2288
+ if ('undefined' !== typeof multiple)
2289
+ ParsleyUtils.setAttr(this.$element, this.options.namespace, 'multiple', multiple);
2290
+ if ('undefined' !== typeof doNotStore) {
2291
+ this.$element.data('ParsleyFieldMultiple', parsleyInstance);
2292
+ return parsleyInstance;
2293
+ }
2294
+ // Store instance if `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple`
2295
+ if (new RegExp('ParsleyF', 'i').test(parsleyInstance.__class__)) {
2296
+ // Store for later access the freshly binded instance in DOM element itself using jQuery `data()`
2297
+ this.$element.data('Parsley', parsleyInstance);
2298
+ // Tell the world we got a new ParsleyForm or ParsleyField instance!
2299
+ $.emit('parsley:' + ('parsleyForm' === type ? 'form' : 'field') + ':init', parsleyInstance);
2300
+ }
2301
+ return parsleyInstance;
2302
+ }
2303
+ };
2304
+ // ### jQuery API
2305
+ // `$('.elem').parsley(options)` or `$('.elem').psly(options)`
2306
+ $.fn.parsley = $.fn.psly = function (options) {
2307
+ if (this.length > 1) {
2308
+ var instances = [];
2309
+ this.each(function () {
2310
+ instances.push($(this).parsley(options));
2311
+ });
2312
+ return instances;
2313
+ }
2314
+ // Return undefined if applied to non existing DOM element
2315
+ if (!$(this).length) {
2316
+ if (window.console && window.console.warn)
2317
+ window.console.warn('You must bind Parsley on an existing element.');
2318
+ return;
2319
+ }
2320
+ return new Parsley(this, options);
2321
+ };
2322
+ // ### ParsleyUI
2323
+ // UI is a class apart that only listen to some events and them modify DOM accordingly
2324
+ // Could be overriden by defining a `window.ParsleyConfig.ParsleyUI` appropriate class (with `listen()` method basically)
2325
+ window.ParsleyUI = 'function' === typeof ParsleyUtils.get(window, 'ParsleyConfig.ParsleyUI') ?
2326
+ new window.ParsleyConfig.ParsleyUI().listen() : new ParsleyUI().listen();
2327
+ // ### ParsleyField and ParsleyForm extension
2328
+ // Ensure that defined if not already the case
2329
+ if ('undefined' === typeof window.ParsleyExtend)
2330
+ window.ParsleyExtend = {};
2331
+ // ### ParsleyConfig
2332
+ // Ensure that defined if not already the case
2333
+ if ('undefined' === typeof window.ParsleyConfig)
2334
+ window.ParsleyConfig = {};
2335
+ // ### Globals
2336
+ window.Parsley = window.psly = Parsley;
2337
+ window.ParsleyUtils = ParsleyUtils;
2338
+ window.ParsleyValidator = new ParsleyValidator(window.ParsleyConfig.validators, window.ParsleyConfig.i18n);
2339
+ // ### PARSLEY auto-binding
2340
+ // Prevent it by setting `ParsleyConfig.autoBind` to `false`
2341
+ if (false !== ParsleyUtils.get(window, 'ParsleyConfig.autoBind'))
2342
+ $(document).ready(function () {
2343
+ // Works only on `data-parsley-validate`.
2344
+ if ($('[data-parsley-validate]').length)
2345
+ $('[data-parsley-validate]').parsley();
2346
+ });
2347
+
2348
+ // AMD Compliance
2349
+ if ('function' === typeof define && define.amd)
2350
+ define('parsley', function() { return window.Parsley; } );
2351
+ })(window.jQuery);