PageSourceSearch

https://dombretanii.org.pl/wp-content/themes/dombretanii/vendor/parsleyjs/parsley.js?ts=2026-10

js dombretanii.org.pl collected 2026-10-02 18:30:04 UTC 54,563 bytes, 1,595 lines download raw bytes

1/*
2 * Parsley.js allows you to verify your form inputs frontend side, without writing a line of javascript. Or so..
3 *
4 * Author: Guillaume Potier - @guillaumepotier
5*/
6
7!function ($) {
8
9  'use strict';
10
11  /**
12  * Validator class stores all constraints functions and associated messages.
13  * Provides public interface to add, remove or modify them
14  *
15  * @class Validator
16  * @constructor
17  */
18  var Validator = function ( options ) {
19    /**
20    * Error messages
21    *
22    * @property messages
23    * @type {Object}
24    */
25    this.messages = {
26        defaultMessage: "This value seems to be invalid."
27      , type: {
28            email:      "This value should be a valid email."
29          , url:        "This value should be a valid url."
30          , urlstrict:  "This value should be a valid url."
31          , number:     "This value should be a valid number."
32          , digits:     "This value should be digits."
33          , dateIso:    "This value should be a valid date (YYYY-MM-DD)."
34          , alphanum:   "This value should be alphanumeric."
35          , phone:      "This value should be a valid phone number."
36        }
37      , notnull:        "This value should not be null."
38      , notblank:       "This value should not be blank."
39      , required:       "This value is required."
40      , regexp:         "This value seems to be invalid."
41      , min:            "This value should be greater than or equal to %s."
42      , max:            "This value should be lower than or equal to %s."
43      , range:          "This value should be between %s and %s."
44      , minlength:      "This value is too short. It should have %s characters or more."
45      , maxlength:      "This value is too long. It should have %s characters or less."
46      , rangelength:    "This value length is invalid. It should be between %s and %s characters long."
47      , mincheck:       "You must select at least %s choices."
48      , maxcheck:       "You must select %s choices or less."
49      , rangecheck:     "You must select between %s and %s choices."
50      , equalto:        "This value should be the same."
51    },
52
53    this.init( options );
54  };
55
56  Validator.prototype = {
57
58    constructor: Validator
59
60    /**
61    * Validator list. Built-in validators functions
62    *
63    * @property validators
64    * @type {Object}
65    */
66    , validators: {
67      notnull: function () {
68        return {
69          validate: function ( val ) {
70            return val.length > 0;
71          }
72          , priority: 2
73        }
74      }
75      , notblank: function () {
76        return {
77          validate: function ( val ) {
78            return 'string' === typeof val && '' !== val.replace( /^\s+/g, '' ).replace( /\s+$/g, '' );
79          }
80          , priority: 2
81        }
82      }
83      , required: function () {
84        var that = this;
85        return {
86          validate: function ( val ) {
87            // for checkboxes and select multiples. Check there is at least one required value
88            if ( 'object' === typeof val ) {
89              for ( var i in val ) {
90                if ( that.required().validate( val[ i ] ) ) {
91                  return true;
92                }
93              }
94
95              return false;
96            }
97
98            return that.notnull().validate( val ) && that.notblank().validate( val );
99          }
100          , priority: 512
101        }
102      }
103      , type: function () {
104        return {
105          validate: function ( val, type ) {
106            var regExp;
107
108            switch ( type ) {
109              case 'number':
110                regExp = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/;
111                break;
112              case 'digits':
113                regExp = /^\d+$/;
114                break;
115              case 'alphanum':
116                regExp = /^\w+$/;
117                break;
118              case 'email':
119                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]))){2,6}$/i;
120                break;
121              case 'url':
122                val = new RegExp( '(https?|s?ftp|git)', 'i' ).test( val ) ? val : 'http://' + val;
123                /* falls through */
124              case 'urlstrict':
125                regExp = /^(https?|s?ftp|git):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
126                break;
127              case 'dateIso':
128                regExp = /^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$/;
129                break;
130              case 'phone':
131                regExp = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/;
132                break;
133              default:
134                return false;
135            }
136
137            // test regExp if not null
138            return '' !== val ? regExp.test( val ) : false;
139          }
140          , priority: 256
141        }
142      }
143      , regexp: function () {
144        return {
145          validate: function ( val, regExp, self ) {
146            return new RegExp( regExp, self.options.regexpFlag || '' ).test( val );
147          }
148          , priority: 64
149        }
150      }
151      , minlength: function () {
152        return {
153          validate: function ( val, min ) {
154            return val.length >= min;
155          }
156          , priority: 32
157        }
158      }
159      , maxlength: function () {
160        return {
161          validate: function ( val, max ) {
162            return val.length <= max;
163          }
164          , priority: 32
165        }
166      }
167      , rangelength: function () {
168        var that = this;
169        return {
170          validate: function ( val, arrayRange ) {
171            return that.minlength().validate( val, arrayRange[ 0 ] ) && that.maxlength().validate( val, arrayRange[ 1 ] );
172          }
173          , priority: 32
174        }
175      }
176      , min: function () {
177        return {
178          validate: function ( val, min ) {
179            return Number( val ) >= min;
180          }
181          , priority: 32
182        }
183      }
184      , max: function () {
185        return {
186          validate: function ( val, max ) {
187            return Number( val ) <= max;
188          }
189          , priority: 32
190        }
191      }
192      , range: function () {
193        var that = this;
194        return {
195          validate: function ( val, arrayRange ) {
196            return that.min().validate( val, arrayRange[ 0 ] ) && that.max().validate( val, arrayRange[ 1 ] );
197          }
198          , priority: 32
199        }
200      }
201      , equalto: function () {
202        return {
203          validate: function ( val, elem, self ) {
204            self.options.validateIfUnchanged = true;
205            return val === $( elem ).val();
206          }
207          , priority: 64
208        }
209      }
210      , remote: function () {
211        return {
212          validate: function ( val, url, self ) {
213            var result = null
214              , data = {}
215              , dataType = {};
216
217            data[ self.$element.attr( 'name' ) ] = val;
218
219            if ( 'undefined' !== typeof self.options.remoteDatatype )
220              dataType = { dataType: self.options.remoteDatatype };
221
222            var manage = function ( isConstraintValid, message ) {
223              // remove error message if we got a server message, different from previous message
224              if ( 'undefined' !== typeof message && 'undefined' !== typeof self.Validator.messages.remote && message !== self.Validator.messages.remote ) {
225                $( self.UI.ulError + ' .remote' ).remove();
226              }
227
228              if (false === isConstraintValid) {
229                  self.options.listeners.onFieldError( self.element, self.constraints, self );
230              } else if (true === isConstraintValid && false === self.options.listeners.onFieldSuccess( self.element, self.constraints, self )) {
231                  // if onFieldSuccess returns (bool) false, consider that field is invalid
232                  isConstraintValid = false;
233              }
234
235              self.updtConstraint( { name: 'remote', valid: isConstraintValid }, message );
236              self.manageValidationResult();
237            };
238
239            // transform string response into object
240            var handleResponse = function ( response ) {
241              if ( 'object' === typeof response ) {
242                return response;
243              }
244
245              try {
246                response = $.parseJSON( response );
247              } catch ( err ) {}
248
249              return response;
250            }
251
252            var manageErrorMessage = function ( response ) {
253              return 'object' === typeof response && null !== response ? ( 'undefined' !== typeof response.error ? response.error : ( 'undefined' !== typeof response.message ? response.message : null ) ) : null;
254            }
255
256            $.ajax( $.extend( {}, {
257                url: url
258              , data: data
259              , type: self.options.remoteMethod || 'GET'
260              , success: function ( response ) {
261                response = handleResponse( response );
262                manage( 1 === response || true === response || ( 'object' === typeof response && null !== response && 'undefined' !== typeof response.success ), manageErrorMessage( response )
263                );
264              }
265              , error: function ( response ) {
266                response = handleResponse( response );
267                manage( false, manageErrorMessage( response ) );
268              }
269            }, dataType ) );
270
271            return result;
272          }
273          , priority: 64
274        }
275      }
276
277      /**
278      * Aliases for checkboxes constraints
279      */
280      , mincheck: function () {
281        var that = this;
282        return {
283          validate: function ( obj, val ) { return that.minlength().validate( obj, val ) }
284          , priority: 32
285        }
286      }
287      , maxcheck: function () {
288        var that = this;
289        return {
290          validate: function ( obj, val ) { return that.maxlength().validate( obj, val ) }
291          , priority: 32
292        }
293      }
294      , rangecheck: function () {
295        var that = this;
296        return {
297          validate: function ( obj, arrayRange ) { return that.rangelength().validate( obj, arrayRange ) }
298          , priority: 32
299        }
300      }
301    }
302
303    /*
304    * Register custom validators and messages
305    */
306    , init: function ( options ) {
307      var customValidators = options.validators
308        , customMessages = options.messages
309        , key;
310
311      for ( key in customValidators ) {
312        this.addValidator(key, customValidators[ key ]);
313      }
314
315      for ( key in customMessages ) {
316        this.addMessage(key, customMessages[ key ]);
317      }
318    }
319
320    /**
321    * Replace %s placeholders by values
322    *
323    * @method formatMesssage
324    * @param {String} message Message key
325    * @param {Mixed} args Args passed by validators functions. Could be string, number or object
326    * @return {String} Formatted string
327    */
328    , formatMesssage: function ( message, args ) {
329
330      if ( 'object' === typeof args ) {
331        for ( var i in args ) {
332          message = this.formatMesssage( message, args[ i ] );
333        }
334
335        return message;
336      }
337
338      return 'string' === typeof message ? message.replace( new RegExp( '%s', 'i' ), args ) : '';
339    }
340
341    /**
342    * Add / override a validator in validators list
343    *
344    * @method addValidator
345    * @param {String} name Validator name.
346    * @param {Function}
346 fn Validator. Must return { validator: fn(), priority: int }
347    */
348    , addValidator: function ( name, fn ) {
349      if ('undefined' === typeof fn().validate) {
350        throw new Error( 'Validator `' + name + '` must have a validate method. See more here: http://parsleyjs.org/documentation.html#javascript-general' );
351      }
352
353      // add default prioirty if not given.
354      if ('undefined' === typeof fn().priority) {
355        fn = {
356            validate: fn().validate
357          , priority: 32
358        };
359
360        // Warn if possible
361        if (window.console && window.console.warn) {
362          window.console.warn( 'Validator `' + name + '` should have a priority. Default priority 32 given' );
363        }
364      }
365
366      this.validators[ name ] = fn;
367    }
368
369    /**
370    * Add / override error message
371    *
372    * @method addMessage
373    * @param {String} name Message name. Will automatically be binded to validator with same name
374    * @param {String} message Message
375    */
376    , addMessage: function ( key, message, type ) {
377
378      if ( 'undefined' !== typeof type && true === type ) {
379        this.messages.type[ key ] = message;
380        return;
381      }
382
383      // custom types messages are a bit tricky cuz' nested ;)
384      if ( 'type' === key ) {
385        for ( var i in message ) {
386          this.messages.type[ i ] = message[ i ];
387        }
388
389        return;
390      }
391
392      this.messages[ key ] = message;
393    }
394  };
395
396  var ParsleyUI = function ( ParsleyInstance ) {
397    this.init( ParsleyInstance );
398  };
399
400  ParsleyUI.prototype = {
401
402    constructor: ParsleyUI
403
404    , init: function ( ParsleyInstance ) {
405      this.ParsleyInstance = ParsleyInstance;
406      this.hash = ParsleyInstance.hash;
407      this.options = this.ParsleyInstance.options;
408      this.errorClassHandler = this.options.errors.classHandler( this.ParsleyInstance.element, this.ParsleyInstance.isRadioOrCheckbox ) || this.ParsleyInstance.$element;
409      this.ulErrorManagement();
410    }
411
412    /**
413    * Manage ul error Container
414    *
415    * @private
416    * @method ulErrorManagement
417    */
418    , ulErrorManagement: function () {
419      this.ulError = '#' + this.hash;
420      this.ulTemplate = $( this.options.errors.errorsWrapper ).attr( 'id', this.hash ).addClass( 'parsley-error-list' );
421    }
422
423    /**
424    * Remove li / ul error
425    *
426    * @method removeError
427    * @param  {String} constraintName Method Name
428    * @return ParsleyUI
429    */
430    , removeError: function ( constraintName ) {
431      var liError = this.ulError + ' .' + constraintName
432        , that = this;
433
434      this.options.animate ? $( liError ).fadeOut( this.options.animateDuration, function () {
435        $( this ).remove();
436
437        if ( that.ulError && $( that.ulError ).children().length === 0 ) {
438          that.removeErrors();
439        } } ) : $( liError ).remove();
440
441        return this;
442    }
443
444    /**
445    * Add li error
446    *
447    * @method addError
448    * @param  {Object} { minlength: "error message for minlength constraint" }
449    * @return ParsleyUI
450    */
451    , addError: function ( error ) {
452      for ( var constraint in error ) {
453        var liTemplate = $( this.options.errors.errorElem ).addClass( constraint );
454
455        $( this.ulError ).append( this.options.animate ? $( liTemplate ).html( error[ constraint ] ).hide().fadeIn( this.options.animateDuration ) : $( liTemplate ).html( error[ constraint ] ) );
456      }
457
458      return this;
459    }
460
461    /**
462    * Update existing error if text has changed
463    *
464    * @method updateError
465    * @param  {Object} { minlength: "error message for minlength constraint" }
466    * @return ParsleyUI
467    */
468    , updateError: function ( error ) {
469      for ( var constraint in error ) {
470        if ( error[ constraint ] !==  $( this.ulError +  " > li." + constraint ).html() ) {
471          this.removeError( constraint ).addError( error );
472        }
473      }
474
475      return this;
476    }
477
478    /**
479    * Remove all ul / li errors
480    *
481    * @method removeErrors
482    * @return ParsleyUI
483    */
484    , removeErrors: function () {
485      this.options.animate ? $( this.ulError ).fadeOut( this.options.animateDuration, function () { $( this ).remove(); } ) : $( this.ulError ).remove();
486
487      return this;
488    }
489
490    /**
491    * Remove ul errors and parsley error or success classes
492    *
493    * @method reset
494    * @return ParsleyUI
495    */
496    , reset: function () {
497      this.ParsleyInstance.valid = null;
498      this.removeErrors();
499      this.ParsleyInstance.validatedOnce = false;
500      this.errorClassHandler.removeClass( this.options.successClass ).removeClass( this.options.errorClass );
501
502      for ( var constraint in this.constraints ) {
503        this.constraints[ constraint ].valid = null;
504      }
505
506      return this;
507    }
508
509    /**
510    * Add li / ul errors messages
511    *
512    * @method manageError
513    * @param  {Object} constraint
514    * @return ParsleyUI
515    */
516    , manageError: function ( constraint ) {
517      // display ulError container if it has been removed previously (or never shown)
518      if ( !$( this.ulError ).length ) {
519        this.manageErrorContainer();
520      }
521
522      // TODO: refacto properly
523      // if required constraint but field is not null, do not display
524      if ( 'required' === constraint.name && null !== this.ParsleyInstance.getVal() && this.ParsleyInstance.getVal().length > 0 ) {
525        return this;
526
527      // if empty required field and non required constraint fails, do not display
528      } else if ( this.ParsleyInstance.isRequired && 'required' !== constraint.name && ( null === this.ParsleyInstance.getVal() || 0 === this.ParsleyInstance.getVal().length ) ) {
529        this.removeError( constraint.name );
530
531        return this;
532      }
533
534      // TODO: refacto error name w/ proper & readable function
535      var constraintName = constraint.name
536        , liClass = false !== this.options.errorMessage ? 'custom-error-message' : constraintName
537        , liError = {}
538        , message = false !== this.options.errorMessage ? this.options.errorMessage : ( constraint.name === 'type' ?
539            this.ParsleyInstance.Validator.messages[ constraintName ][ constraint.requirements ] : ( 'undefined' === typeof this.ParsleyInstance.Validator.messages[ constraintName ] ?
540              this.ParsleyInstance.Validator.messages.defaultMessage : this.ParsleyInstance.Validator.formatMesssage( this.ParsleyInstance.Validator.messages[ constraintName ], constraint.requirements ) ) );
541
542      liError[ liClass ] = message;
543
544      // add liError if not shown. update if already exist
545      !$( this.ulError + ' .' + liClass ).length ? this.addError( liError ) : this.updateError( liError );
546
547      return this;
548    }
549
550    /**
551    * Create ul error container
552    *
553    * @method manageErrorContainer
554    * @return ParsleyUI
555    */
556    , manageErrorContainer: function () {
557      var errorContainer = this.options.errorContainer || this.options.errors.container( this.ParsleyInstance.element, this.ParsleyInstance.isRadioOrCheckbox )
558        , ulTemplate = this.options.animate ? this.ulTemplate.css('display', '') : this.ulTemplate;
559
560      if ( 'undefined' !== typeof errorContainer ) {
561        $( errorContainer ).append( ulTemplate );
562        return;
563      }
564
565      !this.ParsleyInstance.isRadioOrCheckbox ? this.ParsleyInstance.$element.after( ulTemplate ) : this.ParsleyInstance.$element.parent().after( ulTemplate );
566
567      return this;
568    }
569  };
570
571  /**
572  * ParsleyField class manage each form field inside a validated Parsley form.
573  * Returns if field valid or not depending on its value and constraints
574  * Manage field error display and behavior, event triggers and more
575  *
576  * @class ParsleyField
577  * @constructor
578  */
579  var ParsleyField = function ( element, options, type ) {
580    this.options = options;
581
582    // if type is ParsleyFieldMultiple, just return this. used for clone
583    if ( type === 'ParsleyFieldMultiple' ) {
584      return this;
585    }
586
587    this.init( element, type || 'ParsleyField' );
588  };
589
590  ParsleyField.prototype = {
591
592    constructor: ParsleyField
593
594    /**
595    * Set some properties, bind constraint validators and validation events
596    *
597    * @method init
598    * @param {Object} element
599    * @param {Object} options
600    */
601    , init: function ( element, type ) {
602      this.type = type;
603      this.valid = true;
604      this.element = element;
605      this.validatedOnce = false;
606      this.$element = $( element );
607      this.val = this.$element.val();
608      this.Validator = new Validator( this.options );
609      this.isRequired = false;
610      this.constraints = {};
611
612      // overriden by ParsleyItemMultiple if radio or checkbox input
613      if ( 'undefined' === typeof this.isRadioOrCheckbox ) {
614        this.isRadioOrCheckbox = false;
615        this.hash = this.generateHash();
616      }
617
618      // error ul dom management done only once at init
619      this.UI = new ParsleyUI( this );
620
621      // bind some html5 properties
622      if ( this.options.useHtml5Constraints ) {
623        this.bindHtml5Constraints();
624      }
625
626      // bind validators to field
627      this.addConstraints();
628
629      // bind parsley events if validators have been registered
630      if ( this.hasConstraints() ) {
631        this.bindValidationEvents();
632      }
633    }
634
635    , setParent: function ( elem ) {
636      this.$parent = $( elem );
637    }
638
639    , getParent: function () {
640      return this.$parent;
641    }
642
643    /**
644    * Bind some extra html5 types / validators
645    *
646    * @private
647    * @method bindHtml5Constraints
648    */
649    , bindHtml5Constraints: function () {
650      // add html5 required support + class required support
651      if ( this.$element.hasClass( 'required' ) || this.$element.attr( 'required' ) ) {
652        this.options.required = true;
653      }
654
655      // add html5 supported types & options
656      var type = this.$element.attr( 'type' );
657      if ( 'undefined' !== typeof type && new RegExp( type, 'i' ).test( 'email url number range tel' ) ) {
658        this.options.type = 'tel' === type ? 'phone' : type;
659
660        // number and range types could have min and/or max values
661        if ( new RegExp( this.options.type, 'i' ).test( 'number range' ) ) {
662          this.options.type = 'number';
663
664          // double condition to support jQuery and Zepto.. :(
665          if ( 'undefined' !== typeof this.$element.attr( 'min' ) && this.$element.attr( 'min' ).length ) {
666            this.options.min = this.$element.attr( 'min' );
667          }
668
669          if ( 'undefined' !== typeof this.$element.attr( 'max' ) && this.$element.attr( 'max' ).length ) {
670            this.options.max = this.$element.attr( 'max' );
671          }
672        }
673      }
674
675      if ( 'string' === typeof this.$element.attr( 'pattern' ) && this.$element.attr( 'pattern' ).length ) {
676          this.options.regexp = this.$element.attr( 'pattern' );
677      }
678
679    }
680
681    /**
682    * Attach field validators functions passed through data-api
683    *
684    * @private
685    * @method addConstraints
686    */
687    , addConstraints: function () {
688      for ( var constraint in this.options ) {
689        var addConstraint = {};
690        addConstraint[ constraint ] = this.options[ constraint ];
691        this.addConstraint( addConstraint, true, false );
692      }
693    }
694
695    /**
696    * Dynamically add a new constraint to a field
697    *
698    * @method addConstraint
699    * @param {Object} constraint { name: requirements }
700    */
701    , addConstraint: function ( constraint, doNotUpdateValidationEvents, sort ) {
702        for ( var name in constraint ) {
703          name = name.toLowerCase();
704
705          if ( 'function' === typeof this.Validator.validators[ name ] ) {
706            this.constraints[ name ] = {
707                name: name
708              , requirements: constraint[ name ]
709              , valid: null
710            }
711
712            if ( name === 'required' ) {
713              this.isRequired = true;
714            }
715
716            this.addCustomConstraintMessage( name );
717          }
718        }
719
720        // force field validation next check and reset validation events
721        if ( 'undefined' === typeof doNotUpdateValidationEvents ) {
722          this.bindValidationEvents();
723        }
724    }
725
726    /**
727    * Dynamically update an existing constraint to a field.
728    * Simple API: { name: requirements }
729    *
730    * @method updtConstraint
731    * @param {Object} constraint
732    */
733    , updateConstraint: function ( constraint, message ) {
734      for ( var name in constraint ) {
735        this.updtConstraint( { name: name, requirements: constraint[ name ], valid: null }, message );
736      }
737    }
738
739    /**
740    * Dynamically update an existing constraint to a field.
741    * Complex API: { name: name, requirements: requirements, valid: boolean }
742    *
743    * @method updtConstraint
744    * @param {Object} constraint
745    */
746    , updtConstraint: function ( constraint, message ) {
747      this.constraints[ constraint.name ] = $.extend( true, this.constraints[ c
747onstraint.name ], constraint );
748
749      if ( 'string' === typeof message ) {
750        if ( constraint.name ===  'type' )
751          this.Validator.messages[ constraint.name ][ constraint.requirements ] = message ;
752        else
753          this.Validator.messages[ constraint.name ] = message ;
754      }
755
756      // force field validation next check and reset validation events
757      this.bindValidationEvents();
758    }
759
760    /**
761    * Dynamically remove an existing constraint to a field.
762    *
763    * @method removeConstraint
764    * @param {String} constraintName
765    */
766    , removeConstraint: function ( constraintName ) {
767      var constraintName = constraintName.toLowerCase();
768
769      delete this.constraints[ constraintName ];
770
771      if ( constraintName === 'required' ) {
772        this.isRequired = false;
773      }
774
775      // if there are no more constraint, reset errors and validation state
776      if ( !this.hasConstraints() ) {
777        this.UI.reset();
778        return;
779      }
780
781      this.bindValidationEvents();
782    }
783
784    /**
785    * Add custom constraint message, passed through data-API
786    *
787    * @private
788    * @method addCustomConstraintMessage
789    * @param constraint
790    */
791    , addCustomConstraintMessage: function ( constraint ) {
792      // custom message type data-type-email-message -> typeEmailMessage | data-minlength-error => minlengthMessage
793      var customMessage = constraint
794        + ( 'type' === constraint && 'undefined' !== typeof this.options[ constraint ] ? this.options[ constraint ].charAt( 0 ).toUpperCase() + this.options[ constraint ].substr( 1 ) : '' )
795        + 'Message';
796
797      if ( 'undefined' !== typeof this.options[ customMessage ] ) {
798        this.Validator.addMessage( 'type' === constraint ? this.options[ constraint ] : constraint, this.options[ customMessage ], 'type' === constraint );
799      }
800    }
801
802    /**
803    * Bind validation events on a field
804    *
805    * @private
806    * @method bindValidationEvents
807    */
808    , bindValidationEvents: function () {
809      // this field has validation events, that means it has to be validated
810      this.valid = null;
811      this.$element.addClass( 'parsley-validated' );
812
813      // remove eventually already binded events
814      this.$element.off( '.' + this.type );
815
816      // force add 'change' event if async remote validator here to have result before form submitting
817      if ( this.options.remote && !new RegExp( 'change', 'i' ).test( this.options.trigger ) ) {
818        this.options.trigger = !this.options.trigger ? 'change' : ' change';
819      }
820
821      // always bind keyup event, for better UX when a field is invalid
822      var triggers = ( !this.options.trigger ? '' : this.options.trigger )
823        + ( new RegExp( 'key', 'i' ).test( this.options.trigger ) ? '' : ' keyup' );
824
825      // always bind change event, for better UX when a select is invalid
826      if ( this.$element.is( 'select' ) ) {
827        triggers += new RegExp( 'change', 'i' ).test( triggers ) ? '' : ' change';
828      }
829
830      // trim triggers to bind them correctly with .on()
831      triggers = triggers.replace( /^\s+/g , '' ).replace( /\s+$/g , '' );
832
833      this.$element.on( ( triggers + ' ' ).split( ' ' ).join( '.' + this.type + ' ' ), false, $.proxy( this.eventValidation, this ) );
834    }
835
836    /**
837    * Hash management. Used for ul error
838    *
839    * @method generateHash
840    * @returns {String} 5 letters unique hash
841    */
842    , generateHash: function () {
843      return 'parsley-' + ( Math.random() + '' ).substring( 2 );
844    }
845
846    /**
847    * Public getHash accessor
848    *
849    * @method getHash
850    * @returns {String} hash
851    */
852    , getHash: function () {
853      return this.hash;
854    }
855
856    /**
857    * Returns field val needed for validation
858    * Special treatment for radio & checkboxes
859    *
860    * @method getVal
861    * @returns {String} val
862    */
863    , getVal: function () {
864      if ('undefined' !== typeof this.$element.domApi( this.options.namespace )[ 'value' ]) {
865        return this.$element.domApi( this.options.namespace )[ 'value' ];
866      }
867
868      return this.$element.val();
869    }
870
871    /**
872    * Called when validation is triggered by an event
873    * Do nothing if val.length < this.options.validationMinlength
874    *
875    * @method eventValidation
876    * @param {Object} event jQuery event
877    */
878    , eventValidation: function ( event ) {
879      var val = this.getVal();
880
881      // do nothing on keypress event if not explicitely passed as data-trigger and if field has not already been validated once
882      if ( event.type === 'keyup' && !/keyup/i.test( this.options.trigger ) && !this.validatedOnce ) {
883        return true;
884      }
885
886      // do nothing on change event if not explicitely passed as data-trigger and if field has not already been validated once
887      if ( event.type === 'change' && !/change/i.test( this.options.trigger ) && !this.validatedOnce ) {
888        return true;
889      }
890
891      // start validation process only if field has enough chars and validation never started
892      if ( !this.isRadioOrCheckbox && this.getLength(val) < this.options.validationMinlength && !this.validatedOnce ) {
893        return true;
894      }
895
896      this.validate();
897    }
898
899    /**
900     * Get the length of a given value
901     *
902     * @method getLength
903     * @return {int} The length of the value
904     */
905    , getLength: function ( val ) {
906      return !val || !val.hasOwnProperty( 'length' ) ? 0 : val.length;
907    }
908
909    /**
910    * Return if field verify its constraints
911    *
912    * @method isValid
913    * @return {Boolean} Is field valid or not
914    */
915    , isValid: function () {
916      return this.validate( false );
917    }
918
919    /**
920    * Return if field has constraints
921    *
922    * @method hasConstraints
923    * @return {Boolean} Is field has constraints or not
924    */
925    , hasConstraints: function () {
926      for ( var constraint in this.constraints ) {
927        return true;
928      }
929
930      return false;
931    }
932
933    /**
934    * Validate a field & display errors
935    *
936    * @method validate
937    * @param {Boolean} errorBubbling set to false if you just want valid boolean without error bubbling next to fields
938    * @return {Boolean} Is field valid or not
939    */
940    , validate: function ( errorBubbling ) {
941      var val = this.getVal()
942        , valid = null;
943
944      // do not even bother trying validating a field w/o constraints
945      if ( !this.hasConstraints() ) {
946        return null;
947      }
948
949      // do not validate excluded fields
950      if ( this.$element.is( this.options.excluded ) ) {
951        return null;
952      }
953
954      // reset Parsley validation if onFieldValidate returns true, or if field is empty and not required
955      if ( this.options.listeners.onFieldValidate( this.element, this ) || ( '' === val && !this.isRequired ) ) {
956        this.UI.reset();
957        return null;
958      }
959
960      // do not validate a field already validated and unchanged !
961      if ( !this.needsValidation( val ) ) {
962        return this.valid;
963      }
964
965      valid = this.applyValidators();
966
967      if ( 'undefined' !== typeof errorBubbling ? errorBubbling : this.options.showErrors ) {
968        this.manageValidationResult();
969      }
970
971      return valid;
972    }
973
974    /**
975    * Check if value has changed since previous validation
976    *
977    * @method needsValidation
978    * @param value
979    * @return {Boolean}
980    */
981    , needsValidation: function ( val ) {
982      if ( !this.options.validateIfUnchanged && this.valid !== null && this.val === val && this.validatedOnce ) {
983        return false;
984      }
985
986      this.val = val;
987      return this.validatedOnce = true;
988    }
989
990    /**
991    * Loop through every fields validators
992    * Adds errors after unvalid fields
993    *
994    * @method applyValidators
995    * @return {Mixed} {Boolean} If field valid or not, null if not validated
996    */
997    , applyValidators: function () {
998      var valid = null;
999
1000      for ( var constraint in this.constraints ) {
1001        var result = this.Validator.validators[ this.constraints[ constraint ].name ]().validate( this.val, this.constraints[ constraint ].requirements, this );
1002
1003        if ( false === result ) {
1004          valid = false;
1005          this.constraints[ constraint ].valid = valid;
1006        } else if ( true === result ) {
1007          this.constraints[ constraint ].valid = true;
1008          valid = false !== valid;
1009        }
1010      }
1011
1012      // listeners' ballet
1013      if (false === valid) {
1014        this.options.listeners.onFieldError( this.element, this.constraints, this );
1015      } else if (true === valid && false === this.options.listeners.onFieldSuccess( this.element, this.constraints, this )) {
1016        // if onFieldSuccess returns (bool) false, consider that field si invalid
1017        valid = false;
1018      }
1019
1020      return valid;
1021    }
1022
1023    /**
1024    * Fired when all validators have be executed
1025    * Returns true or false if field is valid or not
1026    * Display errors messages below failed fields
1027    * Adds parsley-success or parsley-error class on fields
1028    *
1029    * @method manageValidationResult
1030    * @return {Boolean} Is field valid or not
1031    */
1032    , manageValidationResult: function () {
1033      var valid = null
1034        , errors = [];
1035
1036      for ( var constraint in this.constraints ) {
1037        if ( false === this.constraints[ constraint ].valid ) {
1038          errors.push( this.constraints[ constraint ]);
1039          valid = false;
1040        } else if ( true === this.constraints[ constraint ].valid ) {
1041          this.UI.removeError( this.constraints[ constraint ].name );
1042          valid = false !== valid;
1043        }
1044      }
1045
1046      this.valid = valid;
1047
1048      if ( true === this.valid ) {
1049        this.UI.removeErrors();
1050        this.UI.errorClassHandler.removeClass( this.options.errorClass ).addClass( this.options.successClass );
1051
1052        return true;
1053      } else if ( false === this.valid ) {
1054        if ( true === this.options.priorityEnabled ) {
1055          var maxPriority = 0, constraint, priority, error, errorArr = [];
1056          for ( var i = 0; i < errors.length; i++ ) {
1057            error = this.Validator.validators[ errors[ i ].name ]();
1058            priority = error.priority;
1059            errorArr.push(errors[ i ]);
1060
1061            if ( priority > maxPriority ) {
1062              constraint = errors[ i ];
1063              maxPriority = priority;
1064            }
1065          }
1066          for ( var i = 0; i < errorArr.length; i++ ) {
1067            if ( constraint === errorArr[ i ] ) {
1068              this.UI.manageError( constraint );
1069            } else {
1070              this.UI.removeError( errorArr[ i ].name );
1071            }
1072          }
1073        } else {
1074          for ( var i = 0; i < errors.length; i++ )
1075            this.UI.manageError( errors[ i ] );
1076        }
1077
1078        this.UI.errorClassHandler.removeClass( this.options.successClass ).addClass( this.options.errorClass );
1079        return false;
1080      }
1081
1082      // remove li error, and ul error if no more li inside
1083      if ( this.UI.ulError && $( this.ulError ).children().length === 0 ) {
1084        this.UI.removeErrors();
1085      }
1086
1087      return valid;
1088    }
1089
1090    /**
1091    * Add custom listeners
1092    *
1093    * @param {Object} { listener: function () {} }, eg { onFormValidate: function ( valid, event, focus ) { ... } }
1094    */
1095    , addListener: function ( object ) {
1096      for ( var listener in object ) {
1097        this.options.listeners[ listener ] = object[ listener ];
1098      }
1099    }
1100
1101    /**
1102    * Destroy parsley field instance
1103    *
1104    * @private
1105    * @method destroy
1106    */
1107    , destroy: function () {
1108      this.$element.removeClass( 'parsley-validated' );
1109      this.UI.reset();
1110      this.$element.off( '.' + this.type ).removeData( this.type );
1111    }
1112  };
1113
1114  /**
1115  * ParsleyFieldMultiple override ParsleyField for checkbox and radio inputs
1116  * Pseudo-heritance to manage divergent behavior from ParsleyItem in dedicated methods
1117  *
1118  * @class ParsleyFieldMultiple
1119  * @constructor
1120  */
1121  var ParsleyFieldMultiple = function ( element, options, type ) {
1122    this.initMultiple( element, options );
1123    this.inherit( element, options );
1124    this.Validator = new Validator( options );
1125
1126    // call ParsleyField constructor
1127    this.init( element, type || 'ParsleyFieldMultiple' );
1128  };
1129
1130  ParsleyFieldMultiple.prototype = {
1131
1132    constructor: ParsleyFieldMultiple
1133
1134    /**
1135    * Set some specific properties, call some extra methods to manage radio / checkbox
1136    *
1137    * @method init
1138    * @param {Object} element
1139    * @param {Object} options
1140    */
1141    , initMultiple: function ( element, options ) {
1142      this.element = element;
1143      this.$element = $( element );
1144      this.group = options.group || false;
1145      this.hash = this.getName();
1146      this.siblings = this.group ? '[' + options.namespace + 'group="' + this.group + '"]' : 'input[name="' + this.$element.attr( 'name' ) + '"]';
1147      this.isRadioOrCheckbox = true;
1148      this.isRadio = this.$element.is( 'input[type=radio]' );
1149      this.isCheckbox = this.$element.is( 'input[type=checkbox]' );
1150      this.errorClassHandler = options.errors.classHandler( element, this.isRadioOrCheckbox ) || this.$element.parent();
1151    }
1152
1153    /**
1154    * Set specific constraints messages, do pseudo-heritance
1155    *
1156    * @private
1157    * @method inherit
1158    * @param {Object} element
1159    * @param {Object} options
1160    */
1161    , inherit: function ( element, options ) {
1162      var clone = new ParsleyField( element, options, 'ParsleyFieldMultiple' );
1163
1164      for ( var property in clone ) {
1165        if ( 'undefined' === typeof this[ property ] ) {
1166          this[ property ] = clone [ property ];
1167        }
1168      }
1169    }
1170
1171    /**
1172    * Set specific constraints messages, do pseudo-heritance
1173    *
1174    * @method getName
1175    * @returns {String} radio / checkbox hash is cleaned 'name' or data-group property
1176    */
1177   , getName: function () {
1178     if ( this.group ) {
1179       return 'parsley-' + this.group;
1180     }
1181
1182     if ( 'undefined' === typeof this.$element.attr( 'name' ) ) {
1183       throw "A radio / checkbox input must have a parsley-group attribute or a name to be Parsley validated !";
1184     }
1185
1186     return 'parsley-' + this.$element.attr( 'name' ).replace( /(:|\.|\[|\]|\$)/g, '' );
1187   }
1188
1189   /**
1190   * Special treatment for radio & checkboxes
1191   * Returns checked radio or checkboxes values
1192   *
1193   * @method getVal
1194   * @returns {String} val
1195   */
1196   , getVal: function () {
1197      if ( this.isRadio ) {
1198        return $( this.siblings + ':checked' ).val() || '';
1199      }
1200
1201      if ( this.isCheckbox ) {
1202        var values = [];
1203
1204        $( this.siblings + ':checked' ).each( function () {
1205          values.push( $( this ).val() );
1206        } );
1207
1208        return values;
1209      }
1210   }
1211
1212   /**
1213   * Bind validation events on a field
1214   *
1215   * @private
1216   * @method bindValidationEvents
1217   */
1218   , bindValidationEvents: function () {
1219     // this field has validation events, that means it has to be validated
1220     this.valid = null;
1221     this.$element.addClass( 'parsley-validated' );
1222
1223     // remove eventually already binded events
1224     this.$element.off( '.' + this.type );
1225
1226      // always bind keyup event, for better UX when a field is invali
1226d
1227      var self = this
1228        , triggers = ( !this.options.trigger ? '' : this.options.trigger )
1229        + ( new RegExp( 'change', 'i' ).test( this.options.trigger ) ? '' : ' change' );
1230
1231      // trim triggers to bind them correctly with .on()
1232      triggers = triggers.replace( /^\s+/g , '' ).replace( /\s+$/g ,'' );
1233
1234     // bind trigger event on every siblings
1235     $( this.siblings ).each(function () {
1236       $( this ).on( triggers.split( ' ' ).join( '.' + self.type + ' ' ) , false, $.proxy( self.eventValidation, self ) );
1237     } )
1238   }
1239  };
1240
1241  /**
1242  * ParsleyForm class manage Parsley validated form.
1243  * Manage its fields and global validation
1244  *
1245  * @class ParsleyForm
1246  * @constructor
1247  */
1248  var ParsleyForm = function ( element, options, type ) {
1249    this.init( element, options, type || 'parsleyForm' );
1250  };
1251
1252  ParsleyForm.prototype = {
1253
1254    constructor: ParsleyForm
1255
1256    /* init data, bind jQuery on() actions */
1257    , init: function ( element, options, type ) {
1258      this.type = type;
1259      this.items = [];
1260      this.$element = $( element );
1261      this.options = options;
1262      var self = this;
1263
1264      this.$element.find( options.inputs ).each( function () {
1265        self.addItem( this );
1266      });
1267
1268      this.$element.on( 'submit.' + this.type , false, $.proxy( this.validate, this ) );
1269    }
1270
1271    /**
1272    * Add custom listeners
1273    *
1274    * @param {Object} { listener: function () {} }, eg { onFormValidate: function ( valid, event, focus ) { ... } }
1275    */
1276    , addListener: function ( object ) {
1277      for ( var listener in object ) {
1278        if ( new RegExp( 'Field' ).test( listener ) ) {
1279          for ( var item = 0; item < this.items.length; item++ ) {
1280            this.items[ item ].addListener( object );
1281          }
1282        } else {
1283          this.options.listeners[ listener ] = object[ listener ];
1284        }
1285      }
1286    }
1287
1288    /**
1289    * Adds a new parsleyItem child to ParsleyForm
1290    *
1291    * @method addItem
1292    * @param elem
1293    */
1294    , addItem: function ( elem ) {
1295      var ParsleyField = $( elem ).parsley( this.options );
1296      ParsleyField.setParent( this );
1297
1298      this.items.push( ParsleyField );
1299    }
1300
1301    /**
1302    * Removes a parsleyItem child from ParsleyForm
1303    *
1304    * @method removeItem
1305    * @param elem
1306    * @return {Boolean}
1307    */
1308    , removeItem: function ( elem ) {
1309      var parsleyItem = $( elem ).parsley();
1310
1311      // identify & remove item if same Parsley hash
1312      for ( var i = 0; i < this.items.length; i++ ) {
1313        if ( this.items[ i ].hash === parsleyItem.hash ) {
1314          this.items[ i ].destroy();
1315          this.items.splice( i, 1 );
1316          return true;
1317        }
1318      }
1319
1320      return false;
1321    }
1322
1323    /**
1324    * Process each form field validation
1325    * Display errors, call custom onFormValidate() function
1326    *
1327    * @method validate
1328    * @param {Object} event jQuery Event
1329    * @return {Boolean} Is form valid or not
1330    */
1331    , validate: function ( event ) {
1332      var valid = true;
1333      this.focusedField = false;
1334
1335      for ( var item = 0; item < this.items.length; item++ ) {
1336        if ( 'undefined' !== typeof this.items[ item ] && false === this.items[ item ].validate() ) {
1337          valid = false;
1338
1339          if ( !this.focusedField && 'first' === this.options.focus || 'last' === this.options.focus ) {
1340            this.focusedField = this.items[ item ].$element;
1341          }
1342        }
1343      }
1344
1345      // form is invalid, focus an error field depending on focus policy
1346      if ( this.focusedField && !valid ) {
1347        // Scroll smoothly
1348        if ( this.options.scrollDuration > 0 ) {
1349          var that = this,
1350              top = this.focusedField.offset().top - $( window ).height() / 2; // Center the window on the field
1351
1352          $( 'html, body' ).animate( {
1353              scrollTop: top
1354            },
1355            this.options.scrollDuration,
1356            function () {
1357              that.focusedField.focus();
1358            }
1359          );
1360        // Just focus on the field and let the browser do the rest
1361        } else {
1362          this.focusedField.focus();
1363        }
1364      }
1365
1366      // if onFormValidate returns (bool) false, form won't be submitted, even if valid
1367      var onFormValidate = this.options.listeners.onFormValidate( valid, event, this );
1368      if ('undefined' !== typeof onFormValidate) {
1369        return onFormValidate;
1370      }
1371
1372      return valid;
1373    }
1374
1375    , isValid: function () {
1376      for ( var item = 0; item < this.items.length; item++ ) {
1377        if ( false === this.items[ item ].isValid() ) {
1378          return false;
1379        }
1380      }
1381
1382      return true;
1383    }
1384
1385    /**
1386    * Remove all errors ul under invalid fields
1387    *
1388    * @method removeErrors
1389    */
1390    , removeErrors: function () {
1391      for ( var item = 0; item < this.items.length; item++ ) {
1392        this.items[ item ].parsley( 'reset' );
1393      }
1394    }
1395
1396    /**
1397    * destroy Parsley binded on the form and its fields
1398    *
1399    * @method destroy
1400    */
1401    , destroy: function () {
1402      for ( var item = 0; item < this.items.length; item++ ) {
1403        this.items[ item ].destroy();
1404      }
1405
1406      this.$element.off( '.' + this.type ).removeData( this.type );
1407    }
1408
1409    /**
1410    * reset Parsley binded on the form and its fields
1411    *
1412    * @method reset
1413    */
1414    , reset: function () {
1415      for ( var item = 0; item < this.items.length; item++ ) {
1416        this.items[ item ].UI.reset();
1417      }
1418    }
1419  };
1420
1421  /**
1422  * Parsley plugin definition
1423  * Provides an interface to access public Validator, ParsleyForm and ParsleyField functions
1424  *
1425  * @class Parsley
1426  * @constructor
1427  * @param {Mixed} Options. {Object} to configure Parsley or {String} method name to call a public class method
1428  * @param {Function} Callback function
1429  * @return {Mixed} public class method return
1430  */
1431  $.fn.parsley = function ( option, fn ) {
1432    var namespace = { namespace: $( this ).data( 'parsleyNamespace' ) ? $( this ).data( 'parsleyNamespace' ) : ( 'undefined' !== typeof option && 'undefined' !== typeof option.namespace ? option.namespace : $.fn.parsley.defaults.namespace ) }
1433      , options = $.extend( true, {}, $.fn.parsley.defaults, 'undefined' !== typeof window.ParsleyConfig ? window.ParsleyConfig : {}, option, this.domApi( namespace.namespace ) )
1434      , newInstance = null
1435      , args = Array.prototype.slice.call(arguments, 1);
1436
1437    function bind ( self, type ) {
1438      var parsleyInstance = $( self ).data( type );
1439
1440      // if data never binded or we want to clone a build (for radio & checkboxes), bind it right now!
1441      if ( !parsleyInstance ) {
1442        switch ( type ) {
1443          case 'parsleyForm':
1444            parsleyInstance = new ParsleyForm( self, options, 'parsleyForm' );
1445            break;
1446          case 'parsleyField':
1447            parsleyInstance = new ParsleyField( self, options, 'parsleyField' );
1448            break;
1449          case 'parsleyFieldMultiple':
1450            parsleyInstance = new ParsleyFieldMultiple( self, options, 'parsleyFieldMultiple' );
1451            break;
1452          default:
1453            return;
1454        }
1455
1456        $( self ).data( type, parsleyInstance );
1457      }
1458
1459      // here is our parsley public function accessor
1460      if ( 'string' === typeof option && 'function' === typeof parsleyInstance[ option ] ) {
1461        var response = parsleyInstance[ option ].apply( parsleyInstance, args );
1462
1463        return 'undefined' !== typeof response ? response : $( self );
1464      }
1465
1466      return parsleyInstance;
1467    }
1468
1469    // if a form elem is given, bind all its input children
1470    if ( $( this ).is( 'form' ) || 'undefined' !== typeof $( this ).domApi( namespace.namespace )[ 'bind' ] ) {
1471      newInstance = bind ( $( this ), 'parsleyForm' );
1472
1473    // if it is a Parsley supported single element, bind it too, except inputs type hidden
1474    // add here a return instance, cuz' we could call public methods on single elems with data[ option ]() above
1475    } else if ( $( this ).is( options.inputs ) ) {
1476      newInstance = bind( $( this ), !$( this ).is( 'input[type=radio], input[type=checkbox]' ) ? 'parsleyField' : 'parsleyFieldMultiple' );
1477    }
1478
1479    return 'function' === typeof fn ? fn() : newInstance;
1480  };
1481
1482  /* PARSLEY auto-binding
1483  * =================================================== */
1484  $( window ).on( 'load', function () {
1485    $( '[parsley-validate], [data-parsley-validate]' ).each( function () {
1486      $( this ).parsley();
1487    } );
1488  } );
1489
1490  /* PARSLEY DOM API
1491  * =================================================== */
1492  $.fn.domApi = function ( namespace ) {
1493    var attribute,
1494      obj = {}
1495      , regex = new RegExp("^" + namespace, 'i');
1496
1497    if ( 'undefined' === typeof this[ 0 ] ) {
1498      return {};
1499    }
1500
1501    for ( var i in this[ 0 ].attributes ) {
1502      attribute = this[ 0 ].attributes[ i ];
1503
1504      if ( 'undefined' !== typeof attribute && null !== attribute && attribute.specified && regex.test( attribute.name ) ) {
1505        obj[ camelize( attribute.name.replace( namespace, '' ) ) ] = deserializeValue( attribute.value );
1506      }
1507    }
1508
1509    return obj;
1510  };
1511
1512  // Zepto deserializeValue function
1513  // "true"  => true
1514  // "false" => false
1515  // "null"  => null
1516  // "42"    => 42
1517  // "42.5"  => 42.5
1518  // JSON    =>
1518 parse if valid
1519  // String  => self
1520  var deserializeValue = function( value ) {
1521    var num
1522    try {
1523      return value ?
1524        value == "true" ||
1525        ( value == "false" ? false :
1526          value == "null" ? null :
1527          !isNaN( num = Number( value ) ) ? num :
1528          /^[\[\{]/.test( value ) ? $.parseJSON( value ) :
1529          value )
1530        : value;
1531    } catch ( e ) {
1532      return value;
1533    }
1534  };
1535
1536  // Zepto camelize function
1537  var camelize = function ( str ) {
1538    return str.replace( /-+(.)?/g, function ( match, chr ) {
1539      return chr ? chr.toUpperCase() : '';
1540    } )
1541  };
1542
1543  // Zepto dasherize function
1544  var dasherize = function ( str ) {
1545    return str.replace( /::/g, '/' )
1546           .replace( /([A-Z]+)([A-Z][a-z])/g, '$1_$2' )
1547           .replace( /([a-z\d])([A-Z])/g, '$1_$2' )
1548           .replace( /_/g, '-' )
1549           .toLowerCase()
1550  };
1551
1552  /**
1553  * Parsley plugin configuration
1554  *
1555  * @property $.fn.parsley.defaults
1556  * @type {Object}
1557  */
1558  $.fn.parsley.defaults = {
1559    // basic data-api overridable properties here..
1560    namespace: 'parsley-'                       // DOM-API, default 'parsley-'. W3C valid would be 'data-parsley-' but quite ugly
1561    , inputs: 'input, textarea, select'         // Default supported inputs.
1562    , excluded: 'input[type=hidden], input[type=file], :disabled' // Do not validate input[type=hidden] & :disabled.
1563    , priorityEnabled: true                     // Will display only one error at the time depending on validators priorities
1564    , trigger: false                            // $.Event() that will trigger validation. eg: keyup, change..
1565    , animate: true                             // fade in / fade out error messages
1566    , animateDuration: 300                      // fadein/fadout ms time
1567    , scrollDuration: 500                       // Duration in ms time of the window scroll when focusing on invalid field (0 = no scroll)
1568    , focus: 'first'                            // 'fist'|'last'|'none' which error field would have focus first on form validation
1569    , validationMinlength: 3                    // If trigger validation specified, only if value.length > validationMinlength
1570    , successClass: 'parsley-success'           // Class name on each valid input
1571    , errorClass: 'parsley-error'               // Class name on each invalid input
1572    , errorMessage: false                       // Customize an unique error message showed if one constraint fails
1573    , validators: {}                            // Add your custom validators functions
1574    , showErrors: true                          // Set to false if you don't want Parsley to display error messages
1575    , useHtml5Constraints: true                 // Set to false if you don't want Parsley to use html5 constraints
1576    , messages: {}                              // Add your own error messages here
1577
1578    //some quite advanced configuration here..
1579    , validateIfUnchanged: false                                          // false: validate once by field value change
1580    , errors: {
1581        classHandler: function ( elem, isRadioOrCheckbox ) {}             // specify where parsley error-success classes are set
1582      , container: function ( elem, isRadioOrCheckbox ) {}                // specify an elem where errors will be **apened**
1583      , errorsWrapper: '<ul></ul>'                                        // do not set an id for this elem, it would have an auto-generated id
1584      , errorElem: '<li></li>'                                            // each field constraint fail in an li
1585      }
1586    , listeners: {
1587        onFieldValidate: function ( elem, ParsleyField ) { return false; } // Executed on validation. Return true to ignore field validation
1588      , onFormValidate: function ( isFormValid, event, ParsleyForm ) {}     // Executed once on form validation. Return (bool) false to block submit, even if valid
1589      , onFieldError: function ( elem, constraints, ParsleyField ) {}     // Executed when a field is detected as invalid
1590      , onFieldSuccess: function ( elem, constraints, ParsleyField ) {}   // Executed when a field passes validation
1591    }
1592  };
1593
1594// This plugin works with jQuery or Zepto (with data extension built for Zepto.)
1595} ( window.jQuery || window.Zepto );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.