conschedule 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +154 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/conschedule_manifest.js +2 -0
  6. data/app/assets/fonts/icomoon.eot +0 -0
  7. data/app/assets/fonts/proxima_nova_100.eot +0 -0
  8. data/app/assets/fonts/proxima_nova_400.eot +0 -0
  9. data/app/assets/fonts/proxima_nova_700.eot +0 -0
  10. data/app/assets/images/conschedule/chosen-sprite-small.png +0 -0
  11. data/app/assets/images/conschedule/chosen-sprite.png +0 -0
  12. data/app/assets/images/conschedule/close.png +0 -0
  13. data/app/assets/images/conschedule/spinner-small.gif +0 -0
  14. data/app/assets/images/conschedule/spinner.gif +0 -0
  15. data/app/assets/javascripts/conschedule/application.js +22 -0
  16. data/app/assets/javascripts/conschedule/calendar.js +467 -0
  17. data/app/assets/javascripts/conschedule/date.js +104 -0
  18. data/app/assets/javascripts/conschedule/form.js +53 -0
  19. data/app/assets/javascripts/conschedule/home.js +149 -0
  20. data/app/assets/javascripts/conschedule/jquery-ui-datepicker.min.js +1674 -0
  21. data/app/assets/javascripts/conschedule/jquery.easing.min.js +44 -0
  22. data/app/assets/javascripts/conschedule/jquery.validate.js +1532 -0
  23. data/app/assets/javascripts/conschedule/js_time_zone.js +1433 -0
  24. data/app/assets/javascripts/conschedule/moment-timezone.js +1209 -0
  25. data/app/assets/javascripts/conschedule/moment.js +4195 -0
  26. data/app/assets/javascripts/conschedule/schedules.js +22 -0
  27. data/app/assets/javascripts/conschedule/time_zones_drop_down.js +609 -0
  28. data/app/assets/stylesheets/conschedule/application.css +4 -0
  29. data/app/assets/stylesheets/conschedule/common.css +816 -0
  30. data/app/assets/stylesheets/conschedule/home.css +2441 -0
  31. data/app/assets/stylesheets/conschedule/main.css +884 -0
  32. data/app/assets/stylesheets/conschedule/schedules.css +4 -0
  33. data/app/controllers/conschedule/application_controller.rb +49 -0
  34. data/app/controllers/conschedule/schedules_controller.rb +125 -0
  35. data/app/helpers/conschedule/application_helper.rb +15 -0
  36. data/app/helpers/conschedule/schedules_helper.rb +43 -0
  37. data/app/jobs/conschedule/application_job.rb +4 -0
  38. data/app/mailers/conschedule/schedules_mailer.rb +46 -0
  39. data/app/models/conschedule/application_record.rb +5 -0
  40. data/app/models/conschedule/schedule.rb +63 -0
  41. data/app/views/conschedule/schedules/_confirmation.html.erb +38 -0
  42. data/app/views/conschedule/schedules/_page_one.html.erb +75 -0
  43. data/app/views/conschedule/schedules/_page_three.html.erb +94 -0
  44. data/app/views/conschedule/schedules/_page_two.html.erb +22 -0
  45. data/app/views/conschedule/schedules/_schedules.html.erb +122 -0
  46. data/app/views/conschedule/schedules/create.js.erb +11 -0
  47. data/app/views/conschedule/schedules/index.html.erb +123 -0
  48. data/app/views/conschedule/schedules/index.js.erb +3 -0
  49. data/app/views/conschedule/schedules/new.html.erb +11 -0
  50. data/app/views/conschedule/schedules_mailer/send_cancel_email_owner.html.erb +61 -0
  51. data/app/views/conschedule/schedules_mailer/send_cancel_email_user.html.erb +29 -0
  52. data/app/views/conschedule/schedules_mailer/send_confirmation_email_owner.html.erb +77 -0
  53. data/app/views/conschedule/schedules_mailer/send_confirmation_email_user.html.erb +30 -0
  54. data/app/views/kaminari/custom_theme/_first_page.html.erb +3 -0
  55. data/app/views/kaminari/custom_theme/_gap.html.erb +1 -0
  56. data/app/views/kaminari/custom_theme/_last_page.html.erb +3 -0
  57. data/app/views/kaminari/custom_theme/_next_page.html.erb +1 -0
  58. data/app/views/kaminari/custom_theme/_page.html.erb +5 -0
  59. data/app/views/kaminari/custom_theme/_paginator.html.erb +23 -0
  60. data/app/views/kaminari/custom_theme/_prev_page.html.erb +1 -0
  61. data/app/views/layouts/conschedule/application.html.erb +14 -0
  62. data/app/views/layouts/conschedule/mailer.html.erb +11 -0
  63. data/app/views/layouts/conschedule/schedules.html.erb +14 -0
  64. data/config/initializers/assets.rb +11 -0
  65. data/config/routes.rb +11 -0
  66. data/db/migrate/20160812085505_create_conschedule_schedules.rb +27 -0
  67. data/lib/conschedule.rb +34 -0
  68. data/lib/conschedule/configuration.rb +25 -0
  69. data/lib/conschedule/engine.rb +5 -0
  70. data/lib/conschedule/gem_dependencies.rb +2 -0
  71. data/lib/conschedule/version.rb +3 -0
  72. data/lib/generators/conschedule/install_generator.rb +23 -0
  73. data/lib/generators/conschedule/views_generator.rb +45 -0
  74. data/lib/generators/templates/README +40 -0
  75. data/lib/generators/templates/initializer.rb +25 -0
  76. data/lib/tasks/conschedule_tasks.rake +4 -0
  77. metadata +182 -0
