parsley-rails 1.2.4.0 → 2.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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);