parsley-rails 1.1.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ // parsley //////////////////////////////////////
7
+ defaultMessage: "Þetta gildi virðist vera ógilt."
8
+ , type: {
9
+ email: "Þetta ætti að vera gilt netfang."
10
+ , url: "Þetta ætti að vera gild vefslóð."
11
+ , urlstrict: "Þetta ætti að vera gild vefslóð."
12
+ , number: "Þetta ætti að vera gild tala."
13
+ , digits: "Þetta ætti að innihalda tölur."
14
+ , dateIso: "Þetta ætti að vera gild dagsetning (ÁÁÁÁ-MM-DD)."
15
+ , alphanum: "Þetta ætti að vera innihalda tölur og bókstafi."
16
+ }
17
+ , notnull: "Þetta gildi ætti ekki að vera autt."
18
+ , notblank: "Þetta gildi ætti ekki að vera tómt."
19
+ , required: "Þetta gildi er nauðsynlegt að fylla út."
20
+ , regexp: "Þetta gildi virðist vera ógilt."
21
+ , min: "Þetta gildi ætti að vera stærra en %s."
22
+ , max: "Þetta gildi ætti að vera minna en %s."
23
+ , range: "Þetta gildi ætti að vera milli %s og %s."
24
+ , minlength: "Þetta gildi er of stutt. Það ætti að innihalda %s stafi eða fleiri."
25
+ , maxlength: "Þetta gildi er of langt. Það ætti að innihalda %s stafi eða færri."
26
+ , rangelength: "Þetta gildi er ógilt. Það ætti að vera %s-%s stafir að lengd."
27
+ , equalto: "Þetta gildi ætti að vera eins."
28
+
29
+ // parsley.extend ///////////////////////////////
30
+ }
31
+ });
32
+ }(window.jQuery || window.Zepto));
@@ -0,0 +1,37 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ // parsley //////////////////////////////////////
7
+ defaultMessage: "Deze waarde lijkt onjuist."
8
+ , type: {
9
+ email: "Dit lijkt geen geldig e-mail adres te zijn."
10
+ , url: "Dit lijkt geen geldige URL te zijn."
11
+ , urlstrict: "Dit is geen geldige URL."
12
+ , number: "Deze waarde moet een nummer zijn."
13
+ , digits: "Deze waarde moet numeriek zijn."
14
+ , dateIso: "Deze waarde moet een datum in het volgende formaat zijn: (YYYY-MM-DD)."
15
+ , alphanum: "Deze waarde moet alfanumeriek zijn."
16
+ }
17
+ , notnull: "Deze waarde mag niet leeg zijn."
18
+ , notblank: "Deze waarde mag niet leeg zijn."
19
+ , required: "Dit veld is verplicht"
20
+ , regexp: "Deze waarde lijkt onjuist te zijn."
21
+ , min: "Deze waarde mag niet lager zijn dan %s."
22
+ , max: "Deze waarde mag niet groter zijn dan %s."
23
+ , range: "Deze waarde moet tussen %s en %s liggen."
24
+ , minlength: "Deze tekst is te kort. Deze moet uit minimaal %s karakters bestaan."
25
+ , maxlength: "Deze waarde is te lang. Deze mag maximaal %s karakters lang zijn."
26
+ , rangelength: "Deze waarde moet tussen %s en %s karakters lang zijn."
27
+ , equalto: "Deze waardes moeten identiek zijn."
28
+
29
+ // parsley.extend ///////////////////////////////
30
+ , minwords: "Deze waarde moet minstens %s woorden bevatten."
31
+ , maxwords: "Deze waarde mag maximaal %s woorden bevatten."
32
+ , rangewords: "Deze waarde moet tussen de %s en %s woorden bevatten."
33
+ , greaterthan: "Deze waarde moet groter dan %s zijn."
34
+ , lessthan: "Deze waarde moet kleiner dan %s zijn."
35
+ }
36
+ });
37
+ }(window.jQuery || window.Zepto));
@@ -0,0 +1,32 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ // parsley //////////////////////////////////////
7
+ defaultMessage: "Denne verdien er ikke gyldig."
8
+ , type: {
9
+ email: "Denne verdien må være en gyldig e-post."
10
+ , url: "Denne verdien må være en gyldig nettadresse."
11
+ , urlstrict: "Denne verdien må være en gyldig nettadresse."
12
+ , number: "Denne verdien må være et gyldig tall."
13
+ , digits: "Denne verdien må være tall."
14
+ , dateIso: "Denne verdien må være en gyldig dato (YYYY-MM-DD)."
15
+ , alphanum: "Denne verdien må være alfanumerisk(tall eller bokstaver)."
16
+ }
17
+ , notnull: "Denne verdien kan ikke være tom."
18
+ , notblank: "Denne verdien kan ikke være blank."
19
+ , required: "Dette feltet er obligatorisk."
20
+ , regexp: "Denne verdien er ikke gyldig."
21
+ , min: "Denne verdien må være større enn %s."
22
+ , max: "Denne verdien må være mindre enn %s."
23
+ , range: "Denne verdien må være mellom %s og %s."
24
+ , minlength: "Denne verdien er for kort. Den må være minst %s tegn."
25
+ , maxlength: "Denne verdien er for lang. Den må ikke være lenger enn %s tegn."
26
+ , rangelength: "Denne verdien har feil lengde. Lengden må være mellom %s og %s tegn."
27
+ , equalto: "Denne verdien må være lik."
28
+
29
+ // parsley.extend ///////////////////////////////
30
+ }
31
+ });
32
+ }(window.jQuery || window.Zepto));
@@ -0,0 +1,40 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ // parsley ////////PL/by/Tymek///////////////////
7
+ defaultMessage: "Wartość wygląda na nieprawidłową"
8
+ , type: {
9
+ email: "Wpisz poprawny adres e-mail"
10
+ , url: "Wpisz poprawny adres URL"
11
+ , urlstrict: "Wpisz poprawny adres URL"
12
+ , number: "Wpisz poprawną liczbę"
13
+ , digits: "Dozwolone jedynie cyfry"
14
+ , dateIso: "Wpisz poprawny format daty (RRRR-MM-DD)"
15
+ , alphanum: "Dozwolone jedynie znaki alfanumeryczne"
16
+ }
17
+ , notnull: "Wartość musi być różna od zera"
18
+ , notblank: "Pole nie może pozostać puste"
19
+ , required: "Pole wymagane"
20
+ , regexp: "Wartość wygląda na nieprawidłową"
21
+ , min: "Wartość powinna być większa od %s"
22
+ , max: "Wartość powinna być mniejsza od %s"
23
+ , range: "Wartość powinna być większa od %s i mniejsza od %s"
24
+ , minlength: "Ilość znaków powinna wynosić %s lub więcej"
25
+ , maxlength: "Ilość znaków powinna wynosić %s lub mniej"
26
+ , rangelength: "Ilość znaków powinna wynosić od %s do %s"
27
+ , mincheck: "Wybierz %s lub więcej opcji"
28
+ , maxcheck: "Wybierz %s lub mniej opcji"
29
+ , rangecheck: "Wybierz od %s do %s opcji"
30
+ , equalto: "Wartość musi być identyczna"
31
+
32
+ // parsley.extend ///////////////////////////////
33
+ , minwords: "Wpisz więcej niż %s wyrazów"
34
+ , maxwords: "Wartość nie może przekraczać %s wyrazów"
35
+ , rangewords: "Wartość musi zawierać od %s do %s wyrazów"
36
+ , greaterthan: "Wartość musi być większa niż %s"
37
+ , lessthan: "Wartość musi być mniejsza niż %s"
38
+ }
39
+ });
40
+ }(window.jQuery || window.Zepto));
@@ -0,0 +1,32 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ defaultMessage: "Este valor parece estar inválido."
7
+ , type: {
8
+ email: "Este valor deve ser um e-mail válido."
9
+ , url: "Este valor deve ser uma URL válida."
10
+ , urlstrict: "Este valor deve ser uma URL válida."
11
+ , number: "Este valor deve ser um número válido."
12
+ , digits: "Este valor deve ser um dígito válido."
13
+ , dateIso: "Este valor deve ser uma data válida (YYYY-MM-DD)."
14
+ , alphanum: "Este valor deve ser alfanumérico."
15
+ }
16
+ , notnull: "Este valor não deve ser nulo."
17
+ , notblank: "Este valor não deve ser branco."
18
+ , required: "Este valor é obrigatório."
19
+ , regexp: "Este valor parece estar inválido."
20
+ , min: "Este valor deve ser maior que %s."
21
+ , max: "Este valor deve ser menor que %s."
22
+ , range: "Este valor deve estar entre %s e %s."
23
+ , minlength: "Este valor é muito pequeno. Ele deve ter %s caracteres ou mais."
24
+ , maxlength: "Este valor é muito grande. Ele deve ter %s caracteres ou menos."
25
+ , rangelength: "O tamanho deste valor é inválido. Ele deve possuir entre %s e %s caracteres."
26
+ , equalto: "Este valor deve ser o mesmo."
27
+ , minwords: "Este valor deve possuir no mínimo %s palavras."
28
+ , maxwords: "Este valor deve possuir no máximo %s palavras."
29
+ , rangewords: "Este valor deve possuir entre %s e %s palavras."
30
+ }
31
+ });
32
+ }(window.jQuery || window.Zepto));
@@ -0,0 +1,35 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ // parsley //////////////////////////////////////
7
+ defaultMessage: "Поле заполнено некорректно."
8
+ , type: {
9
+ email: "Поле должно быть адресом электронной почты."
10
+ , url: "Поле должно быть ссылкой на сайт."
11
+ , urlstrict: "Поле должно быть ссылкой на сайт."
12
+ , number: "Поле должно быть числом."
13
+ , digits: "Поле должно содержать только цифры."
14
+ , dateIso: "Поле должно быть датой в формате (ГГГГ-ММ-ДД)."
15
+ , alphanum: "Поле должно содержать только цифры и буквы"
16
+ }
17
+ , notnull: "Поле должно быть не нулевым."
18
+ , notblank: "Поле не должно быть пустым."
19
+ , required: "Поле обязательно для заполнения."
20
+ , regexp: "Поле заполнено некорректно."
21
+ , min: "Значение поля должно быть больше %s."
22
+ , max: "Значение поля должно быть меньше %s."
23
+ , range: "Значение поля должно быть между %s и %s."
24
+ , minlength: "В поле должно быть минимум %s символов(а)."
25
+ , maxlength: "В поле должно быть не больше %s символов(а)."
26
+ , rangelength: "В поле должно быть от %s до %s символов(а)."
27
+ , mincheck: "Необходимо выбрать не менее %s пунктов(а)."
28
+ , maxcheck: "Необходимо выбрать не более %s пунктов(а)."
29
+ , rangecheck: "Необходимо выбрать от %s до %s пунктов."
30
+ , equalto: "Значения полей должны быть одинаковыми."
31
+
32
+ // parsley.extend ///////////////////////////////
33
+ }
34
+ });
35
+ }(window.jQuery || window.Zepto));
@@ -0,0 +1,36 @@
1
+ window.ParsleyConfig = window.ParsleyConfig || {};
2
+
3
+ (function ($) {
4
+ window.ParsleyConfig = $.extend( true, {}, window.ParsleyConfig, {
5
+ messages: {
6
+ // parsley //////////////////////////////////////
7
+ defaultMessage: "Thông tin này không hợp lệ."
8
+ , type: {
9
+ email: "Email không hợp lệ."
10
+ , url: "Url không hợp lệ."
11
+ , urlstrict: "Yêu cầu nhập địa chỉ url."
12
+ , number: "Yêu cầu nhập giá trị kiểu số."
13
+ , digits: "Yêu cầu nhập vào các chữ số."
14
+ , dateIso: "Yêu cầu nhập ngày tháng theo chuẩn sau (YYYY-MM-DD)."
15
+ , alphanum: "Yêu cầu nhập chữ cái hoặc chữ số."
16
+ }
17
+ , notnull: "Thông tin này chưa nhập."
18
+ , notblank: "Thông tin này không được để trống."
19
+ , required: "Thông tin này là bắt buộc."
20
+ , regexp: "Thông tin này không hợp lệ."
21
+ , min: "Giá trị này phải lớn hơn %s."
22
+ , max: "Giá trị này phải nhỏ hơn %s."
23
+ , range: "Giá trị này phải nằm trong khoảng từ %s đến %s."
24
+ , minlength: "Chuỗi nhập vào quá ngắn. Yêu cầu tối thiểu %s ký tự."
25
+ , maxlength: "Chuỗi nhập vào quá dài. Yêu cầu tối đa %s ký tự."
26
+ , rangelength: "Chuỗi nhập vào không hợp lệ. Yêu cầu độ dài trong khoảng từ %s đến %s ký tự."
27
+ , mincheck: "Không được chọn ít hơn %s lựa chọn."
28
+ , maxcheck: "Không được chọn nhiều hơn %s lựa chọn."
29
+ , rangecheck: "Phải chọn trong khoảng từ %s đến %s lựa chọn."
30
+ , equalto: "Giá trị phải trùng khớp."
31
+
32
+ // parsley.extend ///////////////////////////////
33
+ }
34
+ });
35
+ }(window.jQuery || window.Zepto));
36
+
@@ -0,0 +1,1276 @@
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
+ }
36
+ , notnull: "This value should not be null."
37
+ , notblank: "This value should not be blank."
38
+ , required: "This value is required."
39
+ , regexp: "This value seems to be invalid."
40
+ , min: "This value should be greater than %s."
41
+ , max: "This value should be lower than %s."
42
+ , range: "This value should be between %s and %s."
43
+ , minlength: "This value is too short. It should have %s characters or more."
44
+ , maxlength: "This value is too long. It should have %s characters or less."
45
+ , rangelength: "This value length is invalid. It should be between %s and %s characters long."
46
+ , mincheck: "You must select at least %s choices."
47
+ , maxcheck: "You must select %s choices or less."
48
+ , rangecheck: "You must select between %s and %s choices."
49
+ , equalto: "This value should be the same."
50
+ },
51
+
52
+ this.init( options );
53
+ };
54
+
55
+ Validator.prototype = {
56
+
57
+ constructor: Validator
58
+
59
+ /**
60
+ * Validator list. Built-in validators functions
61
+ *
62
+ * @property validators
63
+ * @type {Object}
64
+ */
65
+ , validators: {
66
+ notnull: function ( val ) {
67
+ return val.length > 0;
68
+ }
69
+
70
+ , notblank: function ( val ) {
71
+ return null !== val && '' !== val.replace( /^\s+/g, '' ).replace( /\s+$/g, '' );
72
+ }
73
+
74
+ // Works on all inputs. val is object for checkboxes
75
+ , required: function ( val ) {
76
+
77
+ // for checkboxes and select multiples. Check there is at least one required value
78
+ if ( 'object' === typeof val ) {
79
+ for ( var i in val ) {
80
+ if ( this.required( val[ i ] ) ) {
81
+ return true;
82
+ }
83
+ }
84
+
85
+ return false;
86
+ }
87
+
88
+ return this.notnull( val ) && this.notblank( val );
89
+ }
90
+
91
+ , type: function ( val, type ) {
92
+ var regExp;
93
+
94
+ switch ( type ) {
95
+ case 'number':
96
+ regExp = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/;
97
+ break;
98
+ case 'digits':
99
+ regExp = /^\d+$/;
100
+ break;
101
+ case 'alphanum':
102
+ regExp = /^\w+$/;
103
+ break;
104
+ case 'email':
105
+ 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;
106
+ break;
107
+ case 'url':
108
+ val = new RegExp( '(https?|s?ftp|git)', 'i' ).test( val ) ? val : 'http://' + val;
109
+ /* falls through */
110
+ case 'urlstrict':
111
+ 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;
112
+ break;
113
+ case 'dateIso':
114
+ regExp = /^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$/;
115
+ break;
116
+ default:
117
+ return false;
118
+ }
119
+
120
+ // test regExp if not null
121
+ return '' !== val ? regExp.test( val ) : false;
122
+ }
123
+
124
+ , regexp: function ( val, regExp ) {
125
+ return new RegExp( regExp, 'i' ).test( val );
126
+ }
127
+
128
+ , minlength: function ( val, min ) {
129
+ return val.length >= min;
130
+ }
131
+
132
+ , maxlength: function ( val, max ) {
133
+ return val.length <= max;
134
+ }
135
+
136
+ , rangelength: function ( val, arrayRange ) {
137
+ return this.minlength( val, arrayRange[ 0 ] ) && this.maxlength( val, arrayRange[ 1 ] );
138
+ }
139
+
140
+ , min: function ( val, min ) {
141
+ return Number( val ) >= min;
142
+ }
143
+
144
+ , max: function ( val, max ) {
145
+ return Number( val ) <= max;
146
+ }
147
+
148
+ , range: function ( val, arrayRange ) {
149
+ return val >= arrayRange[ 0 ] && val <= arrayRange[ 1 ];
150
+ }
151
+
152
+ , equalto: function ( val, elem, self ) {
153
+ self.options.validateIfUnchanged = true;
154
+
155
+ return val === $( elem ).val();
156
+ }
157
+
158
+ , remote: function ( val, url, self ) {
159
+ var result = null
160
+ , data = {}
161
+ , dataType = {};
162
+
163
+ data[ self.$element.attr( 'name' ) ] = val;
164
+
165
+ if ( 'undefined' !== typeof self.options.remoteDatatype ) {
166
+ dataType = { dataType: self.options.remoteDatatype };
167
+ }
168
+
169
+ var manage = function ( isConstraintValid, message ) {
170
+ // remove error message because ajax response message could change depending on the sent value !
171
+ self.removeError( 'remote' );
172
+
173
+ self.updtConstraint( { name: 'remote', isValid: isConstraintValid }, message );
174
+ self.manageValidationResult();
175
+ };
176
+
177
+ // transform string response into object
178
+ var handleResponse = function ( response ) {
179
+ if ( 'object' === typeof response ) {
180
+ return response;
181
+ }
182
+
183
+ try {
184
+ response = $.parseJSON( response );
185
+ } catch ( err ) {}
186
+
187
+ return response;
188
+ }
189
+
190
+ var manageErrorMessage = function ( response ) {
191
+ return 'object' === typeof response && null !== response ? ( 'undefined' !== typeof response.error ? response.error : ( 'undefined' !== typeof response.message ? response.message : null ) ) : null;
192
+ }
193
+
194
+ $.ajax( $.extend( {}, {
195
+ url: url
196
+ , data: data
197
+ , async: self.async
198
+ , method: self.options.remoteMethod || 'GET'
199
+ , success: function ( response ) {
200
+ response = handleResponse( response );
201
+ manage( 1 === response || true === response || ( 'object' === typeof response && null !== response && 'undefined' !== typeof response.success ), manageErrorMessage( response )
202
+ );
203
+ }
204
+ , error: function ( response ) {
205
+ response = handleResponse( response );
206
+ manage( false, manageErrorMessage( response ) );
207
+ }
208
+ }, dataType ) );
209
+
210
+ if ( self.async ) {
211
+ manage( result );
212
+ }
213
+
214
+ return result;
215
+ }
216
+
217
+ /**
218
+ * Aliases for checkboxes constraints
219
+ */
220
+ , mincheck: function ( obj, val ) {
221
+ return this.minlength( obj, val );
222
+ }
223
+
224
+ , maxcheck: function ( obj, val ) {
225
+ return this.maxlength( obj, val);
226
+ }
227
+
228
+ , rangecheck: function ( obj, arrayRange ) {
229
+ return this.rangelength( obj, arrayRange );
230
+ }
231
+ }
232
+
233
+ /*
234
+ * Register custom validators and messages
235
+ */
236
+ , init: function ( options ) {
237
+ var customValidators = options.validators
238
+ , customMessages = options.messages;
239
+
240
+ var key;
241
+ for ( key in customValidators ) {
242
+ this.addValidator(key, customValidators[ key ]);
243
+ }
244
+
245
+ for ( key in customMessages ) {
246
+ this.addMessage(key, customMessages[ key ]);
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Replace %s placeholders by values
252
+ *
253
+ * @method formatMesssage
254
+ * @param {String} message Message key
255
+ * @param {Mixed} args Args passed by validators functions. Could be string, number or object
256
+ * @return {String} Formatted string
257
+ */
258
+ , formatMesssage: function ( message, args ) {
259
+
260
+ if ( 'object' === typeof args ) {
261
+ for ( var i in args ) {
262
+ message = this.formatMesssage( message, args[ i ] );
263
+ }
264
+
265
+ return message;
266
+ }
267
+
268
+ return 'string' === typeof message ? message.replace( new RegExp( '%s', 'i' ), args ) : '';
269
+ }
270
+
271
+ /**
272
+ * Add / override a validator in validators list
273
+ *
274
+ * @method addValidator
275
+ * @param {String} name Validator name. Will automatically bindable through data-name=''
276
+ * @param {Function} fn Validator function. Must return {Boolean}
277
+ */
278
+ , addValidator: function ( name, fn ) {
279
+ this.validators[ name ] = fn;
280
+ }
281
+
282
+ /**
283
+ * Add / override error message
284
+ *
285
+ * @method addMessage
286
+ * @param {String} name Message name. Will automatically be binded to validator with same name
287
+ * @param {String} message Message
288
+ */
289
+ , addMessage: function ( key, message, type ) {
290
+
291
+ if ( 'undefined' !== typeof type && true === type ) {
292
+ this.messages.type[ key ] = message;
293
+ return;
294
+ }
295
+
296
+ // custom types messages are a bit tricky cuz' nested ;)
297
+ if ( 'type' === key ) {
298
+ for ( var i in message ) {
299
+ this.messages.type[ i ] = message[ i ];
300
+ }
301
+
302
+ return;
303
+ }
304
+
305
+ this.messages[ key ] = message;
306
+ }
307
+ };
308
+
309
+ /**
310
+ * ParsleyField class manage each form field inside a validated Parsley form.
311
+ * Returns if field valid or not depending on its value and constraints
312
+ * Manage field error display and behavior, event triggers and more
313
+ *
314
+ * @class ParsleyField
315
+ * @constructor
316
+ */
317
+ var ParsleyField = function ( element, options, type ) {
318
+ this.options = options;
319
+ this.Validator = new Validator( options );
320
+ this.init( element, type || 'ParsleyField' );
321
+ };
322
+
323
+ ParsleyField.prototype = {
324
+
325
+ constructor: ParsleyField
326
+
327
+ /**
328
+ * Set some properties, bind constraint validators and validation events
329
+ *
330
+ * @method init
331
+ * @param {Object} element
332
+ * @param {Object} options
333
+ */
334
+ , init: function ( element, type ) {
335
+ this.type = type;
336
+ this.isValid = true;
337
+ this.element = element;
338
+ this.validatedOnce = false;
339
+ this.$element = $( element );
340
+ this.val = this.$element.val();
341
+ this.isRequired = false;
342
+ this.constraints = [];
343
+
344
+ // overriden by ParsleyItemMultiple if radio or checkbox input
345
+ if ( 'undefined' === typeof this.isRadioOrCheckbox ) {
346
+ this.isRadioOrCheckbox = false;
347
+ this.hash = this.generateHash();
348
+ this.errorClassHandler = this.options.errors.classHandler( element, this.isRadioOrCheckbox ) || this.$element;
349
+ }
350
+
351
+ // error ul dom management done only once at init
352
+ this.ulErrorManagement();
353
+
354
+ // bind some html5 properties
355
+ this.bindHtml5Constraints();
356
+
357
+ // bind validators to field
358
+ this.addConstraints();
359
+
360
+ // bind parsley events if validators have been registered
361
+ if ( this.constraints.length ) {
362
+ this.bindValidationEvents();
363
+ }
364
+ }
365
+
366
+ , setParent: function ( elem ) {
367
+ this.$parent = $( elem );
368
+ }
369
+
370
+ , getParent: function () {
371
+ return this.$parent;
372
+ }
373
+
374
+ /**
375
+ * Bind some extra html5 types / validators
376
+ *
377
+ * @private
378
+ * @method bindHtml5Constraints
379
+ */
380
+ , bindHtml5Constraints: function () {
381
+ // add html5 required support + class required support
382
+ if ( this.$element.hasClass( 'required' ) || this.$element.attr( 'required' ) ) {
383
+ this.options.required = true;
384
+ }
385
+
386
+ // add html5 supported types & options
387
+ if ( 'undefined' !== typeof this.$element.attr( 'type' ) && new RegExp( this.$element.attr( 'type' ), 'i' ).test( 'email url number range' ) ) {
388
+ this.options.type = this.$element.attr( 'type' );
389
+
390
+ // number and range types could have min and/or max values
391
+ if ( new RegExp( this.options.type, 'i' ).test( 'number range' ) ) {
392
+ this.options.type = 'number';
393
+
394
+ // double condition to support jQuery and Zepto.. :(
395
+ if ( 'undefined' !== typeof this.$element.attr( 'min' ) && this.$element.attr( 'min' ).length ) {
396
+ this.options.min = this.$element.attr( 'min' );
397
+ }
398
+
399
+ if ( 'undefined' !== typeof this.$element.attr( 'max' ) && this.$element.attr( 'max' ).length ) {
400
+ this.options.max = this.$element.attr( 'max' );
401
+ }
402
+ }
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Attach field validators functions passed through data-api
408
+ *
409
+ * @private
410
+ * @method addConstraints
411
+ */
412
+ , addConstraints: function () {
413
+ for ( var constraint in this.options ) {
414
+ var addConstraint = {};
415
+ addConstraint[ constraint ] = this.options[ constraint ];
416
+ this.addConstraint( addConstraint, true );
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Dynamically add a new constraint to a field
422
+ *
423
+ * @method addConstraint
424
+ * @param {Object} constraint { name: requirements }
425
+ */
426
+ , addConstraint: function ( constraint, doNotUpdateValidationEvents ) {
427
+ for ( var name in constraint ) {
428
+ name = name.toLowerCase();
429
+
430
+ if ( 'function' === typeof this.Validator.validators[ name ] ) {
431
+ this.constraints.push( {
432
+ name: name
433
+ , requirements: constraint[ name ]
434
+ , isValid: null
435
+ } );
436
+
437
+ if ( name === 'required' ) {
438
+ this.isRequired = true;
439
+ }
440
+
441
+ this.addCustomConstraintMessage( name );
442
+ }
443
+ }
444
+
445
+ // force field validation next check and reset validation events
446
+ if ( 'undefined' === typeof doNotUpdateValidationEvents ) {
447
+ this.bindValidationEvents();
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Dynamically update an existing constraint to a field.
453
+ * Simple API: { name: requirements }
454
+ *
455
+ * @method updtConstraint
456
+ * @param {Object} constraint
457
+ */
458
+ , updateConstraint: function ( constraint, message ) {
459
+ for ( var name in constraint ) {
460
+ this.updtConstraint( { name: name, requirements: constraint[ name ], isValid: null }, message );
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Dynamically update an existing constraint to a field.
466
+ * Complex API: { name: name, requirements: requirements, isValid: boolean }
467
+ *
468
+ * @method updtConstraint
469
+ * @param {Object} constraint
470
+ */
471
+ , updtConstraint: function ( constraint, message ) {
472
+ for ( var i in this.constraints ) {
473
+ if ( this.constraints[ i ].name === constraint.name ) {
474
+ this.constraints[ i ] = $.extend( true, this.constraints[ i ], constraint );
475
+ if ( 'string' === typeof message ) {
476
+ this.Validator.messages[ this.constraints[ i ].name ] = message ;
477
+ }
478
+ }
479
+ }
480
+
481
+ // force field validation next check and reset validation events
482
+ this.bindValidationEvents();
483
+ }
484
+
485
+ /**
486
+ * Dynamically remove an existing constraint to a field.
487
+ *
488
+ * @method removeConstraint
489
+ * @param {String} constraintName
490
+ */
491
+ , removeConstraint: function ( constraintName ) {
492
+ var constraintName = constraintName.toLowerCase()
493
+ , updatedConstraints = [];
494
+
495
+ for ( var constraint in this.constraints ) {
496
+ if ( this.constraints[ constraint ].name !== constraintName ) {
497
+ updatedConstraints.push( this.constraints[ constraint ] );
498
+ }
499
+ }
500
+
501
+ if ( constraintName === 'required' ) {
502
+ this.isRequired = false;
503
+ }
504
+
505
+ this.constraints = updatedConstraints;
506
+
507
+ // if there are no more constraint, destroy parsley instance for this field
508
+ if ( updatedConstraints.length === 0 ) {
509
+ // in a form context, remove item from parent
510
+ if ( 'ParsleyForm' === typeof this.getParent() ) {
511
+ this.getParent().removeItem( this.$element );
512
+ return;
513
+ }
514
+
515
+ this.destroy();
516
+ return;
517
+ }
518
+
519
+ this.bindValidationEvents();
520
+ }
521
+
522
+ /**
523
+ * Add custom constraint message, passed through data-API
524
+ *
525
+ * @private
526
+ * @method addCustomConstraintMessage
527
+ * @param constraint
528
+ */
529
+ , addCustomConstraintMessage: function ( constraint ) {
530
+ // custom message type data-type-email-message -> typeEmailMessage | data-minlength-error => minlengthMessage
531
+ var customMessage = constraint
532
+ + ( 'type' === constraint && 'undefined' !== typeof this.options[ constraint ] ? this.options[ constraint ].charAt( 0 ).toUpperCase() + this.options[ constraint ].substr( 1 ) : '' )
533
+ + 'Message';
534
+
535
+ if ( 'undefined' !== typeof this.options[ customMessage ] ) {
536
+ this.Validator.addMessage( 'type' === constraint ? this.options[ constraint ] : constraint, this.options[ customMessage ], 'type' === constraint );
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Bind validation events on a field
542
+ *
543
+ * @private
544
+ * @method bindValidationEvents
545
+ */
546
+ , bindValidationEvents: function () {
547
+ // this field has validation events, that means it has to be validated
548
+ this.isValid = null;
549
+ this.$element.addClass( 'parsley-validated' );
550
+
551
+ // remove eventually already binded events
552
+ this.$element.off( '.' + this.type );
553
+
554
+ // alaways bind keyup event, for better UX when a field is invalid
555
+ var triggers = ( !this.options.trigger ? '' : this.options.trigger + ' ' )
556
+ + ( new RegExp( 'key', 'i' ).test( this.options.trigger ) ? '' : 'keyup' );
557
+
558
+ // force add 'change' event if async remote validator here to have result before form submitting
559
+ if ( this.options.remote ) {
560
+ triggers += new RegExp( 'change', 'i' ).test( triggers ) ? '' : ' change';
561
+ }
562
+
563
+ this.$element.on( ( triggers + ' ' ).split( ' ' ).join( '.' + this.type + ' ' ), false, $.proxy( this.eventValidation, this ) );
564
+ }
565
+
566
+ /**
567
+ * Hash management. Used for ul error
568
+ *
569
+ * @method generateHash
570
+ * @returns {String} 5 letters unique hash
571
+ */
572
+ , generateHash: function () {
573
+ return 'parsley-' + ( Math.random() + '' ).substring( 2 );
574
+ }
575
+
576
+ /**
577
+ * Public getHash accessor
578
+ *
579
+ * @method getHash
580
+ * @returns {String} hash
581
+ */
582
+ , getHash: function () {
583
+ return this.hash;
584
+ }
585
+
586
+ /**
587
+ * Returns field val needed for validation
588
+ * Special treatment for radio & checkboxes
589
+ *
590
+ * @method getVal
591
+ * @returns {String} val
592
+ */
593
+ , getVal: function () {
594
+ return this.$element.val();
595
+ }
596
+
597
+ /**
598
+ * Called when validation is triggered by an event
599
+ * Do nothing if val.length < this.options.validationMinlength
600
+ *
601
+ * @method eventValidation
602
+ * @param {Object} event jQuery event
603
+ */
604
+ , eventValidation: function ( event ) {
605
+ var val = this.getVal();
606
+
607
+ // do nothing on keypress event if not explicitely passed as data-trigger and if field has not already been validated once
608
+ if ( event.type === 'keyup' && !/keyup/i.test( this.options.trigger ) && !this.validatedOnce ) {
609
+ return true;
610
+ }
611
+
612
+ // start validation process only if field has enough chars and validation never started
613
+ if ( !this.isRadioOrCheckbox && val.length < this.options.validationMinlength && !this.validatedOnce ) {
614
+ return true;
615
+ }
616
+
617
+ this.validate( true, false );
618
+ }
619
+
620
+ /**
621
+ * Return if field verify its constraints
622
+ *
623
+ * @method isValid
624
+ * @return {Boolean} Is field valid or not
625
+ */
626
+ , isFieldValid: function () {
627
+ return this.validate( false, false );
628
+ }
629
+
630
+ /**
631
+ * Validate a field & display errors
632
+ *
633
+ * @method validate
634
+ * @param {Boolean} errorBubbling set to false if you just want isValid boolean without error bubbling next to fields
635
+ * @param {Boolean} async if false, wait ajax calls returns
636
+ * @return {Boolean} Is field valid or not
637
+ */
638
+ , validate: function ( errorBubbling, async ) {
639
+ var val = this.getVal()
640
+ , isValid = null;
641
+
642
+ // reset Parsley validation if onFieldValidate returns true, or if field is empty and not required
643
+ if ( this.options.listeners.onFieldValidate( this.element, this ) || ( '' === val && !this.isRequired ) ) {
644
+ this.reset();
645
+ return null;
646
+ }
647
+
648
+ // do not validate a field already validated and unchanged !
649
+ if ( !this.needsValidation( val ) ) {
650
+ return this.isValid;
651
+ }
652
+
653
+ this.errorBubbling = 'undefined' !== typeof errorBubbling ? errorBubbling : true;
654
+ this.async = 'undefined' !== typeof async ? async : true;
655
+
656
+ isValid = this.applyValidators();
657
+
658
+ if ( this.errorBubbling ) {
659
+ this.manageValidationResult();
660
+ }
661
+
662
+ return isValid;
663
+ }
664
+
665
+ /**
666
+ * Check if value has changed since previous validation
667
+ *
668
+ * @method needsValidation
669
+ * @param value
670
+ * @return {Boolean}
671
+ */
672
+ , needsValidation: function ( val ) {
673
+ if ( !this.options.validateIfUnchanged && this.isValid !== null && this.val === val && this.validatedOnce ) {
674
+ return false;
675
+ }
676
+
677
+ this.val = val;
678
+ return this.validatedOnce = true;
679
+ }
680
+
681
+ /**
682
+ * Loop through every fields validators
683
+ * Adds errors after unvalid fields
684
+ *
685
+ * @method applyValidators
686
+ * @return {Mixed} {Boolean} If field valid or not, null if not validated
687
+ */
688
+ , applyValidators: function () {
689
+ var isValid = null;
690
+
691
+ for ( var constraint = 0; constraint < this.constraints.length; constraint++ ) {
692
+ var result = this.Validator.validators[ this.constraints[ constraint ].name ]( this.val, this.constraints[ constraint ].requirements, this );
693
+
694
+ if ( false === result ) {
695
+ isValid = false;
696
+ this.constraints[ constraint ].isValid = isValid;
697
+ } else if ( true === result ) {
698
+ this.constraints[ constraint ].isValid = true;
699
+ isValid = false !== isValid;
700
+ }
701
+ }
702
+
703
+ return isValid;
704
+ }
705
+
706
+ /**
707
+ * Fired when all validators have be executed
708
+ * Returns true or false if field is valid or not
709
+ * Display errors messages below faild fields
710
+ * Adds parsley-success or parsley-error class on fields
711
+ *
712
+ * @method manageValidationResult
713
+ * @return {Boolean} Is field valid or not
714
+ */
715
+ , manageValidationResult: function () {
716
+ var isValid = null;
717
+
718
+ for ( var constraint = 0; constraint < this.constraints.length; constraint++ ) {
719
+ if ( false === this.constraints[ constraint ].isValid ) {
720
+ this.manageError( this.constraints[ constraint ] );
721
+ isValid = false;
722
+ } else if ( true === this.constraints[ constraint ].isValid ) {
723
+ this.removeError( this.constraints[ constraint ].name );
724
+ isValid = false !== isValid;
725
+ }
726
+ }
727
+
728
+ this.isValid = isValid;
729
+
730
+ if ( true === this.isValid ) {
731
+ this.removeErrors();
732
+ this.errorClassHandler.removeClass( this.options.errorClass ).addClass( this.options.successClass );
733
+ this.options.listeners.onFieldSuccess( this.element, this.constraints, this );
734
+ return true;
735
+ } else if ( false === this.isValid ) {
736
+ this.errorClassHandler.removeClass( this.options.successClass ).addClass( this.options.errorClass );
737
+ this.options.listeners.onFieldError( this.element, this.constraints, this );
738
+ return false;
739
+ }
740
+
741
+ return isValid;
742
+ }
743
+
744
+ /**
745
+ * Manage ul error Container
746
+ *
747
+ * @private
748
+ * @method ulErrorManagement
749
+ */
750
+ , ulErrorManagement: function () {
751
+ this.ulError = '#' + this.hash;
752
+ this.ulTemplate = $( this.options.errors.errorsWrapper ).attr( 'id', this.hash ).addClass( 'parsley-error-list' );
753
+ }
754
+
755
+ /**
756
+ * Remove li / ul error
757
+ *
758
+ * @method removeError
759
+ * @param {String} constraintName Method Name
760
+ */
761
+ , removeError: function ( constraintName ) {
762
+ var liError = this.ulError + ' .' + constraintName;
763
+
764
+ this.options.animate ? $( liError ).fadeOut( this.options.animateDuration, function () { $( this ).remove() } ) : $( liError ).remove();
765
+
766
+ // remove li error, and ul error if no more li inside
767
+ if ( this.ulError && $( this.ulError ).children().length === 0 ) {
768
+ this.removeErrors();
769
+ }
770
+ }
771
+
772
+ /**
773
+ * Add li error
774
+ *
775
+ * @method addError
776
+ * @param {Object} { minlength: "error message for minlength constraint" }
777
+ */
778
+ , addError: function ( error ) {
779
+ for ( var constraint in error ) {
780
+ var liTemplate = $( this.options.errors.errorElem ).addClass( constraint );
781
+
782
+ $( this.ulError ).append( this.options.animate ? $( liTemplate ).text( error[ constraint ] ).hide().fadeIn( this.options.animateDuration ) : $( liTemplate ).text( error[ constraint ] ) );
783
+ }
784
+ }
785
+
786
+ /**
787
+ * Remove all ul / li errors
788
+ *
789
+ * @method removeErrors
790
+ */
791
+ , removeErrors: function () {
792
+ this.options.animate ? $( this.ulError ).fadeOut( this.options.animateDuration, function () { $( this ).remove(); } ) : $( this.ulError ).remove();
793
+ }
794
+
795
+ /**
796
+ * Remove ul errors and parsley error or success classes
797
+ *
798
+ * @method reset
799
+ */
800
+ , reset: function () {
801
+ this.isValid = null;
802
+ this.removeErrors();
803
+ this.errorClassHandler.removeClass( this.options.successClass ).removeClass( this.options.errorClass );
804
+ return this;
805
+ }
806
+
807
+ /**
808
+ * Add li / ul errors messages
809
+ *
810
+ * @method manageError
811
+ * @param {Object} constraint
812
+ */
813
+ , manageError: function ( constraint ) {
814
+ // display ulError container if it has been removed previously (or never shown)
815
+ if ( !$( this.ulError ).length ) {
816
+ this.manageErrorContainer();
817
+ }
818
+
819
+ // TODO: refacto error name w/ proper & readable function
820
+ var constraintName = constraint.name
821
+ , liClass = false !== this.options.errorMessage ? 'custom-error-message' : constraintName
822
+ , liError = {}
823
+ , message = false !== this.options.errorMessage ? this.options.errorMessage : ( constraint.name === 'type' ?
824
+ this.Validator.messages[ constraintName ][ constraint.requirements ] : ( 'undefined' === typeof this.Validator.messages[ constraintName ] ?
825
+ this.Validator.messages.defaultMessage : this.Validator.formatMesssage( this.Validator.messages[ constraintName ], constraint.requirements ) ) );
826
+
827
+ // add liError if not shown. Do not add more than once custom errorMessage if exist
828
+ if ( !$( this.ulError + ' .' + liClass ).length ) {
829
+ liError[ liClass ] = message;
830
+ this.addError( liError );
831
+ }
832
+ }
833
+
834
+ /**
835
+ * Create ul error container
836
+ *
837
+ * @method manageErrorContainer
838
+ */
839
+ , manageErrorContainer: function () {
840
+ var errorContainer = this.options.errors.container( this.element, this.isRadioOrCheckbox )
841
+ , ulTemplate = this.options.animate ? this.ulTemplate.show() : this.ulTemplate;
842
+
843
+ if ( 'undefined' !== typeof errorContainer ) {
844
+ $( errorContainer ).append( ulTemplate );
845
+ return;
846
+ }
847
+
848
+ !this.isRadioOrCheckbox ? this.$element.after( ulTemplate ) : this.$element.parent().after( ulTemplate );
849
+ }
850
+
851
+ /**
852
+ * Add custom listeners
853
+ *
854
+ * @param {Object} { listener: function () {} }, eg { onFormSubmit: function ( isValid, event, focus ) { ... } }
855
+ */
856
+ , addListener: function ( object ) {
857
+ for ( var listener in object ) {
858
+ this.options.listeners[ listener ] = object[ listener ];
859
+ }
860
+ }
861
+
862
+ /**
863
+ * Destroy parsley field instance
864
+ *
865
+ * @private
866
+ * @method destroy
867
+ */
868
+ , destroy: function () {
869
+ this.$element.removeClass( 'parsley-validated' );
870
+ this.errorClassHandler.removeClass( this.options.errorClass ).removeClass( this.options.successClass );
871
+ this.reset().$element.off( '.' + this.type ).removeData( this.type );
872
+ }
873
+ };
874
+
875
+ /**
876
+ * ParsleyFieldMultiple override ParsleyField for checkbox and radio inputs
877
+ * Pseudo-heritance to manage divergent behavior from ParsleyItem in dedicated methods
878
+ *
879
+ * @class ParsleyFieldMultiple
880
+ * @constructor
881
+ */
882
+ var ParsleyFieldMultiple = function ( element, options, type ) {
883
+ this.initMultiple( element, options );
884
+ this.inherit( element, options );
885
+ this.Validator = new Validator( options );
886
+
887
+ // call ParsleyField constructor
888
+ this.init( element, type || 'ParsleyFieldMultiple' );
889
+ };
890
+
891
+ ParsleyFieldMultiple.prototype = {
892
+
893
+ constructor: ParsleyFieldMultiple
894
+
895
+ /**
896
+ * Set some specific properties, call some extra methods to manage radio / checkbox
897
+ *
898
+ * @method init
899
+ * @param {Object} element
900
+ * @param {Object} options
901
+ */
902
+ , initMultiple: function ( element, options ) {
903
+ this.element = element;
904
+ this.$element = $( element );
905
+ this.group = options.group || false;
906
+ this.hash = this.getName();
907
+ this.siblings = this.group ? '[data-group="' + this.group + '"]' : 'input[name="' + this.$element.attr( 'name' ) + '"]';
908
+ this.isRadioOrCheckbox = true;
909
+ this.isRadio = this.$element.is( 'input[type=radio]' );
910
+ this.isCheckbox = this.$element.is( 'input[type=checkbox]' );
911
+ this.errorClassHandler = options.errors.classHandler( element, this.isRadioOrCheckbox ) || this.$element.parent();
912
+ }
913
+
914
+ /**
915
+ * Set specific constraints messages, do pseudo-heritance
916
+ *
917
+ * @private
918
+ * @method inherit
919
+ * @param {Object} element
920
+ * @param {Object} options
921
+ */
922
+ , inherit: function ( element, options ) {
923
+ var clone = new ParsleyField( element, options );
924
+
925
+ for ( var property in clone ) {
926
+ if ( 'undefined' === typeof this[ property ] ) {
927
+ this[ property ] = clone [ property ];
928
+ }
929
+ }
930
+ }
931
+
932
+ /**
933
+ * Set specific constraints messages, do pseudo-heritance
934
+ *
935
+ * @method getName
936
+ * @returns {String} radio / checkbox hash is cleaned 'name' or data-group property
937
+ */
938
+ , getName: function () {
939
+ if ( this.group ) {
940
+ return 'parsley-' + this.group;
941
+ }
942
+
943
+ if ( 'undefined' === typeof this.$element.attr( 'name' ) ) {
944
+ throw "A radio / checkbox input must have a data-group attribute or a name to be Parsley validated !";
945
+ }
946
+
947
+ return 'parsley-' + this.$element.attr( 'name' ).replace( /(:|\.|\[|\])/g, '' );
948
+ }
949
+
950
+ /**
951
+ * Special treatment for radio & checkboxes
952
+ * Returns checked radio or checkboxes values
953
+ *
954
+ * @method getVal
955
+ * @returns {String} val
956
+ */
957
+ , getVal: function () {
958
+ if ( this.isRadio ) {
959
+ return $( this.siblings + ':checked' ).val() || '';
960
+ }
961
+
962
+ if ( this.isCheckbox ) {
963
+ var values = [];
964
+
965
+ $( this.siblings + ':checked' ).each( function () {
966
+ values.push( $( this ).val() );
967
+ } );
968
+
969
+ return values;
970
+ }
971
+ }
972
+
973
+ /**
974
+ * Bind validation events on a field
975
+ *
976
+ * @private
977
+ * @method bindValidationEvents
978
+ */
979
+ , bindValidationEvents: function () {
980
+ // this field has validation events, that means it has to be validated
981
+ this.isValid = null;
982
+ this.$element.addClass( 'parsley-validated' );
983
+
984
+ // remove eventually already binded events
985
+ this.$element.off( '.' + this.type );
986
+
987
+ // alaways bind keyup event, for better UX when a field is invalid
988
+ var self = this
989
+ , triggers = ( !this.options.trigger ? '' : this.options.trigger + ' ' )
990
+ + ( new RegExp( 'change', 'i' ).test( this.options.trigger ) ? '' : 'change' );
991
+
992
+ // bind trigger event on every siblings
993
+ $( this.siblings ).each(function () {
994
+ $( this ).on( triggers.split( ' ' ).join( '.' + self.type + ' ' ), false, $.proxy( self.eventValidation, self ) );
995
+ } )
996
+ }
997
+
998
+ /**
999
+ * Called when validation is triggered by an event
1000
+ * Do nothing if never validated. validate on change otherwise
1001
+ *
1002
+ * @method eventValidation
1003
+ * @param {Object} event jQuery event
1004
+ */
1005
+ , eventValidation: function ( event ) {
1006
+ // start validation process only if field has enough chars and validation never started
1007
+ if ( !this.validatedOnce ) {
1008
+ return true;
1009
+ }
1010
+
1011
+ this.validate( true, false );
1012
+ }
1013
+ };
1014
+
1015
+ /**
1016
+ * ParsleyForm class manage Parsley validated form.
1017
+ * Manage its fields and global validation
1018
+ *
1019
+ * @class ParsleyForm
1020
+ * @constructor
1021
+ */
1022
+ var ParsleyForm = function ( element, options, type ) {
1023
+ this.init( element, options, type || 'parsleyForm' );
1024
+ };
1025
+
1026
+ ParsleyForm.prototype = {
1027
+
1028
+ constructor: ParsleyForm
1029
+
1030
+ /* init data, bind jQuery on() actions */
1031
+ , init: function ( element, options, type ) {
1032
+ this.type = type;
1033
+ this.items = [];
1034
+ this.$element = $( element );
1035
+ this.options = options;
1036
+ var self = this;
1037
+
1038
+ this.$element.find( options.inputs ).each( function () {
1039
+ self.addItem( this );
1040
+ });
1041
+
1042
+ this.$element.on( 'submit.' + this.type , false, $.proxy( this.validate, this ) );
1043
+ }
1044
+
1045
+ /**
1046
+ * Add custom listeners
1047
+ *
1048
+ * @param {Object} { listener: function () {} }, eg { onFormSubmit: function ( isValid, event, focus ) { ... } }
1049
+ */
1050
+ , addListener: function ( object ) {
1051
+ for ( var listener in object ) {
1052
+ if ( new RegExp( 'Field' ).test( listener ) ) {
1053
+ for ( var item = 0; item < this.items.length; item++ ) {
1054
+ this.items[ item ].addListener( object );
1055
+ }
1056
+ } else {
1057
+ this.options.listeners[ listener ] = object[ listener ];
1058
+ }
1059
+ }
1060
+ }
1061
+
1062
+ /**
1063
+ * Adds a new parsleyItem child to ParsleyForm
1064
+ *
1065
+ * @method addItem
1066
+ * @param elem
1067
+ */
1068
+ , addItem: function ( elem ) {
1069
+ if ( $( elem ).is( this.options.excluded ) ) {
1070
+ return false;
1071
+ }
1072
+
1073
+ var ParsleyField = $( elem ).parsley( this.options );
1074
+ ParsleyField.setParent( this );
1075
+
1076
+ this.items.push( ParsleyField );
1077
+ }
1078
+
1079
+ /**
1080
+ * Removes a parsleyItem child from ParsleyForm
1081
+ *
1082
+ * @method removeItem
1083
+ * @param elem
1084
+ * @return {Boolean}
1085
+ */
1086
+ , removeItem: function ( elem ) {
1087
+ var parsleyItem = $( elem ).parsley();
1088
+
1089
+ // identify & remove item if same Parsley hash
1090
+ for ( var i = 0; i < this.items.length; i++ ) {
1091
+ if ( this.items[ i ].hash === parsleyItem.hash ) {
1092
+ this.items[ i ].destroy();
1093
+ this.items.splice( i, 1 );
1094
+ return true;
1095
+ }
1096
+ }
1097
+
1098
+ return false;
1099
+ }
1100
+
1101
+ /**
1102
+ * Process each form field validation
1103
+ * Display errors, call custom onFormSubmit() function
1104
+ *
1105
+ * @method validate
1106
+ * @param {Object} event jQuery Event
1107
+ * @return {Boolean} Is form valid or not
1108
+ */
1109
+ , validate: function ( event ) {
1110
+ var isValid = true;
1111
+ this.focusedField = false;
1112
+
1113
+ for ( var item = 0; item < this.items.length; item++ ) {
1114
+ if ( 'undefined' !== typeof this.items[ item ] && false === this.items[ item ].validate() ) {
1115
+ isValid = false;
1116
+
1117
+ if ( !this.focusedField && 'first' === this.options.focus || 'last' === this.options.focus ) {
1118
+ this.focusedField = this.items[ item ].$element;
1119
+ }
1120
+ }
1121
+ }
1122
+
1123
+ // form is invalid, focus an error field depending on focus policy
1124
+ if ( this.focusedField && !isValid ) {
1125
+ this.focusedField.focus();
1126
+ }
1127
+
1128
+ this.options.listeners.onFormSubmit( isValid, event, this );
1129
+
1130
+ return isValid;
1131
+ }
1132
+
1133
+ /**
1134
+ * Remove all errors ul under invalid fields
1135
+ *
1136
+ * @method removeErrors
1137
+ */
1138
+ , removeErrors: function () {
1139
+ for ( var item = 0; item < this.items.length; item++ ) {
1140
+ this.items[ item ].parsley( 'reset' );
1141
+ }
1142
+ }
1143
+
1144
+ /**
1145
+ * destroy Parsley binded on the form and its fields
1146
+ *
1147
+ * @method destroy
1148
+ */
1149
+ , destroy: function () {
1150
+ for ( var item = 0; item < this.items.length; item++ ) {
1151
+ this.items[ item ].destroy();
1152
+ }
1153
+
1154
+ this.$element.off( '.' + this.type ).removeData( this.type );
1155
+ }
1156
+
1157
+ /**
1158
+ * reset Parsley binded on the form and its fields
1159
+ *
1160
+ * @method reset
1161
+ */
1162
+ , reset: function () {
1163
+ for ( var item = 0; item < this.items.length; item++ ) {
1164
+ this.items[ item ].reset();
1165
+ }
1166
+ }
1167
+ };
1168
+
1169
+ /**
1170
+ * Parsley plugin definition
1171
+ * Provides an interface to access public Validator, ParsleyForm and ParsleyField functions
1172
+ *
1173
+ * @class Parsley
1174
+ * @constructor
1175
+ * @param {Mixed} Options. {Object} to configure Parsley or {String} method name to call a public class method
1176
+ * @param {Function} Callback function
1177
+ * @return {Mixed} public class method return
1178
+ */
1179
+ $.fn.parsley = function ( option, fn ) {
1180
+ var options = $.extend( true, {}, $.fn.parsley.defaults, 'undefined' !== typeof window.ParsleyConfig ? window.ParsleyConfig : {}, option, this.data() )
1181
+ , newInstance = null;
1182
+
1183
+ function bind ( self, type ) {
1184
+ var parsleyInstance = $( self ).data( type );
1185
+
1186
+ // if data never binded or we want to clone a build (for radio & checkboxes), bind it right now!
1187
+ if ( !parsleyInstance ) {
1188
+ switch ( type ) {
1189
+ case 'parsleyForm':
1190
+ parsleyInstance = new ParsleyForm( self, options, 'parsleyForm' );
1191
+ break;
1192
+ case 'parsleyField':
1193
+ parsleyInstance = new ParsleyField( self, options, 'parsleyField' );
1194
+ break;
1195
+ case 'parsleyFieldMultiple':
1196
+ parsleyInstance = new ParsleyFieldMultiple( self, options, 'parsleyFieldMultiple' );
1197
+ break;
1198
+ default:
1199
+ return;
1200
+ }
1201
+
1202
+ $( self ).data( type, parsleyInstance );
1203
+ }
1204
+
1205
+ // here is our parsley public function accessor
1206
+ if ( 'string' === typeof option && 'function' === typeof parsleyInstance[ option ] ) {
1207
+ var response = parsleyInstance[ option ]( fn );
1208
+
1209
+ return 'undefined' !== typeof response ? response : $( self );
1210
+ }
1211
+
1212
+ return parsleyInstance;
1213
+ }
1214
+
1215
+ // if a form elem is given, bind all its input children
1216
+ if ( $( this ).is( 'form' ) ) {
1217
+ newInstance = bind ( $( this ), 'parsleyForm' );
1218
+
1219
+ // if it is a Parsley supported single element, bind it too, except inputs type hidden
1220
+ // add here a return instance, cuz' we could call public methods on single elems with data[ option ]() above
1221
+ } else if ( $( this ).is( options.inputs ) && !$( this ).is( options.excluded ) ) {
1222
+ newInstance = bind( $( this ), !$( this ).is( 'input[type=radio], input[type=checkbox]' ) ? 'parsleyField' : 'parsleyFieldMultiple' );
1223
+ }
1224
+
1225
+ return 'function' === typeof fn ? fn() : newInstance;
1226
+ };
1227
+
1228
+ $.fn.parsley.Constructor = ParsleyForm;
1229
+
1230
+ /**
1231
+ * Parsley plugin configuration
1232
+ *
1233
+ * @property $.fn.parsley.defaults
1234
+ * @type {Object}
1235
+ */
1236
+ $.fn.parsley.defaults = {
1237
+ // basic data-api overridable properties here..
1238
+ inputs: 'input, textarea, select' // Default supported inputs.
1239
+ , excluded: 'input[type=hidden], :disabled' // Do not validate input[type=hidden] & :disabled.
1240
+ , trigger: false // $.Event() that will trigger validation. eg: keyup, change..
1241
+ , animate: true // fade in / fade out error messages
1242
+ , animateDuration: 300 // fadein/fadout ms time
1243
+ , focus: 'first' // 'fist'|'last'|'none' which error field would have focus first on form validation
1244
+ , validationMinlength: 3 // If trigger validation specified, only if value.length > validationMinlength
1245
+ , successClass: 'parsley-success' // Class name on each valid input
1246
+ , errorClass: 'parsley-error' // Class name on each invalid input
1247
+ , errorMessage: false // Customize an unique error message showed if one constraint fails
1248
+ , validators: {} // Add your custom validators functions
1249
+ , messages: {} // Add your own error messages here
1250
+
1251
+ //some quite advanced configuration here..
1252
+ , validateIfUnchanged: false // false: validate once by field value change
1253
+ , errors: {
1254
+ classHandler: function ( elem, isRadioOrCheckbox ) {} // specify where parsley error-success classes are set
1255
+ , container: function ( elem, isRadioOrCheckbox ) {} // specify an elem where errors will be **apened**
1256
+ , errorsWrapper: '<ul></ul>' // do not set an id for this elem, it would have an auto-generated id
1257
+ , errorElem: '<li></li>' // each field constraint fail in an li
1258
+ }
1259
+ , listeners: {
1260
+ onFieldValidate: function ( elem, ParsleyForm ) { return false; } // Executed on validation. Return true to ignore field validation
1261
+ , onFormSubmit: function ( isFormValid, event, ParsleyForm ) {} // Executed once on form validation
1262
+ , onFieldError: function ( elem, constraints, ParsleyField ) {} // Executed when a field is detected as invalid
1263
+ , onFieldSuccess: function ( elem, constraints, ParsleyField ) {} // Executed when a field passes validation
1264
+ }
1265
+ };
1266
+
1267
+ /* PARSLEY auto-bind DATA-API + Global config retrieving
1268
+ * =================================================== */
1269
+ $( window ).on( 'load', function () {
1270
+ $( '[data-validate="parsley"]' ).each( function () {
1271
+ $( this ).parsley();
1272
+ } );
1273
+ } );
1274
+
1275
+ // This plugin works with jQuery or Zepto (with data extension built for Zepto.)
1276
+ }(window.jQuery || window.Zepto);