@@ -0,0 +1,44 @@
1
+ /*
2
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
3
+ *
4
+ * Uses the built in easing capabilities added In jQuery 1.1
5
+ * to offer multiple easing options
6
+ *
7
+ * TERMS OF USE - EASING EQUATIONS
8
+ *
9
+ * Open source under the BSD License.
10
+ *
11
+ * Copyright © 2001 Robert Penner
12
+ * All rights reserved.
13
+ *
14
+ * TERMS OF USE - jQuery Easing
15
+ *
16
+ * Open source under the BSD License.
17
+ *
18
+ * Copyright © 2008 George McGinley Smith
19
+ * All rights reserved.
20
+ *
21
+ * Redistribution and use in source and binary forms, with or without modification,
22
+ * are permitted provided that the following conditions are met:
23
+ *
24
+ * Redistributions of source code must retain the above copyright notice, this list of
25
+ * conditions and the following disclaimer.
26
+ * Redistributions in binary form must reproduce the above copyright notice, this list
27
+ * of conditions and the following disclaimer in the documentation and/or other materials
28
+ * provided with the distribution.
29
+ *
30
+ * Neither the name of the author nor the names of contributors may be used to endorse
31
+ * or promote products derived from this software without specific prior written permission.
32
+ *
33
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
34
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
35
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
36
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
39
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
42
+ *
43
+ */
44
+ jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}});
@@ -0,0 +1,1532 @@
1
+ /*!
2
+ * jQuery Validation Plugin v1.15.0
3
+ *
4
+ * http://jqueryvalidation.org/
5
+ *
6
+ * Copyright (c) 2016 Jörn Zaefferer
7
+ * Released under the MIT license
8
+ */
9
+ (function( factory ) {
10
+ if ( typeof define === "function" && define.amd ) {
11
+ define( ["jquery"], factory );
12
+ } else if (typeof module === "object" && module.exports) {
13
+ module.exports = factory( require( "jquery" ) );
14
+ } else {
15
+ factory( jQuery );
16
+ }
17
+ }(function( $ ) {
18
+
19
+ $.extend( $.fn, {
20
+
21
+ // http://jqueryvalidation.org/validate/
22
+ validate: function( options ) {
23
+
24
+ // If nothing is selected, return nothing; can't chain anyway
25
+ if ( !this.length ) {
26
+ if ( options && options.debug && window.console ) {
27
+ console.warn( "Nothing selected, can't validate, returning nothing." );
28
+ }
29
+ return;
30
+ }
31
+
32
+ // Check if a validator for this form was already created
33
+ var validator = $.data( this[ 0 ], "validator" );
34
+ if ( validator ) {
35
+ return validator;
36
+ }
37
+
38
+ // Add novalidate tag if HTML5.
39
+ this.attr( "novalidate", "novalidate" );
40
+
41
+ validator = new $.validator( options, this[ 0 ] );
42
+ $.data( this[ 0 ], "validator", validator );
43
+
44
+ if ( validator.settings.onsubmit ) {
45
+
46
+ this.on( "click.validate", ":submit", function( event ) {
47
+ if ( validator.settings.submitHandler ) {
48
+ validator.submitButton = event.target;
49
+ }
50
+
51
+ // Allow suppressing validation by adding a cancel class to the submit button
52
+ if ( $( this ).hasClass( "cancel" ) ) {
53
+ validator.cancelSubmit = true;
54
+ }
55
+
56
+ // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
57
+ if ( $( this ).attr( "formnovalidate" ) !== undefined ) {
58
+ validator.cancelSubmit = true;
59
+ }
60
+ } );
61
+
62
+ // Validate the form on submit
63
+ this.on( "submit.validate", function( event ) {
64
+ if ( validator.settings.debug ) {
65
+
66
+ // Prevent form submit to be able to see console output
67
+ event.preventDefault();
68
+ }
69
+ function handle() {
70
+ var hidden, result;
71
+ if ( validator.settings.submitHandler ) {
72
+ if ( validator.submitButton ) {
73
+
74
+ // Insert a hidden input as a replacement for the missing submit button
75
+ hidden = $( "<input type='hidden'/>" )
76
+ .attr( "name", validator.submitButton.name )
77
+ .val( $( validator.submitButton ).val() )
78
+ .appendTo( validator.currentForm );
79
+ }
80
+ result = validator.settings.submitHandler.call( validator, validator.currentForm, event );
81
+ if ( validator.submitButton ) {
82
+
83
+ // And clean up afterwards; thanks to no-block-scope, hidden can be referenced
84
+ hidden.remove();
85
+ }
86
+ if ( result !== undefined ) {
87
+ return result;
88
+ }
89
+ return false;
90
+ }
91
+ return true;
92
+ }
93
+
94
+ // Prevent submit for invalid forms or custom submit handlers
95
+ if ( validator.cancelSubmit ) {
96
+ validator.cancelSubmit = false;
97
+ return handle();
98
+ }
99
+ if ( validator.form() ) {
100
+ if ( validator.pendingRequest ) {
101
+ validator.formSubmitted = true;
102
+ return false;
103
+ }
104
+ return handle();
105
+ } else {
106
+ validator.focusInvalid();
107
+ return false;
108
+ }
109
+ } );
110
+ }
111
+
112
+ return validator;
113
+ },
114
+
115
+ // http://jqueryvalidation.org/valid/
116
+ valid: function() {
117
+ var valid, validator, errorList;
118
+
119
+ if ( $( this[ 0 ] ).is( "form" ) ) {
120
+ valid = this.validate().form();
121
+ } else {
122
+ errorList = [];
123
+ valid = true;
124
+ validator = $( this[ 0 ].form ).validate();
125
+ this.each( function() {
126
+ valid = validator.element( this ) && valid;
127
+ if ( !valid ) {
128
+ errorList = errorList.concat( validator.errorList );
129
+ }
130
+ } );
131
+ validator.errorList = errorList;
132
+ }
133
+ return valid;
134
+ },
135
+
136
+ // http://jqueryvalidation.org/rules/
137
+ rules: function( command, argument ) {
138
+
139
+ // If nothing is selected, return nothing; can't chain anyway
140
+ if ( !this.length ) {
141
+ return;
142
+ }
143
+
144
+ var element = this[ 0 ],
145
+ settings, staticRules, existingRules, data, param, filtered;
146
+
147
+ if ( command ) {
148
+ settings = $.data( element.form, "validator" ).settings;
149
+ staticRules = settings.rules;
150
+ existingRules = $.validator.staticRules( element );
151
+ switch ( command ) {
152
+ case "add":
153
+ $.extend( existingRules, $.validator.normalizeRule( argument ) );
154
+
155
+ // Remove messages from rules, but allow them to be set separately
156
+ delete existingRules.messages;
157
+ staticRules[ element.name ] = existingRules;
158
+ if ( argument.messages ) {
159
+ settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );
160
+ }
161
+ break;
162
+ case "remove":
163
+ if ( !argument ) {
164
+ delete staticRules[ element.name ];
165
+ return existingRules;
166
+ }
167
+ filtered = {};
168
+ $.each( argument.split( /\s/ ), function( index, method ) {
169
+ filtered[ method ] = existingRules[ method ];
170
+ delete existingRules[ method ];
171
+ if ( method === "required" ) {
172
+ $( element ).removeAttr( "aria-required" );
173
+ }
174
+ } );
175
+ return filtered;
176
+ }
177
+ }
178
+
179
+ data = $.validator.normalizeRules(
180
+ $.extend(
181
+ {},
182
+ $.validator.classRules( element ),
183
+ $.validator.attributeRules( element ),
184
+ $.validator.dataRules( element ),
185
+ $.validator.staticRules( element )
186
+ ), element );
187
+
188
+ // Make sure required is at front
189
+ if ( data.required ) {
190
+ param = data.required;
191
+ delete data.required;
192
+ data = $.extend( { required: param }, data );
193
+ $( element ).attr( "aria-required", "true" );
194
+ }
195
+
196
+ // Make sure remote is at back
197
+ if ( data.remote ) {
198
+ param = data.remote;
199
+ delete data.remote;
200
+ data = $.extend( data, { remote: param } );
201
+ }
202
+
203
+ return data;
204
+ }
205
+ } );
206
+
207
+ // Custom selectors
208
+ $.extend( $.expr[ ":" ], {
209
+
210
+ // http://jqueryvalidation.org/blank-selector/
211
+ blank: function( a ) {
212
+ return !$.trim( "" + $( a ).val() );
213
+ },
214
+
215
+ // http://jqueryvalidation.org/filled-selector/
216
+ filled: function( a ) {
217
+ var val = $( a ).val();
218
+ return val !== null && !!$.trim( "" + val );
219
+ },
220
+
221
+ // http://jqueryvalidation.org/unchecked-selector/
222
+ unchecked: function( a ) {
223
+ return !$( a ).prop( "checked" );
224
+ }
225
+ } );
226
+
227
+ // Constructor for validator
228
+ $.validator = function( options, form ) {
229
+ this.settings = $.extend( true, {}, $.validator.defaults, options );
230
+ this.currentForm = form;
231
+ this.init();
232
+ };
233
+
234
+ // http://jqueryvalidation.org/jQuery.validator.format/
235
+ $.validator.format = function( source, params ) {
236
+ if ( arguments.length === 1 ) {
237
+ return function() {
238
+ var args = $.makeArray( arguments );
239
+ args.unshift( source );
240
+ return $.validator.format.apply( this, args );
241
+ };
242
+ }
243
+ if ( params === undefined ) {
244
+ return source;
245
+ }
246
+ if ( arguments.length > 2 && params.constructor !== Array ) {
247
+ params = $.makeArray( arguments ).slice( 1 );
248
+ }
249
+ if ( params.constructor !== Array ) {
250
+ params = [ params ];
251
+ }
252
+ $.each( params, function( i, n ) {
253
+ source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() {
254
+ return n;
255
+ } );
256
+ } );
257
+ return source;
258
+ };
259
+
260
+ $.extend( $.validator, {
261
+
262
+ defaults: {
263
+ messages: {},
264
+ groups: {},
265
+ rules: {},
266
+ errorClass: "error",
267
+ pendingClass: "pending",
268
+ validClass: "valid",
269
+ errorElement: "label",
270
+ focusCleanup: false,
271
+ focusInvalid: true,
272
+ errorContainer: $( [] ),
273
+ errorLabelContainer: $( [] ),
274
+ onsubmit: true,
275
+ ignore: ":hidden",
276
+ ignoreTitle: false,
277
+ onfocusin: function( element ) {
278
+ this.lastActive = element;
279
+
280
+ // Hide error label and remove error class on focus if enabled
281
+ if ( this.settings.focusCleanup ) {
282
+ if ( this.settings.unhighlight ) {
283
+ this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
284
+ }
285
+ this.hideThese( this.errorsFor( element ) );
286
+ }
287
+ },
288
+ onfocusout: function( element ) {
289
+ if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
290
+ this.element( element );
291
+ }
292
+ },
293
+ onkeyup: function( element, event ) {
294
+
295
+ // Avoid revalidate the field when pressing one of the following keys
296
+ // Shift => 16
297
+ // Ctrl => 17
298
+ // Alt => 18
299
+ // Caps lock => 20
300
+ // End => 35
301
+ // Home => 36
302
+ // Left arrow => 37
303
+ // Up arrow => 38
304
+ // Right arrow => 39
305
+ // Down arrow => 40
306
+ // Insert => 45
307
+ // Num lock => 144
308
+ // AltGr key => 225
309
+ var excludedKeys = [
310
+ 16, 17, 18, 20, 35, 36, 37,
311
+ 38, 39, 40, 45, 144, 225
312
+ ];
313
+
314
+ if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {
315
+ return;
316
+ } else if ( element.name in this.submitted || element.name in this.invalid ) {
317
+ this.element( element );
318
+ }
319
+ },
320
+ onclick: function( element ) {
321
+
322
+ // Click on selects, radiobuttons and checkboxes
323
+ if ( element.name in this.submitted ) {
324
+ this.element( element );
325
+
326
+ // Or option elements, check parent select in that case
327
+ } else if ( element.parentNode.name in this.submitted ) {
328
+ this.element( element.parentNode );
329
+ }
330
+ },
331
+ highlight: function( element, errorClass, validClass ) {
332
+ if ( element.type === "radio" ) {
333
+ this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
334
+ } else {
335
+ $( element ).addClass( errorClass ).removeClass( validClass );
336
+ }
337
+ },
338
+ unhighlight: function( element, errorClass, validClass ) {
339
+ if ( element.type === "radio" ) {
340
+ this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
341
+ } else {
342
+ $( element ).removeClass( errorClass ).addClass( validClass );
343
+ }
344
+ }
345
+ },
346
+
347
+ // http://jqueryvalidation.org/jQuery.validator.setDefaults/
348
+ setDefaults: function( settings ) {
349
+ $.extend( $.validator.defaults, settings );
350
+ },
351
+
352
+ messages: {
353
+ required: "This field is required.",
354
+ remote: "Please fix this field.",
355
+ email: "Please enter a valid email address.",
356
+ url: "Please enter a valid URL.",
357
+ date: "Please enter a valid date.",
358
+ dateISO: "Please enter a valid date ( ISO ).",
359
+ number: "Please enter a valid number.",
360
+ digits: "Please enter only digits.",
361
+ equalTo: "Please enter the same value again.",
362
+ maxlength: $.validator.format( "Please enter no more than {0} characters." ),
363
+ minlength: $.validator.format( "Please enter at least {0} characters." ),
364
+ rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ),
365
+ range: $.validator.format( "Please enter a value between {0} and {1}." ),
366
+ max: $.validator.format( "Please enter a value less than or equal to {0}." ),
367
+ min: $.validator.format( "Please enter a value greater than or equal to {0}." ),
368
+ step: $.validator.format( "Please enter a multiple of {0}." )
369
+ },
370
+
371
+ autoCreateRanges: false,
372
+
373
+ prototype: {
374
+
375
+ init: function() {
376
+ this.labelContainer = $( this.settings.errorLabelContainer );
377
+ this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );
378
+ this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );
379
+ this.submitted = {};
380
+ this.valueCache = {};
381
+ this.pendingRequest = 0;
382
+ this.pending = {};
383
+ this.invalid = {};
384
+ this.reset();
385
+
386
+ var groups = ( this.groups = {} ),
387
+ rules;
388
+ $.each( this.settings.groups, function( key, value ) {
389
+ if ( typeof value === "string" ) {
390
+ value = value.split( /\s/ );
391
+ }
392
+ $.each( value, function( index, name ) {
393
+ groups[ name ] = key;
394
+ } );
395
+ } );
396
+ rules = this.settings.rules;
397
+ $.each( rules, function( key, value ) {
398
+ rules[ key ] = $.validator.normalizeRule( value );
399
+ } );
400
+
401
+ function delegate( event ) {
402
+ var validator = $.data( this.form, "validator" ),
403
+ eventType = "on" + event.type.replace( /^validate/, "" ),
404
+ settings = validator.settings;
405
+ if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {
406
+ settings[ eventType ].call( validator, this, event );
407
+ }
408
+ }
409
+
410
+ $( this.currentForm )
411
+ .on( "focusin.validate focusout.validate keyup.validate",
412
+ ":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
413
+ "[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
414
+ "[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
415
+ "[type='radio'], [type='checkbox'], [contenteditable]", delegate )
416
+
417
+ // Support: Chrome, oldIE
418
+ // "select" is provided as event.target when clicking a option
419
+ .on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate );
420
+
421
+ if ( this.settings.invalidHandler ) {
422
+ $( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler );
423
+ }
424
+
425
+ // Add aria-required to any Static/Data/Class required fields before first validation
426
+ // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html
427
+ $( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" );
428
+ },
429
+
430
+ // http://jqueryvalidation.org/Validator.form/
431
+ form: function() {
432
+ this.checkForm();
433
+ $.extend( this.submitted, this.errorMap );
434
+ this.invalid = $.extend( {}, this.errorMap );
435
+ if ( !this.valid() ) {
436
+ $( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
437
+ }
438
+ this.showErrors();
439
+ return this.valid();
440
+ },
441
+
442
+ checkForm: function() {
443
+ this.prepareForm();
444
+ for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {
445
+ this.check( elements[ i ] );
446
+ }
447
+ return this.valid();
448
+ },
449
+
450
+ // http://jqueryvalidation.org/Validator.element/
451
+ element: function( element ) {
452
+ var cleanElement = this.clean( element ),
453
+ checkElement = this.validationTargetFor( cleanElement ),
454
+ v = this,
455
+ result = true,
456
+ rs, group;
457
+
458
+ if ( checkElement === undefined ) {
459
+ delete this.invalid[ cleanElement.name ];
460
+ } else {
461
+ this.prepareElement( checkElement );
462
+ this.currentElements = $( checkElement );
463
+
464
+ // If this element is grouped, then validate all group elements already
465
+ // containing a value
466
+ group = this.groups[ checkElement.name ];
467
+ if ( group ) {
468
+ $.each( this.groups, function( name, testgroup ) {
469
+ if ( testgroup === group && name !== checkElement.name ) {
470
+ cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );
471
+ if ( cleanElement && cleanElement.name in v.invalid ) {
472
+ v.currentElements.push( cleanElement );
473
+ result = result && v.check( cleanElement );
474
+ }
475
+ }
476
+ } );
477
+ }
478
+
479
+ rs = this.check( checkElement ) !== false;
480
+ result = result && rs;
481
+ if ( rs ) {
482
+ this.invalid[ checkElement.name ] = false;
483
+ } else {
484
+ this.invalid[ checkElement.name ] = true;
485
+ }
486
+
487
+ if ( !this.numberOfInvalids() ) {
488
+
489
+ // Hide error containers on last error
490
+ this.toHide = this.toHide.add( this.containers );
491
+ }
492
+ this.showErrors();
493
+
494
+ // Add aria-invalid status for screen readers
495
+ $( element ).attr( "aria-invalid", !rs );
496
+ }
497
+
498
+ return result;
499
+ },
500
+
501
+ // http://jqueryvalidation.org/Validator.showErrors/
502
+ showErrors: function( errors ) {
503
+ if ( errors ) {
504
+ var validator = this;
505
+
506
+ // Add items to error list and map
507
+ $.extend( this.errorMap, errors );
508
+ this.errorList = $.map( this.errorMap, function( message, name ) {
509
+ return {
510
+ message: message,
511
+ element: validator.findByName( name )[ 0 ]
512
+ };
513
+ } );
514
+
515
+ // Remove items from success list
516
+ this.successList = $.grep( this.successList, function( element ) {
517
+ return !( element.name in errors );
518
+ } );
519
+ }
520
+ if ( this.settings.showErrors ) {
521
+ this.settings.showErrors.call( this, this.errorMap, this.errorList );
522
+ } else {
523
+ this.defaultShowErrors();
524
+ }
525
+ },
526
+
527
+ // http://jqueryvalidation.org/Validator.resetForm/
528
+ resetForm: function() {
529
+ if ( $.fn.resetForm ) {
530
+ $( this.currentForm ).resetForm();
531
+ }
532
+ this.invalid = {};
533
+ this.submitted = {};
534
+ this.prepareForm();
535
+ this.hideErrors();
536
+ var elements = this.elements()
537
+ .removeData( "previousValue" )
538
+ .removeAttr( "aria-invalid" );
539
+
540
+ this.resetElements( elements );
541
+ },
542
+
543
+ resetElements: function( elements ) {
544
+ var i;
545
+
546
+ if ( this.settings.unhighlight ) {
547
+ for ( i = 0; elements[ i ]; i++ ) {
548
+ this.settings.unhighlight.call( this, elements[ i ],
549
+ this.settings.errorClass, "" );
550
+ this.findByName( elements[ i ].name ).removeClass( this.settings.validClass );
551
+ }
552
+ } else {
553
+ elements
554
+ .removeClass( this.settings.errorClass )
555
+ .removeClass( this.settings.validClass );
556
+ }
557
+ },
558
+
559
+ numberOfInvalids: function() {
560
+ return this.objectLength( this.invalid );
561
+ },
562
+
563
+ objectLength: function( obj ) {
564
+ /* jshint unused: false */
565
+ var count = 0,
566
+ i;
567
+ for ( i in obj ) {
568
+ if ( obj[ i ] ) {
569
+ count++;
570
+ }
571
+ }
572
+ return count;
573
+ },
574
+
575
+ hideErrors: function() {
576
+ this.hideThese( this.toHide );
577
+ },
578
+
579
+ hideThese: function( errors ) {
580
+ errors.not( this.containers ).text( "" );
581
+ this.addWrapper( errors ).hide();
582
+ },
583
+
584
+ valid: function() {
585
+ return this.size() === 0;
586
+ },
587
+
588
+ size: function() {
589
+ return this.errorList.length;
590
+ },
591
+
592
+ focusInvalid: function() {
593
+ if ( this.settings.focusInvalid ) {
594
+ try {
595
+ $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )
596
+ .filter( ":visible" )
597
+ .focus()
598
+
599
+ // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
600
+ .trigger( "focusin" );
601
+ } catch ( e ) {
602
+
603
+ // Ignore IE throwing errors when focusing hidden elements
604
+ }
605
+ }
606
+ },
607
+
608
+ findLastActive: function() {
609
+ var lastActive = this.lastActive;
610
+ return lastActive && $.grep( this.errorList, function( n ) {
611
+ return n.element.name === lastActive.name;
612
+ } ).length === 1 && lastActive;
613
+ },
614
+
615
+ elements: function() {
616
+ var validator = this,
617
+ rulesCache = {};
618
+
619
+ // Select all valid inputs inside the form (no submit or reset buttons)
620
+ return $( this.currentForm )
621
+ .find( "input, select, textarea, [contenteditable]" )
622
+ .not( ":submit, :reset, :image, :disabled" )
623
+ .not( this.settings.ignore )
624
+ .filter( function() {
625
+ var name = this.name || $( this ).attr( "name" ); // For contenteditable
626
+ if ( !name && validator.settings.debug && window.console ) {
627
+ console.error( "%o has no name assigned", this );
628
+ }
629
+
630
+ // Set form expando on contenteditable
631
+ if ( this.hasAttribute( "contenteditable" ) ) {
632
+ this.form = $( this ).closest( "form" )[ 0 ];
633
+ }
634
+
635
+ // Select only the first element for each name, and only those with rules specified
636
+ if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
637
+ return false;
638
+ }
639
+
640
+ rulesCache[ name ] = true;
641
+ return true;
642
+ } );
643
+ },
644
+
645
+ clean: function( selector ) {
646
+ return $( selector )[ 0 ];
647
+ },
648
+
649
+ errors: function() {
650
+ var errorClass = this.settings.errorClass.split( " " ).join( "." );
651
+ return $( this.settings.errorElement + "." + errorClass, this.errorContext );
652
+ },
653
+
654
+ resetInternals: function() {
655
+ this.successList = [];
656
+ this.errorList = [];
657
+ this.errorMap = {};
658
+ this.toShow = $( [] );
659
+ this.toHide = $( [] );
660
+ },
661
+
662
+ reset: function() {
663
+ this.resetInternals();
664
+ this.currentElements = $( [] );
665
+ },
666
+
667
+ prepareForm: function() {
668
+ this.reset();
669
+ this.toHide = this.errors().add( this.containers );
670
+ },
671
+
672
+ prepareElement: function( element ) {
673
+ this.reset();
674
+ this.toHide = this.errorsFor( element );
675
+ },
676
+
677
+ elementValue: function( element ) {
678
+ var $element = $( element ),
679
+ type = element.type,
680
+ val, idx;
681
+
682
+ if ( type === "radio" || type === "checkbox" ) {
683
+ return this.findByName( element.name ).filter( ":checked" ).val();
684
+ } else if ( type === "number" && typeof element.validity !== "undefined" ) {
685
+ return element.validity.badInput ? "NaN" : $element.val();
686
+ }
687
+
688
+ if ( element.hasAttribute( "contenteditable" ) ) {
689
+ val = $element.text();
690
+ } else {
691
+ val = $element.val();
692
+ }
693
+
694
+ if ( type === "file" ) {
695
+
696
+ // Modern browser (chrome & safari)
697
+ if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) {
698
+ return val.substr( 12 );
699
+ }
700
+
701
+ // Legacy browsers
702
+ // Unix-based path
703
+ idx = val.lastIndexOf( "/" );
704
+ if ( idx >= 0 ) {
705
+ return val.substr( idx + 1 );
706
+ }
707
+
708
+ // Windows-based path
709
+ idx = val.lastIndexOf( "\\" );
710
+ if ( idx >= 0 ) {
711
+ return val.substr( idx + 1 );
712
+ }
713
+
714
+ // Just the file name
715
+ return val;
716
+ }
717
+
718
+ if ( typeof val === "string" ) {
719
+ return val.replace( /\r/g, "" );
720
+ }
721
+ return val;
722
+ },
723
+
724
+ check: function( element ) {
725
+ element = this.validationTargetFor( this.clean( element ) );
726
+
727
+ var rules = $( element ).rules(),
728
+ rulesCount = $.map( rules, function( n, i ) {
729
+ return i;
730
+ } ).length,
731
+ dependencyMismatch = false,
732
+ val = this.elementValue( element ),
733
+ result, method, rule;
734
+
735
+ // If a normalizer is defined for this element, then
736
+ // call it to retreive the changed value instead
737
+ // of using the real one.
738
+ // Note that `this` in the normalizer is `element`.
739
+ if ( typeof rules.normalizer === "function" ) {
740
+ val = rules.normalizer.call( element, val );
741
+
742
+ if ( typeof val !== "string" ) {
743
+ throw new TypeError( "The normalizer should return a string value." );
744
+ }
745
+
746
+ // Delete the normalizer from rules to avoid treating
747
+ // it as a pre-defined method.
748
+ delete rules.normalizer;
749
+ }
750
+
751
+ for ( method in rules ) {
752
+ rule = { method: method, parameters: rules[ method ] };
753
+ try {
754
+ result = $.validator.methods[ method ].call( this, val, element, rule.parameters );
755
+
756
+ // If a method indicates that the field is optional and therefore valid,
757
+ // don't mark it as valid when there are no other rules
758
+ if ( result === "dependency-mismatch" && rulesCount === 1 ) {
759
+ dependencyMismatch = true;
760
+ continue;
761
+ }
762
+ dependencyMismatch = false;
763
+
764
+ if ( result === "pending" ) {
765
+ this.toHide = this.toHide.not( this.errorsFor( element ) );
766
+ return;
767
+ }
768
+
769
+ if ( !result ) {
770
+ this.formatAndAdd( element, rule );
771
+ return false;
772
+ }
773
+ } catch ( e ) {
774
+ if ( this.settings.debug && window.console ) {
775
+ console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e );
776
+ }
777
+ if ( e instanceof TypeError ) {
778
+ e.message += ". Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.";
779
+ }
780
+
781
+ throw e;
782
+ }
783
+ }
784
+ if ( dependencyMismatch ) {
785
+ return;
786
+ }
787
+ if ( this.objectLength( rules ) ) {
788
+ this.successList.push( element );
789
+ }
790
+ return true;
791
+ },
792
+
793
+ // Return the custom message for the given element and validation method
794
+ // specified in the element's HTML5 data attribute
795
+ // return the generic message if present and no method specific message is present
796
+ customDataMessage: function( element, method ) {
797
+ return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() +
798
+ method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" );
799
+ },
800
+
801
+ // Return the custom message for the given element name and validation method
802
+ customMessage: function( name, method ) {
803
+ var m = this.settings.messages[ name ];
804
+ return m && ( m.constructor === String ? m : m[ method ] );
805
+ },
806
+
807
+ // Return the first defined argument, allowing empty strings
808
+ findDefined: function() {
809
+ for ( var i = 0; i < arguments.length; i++ ) {
810
+ if ( arguments[ i ] !== undefined ) {
811
+ return arguments[ i ];
812
+ }
813
+ }
814
+ return undefined;
815
+ },
816
+
817
+ defaultMessage: function( element, rule ) {
818
+ var message = this.findDefined(
819
+ this.customMessage( element.name, rule.method ),
820
+ this.customDataMessage( element, rule.method ),
821
+
822
+ // 'title' is never undefined, so handle empty string as undefined
823
+ !this.settings.ignoreTitle && element.title || undefined,
824
+ $.validator.messages[ rule.method ],
825
+ "<strong>Warning: No message defined for " + element.name + "</strong>"
826
+ ),
827
+ theregex = /\$?\{(\d+)\}/g;
828
+ if ( typeof message === "function" ) {
829
+ message = message.call( this, rule.parameters, element );
830
+ } else if ( theregex.test( message ) ) {
831
+ message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters );
832
+ }
833
+
834
+ return message;
835
+ },
836
+
837
+ formatAndAdd: function( element, rule ) {
838
+ var message = this.defaultMessage( element, rule );
839
+
840
+ this.errorList.push( {
841
+ message: message,
842
+ element: element,
843
+ method: rule.method
844
+ } );
845
+
846
+ this.errorMap[ element.name ] = message;
847
+ this.submitted[ element.name ] = message;
848
+ },
849
+
850
+ addWrapper: function( toToggle ) {
851
+ if ( this.settings.wrapper ) {
852
+ toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
853
+ }
854
+ return toToggle;
855
+ },
856
+
857
+ defaultShowErrors: function() {
858
+ var i, elements, error;
859
+ for ( i = 0; this.errorList[ i ]; i++ ) {
860
+ error = this.errorList[ i ];
861
+ if ( this.settings.highlight ) {
862
+ this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
863
+ }
864
+ this.showLabel( error.element, error.message );
865
+ }
866
+ if ( this.errorList.length ) {
867
+ this.toShow = this.toShow.add( this.containers );
868
+ }
869
+ if ( this.settings.success ) {
870
+ for ( i = 0; this.successList[ i ]; i++ ) {
871
+ this.showLabel( this.successList[ i ] );
872
+ }
873
+ }
874
+ if ( this.settings.unhighlight ) {
875
+ for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {
876
+ this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );
877
+ }
878
+ }
879
+ this.toHide = this.toHide.not( this.toShow );
880
+ this.hideErrors();
881
+ this.addWrapper( this.toShow ).show();
882
+ },
883
+
884
+ validElements: function() {
885
+ return this.currentElements.not( this.invalidElements() );
886
+ },
887
+
888
+ invalidElements: function() {
889
+ return $( this.errorList ).map( function() {
890
+ return this.element;
891
+ } );
892
+ },
893
+
894
+ showLabel: function( element, message ) {
895
+ var place, group, errorID, v,
896
+ error = this.errorsFor( element ),
897
+ elementID = this.idOrName( element ),
898
+ describedBy = $( element ).attr( "aria-describedby" );
899
+
900
+ if ( error.length ) {
901
+
902
+ // Refresh error/success class
903
+ error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
904
+
905
+ // Replace message on existing label
906
+ error.html( message );
907
+ } else {
908
+
909
+ // Create error element
910
+ error = $( "<" + this.settings.errorElement + ">" )
911
+ .attr( "id", elementID + "-error" )
912
+ .addClass( this.settings.errorClass )
913
+ .html( message || "" );
914
+
915
+ // Maintain reference to the element to be placed into the DOM
916
+ place = error;
917
+ if ( this.settings.wrapper ) {
918
+
919
+ // Make sure the element is visible, even in IE
920
+ // actually showing the wrapped element is handled elsewhere
921
+ place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent();
922
+ }
923
+ if ( this.labelContainer.length ) {
924
+ this.labelContainer.append( place );
925
+ } else if ( this.settings.errorPlacement ) {
926
+ this.settings.errorPlacement( place, $( element ) );
927
+ } else {
928
+ place.insertAfter( element );
929
+ }
930
+
931
+ // Link error back to the element
932
+ if ( error.is( "label" ) ) {
933
+
934
+ // If the error is a label, then associate using 'for'
935
+ error.attr( "for", elementID );
936
+
937
+ // If the element is not a child of an associated label, then it's necessary
938
+ // to explicitly apply aria-describedby
939
+ } else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) {
940
+ errorID = error.attr( "id" );
941
+
942
+ // Respect existing non-error aria-describedby
943
+ if ( !describedBy ) {
944
+ describedBy = errorID;
945
+ } else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) {
946
+
947
+ // Add to end of list if not already present
948
+ describedBy += " " + errorID;
949
+ }
950
+ $( element ).attr( "aria-describedby", describedBy );
951
+
952
+ // If this element is grouped, then assign to all elements in the same group
953
+ group = this.groups[ element.name ];
954
+ if ( group ) {
955
+ v = this;
956
+ $.each( v.groups, function( name, testgroup ) {
957
+ if ( testgroup === group ) {
958
+ $( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm )
959
+ .attr( "aria-describedby", error.attr( "id" ) );
960
+ }
961
+ } );
962
+ }
963
+ }
964
+ }
965
+ if ( !message && this.settings.success ) {
966
+ error.text( "" );
967
+ if ( typeof this.settings.success === "string" ) {
968
+ error.addClass( this.settings.success );
969
+ } else {
970
+ this.settings.success( error, element );
971
+ }
972
+ }
973
+ this.toShow = this.toShow.add( error );
974
+ },
975
+
976
+ errorsFor: function( element ) {
977
+ var name = this.escapeCssMeta( this.idOrName( element ) ),
978
+ describer = $( element ).attr( "aria-describedby" ),
979
+ selector = "label[for='" + name + "'], label[for='" + name + "'] *";
980
+
981
+ // 'aria-describedby' should directly reference the error element
982
+ if ( describer ) {
983
+ selector = selector + ", #" + this.escapeCssMeta( describer )
984
+ .replace( /\s+/g, ", #" );
985
+ }
986
+
987
+ return this
988
+ .errors()
989
+ .filter( selector );
990
+ },
991
+
992
+ // See https://api.jquery.com/category/selectors/, for CSS
993
+ // meta-characters that should be escaped in order to be used with JQuery
994
+ // as a literal part of a name/id or any selector.
995
+ escapeCssMeta: function( string ) {
996
+ return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" );
997
+ },
998
+
999
+ idOrName: function( element ) {
1000
+ return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );
1001
+ },
1002
+
1003
+ validationTargetFor: function( element ) {
1004
+
1005
+ // If radio/checkbox, validate first element in group instead
1006
+ if ( this.checkable( element ) ) {
1007
+ element = this.findByName( element.name );
1008
+ }
1009
+
1010
+ // Always apply ignore filter
1011
+ return $( element ).not( this.settings.ignore )[ 0 ];
1012
+ },
1013
+
1014
+ checkable: function( element ) {
1015
+ return ( /radio|checkbox/i ).test( element.type );
1016
+ },
1017
+
1018
+ findByName: function( name ) {
1019
+ return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" );
1020
+ },
1021
+
1022
+ getLength: function( value, element ) {
1023
+ switch ( element.nodeName.toLowerCase() ) {
1024
+ case "select":
1025
+ return $( "option:selected", element ).length;
1026
+ case "input":
1027
+ if ( this.checkable( element ) ) {
1028
+ return this.findByName( element.name ).filter( ":checked" ).length;
1029
+ }
1030
+ }
1031
+ return value.length;
1032
+ },
1033
+
1034
+ depend: function( param, element ) {
1035
+ return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;
1036
+ },
1037
+
1038
+ dependTypes: {
1039
+ "boolean": function( param ) {
1040
+ return param;
1041
+ },
1042
+ "string": function( param, element ) {
1043
+ return !!$( param, element.form ).length;
1044
+ },
1045
+ "function": function( param, element ) {
1046
+ return param( element );
1047
+ }
1048
+ },
1049
+
1050
+ optional: function( element ) {
1051
+ var val = this.elementValue( element );
1052
+ return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch";
1053
+ },
1054
+
1055
+ startRequest: function( element ) {
1056
+ if ( !this.pending[ element.name ] ) {
1057
+ this.pendingRequest++;
1058
+ $( element ).addClass( this.settings.pendingClass );
1059
+ this.pending[ element.name ] = true;
1060
+ }
1061
+ },
1062
+
1063
+ stopRequest: function( element, valid ) {
1064
+ this.pendingRequest--;
1065
+
1066
+ // Sometimes synchronization fails, make sure pendingRequest is never < 0
1067
+ if ( this.pendingRequest < 0 ) {
1068
+ this.pendingRequest = 0;
1069
+ }
1070
+ delete this.pending[ element.name ];
1071
+ $( element ).removeClass( this.settings.pendingClass );
1072
+ if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
1073
+ $( this.currentForm ).submit();
1074
+ this.formSubmitted = false;
1075
+ } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {
1076
+ $( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
1077
+ this.formSubmitted = false;
1078
+ }
1079
+ },
1080
+
1081
+ previousValue: function( element, method ) {
1082
+ return $.data( element, "previousValue" ) || $.data( element, "previousValue", {
1083
+ old: null,
1084
+ valid: true,
1085
+ message: this.defaultMessage( element, { method: method } )
1086
+ } );
1087
+ },
1088
+
1089
+ // Cleans up all forms and elements, removes validator-specific events
1090
+ destroy: function() {
1091
+ this.resetForm();
1092
+
1093
+ $( this.currentForm )
1094
+ .off( ".validate" )
1095
+ .removeData( "validator" )
1096
+ .find( ".validate-equalTo-blur" )
1097
+ .off( ".validate-equalTo" )
1098
+ .removeClass( "validate-equalTo-blur" );
1099
+ }
1100
+
1101
+ },
1102
+
1103
+ classRuleSettings: {
1104
+ required: { required: true },
1105
+ email: { email: true },
1106
+ url: { url: true },
1107
+ date: { date: true },
1108
+ dateISO: { dateISO: true },
1109
+ number: { number: true },
1110
+ digits: { digits: true },
1111
+ creditcard: { creditcard: true }
1112
+ },
1113
+
1114
+ addClassRules: function( className, rules ) {
1115
+ if ( className.constructor === String ) {
1116
+ this.classRuleSettings[ className ] = rules;
1117
+ } else {
1118
+ $.extend( this.classRuleSettings, className );
1119
+ }
1120
+ },
1121
+
1122
+ classRules: function( element ) {
1123
+ var rules = {},
1124
+ classes = $( element ).attr( "class" );
1125
+
1126
+ if ( classes ) {
1127
+ $.each( classes.split( " " ), function() {
1128
+ if ( this in $.validator.classRuleSettings ) {
1129
+ $.extend( rules, $.validator.classRuleSettings[ this ] );
1130
+ }
1131
+ } );
1132
+ }
1133
+ return rules;
1134
+ },
1135
+
1136
+ normalizeAttributeRule: function( rules, type, method, value ) {
1137
+
1138
+ // Convert the value to a number for number inputs, and for text for backwards compability
1139
+ // allows type="date" and others to be compared as strings
1140
+ if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
1141
+ value = Number( value );
1142
+
1143
+ // Support Opera Mini, which returns NaN for undefined minlength
1144
+ if ( isNaN( value ) ) {
1145
+ value = undefined;
1146
+ }
1147
+ }
1148
+
1149
+ if ( value || value === 0 ) {
1150
+ rules[ method ] = value;
1151
+ } else if ( type === method && type !== "range" ) {
1152
+
1153
+ // Exception: the jquery validate 'range' method
1154
+ // does not test for the html5 'range' type
1155
+ rules[ method ] = true;
1156
+ }
1157
+ },
1158
+
1159
+ attributeRules: function( element ) {
1160
+ var rules = {},
1161
+ $element = $( element ),
1162
+ type = element.getAttribute( "type" ),
1163
+ method, value;
1164
+
1165
+ for ( method in $.validator.methods ) {
1166
+
1167
+ // Support for <input required> in both html5 and older browsers
1168
+ if ( method === "required" ) {
1169
+ value = element.getAttribute( method );
1170
+
1171
+ // Some browsers return an empty string for the required attribute
1172
+ // and non-HTML5 browsers might have required="" markup
1173
+ if ( value === "" ) {
1174
+ value = true;
1175
+ }
1176
+
1177
+ // Force non-HTML5 browsers to return bool
1178
+ value = !!value;
1179
+ } else {
1180
+ value = $element.attr( method );
1181
+ }
1182
+
1183
+ this.normalizeAttributeRule( rules, type, method, value );
1184
+ }
1185
+
1186
+ // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
1187
+ if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {
1188
+ delete rules.maxlength;
1189
+ }
1190
+
1191
+ return rules;
1192
+ },
1193
+
1194
+ dataRules: function( element ) {
1195
+ var rules = {},
1196
+ $element = $( element ),
1197
+ type = element.getAttribute( "type" ),
1198
+ method, value;
1199
+
1200
+ for ( method in $.validator.methods ) {
1201
+ value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );
1202
+ this.normalizeAttributeRule( rules, type, method, value );
1203
+ }
1204
+ return rules;
1205
+ },
1206
+
1207
+ staticRules: function( element ) {
1208
+ var rules = {},
1209
+ validator = $.data( element.form, "validator" );
1210
+
1211
+ if ( validator.settings.rules ) {
1212
+ rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};
1213
+ }
1214
+ return rules;
1215
+ },
1216
+
1217
+ normalizeRules: function( rules, element ) {
1218
+
1219
+ // Handle dependency check
1220
+ $.each( rules, function( prop, val ) {
1221
+
1222
+ // Ignore rule when param is explicitly false, eg. required:false
1223
+ if ( val === false ) {
1224
+ delete rules[ prop ];
1225
+ return;
1226
+ }
1227
+ if ( val.param || val.depends ) {
1228
+ var keepRule = true;
1229
+ switch ( typeof val.depends ) {
1230
+ case "string":
1231
+ keepRule = !!$( val.depends, element.form ).length;
1232
+ break;
1233
+ case "function":
1234
+ keepRule = val.depends.call( element, element );
1235
+ break;
1236
+ }
1237
+ if ( keepRule ) {
1238
+ rules[ prop ] = val.param !== undefined ? val.param : true;
1239
+ } else {
1240
+ $.data( element.form, "validator" ).resetElements( $( element ) );
1241
+ delete rules[ prop ];
1242
+ }
1243
+ }
1244
+ } );
1245
+
1246
+ // Evaluate parameters
1247
+ $.each( rules, function( rule, parameter ) {
1248
+ rules[ rule ] = $.isFunction( parameter ) && rule !== "normalizer" ? parameter( element ) : parameter;
1249
+ } );
1250
+
1251
+ // Clean number parameters
1252
+ $.each( [ "minlength", "maxlength" ], function() {
1253
+ if ( rules[ this ] ) {
1254
+ rules[ this ] = Number( rules[ this ] );
1255
+ }
1256
+ } );
1257
+ $.each( [ "rangelength", "range" ], function() {
1258
+ var parts;
1259
+ if ( rules[ this ] ) {
1260
+ if ( $.isArray( rules[ this ] ) ) {
1261
+ rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];
1262
+ } else if ( typeof rules[ this ] === "string" ) {
1263
+ parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ );
1264
+ rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];
1265
+ }
1266
+ }
1267
+ } );
1268
+
1269
+ if ( $.validator.autoCreateRanges ) {
1270
+
1271
+ // Auto-create ranges
1272
+ if ( rules.min != null && rules.max != null ) {
1273
+ rules.range = [ rules.min, rules.max ];
1274
+ delete rules.min;
1275
+ delete rules.max;
1276
+ }
1277
+ if ( rules.minlength != null && rules.maxlength != null ) {
1278
+ rules.rangelength = [ rules.minlength, rules.maxlength ];
1279
+ delete rules.minlength;
1280
+ delete rules.maxlength;
1281
+ }
1282
+ }
1283
+
1284
+ return rules;
1285
+ },
1286
+
1287
+ // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
1288
+ normalizeRule: function( data ) {
1289
+ if ( typeof data === "string" ) {
1290
+ var transformed = {};
1291
+ $.each( data.split( /\s/ ), function() {
1292
+ transformed[ this ] = true;
1293
+ } );
1294
+ data = transformed;
1295
+ }
1296
+ return data;
1297
+ },
1298
+
1299
+ // http://jqueryvalidation.org/jQuery.validator.addMethod/
1300
+ addMethod: function( name, method, message ) {
1301
+ $.validator.methods[ name ] = method;
1302
+ $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];
1303
+ if ( method.length < 3 ) {
1304
+ $.validator.addClassRules( name, $.validator.normalizeRule( name ) );
1305
+ }
1306
+ },
1307
+
1308
+ // http://jqueryvalidation.org/jQuery.validator.methods/
1309
+ methods: {
1310
+
1311
+ // http://jqueryvalidation.org/required-method/
1312
+ required: function( value, element, param ) {
1313
+
1314
+ // Check if dependency is met
1315
+ if ( !this.depend( param, element ) ) {
1316
+ return "dependency-mismatch";
1317
+ }
1318
+ if ( element.nodeName.toLowerCase() === "select" ) {
1319
+
1320
+ // Could be an array for select-multiple or a string, both are fine this way
1321
+ var val = $( element ).val();
1322
+ return val && val.length > 0;
1323
+ }
1324
+ if ( this.checkable( element ) ) {
1325
+ return this.getLength( value, element ) > 0;
1326
+ }
1327
+ return value.length > 0;
1328
+ },
1329
+
1330
+ // http://jqueryvalidation.org/email-method/
1331
+ email: function( value, element ) {
1332
+
1333
+ // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
1334
+ // Retrieved 2014-01-14
1335
+ // If you have a problem with this implementation, report a bug against the above spec
1336
+ // Or use custom methods to implement your own email validation
1337
+ return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value );
1338
+ },
1339
+
1340
+ // http://jqueryvalidation.org/url-method/
1341
+ url: function( value, element ) {
1342
+
1343
+ // Copyright (c) 2010-2013 Diego Perini, MIT licensed
1344
+ // https://gist.github.com/dperini/729294
1345
+ // see also https://mathiasbynens.be/demo/url-regex
1346
+ // modified to allow protocol-relative URLs
1347
+ return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value );
1348
+ },
1349
+
1350
+ // http://jqueryvalidation.org/date-method/
1351
+ date: function( value, element ) {
1352
+ return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );
1353
+ },
1354
+
1355
+ // http://jqueryvalidation.org/dateISO-method/
1356
+ dateISO: function( value, element ) {
1357
+ return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );
1358
+ },
1359
+
1360
+ // http://jqueryvalidation.org/number-method/
1361
+ number: function( value, element ) {
1362
+ return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
1363
+ },
1364
+
1365
+ // http://jqueryvalidation.org/digits-method/
1366
+ digits: function( value, element ) {
1367
+ return this.optional( element ) || /^\d+$/.test( value );
1368
+ },
1369
+
1370
+ // http://jqueryvalidation.org/minlength-method/
1371
+ minlength: function( value, element, param ) {
1372
+ var length = $.isArray( value ) ? value.length : this.getLength( value, element );
1373
+ return this.optional( element ) || length >= param;
1374
+ },
1375
+
1376
+ // http://jqueryvalidation.org/maxlength-method/
1377
+ maxlength: function( value, element, param ) {
1378
+ var length = $.isArray( value ) ? value.length : this.getLength( value, element );
1379
+ return this.optional( element ) || length <= param;
1380
+ },
1381
+
1382
+ // http://jqueryvalidation.org/rangelength-method/
1383
+ rangelength: function( value, element, param ) {
1384
+ var length = $.isArray( value ) ? value.length : this.getLength( value, element );
1385
+ return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );
1386
+ },
1387
+
1388
+ // http://jqueryvalidation.org/min-method/
1389
+ min: function( value, element, param ) {
1390
+ return this.optional( element ) || value >= param;
1391
+ },
1392
+
1393
+ // http://jqueryvalidation.org/max-method/
1394
+ max: function( value, element, param ) {
1395
+ return this.optional( element ) || value <= param;
1396
+ },
1397
+
1398
+ // http://jqueryvalidation.org/range-method/
1399
+ range: function( value, element, param ) {
1400
+ return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
1401
+ },
1402
+
1403
+ // http://jqueryvalidation.org/step-method/
1404
+ step: function( value, element, param ) {
1405
+ var type = $( element ).attr( "type" ),
1406
+ errorMessage = "Step attribute on input type " + type + " is not supported.",
1407
+ supportedTypes = [ "text", "number", "range" ],
1408
+ re = new RegExp( "\\b" + type + "\\b" ),
1409
+ notSupported = type && !re.test( supportedTypes.join() );
1410
+
1411
+ // Works only for text, number and range input types
1412
+ // TODO find a way to support input types date, datetime, datetime-local, month, time and week
1413
+ if ( notSupported ) {
1414
+ throw new Error( errorMessage );
1415
+ }
1416
+ return this.optional( element ) || ( value % param === 0 );
1417
+ },
1418
+
1419
+ // http://jqueryvalidation.org/equalTo-method/
1420
+ equalTo: function( value, element, param ) {
1421
+
1422
+ // Bind to the blur event of the target in order to revalidate whenever the target field is updated
1423
+ var target = $( param );
1424
+ if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) {
1425
+ target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() {
1426
+ $( element ).valid();
1427
+ } );
1428
+ }
1429
+ return value === target.val();
1430
+ },
1431
+
1432
+ // http://jqueryvalidation.org/remote-method/
1433
+ remote: function( value, element, param, method ) {
1434
+ if ( this.optional( element ) ) {
1435
+ return "dependency-mismatch";
1436
+ }
1437
+
1438
+ method = typeof method === "string" && method || "remote";
1439
+
1440
+ var previous = this.previousValue( element, method ),
1441
+ validator, data, optionDataString;
1442
+
1443
+ if ( !this.settings.messages[ element.name ] ) {
1444
+ this.settings.messages[ element.name ] = {};
1445
+ }
1446
+ previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];
1447
+ this.settings.messages[ element.name ][ method ] = previous.message;
1448
+
1449
+ param = typeof param === "string" && { url: param } || param;
1450
+ optionDataString = $.param( $.extend( { data: value }, param.data ) );
1451
+ if ( previous.old === optionDataString ) {
1452
+ return previous.valid;
1453
+ }
1454
+
1455
+ previous.old = optionDataString;
1456
+ validator = this;
1457
+ this.startRequest( element );
1458
+ data = {};
1459
+ data[ element.name ] = value;
1460
+ $.ajax( $.extend( true, {
1461
+ mode: "abort",
1462
+ port: "validate" + element.name,
1463
+ dataType: "json",
1464
+ data: data,
1465
+ context: validator.currentForm,
1466
+ success: function( response ) {
1467
+ var valid = response === true || response === "true",
1468
+ errors, message, submitted;
1469
+
1470
+ validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
1471
+ if ( valid ) {
1472
+ submitted = validator.formSubmitted;
1473
+ validator.resetInternals();
1474
+ validator.toHide = validator.errorsFor( element );
1475
+ validator.formSubmitted = submitted;
1476
+ validator.successList.push( element );
1477
+ validator.invalid[ element.name ] = false;
1478
+ validator.showErrors();
1479
+ } else {
1480
+ errors = {};
1481
+ message = response || validator.defaultMessage( element, { method: method, parameters: value } );
1482
+ errors[ element.name ] = previous.message = message;
1483
+ validator.invalid[ element.name ] = true;
1484
+ validator.showErrors( errors );
1485
+ }
1486
+ previous.valid = valid;
1487
+ validator.stopRequest( element, valid );
1488
+ }
1489
+ }, param ) );
1490
+ return "pending";
1491
+ }
1492
+ }
1493
+
1494
+ } );
1495
+
1496
+ // Ajax mode: abort
1497
+ // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
1498
+ // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
1499
+
1500
+ var pendingRequests = {},
1501
+ ajax;
1502
+
1503
+ // Use a prefilter if available (1.5+)
1504
+ if ( $.ajaxPrefilter ) {
1505
+ $.ajaxPrefilter( function( settings, _, xhr ) {
1506
+ var port = settings.port;
1507
+ if ( settings.mode === "abort" ) {
1508
+ if ( pendingRequests[ port ] ) {
1509
+ pendingRequests[ port ].abort();
1510
+ }
1511
+ pendingRequests[ port ] = xhr;
1512
+ }
1513
+ } );
1514
+ } else {
1515
+
1516
+ // Proxy ajax
1517
+ ajax = $.ajax;
1518
+ $.ajax = function( settings ) {
1519
+ var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
1520
+ port = ( "port" in settings ? settings : $.ajaxSettings ).port;
1521
+ if ( mode === "abort" ) {
1522
+ if ( pendingRequests[ port ] ) {
1523
+ pendingRequests[ port ].abort();
1524
+ }
1525
+ pendingRequests[ port ] = ajax.apply( this, arguments );
1526
+ return pendingRequests[ port ];
1527
+ }
1528
+ return ajax.apply( this, arguments );
1529
+ };
1530
+ }
1531
+
1532
+ }));