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