PluginProbe ʕ •ᴥ•ʔ
Strong Testimonials / 2.33
Strong Testimonials v2.33
3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.0.1 2.30.9 2.31.10 2.32 2.32.1 2.32.2 2.32.3 2.32.4 2.33 2.34 2.35 2.36 2.37 2.38 2.38.1 2.39 2.39.1 2.39.2 2.39.3 2.40.0 2.40.1 2.40.2 2.40.3 2.40.4 2.40.5 2.40.6 2.40.7 2.41.0 2.41.1 2.50.0 2.50.1 2.50.2 2.50.3 2.50.4 2.51.0 2.51.1 2.51.2 2.51.3 2.51.4 2.51.5 2.51.6 2.51.7 2.51.8 2.51.9 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.20 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.17 3.2.18 3.2.19 3.2.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0
strong-testimonials / public / js / lib / validate / jquery.validate.js
strong-testimonials / public / js / lib / validate Last commit date
localization 9 years ago additional-methods.js 9 years ago additional-methods.min.js 9 years ago jquery.validate.js 9 years ago jquery.validate.min.js 9 years ago
jquery.validate.js
1574 lines
1 /*!
2 * jQuery Validation Plugin v1.16.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 var element = this[ 0 ],
139 settings, staticRules, existingRules, data, param, filtered;
140
141 // If nothing is selected, return empty object; can't chain anyway
142 if ( element == null || element.form == null ) {
143 return;
144 }
145
146 if ( command ) {
147 settings = $.data( element.form, "validator" ).settings;
148 staticRules = settings.rules;
149 existingRules = $.validator.staticRules( element );
150 switch ( command ) {
151 case "add":
152 $.extend( existingRules, $.validator.normalizeRule( argument ) );
153
154 // Remove messages from rules, but allow them to be set separately
155 delete existingRules.messages;
156 staticRules[ element.name ] = existingRules;
157 if ( argument.messages ) {
158 settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages );
159 }
160 break;
161 case "remove":
162 if ( !argument ) {
163 delete staticRules[ element.name ];
164 return existingRules;
165 }
166 filtered = {};
167 $.each( argument.split( /\s/ ), function( index, method ) {
168 filtered[ method ] = existingRules[ method ];
169 delete existingRules[ method ];
170 if ( method === "required" ) {
171 $( element ).removeAttr( "aria-required" );
172 }
173 } );
174 return filtered;
175 }
176 }
177
178 data = $.validator.normalizeRules(
179 $.extend(
180 {},
181 $.validator.classRules( element ),
182 $.validator.attributeRules( element ),
183 $.validator.dataRules( element ),
184 $.validator.staticRules( element )
185 ), element );
186
187 // Make sure required is at front
188 if ( data.required ) {
189 param = data.required;
190 delete data.required;
191 data = $.extend( { required: param }, data );
192 $( element ).attr( "aria-required", "true" );
193 }
194
195 // Make sure remote is at back
196 if ( data.remote ) {
197 param = data.remote;
198 delete data.remote;
199 data = $.extend( data, { remote: param } );
200 }
201
202 return data;
203 }
204 } );
205
206 // Custom selectors
207 $.extend( $.expr.pseudos || $.expr[ ":" ], { // '|| $.expr[ ":" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support
208
209 // http://jqueryvalidation.org/blank-selector/
210 blank: function( a ) {
211 return !$.trim( "" + $( a ).val() );
212 },
213
214 // http://jqueryvalidation.org/filled-selector/
215 filled: function( a ) {
216 var val = $( a ).val();
217 return val !== null && !!$.trim( "" + val );
218 },
219
220 // http://jqueryvalidation.org/unchecked-selector/
221 unchecked: function( a ) {
222 return !$( a ).prop( "checked" );
223 }
224 } );
225
226 // Constructor for validator
227 $.validator = function( options, form ) {
228 this.settings = $.extend( true, {}, $.validator.defaults, options );
229 this.currentForm = form;
230 this.init();
231 };
232
233 // http://jqueryvalidation.org/jQuery.validator.format/
234 $.validator.format = function( source, params ) {
235 if ( arguments.length === 1 ) {
236 return function() {
237 var args = $.makeArray( arguments );
238 args.unshift( source );
239 return $.validator.format.apply( this, args );
240 };
241 }
242 if ( params === undefined ) {
243 return source;
244 }
245 if ( arguments.length > 2 && params.constructor !== Array ) {
246 params = $.makeArray( arguments ).slice( 1 );
247 }
248 if ( params.constructor !== Array ) {
249 params = [ params ];
250 }
251 $.each( params, function( i, n ) {
252 source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() {
253 return n;
254 } );
255 } );
256 return source;
257 };
258
259 $.extend( $.validator, {
260
261 defaults: {
262 messages: {},
263 groups: {},
264 rules: {},
265 errorClass: "error",
266 pendingClass: "pending",
267 validClass: "valid",
268 errorElement: "label",
269 focusCleanup: false,
270 focusInvalid: true,
271 errorContainer: $( [] ),
272 errorLabelContainer: $( [] ),
273 onsubmit: true,
274 ignore: ":hidden",
275 ignoreTitle: false,
276 onfocusin: function( element ) {
277 this.lastActive = element;
278
279 // Hide error label and remove error class on focus if enabled
280 if ( this.settings.focusCleanup ) {
281 if ( this.settings.unhighlight ) {
282 this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
283 }
284 this.hideThese( this.errorsFor( element ) );
285 }
286 },
287 onfocusout: function( element ) {
288 if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) {
289 this.element( element );
290 }
291 },
292 onkeyup: function( element, event ) {
293
294 // Avoid revalidate the field when pressing one of the following keys
295 // Shift => 16
296 // Ctrl => 17
297 // Alt => 18
298 // Caps lock => 20
299 // End => 35
300 // Home => 36
301 // Left arrow => 37
302 // Up arrow => 38
303 // Right arrow => 39
304 // Down arrow => 40
305 // Insert => 45
306 // Num lock => 144
307 // AltGr key => 225
308 var excludedKeys = [
309 16, 17, 18, 20, 35, 36, 37,
310 38, 39, 40, 45, 144, 225
311 ];
312
313 if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) {
314 return;
315 } else if ( element.name in this.submitted || element.name in this.invalid ) {
316 this.element( element );
317 }
318 },
319 onclick: function( element ) {
320
321 // Click on selects, radiobuttons and checkboxes
322 if ( element.name in this.submitted ) {
323 this.element( element );
324
325 // Or option elements, check parent select in that case
326 } else if ( element.parentNode.name in this.submitted ) {
327 this.element( element.parentNode );
328 }
329 },
330 highlight: function( element, errorClass, validClass ) {
331 if ( element.type === "radio" ) {
332 this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
333 } else {
334 $( element ).addClass( errorClass ).removeClass( validClass );
335 }
336 },
337 unhighlight: function( element, errorClass, validClass ) {
338 if ( element.type === "radio" ) {
339 this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
340 } else {
341 $( element ).removeClass( errorClass ).addClass( validClass );
342 }
343 }
344 },
345
346 // http://jqueryvalidation.org/jQuery.validator.setDefaults/
347 setDefaults: function( settings ) {
348 $.extend( $.validator.defaults, settings );
349 },
350
351 messages: {
352 required: "This field is required.",
353 remote: "Please fix this field.",
354 email: "Please enter a valid email address.",
355 url: "Please enter a valid URL.",
356 date: "Please enter a valid date.",
357 dateISO: "Please enter a valid date (ISO).",
358 number: "Please enter a valid number.",
359 digits: "Please enter only digits.",
360 equalTo: "Please enter the same value again.",
361 maxlength: $.validator.format( "Please enter no more than {0} characters." ),
362 minlength: $.validator.format( "Please enter at least {0} characters." ),
363 rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ),
364 range: $.validator.format( "Please enter a value between {0} and {1}." ),
365 max: $.validator.format( "Please enter a value less than or equal to {0}." ),
366 min: $.validator.format( "Please enter a value greater than or equal to {0}." ),
367 step: $.validator.format( "Please enter a multiple of {0}." )
368 },
369
370 autoCreateRanges: false,
371
372 prototype: {
373
374 init: function() {
375 this.labelContainer = $( this.settings.errorLabelContainer );
376 this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm );
377 this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer );
378 this.submitted = {};
379 this.valueCache = {};
380 this.pendingRequest = 0;
381 this.pending = {};
382 this.invalid = {};
383 this.reset();
384
385 var groups = ( this.groups = {} ),
386 rules;
387 $.each( this.settings.groups, function( key, value ) {
388 if ( typeof value === "string" ) {
389 value = value.split( /\s/ );
390 }
391 $.each( value, function( index, name ) {
392 groups[ name ] = key;
393 } );
394 } );
395 rules = this.settings.rules;
396 $.each( rules, function( key, value ) {
397 rules[ key ] = $.validator.normalizeRule( value );
398 } );
399
400 function delegate( event ) {
401
402 // Set form expando on contenteditable
403 if ( !this.form && this.hasAttribute( "contenteditable" ) ) {
404 this.form = $( this ).closest( "form" )[ 0 ];
405 }
406
407 var validator = $.data( this.form, "validator" ),
408 eventType = "on" + event.type.replace( /^validate/, "" ),
409 settings = validator.settings;
410 if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {
411 settings[ eventType ].call( validator, this, event );
412 }
413 }
414
415 $( this.currentForm )
416 .on( "focusin.validate focusout.validate keyup.validate",
417 ":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
418 "[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
419 "[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
420 "[type='radio'], [type='checkbox'], [contenteditable], [type='button']", delegate )
421
422 // Support: Chrome, oldIE
423 // "select" is provided as event.target when clicking a option
424 .on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate );
425
426 if ( this.settings.invalidHandler ) {
427 $( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler );
428 }
429
430 // Add aria-required to any Static/Data/Class required fields before first validation
431 // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html
432 $( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" );
433 },
434
435 // http://jqueryvalidation.org/Validator.form/
436 form: function() {
437 this.checkForm();
438 $.extend( this.submitted, this.errorMap );
439 this.invalid = $.extend( {}, this.errorMap );
440 if ( !this.valid() ) {
441 $( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
442 }
443 this.showErrors();
444 return this.valid();
445 },
446
447 checkForm: function() {
448 this.prepareForm();
449 for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) {
450 this.check( elements[ i ] );
451 }
452 return this.valid();
453 },
454
455 // http://jqueryvalidation.org/Validator.element/
456 element: function( element ) {
457 var cleanElement = this.clean( element ),
458 checkElement = this.validationTargetFor( cleanElement ),
459 v = this,
460 result = true,
461 rs, group;
462
463 if ( checkElement === undefined ) {
464 delete this.invalid[ cleanElement.name ];
465 } else {
466 this.prepareElement( checkElement );
467 this.currentElements = $( checkElement );
468
469 // If this element is grouped, then validate all group elements already
470 // containing a value
471 group = this.groups[ checkElement.name ];
472 if ( group ) {
473 $.each( this.groups, function( name, testgroup ) {
474 if ( testgroup === group && name !== checkElement.name ) {
475 cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) );
476 if ( cleanElement && cleanElement.name in v.invalid ) {
477 v.currentElements.push( cleanElement );
478 result = v.check( cleanElement ) && result;
479 }
480 }
481 } );
482 }
483
484 rs = this.check( checkElement ) !== false;
485 result = result && rs;
486 if ( rs ) {
487 this.invalid[ checkElement.name ] = false;
488 } else {
489 this.invalid[ checkElement.name ] = true;
490 }
491
492 if ( !this.numberOfInvalids() ) {
493
494 // Hide error containers on last error
495 this.toHide = this.toHide.add( this.containers );
496 }
497 this.showErrors();
498
499 // Add aria-invalid status for screen readers
500 $( element ).attr( "aria-invalid", !rs );
501 }
502
503 return result;
504 },
505
506 // http://jqueryvalidation.org/Validator.showErrors/
507 showErrors: function( errors ) {
508 if ( errors ) {
509 var validator = this;
510
511 // Add items to error list and map
512 $.extend( this.errorMap, errors );
513 this.errorList = $.map( this.errorMap, function( message, name ) {
514 return {
515 message: message,
516 element: validator.findByName( name )[ 0 ]
517 };
518 } );
519
520 // Remove items from success list
521 this.successList = $.grep( this.successList, function( element ) {
522 return !( element.name in errors );
523 } );
524 }
525 if ( this.settings.showErrors ) {
526 this.settings.showErrors.call( this, this.errorMap, this.errorList );
527 } else {
528 this.defaultShowErrors();
529 }
530 },
531
532 // http://jqueryvalidation.org/Validator.resetForm/
533 resetForm: function() {
534 if ( $.fn.resetForm ) {
535 $( this.currentForm ).resetForm();
536 }
537 this.invalid = {};
538 this.submitted = {};
539 this.prepareForm();
540 this.hideErrors();
541 var elements = this.elements()
542 .removeData( "previousValue" )
543 .removeAttr( "aria-invalid" );
544
545 this.resetElements( elements );
546 },
547
548 resetElements: function( elements ) {
549 var i;
550
551 if ( this.settings.unhighlight ) {
552 for ( i = 0; elements[ i ]; i++ ) {
553 this.settings.unhighlight.call( this, elements[ i ],
554 this.settings.errorClass, "" );
555 this.findByName( elements[ i ].name ).removeClass( this.settings.validClass );
556 }
557 } else {
558 elements
559 .removeClass( this.settings.errorClass )
560 .removeClass( this.settings.validClass );
561 }
562 },
563
564 numberOfInvalids: function() {
565 return this.objectLength( this.invalid );
566 },
567
568 objectLength: function( obj ) {
569 /* jshint unused: false */
570 var count = 0,
571 i;
572 for ( i in obj ) {
573 if ( obj[ i ] ) {
574 count++;
575 }
576 }
577 return count;
578 },
579
580 hideErrors: function() {
581 this.hideThese( this.toHide );
582 },
583
584 hideThese: function( errors ) {
585 errors.not( this.containers ).text( "" );
586 this.addWrapper( errors ).hide();
587 },
588
589 valid: function() {
590 return this.size() === 0;
591 },
592
593 size: function() {
594 return this.errorList.length;
595 },
596
597 focusInvalid: function() {
598 if ( this.settings.focusInvalid ) {
599 try {
600 $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] )
601 .filter( ":visible" )
602 .focus()
603
604 // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
605 .trigger( "focusin" );
606 } catch ( e ) {
607
608 // Ignore IE throwing errors when focusing hidden elements
609 }
610 }
611 },
612
613 findLastActive: function() {
614 var lastActive = this.lastActive;
615 return lastActive && $.grep( this.errorList, function( n ) {
616 return n.element.name === lastActive.name;
617 } ).length === 1 && lastActive;
618 },
619
620 elements: function() {
621 var validator = this,
622 rulesCache = {};
623
624 // Select all valid inputs inside the form (no submit or reset buttons)
625 return $( this.currentForm )
626 .find( "input, select, textarea, [contenteditable]" )
627 .not( ":submit, :reset, :image, :disabled" )
628 .not( this.settings.ignore )
629 .filter( function() {
630 var name = this.name || $( this ).attr( "name" ); // For contenteditable
631 if ( !name && validator.settings.debug && window.console ) {
632 console.error( "%o has no name assigned", this );
633 }
634
635 // Set form expando on contenteditable
636 if ( this.hasAttribute( "contenteditable" ) ) {
637 this.form = $( this ).closest( "form" )[ 0 ];
638 }
639
640 // Select only the first element for each name, and only those with rules specified
641 if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
642 return false;
643 }
644
645 rulesCache[ name ] = true;
646 return true;
647 } );
648 },
649
650 clean: function( selector ) {
651 return $( selector )[ 0 ];
652 },
653
654 errors: function() {
655 var errorClass = this.settings.errorClass.split( " " ).join( "." );
656 return $( this.settings.errorElement + "." + errorClass, this.errorContext );
657 },
658
659 resetInternals: function() {
660 this.successList = [];
661 this.errorList = [];
662 this.errorMap = {};
663 this.toShow = $( [] );
664 this.toHide = $( [] );
665 },
666
667 reset: function() {
668 this.resetInternals();
669 this.currentElements = $( [] );
670 },
671
672 prepareForm: function() {
673 this.reset();
674 this.toHide = this.errors().add( this.containers );
675 },
676
677 prepareElement: function( element ) {
678 this.reset();
679 this.toHide = this.errorsFor( element );
680 },
681
682 elementValue: function( element ) {
683 var $element = $( element ),
684 type = element.type,
685 val, idx;
686
687 if ( type === "radio" || type === "checkbox" ) {
688 return this.findByName( element.name ).filter( ":checked" ).val();
689 } else if ( type === "number" && typeof element.validity !== "undefined" ) {
690 return element.validity.badInput ? "NaN" : $element.val();
691 }
692
693 if ( element.hasAttribute( "contenteditable" ) ) {
694 val = $element.text();
695 } else {
696 val = $element.val();
697 }
698
699 if ( type === "file" ) {
700
701 // Modern browser (chrome & safari)
702 if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) {
703 return val.substr( 12 );
704 }
705
706 // Legacy browsers
707 // Unix-based path
708 idx = val.lastIndexOf( "/" );
709 if ( idx >= 0 ) {
710 return val.substr( idx + 1 );
711 }
712
713 // Windows-based path
714 idx = val.lastIndexOf( "\\" );
715 if ( idx >= 0 ) {
716 return val.substr( idx + 1 );
717 }
718
719 // Just the file name
720 return val;
721 }
722
723 if ( typeof val === "string" ) {
724 return val.replace( /\r/g, "" );
725 }
726 return val;
727 },
728
729 check: function( element ) {
730 element = this.validationTargetFor( this.clean( element ) );
731
732 var rules = $( element ).rules(),
733 rulesCount = $.map( rules, function( n, i ) {
734 return i;
735 } ).length,
736 dependencyMismatch = false,
737 val = this.elementValue( element ),
738 result, method, rule;
739
740 // If a normalizer is defined for this element, then
741 // call it to retreive the changed value instead
742 // of using the real one.
743 // Note that `this` in the normalizer is `element`.
744 if ( typeof rules.normalizer === "function" ) {
745 val = rules.normalizer.call( element, val );
746
747 if ( typeof val !== "string" ) {
748 throw new TypeError( "The normalizer should return a string value." );
749 }
750
751 // Delete the normalizer from rules to avoid treating
752 // it as a pre-defined method.
753 delete rules.normalizer;
754 }
755
756 for ( method in rules ) {
757 rule = { method: method, parameters: rules[ method ] };
758 try {
759 result = $.validator.methods[ method ].call( this, val, element, rule.parameters );
760
761 // If a method indicates that the field is optional and therefore valid,
762 // don't mark it as valid when there are no other rules
763 if ( result === "dependency-mismatch" && rulesCount === 1 ) {
764 dependencyMismatch = true;
765 continue;
766 }
767 dependencyMismatch = false;
768
769 if ( result === "pending" ) {
770 this.toHide = this.toHide.not( this.errorsFor( element ) );
771 return;
772 }
773
774 if ( !result ) {
775 this.formatAndAdd( element, rule );
776 return false;
777 }
778 } catch ( e ) {
779 if ( this.settings.debug && window.console ) {
780 console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e );
781 }
782 if ( e instanceof TypeError ) {
783 e.message += ". Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.";
784 }
785
786 throw e;
787 }
788 }
789 if ( dependencyMismatch ) {
790 return;
791 }
792 if ( this.objectLength( rules ) ) {
793 this.successList.push( element );
794 }
795 return true;
796 },
797
798 // Return the custom message for the given element and validation method
799 // specified in the element's HTML5 data attribute
800 // return the generic message if present and no method specific message is present
801 customDataMessage: function( element, method ) {
802 return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() +
803 method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" );
804 },
805
806 // Return the custom message for the given element name and validation method
807 customMessage: function( name, method ) {
808 var m = this.settings.messages[ name ];
809 return m && ( m.constructor === String ? m : m[ method ] );
810 },
811
812 // Return the first defined argument, allowing empty strings
813 findDefined: function() {
814 for ( var i = 0; i < arguments.length; i++ ) {
815 if ( arguments[ i ] !== undefined ) {
816 return arguments[ i ];
817 }
818 }
819 return undefined;
820 },
821
822 // The second parameter 'rule' used to be a string, and extended to an object literal
823 // of the following form:
824 // rule = {
825 // method: "method name",
826 // parameters: "the given method parameters"
827 // }
828 //
829 // The old behavior still supported, kept to maintain backward compatibility with
830 // old code, and will be removed in the next major release.
831 defaultMessage: function( element, rule ) {
832 if ( typeof rule === "string" ) {
833 rule = { method: rule };
834 }
835
836 var message = this.findDefined(
837 this.customMessage( element.name, rule.method ),
838 this.customDataMessage( element, rule.method ),
839
840 // 'title' is never undefined, so handle empty string as undefined
841 !this.settings.ignoreTitle && element.title || undefined,
842 $.validator.messages[ rule.method ],
843 "<strong>Warning: No message defined for " + element.name + "</strong>"
844 ),
845 theregex = /\$?\{(\d+)\}/g;
846 if ( typeof message === "function" ) {
847 message = message.call( this, rule.parameters, element );
848 } else if ( theregex.test( message ) ) {
849 message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters );
850 }
851
852 return message;
853 },
854
855 formatAndAdd: function( element, rule ) {
856 var message = this.defaultMessage( element, rule );
857
858 this.errorList.push( {
859 message: message,
860 element: element,
861 method: rule.method
862 } );
863
864 this.errorMap[ element.name ] = message;
865 this.submitted[ element.name ] = message;
866 },
867
868 addWrapper: function( toToggle ) {
869 if ( this.settings.wrapper ) {
870 toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
871 }
872 return toToggle;
873 },
874
875 defaultShowErrors: function() {
876 var i, elements, error;
877 for ( i = 0; this.errorList[ i ]; i++ ) {
878 error = this.errorList[ i ];
879 if ( this.settings.highlight ) {
880 this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
881 }
882 this.showLabel( error.element, error.message );
883 }
884 if ( this.errorList.length ) {
885 this.toShow = this.toShow.add( this.containers );
886 }
887 if ( this.settings.success ) {
888 for ( i = 0; this.successList[ i ]; i++ ) {
889 this.showLabel( this.successList[ i ] );
890 }
891 }
892 if ( this.settings.unhighlight ) {
893 for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {
894 this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );
895 }
896 }
897 this.toHide = this.toHide.not( this.toShow );
898 this.hideErrors();
899 this.addWrapper( this.toShow ).show();
900 },
901
902 validElements: function() {
903 return this.currentElements.not( this.invalidElements() );
904 },
905
906 invalidElements: function() {
907 return $( this.errorList ).map( function() {
908 return this.element;
909 } );
910 },
911
912 showLabel: function( element, message ) {
913 var place, group, errorID, v,
914 error = this.errorsFor( element ),
915 elementID = this.idOrName( element ),
916 describedBy = $( element ).attr( "aria-describedby" );
917
918 if ( error.length ) {
919
920 // Refresh error/success class
921 error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
922
923 // Replace message on existing label
924 error.html( message );
925 } else {
926
927 // Create error element
928 error = $( "<" + this.settings.errorElement + ">" )
929 .attr( "id", elementID + "-error" )
930 .addClass( this.settings.errorClass )
931 .html( message || "" );
932
933 // Maintain reference to the element to be placed into the DOM
934 place = error;
935 if ( this.settings.wrapper ) {
936
937 // Make sure the element is visible, even in IE
938 // actually showing the wrapped element is handled elsewhere
939 place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent();
940 }
941 if ( this.labelContainer.length ) {
942 this.labelContainer.append( place );
943 } else if ( this.settings.errorPlacement ) {
944 this.settings.errorPlacement.call( this, place, $( element ) );
945 } else {
946 place.insertAfter( element );
947 }
948
949 // Link error back to the element
950 if ( error.is( "label" ) ) {
951
952 // If the error is a label, then associate using 'for'
953 error.attr( "for", elementID );
954
955 // If the element is not a child of an associated label, then it's necessary
956 // to explicitly apply aria-describedby
957 } else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) {
958 errorID = error.attr( "id" );
959
960 // Respect existing non-error aria-describedby
961 if ( !describedBy ) {
962 describedBy = errorID;
963 } else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) {
964
965 // Add to end of list if not already present
966 describedBy += " " + errorID;
967 }
968 $( element ).attr( "aria-describedby", describedBy );
969
970 // If this element is grouped, then assign to all elements in the same group
971 group = this.groups[ element.name ];
972 if ( group ) {
973 v = this;
974 $.each( v.groups, function( name, testgroup ) {
975 if ( testgroup === group ) {
976 $( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm )
977 .attr( "aria-describedby", error.attr( "id" ) );
978 }
979 } );
980 }
981 }
982 }
983 if ( !message && this.settings.success ) {
984 error.text( "" );
985 if ( typeof this.settings.success === "string" ) {
986 error.addClass( this.settings.success );
987 } else {
988 this.settings.success( error, element );
989 }
990 }
991 this.toShow = this.toShow.add( error );
992 },
993
994 errorsFor: function( element ) {
995 var name = this.escapeCssMeta( this.idOrName( element ) ),
996 describer = $( element ).attr( "aria-describedby" ),
997 selector = "label[for='" + name + "'], label[for='" + name + "'] *";
998
999 // 'aria-describedby' should directly reference the error element
1000 if ( describer ) {
1001 selector = selector + ", #" + this.escapeCssMeta( describer )
1002 .replace( /\s+/g, ", #" );
1003 }
1004
1005 return this
1006 .errors()
1007 .filter( selector );
1008 },
1009
1010 // See https://api.jquery.com/category/selectors/, for CSS
1011 // meta-characters that should be escaped in order to be used with JQuery
1012 // as a literal part of a name/id or any selector.
1013 escapeCssMeta: function( string ) {
1014 return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" );
1015 },
1016
1017 idOrName: function( element ) {
1018 return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name );
1019 },
1020
1021 validationTargetFor: function( element ) {
1022
1023 // If radio/checkbox, validate first element in group instead
1024 if ( this.checkable( element ) ) {
1025 element = this.findByName( element.name );
1026 }
1027
1028 // Always apply ignore filter
1029 return $( element ).not( this.settings.ignore )[ 0 ];
1030 },
1031
1032 checkable: function( element ) {
1033 return ( /radio|checkbox/i ).test( element.type );
1034 },
1035
1036 findByName: function( name ) {
1037 return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" );
1038 },
1039
1040 getLength: function( value, element ) {
1041 switch ( element.nodeName.toLowerCase() ) {
1042 case "select":
1043 return $( "option:selected", element ).length;
1044 case "input":
1045 if ( this.checkable( element ) ) {
1046 return this.findByName( element.name ).filter( ":checked" ).length;
1047 }
1048 }
1049 return value.length;
1050 },
1051
1052 depend: function( param, element ) {
1053 return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true;
1054 },
1055
1056 dependTypes: {
1057 "boolean": function( param ) {
1058 return param;
1059 },
1060 "string": function( param, element ) {
1061 return !!$( param, element.form ).length;
1062 },
1063 "function": function( param, element ) {
1064 return param( element );
1065 }
1066 },
1067
1068 optional: function( element ) {
1069 var val = this.elementValue( element );
1070 return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch";
1071 },
1072
1073 startRequest: function( element ) {
1074 if ( !this.pending[ element.name ] ) {
1075 this.pendingRequest++;
1076 $( element ).addClass( this.settings.pendingClass );
1077 this.pending[ element.name ] = true;
1078 }
1079 },
1080
1081 stopRequest: function( element, valid ) {
1082 this.pendingRequest--;
1083
1084 // Sometimes synchronization fails, make sure pendingRequest is never < 0
1085 if ( this.pendingRequest < 0 ) {
1086 this.pendingRequest = 0;
1087 }
1088 delete this.pending[ element.name ];
1089 $( element ).removeClass( this.settings.pendingClass );
1090 if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
1091 $( this.currentForm ).submit();
1092 this.formSubmitted = false;
1093 } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) {
1094 $( this.currentForm ).triggerHandler( "invalid-form", [ this ] );
1095 this.formSubmitted = false;
1096 }
1097 },
1098
1099 previousValue: function( element, method ) {
1100 method = typeof method === "string" && method || "remote";
1101
1102 return $.data( element, "previousValue" ) || $.data( element, "previousValue", {
1103 old: null,
1104 valid: true,
1105 message: this.defaultMessage( element, { method: method } )
1106 } );
1107 },
1108
1109 // Cleans up all forms and elements, removes validator-specific events
1110 destroy: function() {
1111 this.resetForm();
1112
1113 $( this.currentForm )
1114 .off( ".validate" )
1115 .removeData( "validator" )
1116 .find( ".validate-equalTo-blur" )
1117 .off( ".validate-equalTo" )
1118 .removeClass( "validate-equalTo-blur" );
1119 }
1120
1121 },
1122
1123 classRuleSettings: {
1124 required: { required: true },
1125 email: { email: true },
1126 url: { url: true },
1127 date: { date: true },
1128 dateISO: { dateISO: true },
1129 number: { number: true },
1130 digits: { digits: true },
1131 creditcard: { creditcard: true }
1132 },
1133
1134 addClassRules: function( className, rules ) {
1135 if ( className.constructor === String ) {
1136 this.classRuleSettings[ className ] = rules;
1137 } else {
1138 $.extend( this.classRuleSettings, className );
1139 }
1140 },
1141
1142 classRules: function( element ) {
1143 var rules = {},
1144 classes = $( element ).attr( "class" );
1145
1146 if ( classes ) {
1147 $.each( classes.split( " " ), function() {
1148 if ( this in $.validator.classRuleSettings ) {
1149 $.extend( rules, $.validator.classRuleSettings[ this ] );
1150 }
1151 } );
1152 }
1153 return rules;
1154 },
1155
1156 normalizeAttributeRule: function( rules, type, method, value ) {
1157
1158 // Convert the value to a number for number inputs, and for text for backwards compability
1159 // allows type="date" and others to be compared as strings
1160 if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
1161 value = Number( value );
1162
1163 // Support Opera Mini, which returns NaN for undefined minlength
1164 if ( isNaN( value ) ) {
1165 value = undefined;
1166 }
1167 }
1168
1169 if ( value || value === 0 ) {
1170 rules[ method ] = value;
1171 } else if ( type === method && type !== "range" ) {
1172
1173 // Exception: the jquery validate 'range' method
1174 // does not test for the html5 'range' type
1175 rules[ method ] = true;
1176 }
1177 },
1178
1179 attributeRules: function( element ) {
1180 var rules = {},
1181 $element = $( element ),
1182 type = element.getAttribute( "type" ),
1183 method, value;
1184
1185 for ( method in $.validator.methods ) {
1186
1187 // Support for <input required> in both html5 and older browsers
1188 if ( method === "required" ) {
1189 value = element.getAttribute( method );
1190
1191 // Some browsers return an empty string for the required attribute
1192 // and non-HTML5 browsers might have required="" markup
1193 if ( value === "" ) {
1194 value = true;
1195 }
1196
1197 // Force non-HTML5 browsers to return bool
1198 value = !!value;
1199 } else {
1200 value = $element.attr( method );
1201 }
1202
1203 this.normalizeAttributeRule( rules, type, method, value );
1204 }
1205
1206 // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
1207 if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {
1208 delete rules.maxlength;
1209 }
1210
1211 return rules;
1212 },
1213
1214 dataRules: function( element ) {
1215 var rules = {},
1216 $element = $( element ),
1217 type = element.getAttribute( "type" ),
1218 method, value;
1219
1220 for ( method in $.validator.methods ) {
1221 value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );
1222 this.normalizeAttributeRule( rules, type, method, value );
1223 }
1224 return rules;
1225 },
1226
1227 staticRules: function( element ) {
1228 var rules = {},
1229 validator = $.data( element.form, "validator" );
1230
1231 if ( validator.settings.rules ) {
1232 rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};
1233 }
1234 return rules;
1235 },
1236
1237 normalizeRules: function( rules, element ) {
1238
1239 // Handle dependency check
1240 $.each( rules, function( prop, val ) {
1241
1242 // Ignore rule when param is explicitly false, eg. required:false
1243 if ( val === false ) {
1244 delete rules[ prop ];
1245 return;
1246 }
1247 if ( val.param || val.depends ) {
1248 var keepRule = true;
1249 switch ( typeof val.depends ) {
1250 case "string":
1251 keepRule = !!$( val.depends, element.form ).length;
1252 break;
1253 case "function":
1254 keepRule = val.depends.call( element, element );
1255 break;
1256 }
1257 if ( keepRule ) {
1258 rules[ prop ] = val.param !== undefined ? val.param : true;
1259 } else {
1260 $.data( element.form, "validator" ).resetElements( $( element ) );
1261 delete rules[ prop ];
1262 }
1263 }
1264 } );
1265
1266 // Evaluate parameters
1267 $.each( rules, function( rule, parameter ) {
1268 rules[ rule ] = $.isFunction( parameter ) && rule !== "normalizer" ? parameter( element ) : parameter;
1269 } );
1270
1271 // Clean number parameters
1272 $.each( [ "minlength", "maxlength" ], function() {
1273 if ( rules[ this ] ) {
1274 rules[ this ] = Number( rules[ this ] );
1275 }
1276 } );
1277 $.each( [ "rangelength", "range" ], function() {
1278 var parts;
1279 if ( rules[ this ] ) {
1280 if ( $.isArray( rules[ this ] ) ) {
1281 rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ];
1282 } else if ( typeof rules[ this ] === "string" ) {
1283 parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ );
1284 rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ];
1285 }
1286 }
1287 } );
1288
1289 if ( $.validator.autoCreateRanges ) {
1290
1291 // Auto-create ranges
1292 if ( rules.min != null && rules.max != null ) {
1293 rules.range = [ rules.min, rules.max ];
1294 delete rules.min;
1295 delete rules.max;
1296 }
1297 if ( rules.minlength != null && rules.maxlength != null ) {
1298 rules.rangelength = [ rules.minlength, rules.maxlength ];
1299 delete rules.minlength;
1300 delete rules.maxlength;
1301 }
1302 }
1303
1304 return rules;
1305 },
1306
1307 // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
1308 normalizeRule: function( data ) {
1309 if ( typeof data === "string" ) {
1310 var transformed = {};
1311 $.each( data.split( /\s/ ), function() {
1312 transformed[ this ] = true;
1313 } );
1314 data = transformed;
1315 }
1316 return data;
1317 },
1318
1319 // http://jqueryvalidation.org/jQuery.validator.addMethod/
1320 addMethod: function( name, method, message ) {
1321 $.validator.methods[ name ] = method;
1322 $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ];
1323 if ( method.length < 3 ) {
1324 $.validator.addClassRules( name, $.validator.normalizeRule( name ) );
1325 }
1326 },
1327
1328 // http://jqueryvalidation.org/jQuery.validator.methods/
1329 methods: {
1330
1331 // http://jqueryvalidation.org/required-method/
1332 required: function( value, element, param ) {
1333
1334 // Check if dependency is met
1335 if ( !this.depend( param, element ) ) {
1336 return "dependency-mismatch";
1337 }
1338 if ( element.nodeName.toLowerCase() === "select" ) {
1339
1340 // Could be an array for select-multiple or a string, both are fine this way
1341 var val = $( element ).val();
1342 return val && val.length > 0;
1343 }
1344 if ( this.checkable( element ) ) {
1345 return this.getLength( value, element ) > 0;
1346 }
1347 return value.length > 0;
1348 },
1349
1350 // http://jqueryvalidation.org/email-method/
1351 email: function( value, element ) {
1352
1353 // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
1354 // Retrieved 2014-01-14
1355 // If you have a problem with this implementation, report a bug against the above spec
1356 // Or use custom methods to implement your own email validation
1357 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 );
1358 },
1359
1360 // http://jqueryvalidation.org/url-method/
1361 url: function( value, element ) {
1362
1363 // Copyright (c) 2010-2013 Diego Perini, MIT licensed
1364 // https://gist.github.com/dperini/729294
1365 // see also https://mathiasbynens.be/demo/url-regex
1366 // modified to allow protocol-relative URLs
1367 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 );
1368 },
1369
1370 // http://jqueryvalidation.org/date-method/
1371 date: function( value, element ) {
1372 return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() );
1373 },
1374
1375 // http://jqueryvalidation.org/dateISO-method/
1376 dateISO: function( value, element ) {
1377 return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value );
1378 },
1379
1380 // http://jqueryvalidation.org/number-method/
1381 number: function( value, element ) {
1382 return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
1383 },
1384
1385 // http://jqueryvalidation.org/digits-method/
1386 digits: function( value, element ) {
1387 return this.optional( element ) || /^\d+$/.test( value );
1388 },
1389
1390 // http://jqueryvalidation.org/minlength-method/
1391 minlength: function( value, element, param ) {
1392 var length = $.isArray( value ) ? value.length : this.getLength( value, element );
1393 return this.optional( element ) || length >= param;
1394 },
1395
1396 // http://jqueryvalidation.org/maxlength-method/
1397 maxlength: function( value, element, param ) {
1398 var length = $.isArray( value ) ? value.length : this.getLength( value, element );
1399 return this.optional( element ) || length <= param;
1400 },
1401
1402 // http://jqueryvalidation.org/rangelength-method/
1403 rangelength: function( value, element, param ) {
1404 var length = $.isArray( value ) ? value.length : this.getLength( value, element );
1405 return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );
1406 },
1407
1408 // http://jqueryvalidation.org/min-method/
1409 min: function( value, element, param ) {
1410 return this.optional( element ) || value >= param;
1411 },
1412
1413 // http://jqueryvalidation.org/max-method/
1414 max: function( value, element, param ) {
1415 return this.optional( element ) || value <= param;
1416 },
1417
1418 // http://jqueryvalidation.org/range-method/
1419 range: function( value, element, param ) {
1420 return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
1421 },
1422
1423 // http://jqueryvalidation.org/step-method/
1424 step: function( value, element, param ) {
1425 var type = $( element ).attr( "type" ),
1426 errorMessage = "Step attribute on input type " + type + " is not supported.",
1427 supportedTypes = [ "text", "number", "range" ],
1428 re = new RegExp( "\\b" + type + "\\b" ),
1429 notSupported = type && !re.test( supportedTypes.join() ),
1430 decimalPlaces = function( num ) {
1431 var match = ( "" + num ).match( /(?:\.(\d+))?$/ );
1432 if ( !match ) {
1433 return 0;
1434 }
1435
1436 // Number of digits right of decimal point.
1437 return match[ 1 ] ? match[ 1 ].length : 0;
1438 },
1439 toInt = function( num ) {
1440 return Math.round( num * Math.pow( 10, decimals ) );
1441 },
1442 valid = true,
1443 decimals;
1444
1445 // Works only for text, number and range input types
1446 // TODO find a way to support input types date, datetime, datetime-local, month, time and week
1447 if ( notSupported ) {
1448 throw new Error( errorMessage );
1449 }
1450
1451 decimals = decimalPlaces( param );
1452
1453 // Value can't have too many decimals
1454 if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) {
1455 valid = false;
1456 }
1457
1458 return this.optional( element ) || valid;
1459 },
1460
1461 // http://jqueryvalidation.org/equalTo-method/
1462 equalTo: function( value, element, param ) {
1463
1464 // Bind to the blur event of the target in order to revalidate whenever the target field is updated
1465 var target = $( param );
1466 if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) {
1467 target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() {
1468 $( element ).valid();
1469 } );
1470 }
1471 return value === target.val();
1472 },
1473
1474 // http://jqueryvalidation.org/remote-method/
1475 remote: function( value, element, param, method ) {
1476 if ( this.optional( element ) ) {
1477 return "dependency-mismatch";
1478 }
1479
1480 method = typeof method === "string" && method || "remote";
1481
1482 var previous = this.previousValue( element, method ),
1483 validator, data, optionDataString;
1484
1485 if ( !this.settings.messages[ element.name ] ) {
1486 this.settings.messages[ element.name ] = {};
1487 }
1488 previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ];
1489 this.settings.messages[ element.name ][ method ] = previous.message;
1490
1491 param = typeof param === "string" && { url: param } || param;
1492 optionDataString = $.param( $.extend( { data: value }, param.data ) );
1493 if ( previous.old === optionDataString ) {
1494 return previous.valid;
1495 }
1496
1497 previous.old = optionDataString;
1498 validator = this;
1499 this.startRequest( element );
1500 data = {};
1501 data[ element.name ] = value;
1502 $.ajax( $.extend( true, {
1503 mode: "abort",
1504 port: "validate" + element.name,
1505 dataType: "json",
1506 data: data,
1507 context: validator.currentForm,
1508 success: function( response ) {
1509 var valid = response === true || response === "true",
1510 errors, message, submitted;
1511
1512 validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
1513 if ( valid ) {
1514 submitted = validator.formSubmitted;
1515 validator.resetInternals();
1516 validator.toHide = validator.errorsFor( element );
1517 validator.formSubmitted = submitted;
1518 validator.successList.push( element );
1519 validator.invalid[ element.name ] = false;
1520 validator.showErrors();
1521 } else {
1522 errors = {};
1523 message = response || validator.defaultMessage( element, { method: method, parameters: value } );
1524 errors[ element.name ] = previous.message = message;
1525 validator.invalid[ element.name ] = true;
1526 validator.showErrors( errors );
1527 }
1528 previous.valid = valid;
1529 validator.stopRequest( element, valid );
1530 }
1531 }, param ) );
1532 return "pending";
1533 }
1534 }
1535
1536 } );
1537
1538 // Ajax mode: abort
1539 // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
1540 // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
1541
1542 var pendingRequests = {},
1543 ajax;
1544
1545 // Use a prefilter if available (1.5+)
1546 if ( $.ajaxPrefilter ) {
1547 $.ajaxPrefilter( function( settings, _, xhr ) {
1548 var port = settings.port;
1549 if ( settings.mode === "abort" ) {
1550 if ( pendingRequests[ port ] ) {
1551 pendingRequests[ port ].abort();
1552 }
1553 pendingRequests[ port ] = xhr;
1554 }
1555 } );
1556 } else {
1557
1558 // Proxy ajax
1559 ajax = $.ajax;
1560 $.ajax = function( settings ) {
1561 var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
1562 port = ( "port" in settings ? settings : $.ajaxSettings ).port;
1563 if ( mode === "abort" ) {
1564 if ( pendingRequests[ port ] ) {
1565 pendingRequests[ port ].abort();
1566 }
1567 pendingRequests[ port ] = ajax.apply( this, arguments );
1568 return pendingRequests[ port ];
1569 }
1570 return ajax.apply( this, arguments );
1571 };
1572 }
1573 return $;
1574 }));