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