|
1 /*! |
|
2 * jQuery Validation Plugin v1.17.0 |
|
3 * |
|
4 * https://jqueryvalidation.org/ |
|
5 * |
|
6 * Copyright (c) 2017 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 function handle() { |
|
71 var hidden, result; |
|
72 |
|
73 // Insert a hidden input as a replacement for the missing submit button |
|
74 // The hidden input is inserted in two cases: |
|
75 // - A user defined a `submitHandler` |
|
76 // - There was a pending request due to `remote` method and `stopRequest()` |
|
77 // was called to submit the form in case it's valid |
|
78 if ( validator.submitButton && ( validator.settings.submitHandler || validator.formSubmitted ) ) { |
|
79 hidden = $( "<input type='hidden'/>" ) |
|
80 .attr( "name", validator.submitButton.name ) |
|
81 .val( $( validator.submitButton ).val() ) |
|
82 .appendTo( validator.currentForm ); |
|
83 } |
|
84 |
|
85 if ( validator.settings.submitHandler ) { |
|
86 result = validator.settings.submitHandler.call( validator, validator.currentForm, event ); |
|
87 if ( hidden ) { |
|
88 |
|
89 // And clean up afterwards; thanks to no-block-scope, hidden can be referenced |
|
90 hidden.remove(); |
|
91 } |
|
92 if ( result !== undefined ) { |
|
93 return result; |
|
94 } |
|
95 return false; |
|
96 } |
|
97 return true; |
|
98 } |
|
99 |
|
100 // Prevent submit for invalid forms or custom submit handlers |
|
101 if ( validator.cancelSubmit ) { |
|
102 validator.cancelSubmit = false; |
|
103 return handle(); |
|
104 } |
|
105 if ( validator.form() ) { |
|
106 if ( validator.pendingRequest ) { |
|
107 validator.formSubmitted = true; |
|
108 return false; |
|
109 } |
|
110 return handle(); |
|
111 } else { |
|
112 validator.focusInvalid(); |
|
113 return false; |
|
114 } |
|
115 } ); |
|
116 } |
|
117 |
|
118 return validator; |
|
119 }, |
|
120 |
|
121 // https://jqueryvalidation.org/valid/ |
|
122 valid: function() { |
|
123 var valid, validator, errorList; |
|
124 |
|
125 if ( $( this[ 0 ] ).is( "form" ) ) { |
|
126 valid = this.validate().form(); |
|
127 } else { |
|
128 errorList = []; |
|
129 valid = true; |
|
130 validator = $( this[ 0 ].form ).validate(); |
|
131 this.each( function() { |
|
132 valid = validator.element( this ) && valid; |
|
133 if ( !valid ) { |
|
134 errorList = errorList.concat( validator.errorList ); |
|
135 } |
|
136 } ); |
|
137 validator.errorList = errorList; |
|
138 } |
|
139 return valid; |
|
140 }, |
|
141 |
|
142 // https://jqueryvalidation.org/rules/ |
|
143 rules: function( command, argument ) { |
|
144 var element = this[ 0 ], |
|
145 settings, staticRules, existingRules, data, param, filtered; |
|
146 |
|
147 // If nothing is selected, return empty object; can't chain anyway |
|
148 if ( element == null ) { |
|
149 return; |
|
150 } |
|
151 |
|
152 if ( !element.form && element.hasAttribute( "contenteditable" ) ) { |
|
153 element.form = this.closest( "form" )[ 0 ]; |
|
154 element.name = this.attr( "name" ); |
|
155 } |
|
156 |
|
157 if ( element.form == null ) { |
|
158 return; |
|
159 } |
|
160 |
|
161 if ( command ) { |
|
162 settings = $.data( element.form, "validator" ).settings; |
|
163 staticRules = settings.rules; |
|
164 existingRules = $.validator.staticRules( element ); |
|
165 switch ( command ) { |
|
166 case "add": |
|
167 $.extend( existingRules, $.validator.normalizeRule( argument ) ); |
|
168 |
|
169 // Remove messages from rules, but allow them to be set separately |
|
170 delete existingRules.messages; |
|
171 staticRules[ element.name ] = existingRules; |
|
172 if ( argument.messages ) { |
|
173 settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages ); |
|
174 } |
|
175 break; |
|
176 case "remove": |
|
177 if ( !argument ) { |
|
178 delete staticRules[ element.name ]; |
|
179 return existingRules; |
|
180 } |
|
181 filtered = {}; |
|
182 $.each( argument.split( /\s/ ), function( index, method ) { |
|
183 filtered[ method ] = existingRules[ method ]; |
|
184 delete existingRules[ method ]; |
|
185 } ); |
|
186 return filtered; |
|
187 } |
|
188 } |
|
189 |
|
190 data = $.validator.normalizeRules( |
|
191 $.extend( |
|
192 {}, |
|
193 $.validator.classRules( element ), |
|
194 $.validator.attributeRules( element ), |
|
195 $.validator.dataRules( element ), |
|
196 $.validator.staticRules( element ) |
|
197 ), element ); |
|
198 |
|
199 // Make sure required is at front |
|
200 if ( data.required ) { |
|
201 param = data.required; |
|
202 delete data.required; |
|
203 data = $.extend( { required: param }, data ); |
|
204 } |
|
205 |
|
206 // Make sure remote is at back |
|
207 if ( data.remote ) { |
|
208 param = data.remote; |
|
209 delete data.remote; |
|
210 data = $.extend( data, { remote: param } ); |
|
211 } |
|
212 |
|
213 return data; |
|
214 } |
|
215 } ); |
|
216 |
|
217 // Custom selectors |
|
218 $.extend( $.expr.pseudos || $.expr[ ":" ], { // '|| $.expr[ ":" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support |
|
219 |
|
220 // https://jqueryvalidation.org/blank-selector/ |
|
221 blank: function( a ) { |
|
222 return !$.trim( "" + $( a ).val() ); |
|
223 }, |
|
224 |
|
225 // https://jqueryvalidation.org/filled-selector/ |
|
226 filled: function( a ) { |
|
227 var val = $( a ).val(); |
|
228 return val !== null && !!$.trim( "" + val ); |
|
229 }, |
|
230 |
|
231 // https://jqueryvalidation.org/unchecked-selector/ |
|
232 unchecked: function( a ) { |
|
233 return !$( a ).prop( "checked" ); |
|
234 } |
|
235 } ); |
|
236 |
|
237 // Constructor for validator |
|
238 $.validator = function( options, form ) { |
|
239 this.settings = $.extend( true, {}, $.validator.defaults, options ); |
|
240 this.currentForm = form; |
|
241 this.init(); |
|
242 }; |
|
243 |
|
244 // https://jqueryvalidation.org/jQuery.validator.format/ |
|
245 $.validator.format = function( source, params ) { |
|
246 if ( arguments.length === 1 ) { |
|
247 return function() { |
|
248 var args = $.makeArray( arguments ); |
|
249 args.unshift( source ); |
|
250 return $.validator.format.apply( this, args ); |
|
251 }; |
|
252 } |
|
253 if ( params === undefined ) { |
|
254 return source; |
|
255 } |
|
256 if ( arguments.length > 2 && params.constructor !== Array ) { |
|
257 params = $.makeArray( arguments ).slice( 1 ); |
|
258 } |
|
259 if ( params.constructor !== Array ) { |
|
260 params = [ params ]; |
|
261 } |
|
262 $.each( params, function( i, n ) { |
|
263 source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() { |
|
264 return n; |
|
265 } ); |
|
266 } ); |
|
267 return source; |
|
268 }; |
|
269 |
|
270 $.extend( $.validator, { |
|
271 |
|
272 defaults: { |
|
273 messages: {}, |
|
274 groups: {}, |
|
275 rules: {}, |
|
276 errorClass: "error", |
|
277 pendingClass: "pending", |
|
278 validClass: "valid", |
|
279 errorElement: "label", |
|
280 focusCleanup: false, |
|
281 focusInvalid: true, |
|
282 errorContainer: $( [] ), |
|
283 errorLabelContainer: $( [] ), |
|
284 onsubmit: true, |
|
285 ignore: ":hidden", |
|
286 ignoreTitle: false, |
|
287 onfocusin: function( element ) { |
|
288 this.lastActive = element; |
|
289 |
|
290 // Hide error label and remove error class on focus if enabled |
|
291 if ( this.settings.focusCleanup ) { |
|
292 if ( this.settings.unhighlight ) { |
|
293 this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); |
|
294 } |
|
295 this.hideThese( this.errorsFor( element ) ); |
|
296 } |
|
297 }, |
|
298 onfocusout: function( element ) { |
|
299 if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) { |
|
300 this.element( element ); |
|
301 } |
|
302 }, |
|
303 onkeyup: function( element, event ) { |
|
304 |
|
305 // Avoid revalidate the field when pressing one of the following keys |
|
306 // Shift => 16 |
|
307 // Ctrl => 17 |
|
308 // Alt => 18 |
|
309 // Caps lock => 20 |
|
310 // End => 35 |
|
311 // Home => 36 |
|
312 // Left arrow => 37 |
|
313 // Up arrow => 38 |
|
314 // Right arrow => 39 |
|
315 // Down arrow => 40 |
|
316 // Insert => 45 |
|
317 // Num lock => 144 |
|
318 // AltGr key => 225 |
|
319 var excludedKeys = [ |
|
320 16, 17, 18, 20, 35, 36, 37, |
|
321 38, 39, 40, 45, 144, 225 |
|
322 ]; |
|
323 |
|
324 if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) { |
|
325 return; |
|
326 } else if ( element.name in this.submitted || element.name in this.invalid ) { |
|
327 this.element( element ); |
|
328 } |
|
329 }, |
|
330 onclick: function( element ) { |
|
331 |
|
332 // Click on selects, radiobuttons and checkboxes |
|
333 if ( element.name in this.submitted ) { |
|
334 this.element( element ); |
|
335 |
|
336 // Or option elements, check parent select in that case |
|
337 } else if ( element.parentNode.name in this.submitted ) { |
|
338 this.element( element.parentNode ); |
|
339 } |
|
340 }, |
|
341 highlight: function( element, errorClass, validClass ) { |
|
342 if ( element.type === "radio" ) { |
|
343 this.findByName( element.name ).addClass( errorClass ).removeClass( validClass ); |
|
344 } else { |
|
345 $( element ).addClass( errorClass ).removeClass( validClass ); |
|
346 } |
|
347 }, |
|
348 unhighlight: function( element, errorClass, validClass ) { |
|
349 if ( element.type === "radio" ) { |
|
350 this.findByName( element.name ).removeClass( errorClass ).addClass( validClass ); |
|
351 } else { |
|
352 $( element ).removeClass( errorClass ).addClass( validClass ); |
|
353 } |
|
354 } |
|
355 }, |
|
356 |
|
357 // https://jqueryvalidation.org/jQuery.validator.setDefaults/ |
|
358 setDefaults: function( settings ) { |
|
359 $.extend( $.validator.defaults, settings ); |
|
360 }, |
|
361 |
|
362 messages: { |
|
363 required: "This field is required.", |
|
364 remote: "Please fix this field.", |
|
365 email: "Please enter a valid email address.", |
|
366 url: "Please enter a valid URL.", |
|
367 date: "Please enter a valid date.", |
|
368 dateISO: "Please enter a valid date (ISO).", |
|
369 number: "Please enter a valid number.", |
|
370 digits: "Please enter only digits.", |
|
371 equalTo: "Please enter the same value again.", |
|
372 maxlength: $.validator.format( "Please enter no more than {0} characters." ), |
|
373 minlength: $.validator.format( "Please enter at least {0} characters." ), |
|
374 rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ), |
|
375 range: $.validator.format( "Please enter a value between {0} and {1}." ), |
|
376 max: $.validator.format( "Please enter a value less than or equal to {0}." ), |
|
377 min: $.validator.format( "Please enter a value greater than or equal to {0}." ), |
|
378 step: $.validator.format( "Please enter a multiple of {0}." ) |
|
379 }, |
|
380 |
|
381 autoCreateRanges: false, |
|
382 |
|
383 prototype: { |
|
384 |
|
385 init: function() { |
|
386 this.labelContainer = $( this.settings.errorLabelContainer ); |
|
387 this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm ); |
|
388 this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer ); |
|
389 this.submitted = {}; |
|
390 this.valueCache = {}; |
|
391 this.pendingRequest = 0; |
|
392 this.pending = {}; |
|
393 this.invalid = {}; |
|
394 this.reset(); |
|
395 |
|
396 var groups = ( this.groups = {} ), |
|
397 rules; |
|
398 $.each( this.settings.groups, function( key, value ) { |
|
399 if ( typeof value === "string" ) { |
|
400 value = value.split( /\s/ ); |
|
401 } |
|
402 $.each( value, function( index, name ) { |
|
403 groups[ name ] = key; |
|
404 } ); |
|
405 } ); |
|
406 rules = this.settings.rules; |
|
407 $.each( rules, function( key, value ) { |
|
408 rules[ key ] = $.validator.normalizeRule( value ); |
|
409 } ); |
|
410 |
|
411 function delegate( event ) { |
|
412 |
|
413 // Set form expando on contenteditable |
|
414 if ( !this.form && this.hasAttribute( "contenteditable" ) ) { |
|
415 this.form = $( this ).closest( "form" )[ 0 ]; |
|
416 this.name = $( this ).attr( "name" ); |
|
417 } |
|
418 |
|
419 var validator = $.data( this.form, "validator" ), |
|
420 eventType = "on" + event.type.replace( /^validate/, "" ), |
|
421 settings = validator.settings; |
|
422 if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) { |
|
423 settings[ eventType ].call( validator, this, event ); |
|
424 } |
|
425 } |
|
426 |
|
427 $( this.currentForm ) |
|
428 .on( "focusin.validate focusout.validate keyup.validate", |
|
429 ":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " + |
|
430 "[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " + |
|
431 "[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " + |
|
432 "[type='radio'], [type='checkbox'], [contenteditable], [type='button']", delegate ) |
|
433 |
|
434 // Support: Chrome, oldIE |
|
435 // "select" is provided as event.target when clicking a option |
|
436 .on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate ); |
|
437 |
|
438 if ( this.settings.invalidHandler ) { |
|
439 $( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler ); |
|
440 } |
|
441 }, |
|
442 |
|
443 // https://jqueryvalidation.org/Validator.form/ |
|
444 form: function() { |
|
445 this.checkForm(); |
|
446 $.extend( this.submitted, this.errorMap ); |
|
447 this.invalid = $.extend( {}, this.errorMap ); |
|
448 if ( !this.valid() ) { |
|
449 $( this.currentForm ).triggerHandler( "invalid-form", [ this ] ); |
|
450 } |
|
451 this.showErrors(); |
|
452 return this.valid(); |
|
453 }, |
|
454 |
|
455 checkForm: function() { |
|
456 this.prepareForm(); |
|
457 for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) { |
|
458 this.check( elements[ i ] ); |
|
459 } |
|
460 return this.valid(); |
|
461 }, |
|
462 |
|
463 // https://jqueryvalidation.org/Validator.element/ |
|
464 element: function( element ) { |
|
465 var cleanElement = this.clean( element ), |
|
466 checkElement = this.validationTargetFor( cleanElement ), |
|
467 v = this, |
|
468 result = true, |
|
469 rs, group; |
|
470 |
|
471 if ( checkElement === undefined ) { |
|
472 delete this.invalid[ cleanElement.name ]; |
|
473 } else { |
|
474 this.prepareElement( checkElement ); |
|
475 this.currentElements = $( checkElement ); |
|
476 |
|
477 // If this element is grouped, then validate all group elements already |
|
478 // containing a value |
|
479 group = this.groups[ checkElement.name ]; |
|
480 if ( group ) { |
|
481 $.each( this.groups, function( name, testgroup ) { |
|
482 if ( testgroup === group && name !== checkElement.name ) { |
|
483 cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) ); |
|
484 if ( cleanElement && cleanElement.name in v.invalid ) { |
|
485 v.currentElements.push( cleanElement ); |
|
486 result = v.check( cleanElement ) && result; |
|
487 } |
|
488 } |
|
489 } ); |
|
490 } |
|
491 |
|
492 rs = this.check( checkElement ) !== false; |
|
493 result = result && rs; |
|
494 if ( rs ) { |
|
495 this.invalid[ checkElement.name ] = false; |
|
496 } else { |
|
497 this.invalid[ checkElement.name ] = true; |
|
498 } |
|
499 |
|
500 if ( !this.numberOfInvalids() ) { |
|
501 |
|
502 // Hide error containers on last error |
|
503 this.toHide = this.toHide.add( this.containers ); |
|
504 } |
|
505 this.showErrors(); |
|
506 |
|
507 // Add aria-invalid status for screen readers |
|
508 $( element ).attr( "aria-invalid", !rs ); |
|
509 } |
|
510 |
|
511 return result; |
|
512 }, |
|
513 |
|
514 // https://jqueryvalidation.org/Validator.showErrors/ |
|
515 showErrors: function( errors ) { |
|
516 if ( errors ) { |
|
517 var validator = this; |
|
518 |
|
519 // Add items to error list and map |
|
520 $.extend( this.errorMap, errors ); |
|
521 this.errorList = $.map( this.errorMap, function( message, name ) { |
|
522 return { |
|
523 message: message, |
|
524 element: validator.findByName( name )[ 0 ] |
|
525 }; |
|
526 } ); |
|
527 |
|
528 // Remove items from success list |
|
529 this.successList = $.grep( this.successList, function( element ) { |
|
530 return !( element.name in errors ); |
|
531 } ); |
|
532 } |
|
533 if ( this.settings.showErrors ) { |
|
534 this.settings.showErrors.call( this, this.errorMap, this.errorList ); |
|
535 } else { |
|
536 this.defaultShowErrors(); |
|
537 } |
|
538 }, |
|
539 |
|
540 // https://jqueryvalidation.org/Validator.resetForm/ |
|
541 resetForm: function() { |
|
542 if ( $.fn.resetForm ) { |
|
543 $( this.currentForm ).resetForm(); |
|
544 } |
|
545 this.invalid = {}; |
|
546 this.submitted = {}; |
|
547 this.prepareForm(); |
|
548 this.hideErrors(); |
|
549 var elements = this.elements() |
|
550 .removeData( "previousValue" ) |
|
551 .removeAttr( "aria-invalid" ); |
|
552 |
|
553 this.resetElements( elements ); |
|
554 }, |
|
555 |
|
556 resetElements: function( elements ) { |
|
557 var i; |
|
558 |
|
559 if ( this.settings.unhighlight ) { |
|
560 for ( i = 0; elements[ i ]; i++ ) { |
|
561 this.settings.unhighlight.call( this, elements[ i ], |
|
562 this.settings.errorClass, "" ); |
|
563 this.findByName( elements[ i ].name ).removeClass( this.settings.validClass ); |
|
564 } |
|
565 } else { |
|
566 elements |
|
567 .removeClass( this.settings.errorClass ) |
|
568 .removeClass( this.settings.validClass ); |
|
569 } |
|
570 }, |
|
571 |
|
572 numberOfInvalids: function() { |
|
573 return this.objectLength( this.invalid ); |
|
574 }, |
|
575 |
|
576 objectLength: function( obj ) { |
|
577 /* jshint unused: false */ |
|
578 var count = 0, |
|
579 i; |
|
580 for ( i in obj ) { |
|
581 |
|
582 // This check allows counting elements with empty error |
|
583 // message as invalid elements |
|
584 if ( obj[ i ] !== undefined && obj[ i ] !== null && obj[ i ] !== false ) { |
|
585 count++; |
|
586 } |
|
587 } |
|
588 return count; |
|
589 }, |
|
590 |
|
591 hideErrors: function() { |
|
592 this.hideThese( this.toHide ); |
|
593 }, |
|
594 |
|
595 hideThese: function( errors ) { |
|
596 errors.not( this.containers ).text( "" ); |
|
597 this.addWrapper( errors ).hide(); |
|
598 }, |
|
599 |
|
600 valid: function() { |
|
601 return this.size() === 0; |
|
602 }, |
|
603 |
|
604 size: function() { |
|
605 return this.errorList.length; |
|
606 }, |
|
607 |
|
608 focusInvalid: function() { |
|
609 if ( this.settings.focusInvalid ) { |
|
610 try { |
|
611 $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] ) |
|
612 .filter( ":visible" ) |
|
613 .focus() |
|
614 |
|
615 // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find |
|
616 .trigger( "focusin" ); |
|
617 } catch ( e ) { |
|
618 |
|
619 // Ignore IE throwing errors when focusing hidden elements |
|
620 } |
|
621 } |
|
622 }, |
|
623 |
|
624 findLastActive: function() { |
|
625 var lastActive = this.lastActive; |
|
626 return lastActive && $.grep( this.errorList, function( n ) { |
|
627 return n.element.name === lastActive.name; |
|
628 } ).length === 1 && lastActive; |
|
629 }, |
|
630 |
|
631 elements: function() { |
|
632 var validator = this, |
|
633 rulesCache = {}; |
|
634 |
|
635 // Select all valid inputs inside the form (no submit or reset buttons) |
|
636 return $( this.currentForm ) |
|
637 .find( "input, select, textarea, [contenteditable]" ) |
|
638 .not( ":submit, :reset, :image, :disabled" ) |
|
639 .not( this.settings.ignore ) |
|
640 .filter( function() { |
|
641 var name = this.name || $( this ).attr( "name" ); // For contenteditable |
|
642 if ( !name && validator.settings.debug && window.console ) { |
|
643 console.error( "%o has no name assigned", this ); |
|
644 } |
|
645 |
|
646 // Set form expando on contenteditable |
|
647 if ( this.hasAttribute( "contenteditable" ) ) { |
|
648 this.form = $( this ).closest( "form" )[ 0 ]; |
|
649 this.name = name; |
|
650 } |
|
651 |
|
652 // Select only the first element for each name, and only those with rules specified |
|
653 if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) { |
|
654 return false; |
|
655 } |
|
656 |
|
657 rulesCache[ name ] = true; |
|
658 return true; |
|
659 } ); |
|
660 }, |
|
661 |
|
662 clean: function( selector ) { |
|
663 return $( selector )[ 0 ]; |
|
664 }, |
|
665 |
|
666 errors: function() { |
|
667 var errorClass = this.settings.errorClass.split( " " ).join( "." ); |
|
668 return $( this.settings.errorElement + "." + errorClass, this.errorContext ); |
|
669 }, |
|
670 |
|
671 resetInternals: function() { |
|
672 this.successList = []; |
|
673 this.errorList = []; |
|
674 this.errorMap = {}; |
|
675 this.toShow = $( [] ); |
|
676 this.toHide = $( [] ); |
|
677 }, |
|
678 |
|
679 reset: function() { |
|
680 this.resetInternals(); |
|
681 this.currentElements = $( [] ); |
|
682 }, |
|
683 |
|
684 prepareForm: function() { |
|
685 this.reset(); |
|
686 this.toHide = this.errors().add( this.containers ); |
|
687 }, |
|
688 |
|
689 prepareElement: function( element ) { |
|
690 this.reset(); |
|
691 this.toHide = this.errorsFor( element ); |
|
692 }, |
|
693 |
|
694 elementValue: function( element ) { |
|
695 var $element = $( element ), |
|
696 type = element.type, |
|
697 val, idx; |
|
698 |
|
699 if ( type === "radio" || type === "checkbox" ) { |
|
700 return this.findByName( element.name ).filter( ":checked" ).val(); |
|
701 } else if ( type === "number" && typeof element.validity !== "undefined" ) { |
|
702 return element.validity.badInput ? "NaN" : $element.val(); |
|
703 } |
|
704 |
|
705 if ( element.hasAttribute( "contenteditable" ) ) { |
|
706 val = $element.text(); |
|
707 } else { |
|
708 val = $element.val(); |
|
709 } |
|
710 |
|
711 if ( type === "file" ) { |
|
712 |
|
713 // Modern browser (chrome & safari) |
|
714 if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) { |
|
715 return val.substr( 12 ); |
|
716 } |
|
717 |
|
718 // Legacy browsers |
|
719 // Unix-based path |
|
720 idx = val.lastIndexOf( "/" ); |
|
721 if ( idx >= 0 ) { |
|
722 return val.substr( idx + 1 ); |
|
723 } |
|
724 |
|
725 // Windows-based path |
|
726 idx = val.lastIndexOf( "\\" ); |
|
727 if ( idx >= 0 ) { |
|
728 return val.substr( idx + 1 ); |
|
729 } |
|
730 |
|
731 // Just the file name |
|
732 return val; |
|
733 } |
|
734 |
|
735 if ( typeof val === "string" ) { |
|
736 return val.replace( /\r/g, "" ); |
|
737 } |
|
738 return val; |
|
739 }, |
|
740 |
|
741 check: function( element ) { |
|
742 element = this.validationTargetFor( this.clean( element ) ); |
|
743 |
|
744 var rules = $( element ).rules(), |
|
745 rulesCount = $.map( rules, function( n, i ) { |
|
746 return i; |
|
747 } ).length, |
|
748 dependencyMismatch = false, |
|
749 val = this.elementValue( element ), |
|
750 result, method, rule, normalizer; |
|
751 |
|
752 // Prioritize the local normalizer defined for this element over the global one |
|
753 // if the former exists, otherwise user the global one in case it exists. |
|
754 if ( typeof rules.normalizer === "function" ) { |
|
755 normalizer = rules.normalizer; |
|
756 } else if ( typeof this.settings.normalizer === "function" ) { |
|
757 normalizer = this.settings.normalizer; |
|
758 } |
|
759 |
|
760 // If normalizer is defined, then call it to retreive the changed value instead |
|
761 // of using the real one. |
|
762 // Note that `this` in the normalizer is `element`. |
|
763 if ( normalizer ) { |
|
764 val = normalizer.call( element, val ); |
|
765 |
|
766 if ( typeof val !== "string" ) { |
|
767 throw new TypeError( "The normalizer should return a string value." ); |
|
768 } |
|
769 |
|
770 // Delete the normalizer from rules to avoid treating it as a pre-defined method. |
|
771 delete rules.normalizer; |
|
772 } |
|
773 |
|
774 for ( method in rules ) { |
|
775 rule = { method: method, parameters: rules[ method ] }; |
|
776 try { |
|
777 result = $.validator.methods[ method ].call( this, val, element, rule.parameters ); |
|
778 |
|
779 // If a method indicates that the field is optional and therefore valid, |
|
780 // don't mark it as valid when there are no other rules |
|
781 if ( result === "dependency-mismatch" && rulesCount === 1 ) { |
|
782 dependencyMismatch = true; |
|
783 continue; |
|
784 } |
|
785 dependencyMismatch = false; |
|
786 |
|
787 if ( result === "pending" ) { |
|
788 this.toHide = this.toHide.not( this.errorsFor( element ) ); |
|
789 return; |
|
790 } |
|
791 |
|
792 if ( !result ) { |
|
793 this.formatAndAdd( element, rule ); |
|
794 return false; |
|
795 } |
|
796 } catch ( e ) { |
|
797 if ( this.settings.debug && window.console ) { |
|
798 console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); |
|
799 } |
|
800 if ( e instanceof TypeError ) { |
|
801 e.message += ". Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method."; |
|
802 } |
|
803 |
|
804 throw e; |
|
805 } |
|
806 } |
|
807 if ( dependencyMismatch ) { |
|
808 return; |
|
809 } |
|
810 if ( this.objectLength( rules ) ) { |
|
811 this.successList.push( element ); |
|
812 } |
|
813 return true; |
|
814 }, |
|
815 |
|
816 // Return the custom message for the given element and validation method |
|
817 // specified in the element's HTML5 data attribute |
|
818 // return the generic message if present and no method specific message is present |
|
819 customDataMessage: function( element, method ) { |
|
820 return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() + |
|
821 method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" ); |
|
822 }, |
|
823 |
|
824 // Return the custom message for the given element name and validation method |
|
825 customMessage: function( name, method ) { |
|
826 var m = this.settings.messages[ name ]; |
|
827 return m && ( m.constructor === String ? m : m[ method ] ); |
|
828 }, |
|
829 |
|
830 // Return the first defined argument, allowing empty strings |
|
831 findDefined: function() { |
|
832 for ( var i = 0; i < arguments.length; i++ ) { |
|
833 if ( arguments[ i ] !== undefined ) { |
|
834 return arguments[ i ]; |
|
835 } |
|
836 } |
|
837 return undefined; |
|
838 }, |
|
839 |
|
840 // The second parameter 'rule' used to be a string, and extended to an object literal |
|
841 // of the following form: |
|
842 // rule = { |
|
843 // method: "method name", |
|
844 // parameters: "the given method parameters" |
|
845 // } |
|
846 // |
|
847 // The old behavior still supported, kept to maintain backward compatibility with |
|
848 // old code, and will be removed in the next major release. |
|
849 defaultMessage: function( element, rule ) { |
|
850 if ( typeof rule === "string" ) { |
|
851 rule = { method: rule }; |
|
852 } |
|
853 |
|
854 var message = this.findDefined( |
|
855 this.customMessage( element.name, rule.method ), |
|
856 this.customDataMessage( element, rule.method ), |
|
857 |
|
858 // 'title' is never undefined, so handle empty string as undefined |
|
859 !this.settings.ignoreTitle && element.title || undefined, |
|
860 $.validator.messages[ rule.method ], |
|
861 "<strong>Warning: No message defined for " + element.name + "</strong>" |
|
862 ), |
|
863 theregex = /\$?\{(\d+)\}/g; |
|
864 if ( typeof message === "function" ) { |
|
865 message = message.call( this, rule.parameters, element ); |
|
866 } else if ( theregex.test( message ) ) { |
|
867 message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters ); |
|
868 } |
|
869 |
|
870 return message; |
|
871 }, |
|
872 |
|
873 formatAndAdd: function( element, rule ) { |
|
874 var message = this.defaultMessage( element, rule ); |
|
875 |
|
876 this.errorList.push( { |
|
877 message: message, |
|
878 element: element, |
|
879 method: rule.method |
|
880 } ); |
|
881 |
|
882 this.errorMap[ element.name ] = message; |
|
883 this.submitted[ element.name ] = message; |
|
884 }, |
|
885 |
|
886 addWrapper: function( toToggle ) { |
|
887 if ( this.settings.wrapper ) { |
|
888 toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); |
|
889 } |
|
890 return toToggle; |
|
891 }, |
|
892 |
|
893 defaultShowErrors: function() { |
|
894 var i, elements, error; |
|
895 for ( i = 0; this.errorList[ i ]; i++ ) { |
|
896 error = this.errorList[ i ]; |
|
897 if ( this.settings.highlight ) { |
|
898 this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); |
|
899 } |
|
900 this.showLabel( error.element, error.message ); |
|
901 } |
|
902 if ( this.errorList.length ) { |
|
903 this.toShow = this.toShow.add( this.containers ); |
|
904 } |
|
905 if ( this.settings.success ) { |
|
906 for ( i = 0; this.successList[ i ]; i++ ) { |
|
907 this.showLabel( this.successList[ i ] ); |
|
908 } |
|
909 } |
|
910 if ( this.settings.unhighlight ) { |
|
911 for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) { |
|
912 this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass ); |
|
913 } |
|
914 } |
|
915 this.toHide = this.toHide.not( this.toShow ); |
|
916 this.hideErrors(); |
|
917 this.addWrapper( this.toShow ).show(); |
|
918 }, |
|
919 |
|
920 validElements: function() { |
|
921 return this.currentElements.not( this.invalidElements() ); |
|
922 }, |
|
923 |
|
924 invalidElements: function() { |
|
925 return $( this.errorList ).map( function() { |
|
926 return this.element; |
|
927 } ); |
|
928 }, |
|
929 |
|
930 showLabel: function( element, message ) { |
|
931 var place, group, errorID, v, |
|
932 error = this.errorsFor( element ), |
|
933 elementID = this.idOrName( element ), |
|
934 describedBy = $( element ).attr( "aria-describedby" ); |
|
935 |
|
936 if ( error.length ) { |
|
937 |
|
938 // Refresh error/success class |
|
939 error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); |
|
940 |
|
941 // Replace message on existing label |
|
942 error.html( message ); |
|
943 } else { |
|
944 |
|
945 // Create error element |
|
946 error = $( "<" + this.settings.errorElement + ">" ) |
|
947 .attr( "id", elementID + "-error" ) |
|
948 .addClass( this.settings.errorClass ) |
|
949 .html( message || "" ); |
|
950 |
|
951 // Maintain reference to the element to be placed into the DOM |
|
952 place = error; |
|
953 if ( this.settings.wrapper ) { |
|
954 |
|
955 // Make sure the element is visible, even in IE |
|
956 // actually showing the wrapped element is handled elsewhere |
|
957 place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent(); |
|
958 } |
|
959 if ( this.labelContainer.length ) { |
|
960 this.labelContainer.append( place ); |
|
961 } else if ( this.settings.errorPlacement ) { |
|
962 this.settings.errorPlacement.call( this, place, $( element ) ); |
|
963 } else { |
|
964 place.insertAfter( element ); |
|
965 } |
|
966 |
|
967 // Link error back to the element |
|
968 if ( error.is( "label" ) ) { |
|
969 |
|
970 // If the error is a label, then associate using 'for' |
|
971 error.attr( "for", elementID ); |
|
972 |
|
973 // If the element is not a child of an associated label, then it's necessary |
|
974 // to explicitly apply aria-describedby |
|
975 } else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) { |
|
976 errorID = error.attr( "id" ); |
|
977 |
|
978 // Respect existing non-error aria-describedby |
|
979 if ( !describedBy ) { |
|
980 describedBy = errorID; |
|
981 } else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) { |
|
982 |
|
983 // Add to end of list if not already present |
|
984 describedBy += " " + errorID; |
|
985 } |
|
986 $( element ).attr( "aria-describedby", describedBy ); |
|
987 |
|
988 // If this element is grouped, then assign to all elements in the same group |
|
989 group = this.groups[ element.name ]; |
|
990 if ( group ) { |
|
991 v = this; |
|
992 $.each( v.groups, function( name, testgroup ) { |
|
993 if ( testgroup === group ) { |
|
994 $( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm ) |
|
995 .attr( "aria-describedby", error.attr( "id" ) ); |
|
996 } |
|
997 } ); |
|
998 } |
|
999 } |
|
1000 } |
|
1001 if ( !message && this.settings.success ) { |
|
1002 error.text( "" ); |
|
1003 if ( typeof this.settings.success === "string" ) { |
|
1004 error.addClass( this.settings.success ); |
|
1005 } else { |
|
1006 this.settings.success( error, element ); |
|
1007 } |
|
1008 } |
|
1009 this.toShow = this.toShow.add( error ); |
|
1010 }, |
|
1011 |
|
1012 errorsFor: function( element ) { |
|
1013 var name = this.escapeCssMeta( this.idOrName( element ) ), |
|
1014 describer = $( element ).attr( "aria-describedby" ), |
|
1015 selector = "label[for='" + name + "'], label[for='" + name + "'] *"; |
|
1016 |
|
1017 // 'aria-describedby' should directly reference the error element |
|
1018 if ( describer ) { |
|
1019 selector = selector + ", #" + this.escapeCssMeta( describer ) |
|
1020 .replace( /\s+/g, ", #" ); |
|
1021 } |
|
1022 |
|
1023 return this |
|
1024 .errors() |
|
1025 .filter( selector ); |
|
1026 }, |
|
1027 |
|
1028 // See https://api.jquery.com/category/selectors/, for CSS |
|
1029 // meta-characters that should be escaped in order to be used with JQuery |
|
1030 // as a literal part of a name/id or any selector. |
|
1031 escapeCssMeta: function( string ) { |
|
1032 return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" ); |
|
1033 }, |
|
1034 |
|
1035 idOrName: function( element ) { |
|
1036 return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name ); |
|
1037 }, |
|
1038 |
|
1039 validationTargetFor: function( element ) { |
|
1040 |
|
1041 // If radio/checkbox, validate first element in group instead |
|
1042 if ( this.checkable( element ) ) { |
|
1043 element = this.findByName( element.name ); |
|
1044 } |
|
1045 |
|
1046 // Always apply ignore filter |
|
1047 return $( element ).not( this.settings.ignore )[ 0 ]; |
|
1048 }, |
|
1049 |
|
1050 checkable: function( element ) { |
|
1051 return ( /radio|checkbox/i ).test( element.type ); |
|
1052 }, |
|
1053 |
|
1054 findByName: function( name ) { |
|
1055 return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" ); |
|
1056 }, |
|
1057 |
|
1058 getLength: function( value, element ) { |
|
1059 switch ( element.nodeName.toLowerCase() ) { |
|
1060 case "select": |
|
1061 return $( "option:selected", element ).length; |
|
1062 case "input": |
|
1063 if ( this.checkable( element ) ) { |
|
1064 return this.findByName( element.name ).filter( ":checked" ).length; |
|
1065 } |
|
1066 } |
|
1067 return value.length; |
|
1068 }, |
|
1069 |
|
1070 depend: function( param, element ) { |
|
1071 return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true; |
|
1072 }, |
|
1073 |
|
1074 dependTypes: { |
|
1075 "boolean": function( param ) { |
|
1076 return param; |
|
1077 }, |
|
1078 "string": function( param, element ) { |
|
1079 return !!$( param, element.form ).length; |
|
1080 }, |
|
1081 "function": function( param, element ) { |
|
1082 return param( element ); |
|
1083 } |
|
1084 }, |
|
1085 |
|
1086 optional: function( element ) { |
|
1087 var val = this.elementValue( element ); |
|
1088 return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch"; |
|
1089 }, |
|
1090 |
|
1091 startRequest: function( element ) { |
|
1092 if ( !this.pending[ element.name ] ) { |
|
1093 this.pendingRequest++; |
|
1094 $( element ).addClass( this.settings.pendingClass ); |
|
1095 this.pending[ element.name ] = true; |
|
1096 } |
|
1097 }, |
|
1098 |
|
1099 stopRequest: function( element, valid ) { |
|
1100 this.pendingRequest--; |
|
1101 |
|
1102 // Sometimes synchronization fails, make sure pendingRequest is never < 0 |
|
1103 if ( this.pendingRequest < 0 ) { |
|
1104 this.pendingRequest = 0; |
|
1105 } |
|
1106 delete this.pending[ element.name ]; |
|
1107 $( element ).removeClass( this.settings.pendingClass ); |
|
1108 if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) { |
|
1109 $( this.currentForm ).submit(); |
|
1110 |
|
1111 // Remove the hidden input that was used as a replacement for the |
|
1112 // missing submit button. The hidden input is added by `handle()` |
|
1113 // to ensure that the value of the used submit button is passed on |
|
1114 // for scripted submits triggered by this method |
|
1115 if ( this.submitButton ) { |
|
1116 $( "input:hidden[name='" + this.submitButton.name + "']", this.currentForm ).remove(); |
|
1117 } |
|
1118 |
|
1119 this.formSubmitted = false; |
|
1120 } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) { |
|
1121 $( this.currentForm ).triggerHandler( "invalid-form", [ this ] ); |
|
1122 this.formSubmitted = false; |
|
1123 } |
|
1124 }, |
|
1125 |
|
1126 previousValue: function( element, method ) { |
|
1127 method = typeof method === "string" && method || "remote"; |
|
1128 |
|
1129 return $.data( element, "previousValue" ) || $.data( element, "previousValue", { |
|
1130 old: null, |
|
1131 valid: true, |
|
1132 message: this.defaultMessage( element, { method: method } ) |
|
1133 } ); |
|
1134 }, |
|
1135 |
|
1136 // Cleans up all forms and elements, removes validator-specific events |
|
1137 destroy: function() { |
|
1138 this.resetForm(); |
|
1139 |
|
1140 $( this.currentForm ) |
|
1141 .off( ".validate" ) |
|
1142 .removeData( "validator" ) |
|
1143 .find( ".validate-equalTo-blur" ) |
|
1144 .off( ".validate-equalTo" ) |
|
1145 .removeClass( "validate-equalTo-blur" ); |
|
1146 } |
|
1147 |
|
1148 }, |
|
1149 |
|
1150 classRuleSettings: { |
|
1151 required: { required: true }, |
|
1152 email: { email: true }, |
|
1153 url: { url: true }, |
|
1154 date: { date: true }, |
|
1155 dateISO: { dateISO: true }, |
|
1156 number: { number: true }, |
|
1157 digits: { digits: true }, |
|
1158 creditcard: { creditcard: true } |
|
1159 }, |
|
1160 |
|
1161 addClassRules: function( className, rules ) { |
|
1162 if ( className.constructor === String ) { |
|
1163 this.classRuleSettings[ className ] = rules; |
|
1164 } else { |
|
1165 $.extend( this.classRuleSettings, className ); |
|
1166 } |
|
1167 }, |
|
1168 |
|
1169 classRules: function( element ) { |
|
1170 var rules = {}, |
|
1171 classes = $( element ).attr( "class" ); |
|
1172 |
|
1173 if ( classes ) { |
|
1174 $.each( classes.split( " " ), function() { |
|
1175 if ( this in $.validator.classRuleSettings ) { |
|
1176 $.extend( rules, $.validator.classRuleSettings[ this ] ); |
|
1177 } |
|
1178 } ); |
|
1179 } |
|
1180 return rules; |
|
1181 }, |
|
1182 |
|
1183 normalizeAttributeRule: function( rules, type, method, value ) { |
|
1184 |
|
1185 // Convert the value to a number for number inputs, and for text for backwards compability |
|
1186 // allows type="date" and others to be compared as strings |
|
1187 if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) { |
|
1188 value = Number( value ); |
|
1189 |
|
1190 // Support Opera Mini, which returns NaN for undefined minlength |
|
1191 if ( isNaN( value ) ) { |
|
1192 value = undefined; |
|
1193 } |
|
1194 } |
|
1195 |
|
1196 if ( value || value === 0 ) { |
|
1197 rules[ method ] = value; |
|
1198 } else if ( type === method && type !== "range" ) { |
|
1199 |
|
1200 // Exception: the jquery validate 'range' method |
|
1201 // does not test for the html5 'range' type |
|
1202 rules[ method ] = true; |
|
1203 } |
|
1204 }, |
|
1205 |
|
1206 attributeRules: function( element ) { |
|
1207 var rules = {}, |
|
1208 $element = $( element ), |
|
1209 type = element.getAttribute( "type" ), |
|
1210 method, value; |
|
1211 |
|
1212 for ( method in $.validator.methods ) { |
|
1213 |
|
1214 // Support for <input required> in both html5 and older browsers |
|
1215 if ( method === "required" ) { |
|
1216 value = element.getAttribute( method ); |
|
1217 |
|
1218 // Some browsers return an empty string for the required attribute |
|
1219 // and non-HTML5 browsers might have required="" markup |
|
1220 if ( value === "" ) { |
|
1221 value = true; |
|
1222 } |
|
1223 |
|
1224 // Force non-HTML5 browsers to return bool |
|
1225 value = !!value; |
|
1226 } else { |
|
1227 value = $element.attr( method ); |
|
1228 } |
|
1229 |
|
1230 this.normalizeAttributeRule( rules, type, method, value ); |
|
1231 } |
|
1232 |
|
1233 // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs |
|
1234 if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) { |
|
1235 delete rules.maxlength; |
|
1236 } |
|
1237 |
|
1238 return rules; |
|
1239 }, |
|
1240 |
|
1241 dataRules: function( element ) { |
|
1242 var rules = {}, |
|
1243 $element = $( element ), |
|
1244 type = element.getAttribute( "type" ), |
|
1245 method, value; |
|
1246 |
|
1247 for ( method in $.validator.methods ) { |
|
1248 value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() ); |
|
1249 this.normalizeAttributeRule( rules, type, method, value ); |
|
1250 } |
|
1251 return rules; |
|
1252 }, |
|
1253 |
|
1254 staticRules: function( element ) { |
|
1255 var rules = {}, |
|
1256 validator = $.data( element.form, "validator" ); |
|
1257 |
|
1258 if ( validator.settings.rules ) { |
|
1259 rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {}; |
|
1260 } |
|
1261 return rules; |
|
1262 }, |
|
1263 |
|
1264 normalizeRules: function( rules, element ) { |
|
1265 |
|
1266 // Handle dependency check |
|
1267 $.each( rules, function( prop, val ) { |
|
1268 |
|
1269 // Ignore rule when param is explicitly false, eg. required:false |
|
1270 if ( val === false ) { |
|
1271 delete rules[ prop ]; |
|
1272 return; |
|
1273 } |
|
1274 if ( val.param || val.depends ) { |
|
1275 var keepRule = true; |
|
1276 switch ( typeof val.depends ) { |
|
1277 case "string": |
|
1278 keepRule = !!$( val.depends, element.form ).length; |
|
1279 break; |
|
1280 case "function": |
|
1281 keepRule = val.depends.call( element, element ); |
|
1282 break; |
|
1283 } |
|
1284 if ( keepRule ) { |
|
1285 rules[ prop ] = val.param !== undefined ? val.param : true; |
|
1286 } else { |
|
1287 $.data( element.form, "validator" ).resetElements( $( element ) ); |
|
1288 delete rules[ prop ]; |
|
1289 } |
|
1290 } |
|
1291 } ); |
|
1292 |
|
1293 // Evaluate parameters |
|
1294 $.each( rules, function( rule, parameter ) { |
|
1295 rules[ rule ] = $.isFunction( parameter ) && rule !== "normalizer" ? parameter( element ) : parameter; |
|
1296 } ); |
|
1297 |
|
1298 // Clean number parameters |
|
1299 $.each( [ "minlength", "maxlength" ], function() { |
|
1300 if ( rules[ this ] ) { |
|
1301 rules[ this ] = Number( rules[ this ] ); |
|
1302 } |
|
1303 } ); |
|
1304 $.each( [ "rangelength", "range" ], function() { |
|
1305 var parts; |
|
1306 if ( rules[ this ] ) { |
|
1307 if ( $.isArray( rules[ this ] ) ) { |
|
1308 rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ]; |
|
1309 } else if ( typeof rules[ this ] === "string" ) { |
|
1310 parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ ); |
|
1311 rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ]; |
|
1312 } |
|
1313 } |
|
1314 } ); |
|
1315 |
|
1316 if ( $.validator.autoCreateRanges ) { |
|
1317 |
|
1318 // Auto-create ranges |
|
1319 if ( rules.min != null && rules.max != null ) { |
|
1320 rules.range = [ rules.min, rules.max ]; |
|
1321 delete rules.min; |
|
1322 delete rules.max; |
|
1323 } |
|
1324 if ( rules.minlength != null && rules.maxlength != null ) { |
|
1325 rules.rangelength = [ rules.minlength, rules.maxlength ]; |
|
1326 delete rules.minlength; |
|
1327 delete rules.maxlength; |
|
1328 } |
|
1329 } |
|
1330 |
|
1331 return rules; |
|
1332 }, |
|
1333 |
|
1334 // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} |
|
1335 normalizeRule: function( data ) { |
|
1336 if ( typeof data === "string" ) { |
|
1337 var transformed = {}; |
|
1338 $.each( data.split( /\s/ ), function() { |
|
1339 transformed[ this ] = true; |
|
1340 } ); |
|
1341 data = transformed; |
|
1342 } |
|
1343 return data; |
|
1344 }, |
|
1345 |
|
1346 // https://jqueryvalidation.org/jQuery.validator.addMethod/ |
|
1347 addMethod: function( name, method, message ) { |
|
1348 $.validator.methods[ name ] = method; |
|
1349 $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ]; |
|
1350 if ( method.length < 3 ) { |
|
1351 $.validator.addClassRules( name, $.validator.normalizeRule( name ) ); |
|
1352 } |
|
1353 }, |
|
1354 |
|
1355 // https://jqueryvalidation.org/jQuery.validator.methods/ |
|
1356 methods: { |
|
1357 |
|
1358 // https://jqueryvalidation.org/required-method/ |
|
1359 required: function( value, element, param ) { |
|
1360 |
|
1361 // Check if dependency is met |
|
1362 if ( !this.depend( param, element ) ) { |
|
1363 return "dependency-mismatch"; |
|
1364 } |
|
1365 if ( element.nodeName.toLowerCase() === "select" ) { |
|
1366 |
|
1367 // Could be an array for select-multiple or a string, both are fine this way |
|
1368 var val = $( element ).val(); |
|
1369 return val && val.length > 0; |
|
1370 } |
|
1371 if ( this.checkable( element ) ) { |
|
1372 return this.getLength( value, element ) > 0; |
|
1373 } |
|
1374 return value.length > 0; |
|
1375 }, |
|
1376 |
|
1377 // https://jqueryvalidation.org/email-method/ |
|
1378 email: function( value, element ) { |
|
1379 |
|
1380 // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address |
|
1381 // Retrieved 2014-01-14 |
|
1382 // If you have a problem with this implementation, report a bug against the above spec |
|
1383 // Or use custom methods to implement your own email validation |
|
1384 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 ); |
|
1385 }, |
|
1386 |
|
1387 // https://jqueryvalidation.org/url-method/ |
|
1388 url: function( value, element ) { |
|
1389 |
|
1390 // Copyright (c) 2010-2013 Diego Perini, MIT licensed |
|
1391 // https://gist.github.com/dperini/729294 |
|
1392 // see also https://mathiasbynens.be/demo/url-regex |
|
1393 // modified to allow protocol-relative URLs |
|
1394 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 ); |
|
1395 }, |
|
1396 |
|
1397 // https://jqueryvalidation.org/date-method/ |
|
1398 date: function( value, element ) { |
|
1399 return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() ); |
|
1400 }, |
|
1401 |
|
1402 // https://jqueryvalidation.org/dateISO-method/ |
|
1403 dateISO: function( value, element ) { |
|
1404 return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value ); |
|
1405 }, |
|
1406 |
|
1407 // https://jqueryvalidation.org/number-method/ |
|
1408 number: function( value, element ) { |
|
1409 return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value ); |
|
1410 }, |
|
1411 |
|
1412 // https://jqueryvalidation.org/digits-method/ |
|
1413 digits: function( value, element ) { |
|
1414 return this.optional( element ) || /^\d+$/.test( value ); |
|
1415 }, |
|
1416 |
|
1417 // https://jqueryvalidation.org/minlength-method/ |
|
1418 minlength: function( value, element, param ) { |
|
1419 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); |
|
1420 return this.optional( element ) || length >= param; |
|
1421 }, |
|
1422 |
|
1423 // https://jqueryvalidation.org/maxlength-method/ |
|
1424 maxlength: function( value, element, param ) { |
|
1425 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); |
|
1426 return this.optional( element ) || length <= param; |
|
1427 }, |
|
1428 |
|
1429 // https://jqueryvalidation.org/rangelength-method/ |
|
1430 rangelength: function( value, element, param ) { |
|
1431 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); |
|
1432 return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] ); |
|
1433 }, |
|
1434 |
|
1435 // https://jqueryvalidation.org/min-method/ |
|
1436 min: function( value, element, param ) { |
|
1437 return this.optional( element ) || value >= param; |
|
1438 }, |
|
1439 |
|
1440 // https://jqueryvalidation.org/max-method/ |
|
1441 max: function( value, element, param ) { |
|
1442 return this.optional( element ) || value <= param; |
|
1443 }, |
|
1444 |
|
1445 // https://jqueryvalidation.org/range-method/ |
|
1446 range: function( value, element, param ) { |
|
1447 return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] ); |
|
1448 }, |
|
1449 |
|
1450 // https://jqueryvalidation.org/step-method/ |
|
1451 step: function( value, element, param ) { |
|
1452 var type = $( element ).attr( "type" ), |
|
1453 errorMessage = "Step attribute on input type " + type + " is not supported.", |
|
1454 supportedTypes = [ "text", "number", "range" ], |
|
1455 re = new RegExp( "\\b" + type + "\\b" ), |
|
1456 notSupported = type && !re.test( supportedTypes.join() ), |
|
1457 decimalPlaces = function( num ) { |
|
1458 var match = ( "" + num ).match( /(?:\.(\d+))?$/ ); |
|
1459 if ( !match ) { |
|
1460 return 0; |
|
1461 } |
|
1462 |
|
1463 // Number of digits right of decimal point. |
|
1464 return match[ 1 ] ? match[ 1 ].length : 0; |
|
1465 }, |
|
1466 toInt = function( num ) { |
|
1467 return Math.round( num * Math.pow( 10, decimals ) ); |
|
1468 }, |
|
1469 valid = true, |
|
1470 decimals; |
|
1471 |
|
1472 // Works only for text, number and range input types |
|
1473 // TODO find a way to support input types date, datetime, datetime-local, month, time and week |
|
1474 if ( notSupported ) { |
|
1475 throw new Error( errorMessage ); |
|
1476 } |
|
1477 |
|
1478 decimals = decimalPlaces( param ); |
|
1479 |
|
1480 // Value can't have too many decimals |
|
1481 if ( decimalPlaces( value ) > decimals || toInt( value ) % toInt( param ) !== 0 ) { |
|
1482 valid = false; |
|
1483 } |
|
1484 |
|
1485 return this.optional( element ) || valid; |
|
1486 }, |
|
1487 |
|
1488 // https://jqueryvalidation.org/equalTo-method/ |
|
1489 equalTo: function( value, element, param ) { |
|
1490 |
|
1491 // Bind to the blur event of the target in order to revalidate whenever the target field is updated |
|
1492 var target = $( param ); |
|
1493 if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) { |
|
1494 target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() { |
|
1495 $( element ).valid(); |
|
1496 } ); |
|
1497 } |
|
1498 return value === target.val(); |
|
1499 }, |
|
1500 |
|
1501 // https://jqueryvalidation.org/remote-method/ |
|
1502 remote: function( value, element, param, method ) { |
|
1503 if ( this.optional( element ) ) { |
|
1504 return "dependency-mismatch"; |
|
1505 } |
|
1506 |
|
1507 method = typeof method === "string" && method || "remote"; |
|
1508 |
|
1509 var previous = this.previousValue( element, method ), |
|
1510 validator, data, optionDataString; |
|
1511 |
|
1512 if ( !this.settings.messages[ element.name ] ) { |
|
1513 this.settings.messages[ element.name ] = {}; |
|
1514 } |
|
1515 previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ]; |
|
1516 this.settings.messages[ element.name ][ method ] = previous.message; |
|
1517 |
|
1518 param = typeof param === "string" && { url: param } || param; |
|
1519 optionDataString = $.param( $.extend( { data: value }, param.data ) ); |
|
1520 if ( previous.old === optionDataString ) { |
|
1521 return previous.valid; |
|
1522 } |
|
1523 |
|
1524 previous.old = optionDataString; |
|
1525 validator = this; |
|
1526 this.startRequest( element ); |
|
1527 data = {}; |
|
1528 data[ element.name ] = value; |
|
1529 $.ajax( $.extend( true, { |
|
1530 mode: "abort", |
|
1531 port: "validate" + element.name, |
|
1532 dataType: "json", |
|
1533 data: data, |
|
1534 context: validator.currentForm, |
|
1535 success: function( response ) { |
|
1536 var valid = response === true || response === "true", |
|
1537 errors, message, submitted; |
|
1538 |
|
1539 validator.settings.messages[ element.name ][ method ] = previous.originalMessage; |
|
1540 if ( valid ) { |
|
1541 submitted = validator.formSubmitted; |
|
1542 validator.resetInternals(); |
|
1543 validator.toHide = validator.errorsFor( element ); |
|
1544 validator.formSubmitted = submitted; |
|
1545 validator.successList.push( element ); |
|
1546 validator.invalid[ element.name ] = false; |
|
1547 validator.showErrors(); |
|
1548 } else { |
|
1549 errors = {}; |
|
1550 message = response || validator.defaultMessage( element, { method: method, parameters: value } ); |
|
1551 errors[ element.name ] = previous.message = message; |
|
1552 validator.invalid[ element.name ] = true; |
|
1553 validator.showErrors( errors ); |
|
1554 } |
|
1555 previous.valid = valid; |
|
1556 validator.stopRequest( element, valid ); |
|
1557 } |
|
1558 }, param ) ); |
|
1559 return "pending"; |
|
1560 } |
|
1561 } |
|
1562 |
|
1563 } ); |
|
1564 |
|
1565 // Ajax mode: abort |
|
1566 // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); |
|
1567 // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() |
|
1568 |
|
1569 var pendingRequests = {}, |
|
1570 ajax; |
|
1571 |
|
1572 // Use a prefilter if available (1.5+) |
|
1573 if ( $.ajaxPrefilter ) { |
|
1574 $.ajaxPrefilter( function( settings, _, xhr ) { |
|
1575 var port = settings.port; |
|
1576 if ( settings.mode === "abort" ) { |
|
1577 if ( pendingRequests[ port ] ) { |
|
1578 pendingRequests[ port ].abort(); |
|
1579 } |
|
1580 pendingRequests[ port ] = xhr; |
|
1581 } |
|
1582 } ); |
|
1583 } else { |
|
1584 |
|
1585 // Proxy ajax |
|
1586 ajax = $.ajax; |
|
1587 $.ajax = function( settings ) { |
|
1588 var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, |
|
1589 port = ( "port" in settings ? settings : $.ajaxSettings ).port; |
|
1590 if ( mode === "abort" ) { |
|
1591 if ( pendingRequests[ port ] ) { |
|
1592 pendingRequests[ port ].abort(); |
|
1593 } |
|
1594 pendingRequests[ port ] = ajax.apply( this, arguments ); |
|
1595 return pendingRequests[ port ]; |
|
1596 } |
|
1597 return ajax.apply( this, arguments ); |
|
1598 }; |
|
1599 } |
|
1600 return $; |
|
1601 })); |
|
1602 |
|
1603 /*! |
|
1604 * jQuery Validation Plugin v1.17.0 |
|
1605 * |
|
1606 * https://jqueryvalidation.org/ |
|
1607 * |
|
1608 * Copyright (c) 2017 Jörn Zaefferer |
|
1609 * Released under the MIT license |
|
1610 */ |
|
1611 (function( factory ) { |
|
1612 if ( typeof define === "function" && define.amd ) { |
|
1613 define( ["jquery", "./jquery.validate"], factory ); |
|
1614 } else if (typeof module === "object" && module.exports) { |
|
1615 module.exports = factory( require( "jquery" ) ); |
|
1616 } else { |
|
1617 factory( jQuery ); |
|
1618 } |
|
1619 }(function( $ ) { |
|
1620 |
|
1621 ( function() { |
|
1622 |
|
1623 function stripHtml( value ) { |
|
1624 |
|
1625 // Remove html tags and space chars |
|
1626 return value.replace( /<.[^<>]*?>/g, " " ).replace( / | /gi, " " ) |
|
1627 |
|
1628 // Remove punctuation |
|
1629 .replace( /[.(),;:!?%#$'\"_+=\/\-“”’]*/g, "" ); |
|
1630 } |
|
1631 |
|
1632 $.validator.addMethod( "maxWords", function( value, element, params ) { |
|
1633 return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length <= params; |
|
1634 }, $.validator.format( "Please enter {0} words or less." ) ); |
|
1635 |
|
1636 $.validator.addMethod( "minWords", function( value, element, params ) { |
|
1637 return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length >= params; |
|
1638 }, $.validator.format( "Please enter at least {0} words." ) ); |
|
1639 |
|
1640 $.validator.addMethod( "rangeWords", function( value, element, params ) { |
|
1641 var valueStripped = stripHtml( value ), |
|
1642 regex = /\b\w+\b/g; |
|
1643 return this.optional( element ) || valueStripped.match( regex ).length >= params[ 0 ] && valueStripped.match( regex ).length <= params[ 1 ]; |
|
1644 }, $.validator.format( "Please enter between {0} and {1} words." ) ); |
|
1645 |
|
1646 }() ); |
|
1647 |
|
1648 // Accept a value from a file input based on a required mimetype |
|
1649 $.validator.addMethod( "accept", function( value, element, param ) { |
|
1650 |
|
1651 // Split mime on commas in case we have multiple types we can accept |
|
1652 var typeParam = typeof param === "string" ? param.replace( /\s/g, "" ) : "image/*", |
|
1653 optionalValue = this.optional( element ), |
|
1654 i, file, regex; |
|
1655 |
|
1656 // Element is optional |
|
1657 if ( optionalValue ) { |
|
1658 return optionalValue; |
|
1659 } |
|
1660 |
|
1661 if ( $( element ).attr( "type" ) === "file" ) { |
|
1662 |
|
1663 // Escape string to be used in the regex |
|
1664 // see: https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex |
|
1665 // Escape also "/*" as "/.*" as a wildcard |
|
1666 typeParam = typeParam |
|
1667 .replace( /[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, "\\$&" ) |
|
1668 .replace( /,/g, "|" ) |
|
1669 .replace( /\/\*/g, "/.*" ); |
|
1670 |
|
1671 // Check if the element has a FileList before checking each file |
|
1672 if ( element.files && element.files.length ) { |
|
1673 regex = new RegExp( ".?(" + typeParam + ")$", "i" ); |
|
1674 for ( i = 0; i < element.files.length; i++ ) { |
|
1675 file = element.files[ i ]; |
|
1676 |
|
1677 // Grab the mimetype from the loaded file, verify it matches |
|
1678 if ( !file.type.match( regex ) ) { |
|
1679 return false; |
|
1680 } |
|
1681 } |
|
1682 } |
|
1683 } |
|
1684 |
|
1685 // Either return true because we've validated each file, or because the |
|
1686 // browser does not support element.files and the FileList feature |
|
1687 return true; |
|
1688 }, $.validator.format( "Please enter a value with a valid mimetype." ) ); |
|
1689 |
|
1690 $.validator.addMethod( "alphanumeric", function( value, element ) { |
|
1691 return this.optional( element ) || /^\w+$/i.test( value ); |
|
1692 }, "Letters, numbers, and underscores only please" ); |
|
1693 |
|
1694 /* |
|
1695 * Dutch bank account numbers (not 'giro' numbers) have 9 digits |
|
1696 * and pass the '11 check'. |
|
1697 * We accept the notation with spaces, as that is common. |
|
1698 * acceptable: 123456789 or 12 34 56 789 |
|
1699 */ |
|
1700 $.validator.addMethod( "bankaccountNL", function( value, element ) { |
|
1701 if ( this.optional( element ) ) { |
|
1702 return true; |
|
1703 } |
|
1704 if ( !( /^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test( value ) ) ) { |
|
1705 return false; |
|
1706 } |
|
1707 |
|
1708 // Now '11 check' |
|
1709 var account = value.replace( / /g, "" ), // Remove spaces |
|
1710 sum = 0, |
|
1711 len = account.length, |
|
1712 pos, factor, digit; |
|
1713 for ( pos = 0; pos < len; pos++ ) { |
|
1714 factor = len - pos; |
|
1715 digit = account.substring( pos, pos + 1 ); |
|
1716 sum = sum + factor * digit; |
|
1717 } |
|
1718 return sum % 11 === 0; |
|
1719 }, "Please specify a valid bank account number" ); |
|
1720 |
|
1721 $.validator.addMethod( "bankorgiroaccountNL", function( value, element ) { |
|
1722 return this.optional( element ) || |
|
1723 ( $.validator.methods.bankaccountNL.call( this, value, element ) ) || |
|
1724 ( $.validator.methods.giroaccountNL.call( this, value, element ) ); |
|
1725 }, "Please specify a valid bank or giro account number" ); |
|
1726 |
|
1727 /** |
|
1728 * BIC is the business identifier code (ISO 9362). This BIC check is not a guarantee for authenticity. |
|
1729 * |
|
1730 * BIC pattern: BBBBCCLLbbb (8 or 11 characters long; bbb is optional) |
|
1731 * |
|
1732 * Validation is case-insensitive. Please make sure to normalize input yourself. |
|
1733 * |
|
1734 * BIC definition in detail: |
|
1735 * - First 4 characters - bank code (only letters) |
|
1736 * - Next 2 characters - ISO 3166-1 alpha-2 country code (only letters) |
|
1737 * - Next 2 characters - location code (letters and digits) |
|
1738 * a. shall not start with '0' or '1' |
|
1739 * b. second character must be a letter ('O' is not allowed) or digit ('0' for test (therefore not allowed), '1' denoting passive participant, '2' typically reverse-billing) |
|
1740 * - Last 3 characters - branch code, optional (shall not start with 'X' except in case of 'XXX' for primary office) (letters and digits) |
|
1741 */ |
|
1742 $.validator.addMethod( "bic", function( value, element ) { |
|
1743 return this.optional( element ) || /^([A-Z]{6}[A-Z2-9][A-NP-Z1-9])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test( value.toUpperCase() ); |
|
1744 }, "Please specify a valid BIC code" ); |
|
1745 |
|
1746 /* |
|
1747 * Código de identificación fiscal ( CIF ) is the tax identification code for Spanish legal entities |
|
1748 * Further rules can be found in Spanish on http://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal |
|
1749 * |
|
1750 * Spanish CIF structure: |
|
1751 * |
|
1752 * [ T ][ P ][ P ][ N ][ N ][ N ][ N ][ N ][ C ] |
|
1753 * |
|
1754 * Where: |
|
1755 * |
|
1756 * T: 1 character. Kind of Organization Letter: [ABCDEFGHJKLMNPQRSUVW] |
|
1757 * P: 2 characters. Province. |
|
1758 * N: 5 characters. Secuencial Number within the province. |
|
1759 * C: 1 character. Control Digit: [0-9A-J]. |
|
1760 * |
|
1761 * [ T ]: Kind of Organizations. Possible values: |
|
1762 * |
|
1763 * A. Corporations |
|
1764 * B. LLCs |
|
1765 * C. General partnerships |
|
1766 * D. Companies limited partnerships |
|
1767 * E. Communities of goods |
|
1768 * F. Cooperative Societies |
|
1769 * G. Associations |
|
1770 * H. Communities of homeowners in horizontal property regime |
|
1771 * J. Civil Societies |
|
1772 * K. Old format |
|
1773 * L. Old format |
|
1774 * M. Old format |
|
1775 * N. Nonresident entities |
|
1776 * P. Local authorities |
|
1777 * Q. Autonomous bodies, state or not, and the like, and congregations and religious institutions |
|
1778 * R. Congregations and religious institutions (since 2008 ORDER EHA/451/2008) |
|
1779 * S. Organs of State Administration and regions |
|
1780 * V. Agrarian Transformation |
|
1781 * W. Permanent establishments of non-resident in Spain |
|
1782 * |
|
1783 * [ C ]: Control Digit. It can be a number or a letter depending on T value: |
|
1784 * [ T ] --> [ C ] |
|
1785 * ------ ---------- |
|
1786 * A Number |
|
1787 * B Number |
|
1788 * E Number |
|
1789 * H Number |
|
1790 * K Letter |
|
1791 * P Letter |
|
1792 * Q Letter |
|
1793 * S Letter |
|
1794 * |
|
1795 */ |
|
1796 $.validator.addMethod( "cifES", function( value, element ) { |
|
1797 "use strict"; |
|
1798 |
|
1799 if ( this.optional( element ) ) { |
|
1800 return true; |
|
1801 } |
|
1802 |
|
1803 var cifRegEx = new RegExp( /^([ABCDEFGHJKLMNPQRSUVW])(\d{7})([0-9A-J])$/gi ); |
|
1804 var letter = value.substring( 0, 1 ), // [ T ] |
|
1805 number = value.substring( 1, 8 ), // [ P ][ P ][ N ][ N ][ N ][ N ][ N ] |
|
1806 control = value.substring( 8, 9 ), // [ C ] |
|
1807 all_sum = 0, |
|
1808 even_sum = 0, |
|
1809 odd_sum = 0, |
|
1810 i, n, |
|
1811 control_digit, |
|
1812 control_letter; |
|
1813 |
|
1814 function isOdd( n ) { |
|
1815 return n % 2 === 0; |
|
1816 } |
|
1817 |
|
1818 // Quick format test |
|
1819 if ( value.length !== 9 || !cifRegEx.test( value ) ) { |
|
1820 return false; |
|
1821 } |
|
1822 |
|
1823 for ( i = 0; i < number.length; i++ ) { |
|
1824 n = parseInt( number[ i ], 10 ); |
|
1825 |
|
1826 // Odd positions |
|
1827 if ( isOdd( i ) ) { |
|
1828 |
|
1829 // Odd positions are multiplied first. |
|
1830 n *= 2; |
|
1831 |
|
1832 // If the multiplication is bigger than 10 we need to adjust |
|
1833 odd_sum += n < 10 ? n : n - 9; |
|
1834 |
|
1835 // Even positions |
|
1836 // Just sum them |
|
1837 } else { |
|
1838 even_sum += n; |
|
1839 } |
|
1840 } |
|
1841 |
|
1842 all_sum = even_sum + odd_sum; |
|
1843 control_digit = ( 10 - ( all_sum ).toString().substr( -1 ) ).toString(); |
|
1844 control_digit = parseInt( control_digit, 10 ) > 9 ? "0" : control_digit; |
|
1845 control_letter = "JABCDEFGHI".substr( control_digit, 1 ).toString(); |
|
1846 |
|
1847 // Control must be a digit |
|
1848 if ( letter.match( /[ABEH]/ ) ) { |
|
1849 return control === control_digit; |
|
1850 |
|
1851 // Control must be a letter |
|
1852 } else if ( letter.match( /[KPQS]/ ) ) { |
|
1853 return control === control_letter; |
|
1854 } |
|
1855 |
|
1856 // Can be either |
|
1857 return control === control_digit || control === control_letter; |
|
1858 |
|
1859 }, "Please specify a valid CIF number." ); |
|
1860 |
|
1861 /* |
|
1862 * Brazillian CPF number (Cadastrado de Pessoas Físicas) is the equivalent of a Brazilian tax registration number. |
|
1863 * CPF numbers have 11 digits in total: 9 numbers followed by 2 check numbers that are being used for validation. |
|
1864 */ |
|
1865 $.validator.addMethod( "cpfBR", function( value ) { |
|
1866 |
|
1867 // Removing special characters from value |
|
1868 value = value.replace( /([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g, "" ); |
|
1869 |
|
1870 // Checking value to have 11 digits only |
|
1871 if ( value.length !== 11 ) { |
|
1872 return false; |
|
1873 } |
|
1874 |
|
1875 var sum = 0, |
|
1876 firstCN, secondCN, checkResult, i; |
|
1877 |
|
1878 firstCN = parseInt( value.substring( 9, 10 ), 10 ); |
|
1879 secondCN = parseInt( value.substring( 10, 11 ), 10 ); |
|
1880 |
|
1881 checkResult = function( sum, cn ) { |
|
1882 var result = ( sum * 10 ) % 11; |
|
1883 if ( ( result === 10 ) || ( result === 11 ) ) { |
|
1884 result = 0; |
|
1885 } |
|
1886 return ( result === cn ); |
|
1887 }; |
|
1888 |
|
1889 // Checking for dump data |
|
1890 if ( value === "" || |
|
1891 value === "00000000000" || |
|
1892 value === "11111111111" || |
|
1893 value === "22222222222" || |
|
1894 value === "33333333333" || |
|
1895 value === "44444444444" || |
|
1896 value === "55555555555" || |
|
1897 value === "66666666666" || |
|
1898 value === "77777777777" || |
|
1899 value === "88888888888" || |
|
1900 value === "99999999999" |
|
1901 ) { |
|
1902 return false; |
|
1903 } |
|
1904 |
|
1905 // Step 1 - using first Check Number: |
|
1906 for ( i = 1; i <= 9; i++ ) { |
|
1907 sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 11 - i ); |
|
1908 } |
|
1909 |
|
1910 // If first Check Number (CN) is valid, move to Step 2 - using second Check Number: |
|
1911 if ( checkResult( sum, firstCN ) ) { |
|
1912 sum = 0; |
|
1913 for ( i = 1; i <= 10; i++ ) { |
|
1914 sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 12 - i ); |
|
1915 } |
|
1916 return checkResult( sum, secondCN ); |
|
1917 } |
|
1918 return false; |
|
1919 |
|
1920 }, "Please specify a valid CPF number" ); |
|
1921 |
|
1922 // https://jqueryvalidation.org/creditcard-method/ |
|
1923 // based on https://en.wikipedia.org/wiki/Luhn_algorithm |
|
1924 $.validator.addMethod( "creditcard", function( value, element ) { |
|
1925 if ( this.optional( element ) ) { |
|
1926 return "dependency-mismatch"; |
|
1927 } |
|
1928 |
|
1929 // Accept only spaces, digits and dashes |
|
1930 if ( /[^0-9 \-]+/.test( value ) ) { |
|
1931 return false; |
|
1932 } |
|
1933 |
|
1934 var nCheck = 0, |
|
1935 nDigit = 0, |
|
1936 bEven = false, |
|
1937 n, cDigit; |
|
1938 |
|
1939 value = value.replace( /\D/g, "" ); |
|
1940 |
|
1941 // Basing min and max length on |
|
1942 // https://developer.ean.com/general_info/Valid_Credit_Card_Types |
|
1943 if ( value.length < 13 || value.length > 19 ) { |
|
1944 return false; |
|
1945 } |
|
1946 |
|
1947 for ( n = value.length - 1; n >= 0; n-- ) { |
|
1948 cDigit = value.charAt( n ); |
|
1949 nDigit = parseInt( cDigit, 10 ); |
|
1950 if ( bEven ) { |
|
1951 if ( ( nDigit *= 2 ) > 9 ) { |
|
1952 nDigit -= 9; |
|
1953 } |
|
1954 } |
|
1955 |
|
1956 nCheck += nDigit; |
|
1957 bEven = !bEven; |
|
1958 } |
|
1959 |
|
1960 return ( nCheck % 10 ) === 0; |
|
1961 }, "Please enter a valid credit card number." ); |
|
1962 |
|
1963 /* NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator |
|
1964 * Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0 |
|
1965 * Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings) |
|
1966 */ |
|
1967 $.validator.addMethod( "creditcardtypes", function( value, element, param ) { |
|
1968 if ( /[^0-9\-]+/.test( value ) ) { |
|
1969 return false; |
|
1970 } |
|
1971 |
|
1972 value = value.replace( /\D/g, "" ); |
|
1973 |
|
1974 var validTypes = 0x0000; |
|
1975 |
|
1976 if ( param.mastercard ) { |
|
1977 validTypes |= 0x0001; |
|
1978 } |
|
1979 if ( param.visa ) { |
|
1980 validTypes |= 0x0002; |
|
1981 } |
|
1982 if ( param.amex ) { |
|
1983 validTypes |= 0x0004; |
|
1984 } |
|
1985 if ( param.dinersclub ) { |
|
1986 validTypes |= 0x0008; |
|
1987 } |
|
1988 if ( param.enroute ) { |
|
1989 validTypes |= 0x0010; |
|
1990 } |
|
1991 if ( param.discover ) { |
|
1992 validTypes |= 0x0020; |
|
1993 } |
|
1994 if ( param.jcb ) { |
|
1995 validTypes |= 0x0040; |
|
1996 } |
|
1997 if ( param.unknown ) { |
|
1998 validTypes |= 0x0080; |
|
1999 } |
|
2000 if ( param.all ) { |
|
2001 validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080; |
|
2002 } |
|
2003 if ( validTypes & 0x0001 && /^(5[12345])/.test( value ) ) { // Mastercard |
|
2004 return value.length === 16; |
|
2005 } |
|
2006 if ( validTypes & 0x0002 && /^(4)/.test( value ) ) { // Visa |
|
2007 return value.length === 16; |
|
2008 } |
|
2009 if ( validTypes & 0x0004 && /^(3[47])/.test( value ) ) { // Amex |
|
2010 return value.length === 15; |
|
2011 } |
|
2012 if ( validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test( value ) ) { // Dinersclub |
|
2013 return value.length === 14; |
|
2014 } |
|
2015 if ( validTypes & 0x0010 && /^(2(014|149))/.test( value ) ) { // Enroute |
|
2016 return value.length === 15; |
|
2017 } |
|
2018 if ( validTypes & 0x0020 && /^(6011)/.test( value ) ) { // Discover |
|
2019 return value.length === 16; |
|
2020 } |
|
2021 if ( validTypes & 0x0040 && /^(3)/.test( value ) ) { // Jcb |
|
2022 return value.length === 16; |
|
2023 } |
|
2024 if ( validTypes & 0x0040 && /^(2131|1800)/.test( value ) ) { // Jcb |
|
2025 return value.length === 15; |
|
2026 } |
|
2027 if ( validTypes & 0x0080 ) { // Unknown |
|
2028 return true; |
|
2029 } |
|
2030 return false; |
|
2031 }, "Please enter a valid credit card number." ); |
|
2032 |
|
2033 /** |
|
2034 * Validates currencies with any given symbols by @jameslouiz |
|
2035 * Symbols can be optional or required. Symbols required by default |
|
2036 * |
|
2037 * Usage examples: |
|
2038 * currency: ["£", false] - Use false for soft currency validation |
|
2039 * currency: ["$", false] |
|
2040 * currency: ["RM", false] - also works with text based symbols such as "RM" - Malaysia Ringgit etc |
|
2041 * |
|
2042 * <input class="currencyInput" name="currencyInput"> |
|
2043 * |
|
2044 * Soft symbol checking |
|
2045 * currencyInput: { |
|
2046 * currency: ["$", false] |
|
2047 * } |
|
2048 * |
|
2049 * Strict symbol checking (default) |
|
2050 * currencyInput: { |
|
2051 * currency: "$" |
|
2052 * //OR |
|
2053 * currency: ["$", true] |
|
2054 * } |
|
2055 * |
|
2056 * Multiple Symbols |
|
2057 * currencyInput: { |
|
2058 * currency: "$,£,¢" |
|
2059 * } |
|
2060 */ |
|
2061 $.validator.addMethod( "currency", function( value, element, param ) { |
|
2062 var isParamString = typeof param === "string", |
|
2063 symbol = isParamString ? param : param[ 0 ], |
|
2064 soft = isParamString ? true : param[ 1 ], |
|
2065 regex; |
|
2066 |
|
2067 symbol = symbol.replace( /,/g, "" ); |
|
2068 symbol = soft ? symbol + "]" : symbol + "]?"; |
|
2069 regex = "^[" + symbol + "([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$"; |
|
2070 regex = new RegExp( regex ); |
|
2071 return this.optional( element ) || regex.test( value ); |
|
2072 |
|
2073 }, "Please specify a valid currency" ); |
|
2074 |
|
2075 $.validator.addMethod( "dateFA", function( value, element ) { |
|
2076 return this.optional( element ) || /^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test( value ); |
|
2077 }, $.validator.messages.date ); |
|
2078 |
|
2079 /** |
|
2080 * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy. |
|
2081 * |
|
2082 * @example $.validator.methods.date("01/01/1900") |
|
2083 * @result true |
|
2084 * |
|
2085 * @example $.validator.methods.date("01/13/1990") |
|
2086 * @result false |
|
2087 * |
|
2088 * @example $.validator.methods.date("01.01.1900") |
|
2089 * @result false |
|
2090 * |
|
2091 * @example <input name="pippo" class="{dateITA:true}" /> |
|
2092 * @desc Declares an optional input element whose value must be a valid date. |
|
2093 * |
|
2094 * @name $.validator.methods.dateITA |
|
2095 * @type Boolean |
|
2096 * @cat Plugins/Validate/Methods |
|
2097 */ |
|
2098 $.validator.addMethod( "dateITA", function( value, element ) { |
|
2099 var check = false, |
|
2100 re = /^\d{1,2}\/\d{1,2}\/\d{4}$/, |
|
2101 adata, gg, mm, aaaa, xdata; |
|
2102 if ( re.test( value ) ) { |
|
2103 adata = value.split( "/" ); |
|
2104 gg = parseInt( adata[ 0 ], 10 ); |
|
2105 mm = parseInt( adata[ 1 ], 10 ); |
|
2106 aaaa = parseInt( adata[ 2 ], 10 ); |
|
2107 xdata = new Date( Date.UTC( aaaa, mm - 1, gg, 12, 0, 0, 0 ) ); |
|
2108 if ( ( xdata.getUTCFullYear() === aaaa ) && ( xdata.getUTCMonth() === mm - 1 ) && ( xdata.getUTCDate() === gg ) ) { |
|
2109 check = true; |
|
2110 } else { |
|
2111 check = false; |
|
2112 } |
|
2113 } else { |
|
2114 check = false; |
|
2115 } |
|
2116 return this.optional( element ) || check; |
|
2117 }, $.validator.messages.date ); |
|
2118 |
|
2119 $.validator.addMethod( "dateNL", function( value, element ) { |
|
2120 return this.optional( element ) || /^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test( value ); |
|
2121 }, $.validator.messages.date ); |
|
2122 |
|
2123 // Older "accept" file extension method. Old docs: http://docs.jquery.com/Plugins/Validation/Methods/accept |
|
2124 $.validator.addMethod( "extension", function( value, element, param ) { |
|
2125 param = typeof param === "string" ? param.replace( /,/g, "|" ) : "png|jpe?g|gif"; |
|
2126 return this.optional( element ) || value.match( new RegExp( "\\.(" + param + ")$", "i" ) ); |
|
2127 }, $.validator.format( "Please enter a value with a valid extension." ) ); |
|
2128 |
|
2129 /** |
|
2130 * Dutch giro account numbers (not bank numbers) have max 7 digits |
|
2131 */ |
|
2132 $.validator.addMethod( "giroaccountNL", function( value, element ) { |
|
2133 return this.optional( element ) || /^[0-9]{1,7}$/.test( value ); |
|
2134 }, "Please specify a valid giro account number" ); |
|
2135 |
|
2136 /** |
|
2137 * IBAN is the international bank account number. |
|
2138 * It has a country - specific format, that is checked here too |
|
2139 * |
|
2140 * Validation is case-insensitive. Please make sure to normalize input yourself. |
|
2141 */ |
|
2142 $.validator.addMethod( "iban", function( value, element ) { |
|
2143 |
|
2144 // Some quick simple tests to prevent needless work |
|
2145 if ( this.optional( element ) ) { |
|
2146 return true; |
|
2147 } |
|
2148 |
|
2149 // Remove spaces and to upper case |
|
2150 var iban = value.replace( / /g, "" ).toUpperCase(), |
|
2151 ibancheckdigits = "", |
|
2152 leadingZeroes = true, |
|
2153 cRest = "", |
|
2154 cOperator = "", |
|
2155 countrycode, ibancheck, charAt, cChar, bbanpattern, bbancountrypatterns, ibanregexp, i, p; |
|
2156 |
|
2157 // Check for IBAN code length. |
|
2158 // It contains: |
|
2159 // country code ISO 3166-1 - two letters, |
|
2160 // two check digits, |
|
2161 // Basic Bank Account Number (BBAN) - up to 30 chars |
|
2162 var minimalIBANlength = 5; |
|
2163 if ( iban.length < minimalIBANlength ) { |
|
2164 return false; |
|
2165 } |
|
2166 |
|
2167 // Check the country code and find the country specific format |
|
2168 countrycode = iban.substring( 0, 2 ); |
|
2169 bbancountrypatterns = { |
|
2170 "AL": "\\d{8}[\\dA-Z]{16}", |
|
2171 "AD": "\\d{8}[\\dA-Z]{12}", |
|
2172 "AT": "\\d{16}", |
|
2173 "AZ": "[\\dA-Z]{4}\\d{20}", |
|
2174 "BE": "\\d{12}", |
|
2175 "BH": "[A-Z]{4}[\\dA-Z]{14}", |
|
2176 "BA": "\\d{16}", |
|
2177 "BR": "\\d{23}[A-Z][\\dA-Z]", |
|
2178 "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}", |
|
2179 "CR": "\\d{17}", |
|
2180 "HR": "\\d{17}", |
|
2181 "CY": "\\d{8}[\\dA-Z]{16}", |
|
2182 "CZ": "\\d{20}", |
|
2183 "DK": "\\d{14}", |
|
2184 "DO": "[A-Z]{4}\\d{20}", |
|
2185 "EE": "\\d{16}", |
|
2186 "FO": "\\d{14}", |
|
2187 "FI": "\\d{14}", |
|
2188 "FR": "\\d{10}[\\dA-Z]{11}\\d{2}", |
|
2189 "GE": "[\\dA-Z]{2}\\d{16}", |
|
2190 "DE": "\\d{18}", |
|
2191 "GI": "[A-Z]{4}[\\dA-Z]{15}", |
|
2192 "GR": "\\d{7}[\\dA-Z]{16}", |
|
2193 "GL": "\\d{14}", |
|
2194 "GT": "[\\dA-Z]{4}[\\dA-Z]{20}", |
|
2195 "HU": "\\d{24}", |
|
2196 "IS": "\\d{22}", |
|
2197 "IE": "[\\dA-Z]{4}\\d{14}", |
|
2198 "IL": "\\d{19}", |
|
2199 "IT": "[A-Z]\\d{10}[\\dA-Z]{12}", |
|
2200 "KZ": "\\d{3}[\\dA-Z]{13}", |
|
2201 "KW": "[A-Z]{4}[\\dA-Z]{22}", |
|
2202 "LV": "[A-Z]{4}[\\dA-Z]{13}", |
|
2203 "LB": "\\d{4}[\\dA-Z]{20}", |
|
2204 "LI": "\\d{5}[\\dA-Z]{12}", |
|
2205 "LT": "\\d{16}", |
|
2206 "LU": "\\d{3}[\\dA-Z]{13}", |
|
2207 "MK": "\\d{3}[\\dA-Z]{10}\\d{2}", |
|
2208 "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}", |
|
2209 "MR": "\\d{23}", |
|
2210 "MU": "[A-Z]{4}\\d{19}[A-Z]{3}", |
|
2211 "MC": "\\d{10}[\\dA-Z]{11}\\d{2}", |
|
2212 "MD": "[\\dA-Z]{2}\\d{18}", |
|
2213 "ME": "\\d{18}", |
|
2214 "NL": "[A-Z]{4}\\d{10}", |
|
2215 "NO": "\\d{11}", |
|
2216 "PK": "[\\dA-Z]{4}\\d{16}", |
|
2217 "PS": "[\\dA-Z]{4}\\d{21}", |
|
2218 "PL": "\\d{24}", |
|
2219 "PT": "\\d{21}", |
|
2220 "RO": "[A-Z]{4}[\\dA-Z]{16}", |
|
2221 "SM": "[A-Z]\\d{10}[\\dA-Z]{12}", |
|
2222 "SA": "\\d{2}[\\dA-Z]{18}", |
|
2223 "RS": "\\d{18}", |
|
2224 "SK": "\\d{20}", |
|
2225 "SI": "\\d{15}", |
|
2226 "ES": "\\d{20}", |
|
2227 "SE": "\\d{20}", |
|
2228 "CH": "\\d{5}[\\dA-Z]{12}", |
|
2229 "TN": "\\d{20}", |
|
2230 "TR": "\\d{5}[\\dA-Z]{17}", |
|
2231 "AE": "\\d{3}\\d{16}", |
|
2232 "GB": "[A-Z]{4}\\d{14}", |
|
2233 "VG": "[\\dA-Z]{4}\\d{16}" |
|
2234 }; |
|
2235 |
|
2236 bbanpattern = bbancountrypatterns[ countrycode ]; |
|
2237 |
|
2238 // As new countries will start using IBAN in the |
|
2239 // future, we only check if the countrycode is known. |
|
2240 // This prevents false negatives, while almost all |
|
2241 // false positives introduced by this, will be caught |
|
2242 // by the checksum validation below anyway. |
|
2243 // Strict checking should return FALSE for unknown |
|
2244 // countries. |
|
2245 if ( typeof bbanpattern !== "undefined" ) { |
|
2246 ibanregexp = new RegExp( "^[A-Z]{2}\\d{2}" + bbanpattern + "$", "" ); |
|
2247 if ( !( ibanregexp.test( iban ) ) ) { |
|
2248 return false; // Invalid country specific format |
|
2249 } |
|
2250 } |
|
2251 |
|
2252 // Now check the checksum, first convert to digits |
|
2253 ibancheck = iban.substring( 4, iban.length ) + iban.substring( 0, 4 ); |
|
2254 for ( i = 0; i < ibancheck.length; i++ ) { |
|
2255 charAt = ibancheck.charAt( i ); |
|
2256 if ( charAt !== "0" ) { |
|
2257 leadingZeroes = false; |
|
2258 } |
|
2259 if ( !leadingZeroes ) { |
|
2260 ibancheckdigits += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf( charAt ); |
|
2261 } |
|
2262 } |
|
2263 |
|
2264 // Calculate the result of: ibancheckdigits % 97 |
|
2265 for ( p = 0; p < ibancheckdigits.length; p++ ) { |
|
2266 cChar = ibancheckdigits.charAt( p ); |
|
2267 cOperator = "" + cRest + "" + cChar; |
|
2268 cRest = cOperator % 97; |
|
2269 } |
|
2270 return cRest === 1; |
|
2271 }, "Please specify a valid IBAN" ); |
|
2272 |
|
2273 $.validator.addMethod( "integer", function( value, element ) { |
|
2274 return this.optional( element ) || /^-?\d+$/.test( value ); |
|
2275 }, "A positive or negative non-decimal number please" ); |
|
2276 |
|
2277 $.validator.addMethod( "ipv4", function( value, element ) { |
|
2278 return this.optional( element ) || /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test( value ); |
|
2279 }, "Please enter a valid IP v4 address." ); |
|
2280 |
|
2281 $.validator.addMethod( "ipv6", function( value, element ) { |
|
2282 return this.optional( element ) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test( value ); |
|
2283 }, "Please enter a valid IP v6 address." ); |
|
2284 |
|
2285 $.validator.addMethod( "lettersonly", function( value, element ) { |
|
2286 return this.optional( element ) || /^[a-z]+$/i.test( value ); |
|
2287 }, "Letters only please" ); |
|
2288 |
|
2289 $.validator.addMethod( "letterswithbasicpunc", function( value, element ) { |
|
2290 return this.optional( element ) || /^[a-z\-.,()'"\s]+$/i.test( value ); |
|
2291 }, "Letters or punctuation only please" ); |
|
2292 |
|
2293 $.validator.addMethod( "mobileNL", function( value, element ) { |
|
2294 return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); |
|
2295 }, "Please specify a valid mobile number" ); |
|
2296 |
|
2297 /* For UK phone functions, do the following server side processing: |
|
2298 * Compare original input with this RegEx pattern: |
|
2299 * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ |
|
2300 * Extract $1 and set $prefix to '+44<space>' if $1 is '44', otherwise set $prefix to '0' |
|
2301 * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. |
|
2302 * A number of very detailed GB telephone number RegEx patterns can also be found at: |
|
2303 * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers |
|
2304 */ |
|
2305 $.validator.addMethod( "mobileUK", function( phone_number, element ) { |
|
2306 phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); |
|
2307 return this.optional( element ) || phone_number.length > 9 && |
|
2308 phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/ ); |
|
2309 }, "Please specify a valid mobile number" ); |
|
2310 |
|
2311 $.validator.addMethod( "netmask", function( value, element ) { |
|
2312 return this.optional( element ) || /^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test( value ); |
|
2313 }, "Please enter a valid netmask." ); |
|
2314 |
|
2315 /* |
|
2316 * The NIE (Número de Identificación de Extranjero) is a Spanish tax identification number assigned by the Spanish |
|
2317 * authorities to any foreigner. |
|
2318 * |
|
2319 * The NIE is the equivalent of a Spaniards Número de Identificación Fiscal (NIF) which serves as a fiscal |
|
2320 * identification number. The CIF number (Certificado de Identificación Fiscal) is equivalent to the NIF, but applies to |
|
2321 * companies rather than individuals. The NIE consists of an 'X' or 'Y' followed by 7 or 8 digits then another letter. |
|
2322 */ |
|
2323 $.validator.addMethod( "nieES", function( value, element ) { |
|
2324 "use strict"; |
|
2325 |
|
2326 if ( this.optional( element ) ) { |
|
2327 return true; |
|
2328 } |
|
2329 |
|
2330 var nieRegEx = new RegExp( /^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi ); |
|
2331 var validChars = "TRWAGMYFPDXBNJZSQVHLCKET", |
|
2332 letter = value.substr( value.length - 1 ).toUpperCase(), |
|
2333 number; |
|
2334 |
|
2335 value = value.toString().toUpperCase(); |
|
2336 |
|
2337 // Quick format test |
|
2338 if ( value.length > 10 || value.length < 9 || !nieRegEx.test( value ) ) { |
|
2339 return false; |
|
2340 } |
|
2341 |
|
2342 // X means same number |
|
2343 // Y means number + 10000000 |
|
2344 // Z means number + 20000000 |
|
2345 value = value.replace( /^[X]/, "0" ) |
|
2346 .replace( /^[Y]/, "1" ) |
|
2347 .replace( /^[Z]/, "2" ); |
|
2348 |
|
2349 number = value.length === 9 ? value.substr( 0, 8 ) : value.substr( 0, 9 ); |
|
2350 |
|
2351 return validChars.charAt( parseInt( number, 10 ) % 23 ) === letter; |
|
2352 |
|
2353 }, "Please specify a valid NIE number." ); |
|
2354 |
|
2355 /* |
|
2356 * The Número de Identificación Fiscal ( NIF ) is the way tax identification used in Spain for individuals |
|
2357 */ |
|
2358 $.validator.addMethod( "nifES", function( value, element ) { |
|
2359 "use strict"; |
|
2360 |
|
2361 if ( this.optional( element ) ) { |
|
2362 return true; |
|
2363 } |
|
2364 |
|
2365 value = value.toUpperCase(); |
|
2366 |
|
2367 // Basic format test |
|
2368 if ( !value.match( "((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)" ) ) { |
|
2369 return false; |
|
2370 } |
|
2371 |
|
2372 // Test NIF |
|
2373 if ( /^[0-9]{8}[A-Z]{1}$/.test( value ) ) { |
|
2374 return ( "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 0 ) % 23 ) === value.charAt( 8 ) ); |
|
2375 } |
|
2376 |
|
2377 // Test specials NIF (starts with K, L or M) |
|
2378 if ( /^[KLM]{1}/.test( value ) ) { |
|
2379 return ( value[ 8 ] === "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 1 ) % 23 ) ); |
|
2380 } |
|
2381 |
|
2382 return false; |
|
2383 |
|
2384 }, "Please specify a valid NIF number." ); |
|
2385 |
|
2386 /* |
|
2387 * Numer identyfikacji podatkowej ( NIP ) is the way tax identification used in Poland for companies |
|
2388 */ |
|
2389 $.validator.addMethod( "nipPL", function( value ) { |
|
2390 "use strict"; |
|
2391 |
|
2392 value = value.replace( /[^0-9]/g, "" ); |
|
2393 |
|
2394 if ( value.length !== 10 ) { |
|
2395 return false; |
|
2396 } |
|
2397 |
|
2398 var arrSteps = [ 6, 5, 7, 2, 3, 4, 5, 6, 7 ]; |
|
2399 var intSum = 0; |
|
2400 for ( var i = 0; i < 9; i++ ) { |
|
2401 intSum += arrSteps[ i ] * value[ i ]; |
|
2402 } |
|
2403 var int2 = intSum % 11; |
|
2404 var intControlNr = ( int2 === 10 ) ? 0 : int2; |
|
2405 |
|
2406 return ( intControlNr === parseInt( value[ 9 ], 10 ) ); |
|
2407 }, "Please specify a valid NIP number." ); |
|
2408 |
|
2409 $.validator.addMethod( "notEqualTo", function( value, element, param ) { |
|
2410 return this.optional( element ) || !$.validator.methods.equalTo.call( this, value, element, param ); |
|
2411 }, "Please enter a different value, values must not be the same." ); |
|
2412 |
|
2413 $.validator.addMethod( "nowhitespace", function( value, element ) { |
|
2414 return this.optional( element ) || /^\S+$/i.test( value ); |
|
2415 }, "No white space please" ); |
|
2416 |
|
2417 /** |
|
2418 * Return true if the field value matches the given format RegExp |
|
2419 * |
|
2420 * @example $.validator.methods.pattern("AR1004",element,/^AR\d{4}$/) |
|
2421 * @result true |
|
2422 * |
|
2423 * @example $.validator.methods.pattern("BR1004",element,/^AR\d{4}$/) |
|
2424 * @result false |
|
2425 * |
|
2426 * @name $.validator.methods.pattern |
|
2427 * @type Boolean |
|
2428 * @cat Plugins/Validate/Methods |
|
2429 */ |
|
2430 $.validator.addMethod( "pattern", function( value, element, param ) { |
|
2431 if ( this.optional( element ) ) { |
|
2432 return true; |
|
2433 } |
|
2434 if ( typeof param === "string" ) { |
|
2435 param = new RegExp( "^(?:" + param + ")$" ); |
|
2436 } |
|
2437 return param.test( value ); |
|
2438 }, $.validator.messages.invalidformat || "Invalid format." ); |
|
2439 |
|
2440 /** |
|
2441 * Dutch phone numbers have 10 digits (or 11 and start with +31). |
|
2442 */ |
|
2443 $.validator.addMethod( "phoneNL", function( value, element ) { |
|
2444 return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); |
|
2445 }, "Please specify a valid phone number." ); |
|
2446 |
|
2447 /* For UK phone functions, do the following server side processing: |
|
2448 * Compare original input with this RegEx pattern: |
|
2449 * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ |
|
2450 * Extract $1 and set $prefix to '+44<space>' if $1 is '44', otherwise set $prefix to '0' |
|
2451 * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. |
|
2452 * A number of very detailed GB telephone number RegEx patterns can also be found at: |
|
2453 * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers |
|
2454 */ |
|
2455 |
|
2456 // Matches UK landline + mobile, accepting only 01-3 for landline or 07 for mobile to exclude many premium numbers |
|
2457 $.validator.addMethod( "phonesUK", function( phone_number, element ) { |
|
2458 phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); |
|
2459 return this.optional( element ) || phone_number.length > 9 && |
|
2460 phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/ ); |
|
2461 }, "Please specify a valid uk phone number" ); |
|
2462 |
|
2463 /* For UK phone functions, do the following server side processing: |
|
2464 * Compare original input with this RegEx pattern: |
|
2465 * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ |
|
2466 * Extract $1 and set $prefix to '+44<space>' if $1 is '44', otherwise set $prefix to '0' |
|
2467 * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. |
|
2468 * A number of very detailed GB telephone number RegEx patterns can also be found at: |
|
2469 * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers |
|
2470 */ |
|
2471 $.validator.addMethod( "phoneUK", function( phone_number, element ) { |
|
2472 phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); |
|
2473 return this.optional( element ) || phone_number.length > 9 && |
|
2474 phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/ ); |
|
2475 }, "Please specify a valid phone number" ); |
|
2476 |
|
2477 /** |
|
2478 * Matches US phone number format |
|
2479 * |
|
2480 * where the area code may not start with 1 and the prefix may not start with 1 |
|
2481 * allows '-' or ' ' as a separator and allows parens around area code |
|
2482 * some people may want to put a '1' in front of their number |
|
2483 * |
|
2484 * 1(212)-999-2345 or |
|
2485 * 212 999 2344 or |
|
2486 * 212-999-0983 |
|
2487 * |
|
2488 * but not |
|
2489 * 111-123-5434 |
|
2490 * and not |
|
2491 * 212 123 4567 |
|
2492 */ |
|
2493 $.validator.addMethod( "phoneUS", function( phone_number, element ) { |
|
2494 phone_number = phone_number.replace( /\s+/g, "" ); |
|
2495 return this.optional( element ) || phone_number.length > 9 && |
|
2496 phone_number.match( /^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/ ); |
|
2497 }, "Please specify a valid phone number" ); |
|
2498 |
|
2499 /* |
|
2500 * Valida CEPs do brasileiros: |
|
2501 * |
|
2502 * Formatos aceitos: |
|
2503 * 99999-999 |
|
2504 * 99.999-999 |
|
2505 * 99999999 |
|
2506 */ |
|
2507 $.validator.addMethod( "postalcodeBR", function( cep_value, element ) { |
|
2508 return this.optional( element ) || /^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test( cep_value ); |
|
2509 }, "Informe um CEP válido." ); |
|
2510 |
|
2511 /** |
|
2512 * Matches a valid Canadian Postal Code |
|
2513 * |
|
2514 * @example jQuery.validator.methods.postalCodeCA( "H0H 0H0", element ) |
|
2515 * @result true |
|
2516 * |
|
2517 * @example jQuery.validator.methods.postalCodeCA( "H0H0H0", element ) |
|
2518 * @result false |
|
2519 * |
|
2520 * @name jQuery.validator.methods.postalCodeCA |
|
2521 * @type Boolean |
|
2522 * @cat Plugins/Validate/Methods |
|
2523 */ |
|
2524 $.validator.addMethod( "postalCodeCA", function( value, element ) { |
|
2525 return this.optional( element ) || /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test( value ); |
|
2526 }, "Please specify a valid postal code" ); |
|
2527 |
|
2528 /* Matches Italian postcode (CAP) */ |
|
2529 $.validator.addMethod( "postalcodeIT", function( value, element ) { |
|
2530 return this.optional( element ) || /^\d{5}$/.test( value ); |
|
2531 }, "Please specify a valid postal code" ); |
|
2532 |
|
2533 $.validator.addMethod( "postalcodeNL", function( value, element ) { |
|
2534 return this.optional( element ) || /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test( value ); |
|
2535 }, "Please specify a valid postal code" ); |
|
2536 |
|
2537 // Matches UK postcode. Does not match to UK Channel Islands that have their own postcodes (non standard UK) |
|
2538 $.validator.addMethod( "postcodeUK", function( value, element ) { |
|
2539 return this.optional( element ) || /^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test( value ); |
|
2540 }, "Please specify a valid UK postcode" ); |
|
2541 |
|
2542 /* |
|
2543 * Lets you say "at least X inputs that match selector Y must be filled." |
|
2544 * |
|
2545 * The end result is that neither of these inputs: |
|
2546 * |
|
2547 * <input class="productinfo" name="partnumber"> |
|
2548 * <input class="productinfo" name="description"> |
|
2549 * |
|
2550 * ...will validate unless at least one of them is filled. |
|
2551 * |
|
2552 * partnumber: {require_from_group: [1,".productinfo"]}, |
|
2553 * description: {require_from_group: [1,".productinfo"]} |
|
2554 * |
|
2555 * options[0]: number of fields that must be filled in the group |
|
2556 * options[1]: CSS selector that defines the group of conditionally required fields |
|
2557 */ |
|
2558 $.validator.addMethod( "require_from_group", function( value, element, options ) { |
|
2559 var $fields = $( options[ 1 ], element.form ), |
|
2560 $fieldsFirst = $fields.eq( 0 ), |
|
2561 validator = $fieldsFirst.data( "valid_req_grp" ) ? $fieldsFirst.data( "valid_req_grp" ) : $.extend( {}, this ), |
|
2562 isValid = $fields.filter( function() { |
|
2563 return validator.elementValue( this ); |
|
2564 } ).length >= options[ 0 ]; |
|
2565 |
|
2566 // Store the cloned validator for future validation |
|
2567 $fieldsFirst.data( "valid_req_grp", validator ); |
|
2568 |
|
2569 // If element isn't being validated, run each require_from_group field's validation rules |
|
2570 if ( !$( element ).data( "being_validated" ) ) { |
|
2571 $fields.data( "being_validated", true ); |
|
2572 $fields.each( function() { |
|
2573 validator.element( this ); |
|
2574 } ); |
|
2575 $fields.data( "being_validated", false ); |
|
2576 } |
|
2577 return isValid; |
|
2578 }, $.validator.format( "Please fill at least {0} of these fields." ) ); |
|
2579 |
|
2580 /* |
|
2581 * Lets you say "either at least X inputs that match selector Y must be filled, |
|
2582 * OR they must all be skipped (left blank)." |
|
2583 * |
|
2584 * The end result, is that none of these inputs: |
|
2585 * |
|
2586 * <input class="productinfo" name="partnumber"> |
|
2587 * <input class="productinfo" name="description"> |
|
2588 * <input class="productinfo" name="color"> |
|
2589 * |
|
2590 * ...will validate unless either at least two of them are filled, |
|
2591 * OR none of them are. |
|
2592 * |
|
2593 * partnumber: {skip_or_fill_minimum: [2,".productinfo"]}, |
|
2594 * description: {skip_or_fill_minimum: [2,".productinfo"]}, |
|
2595 * color: {skip_or_fill_minimum: [2,".productinfo"]} |
|
2596 * |
|
2597 * options[0]: number of fields that must be filled in the group |
|
2598 * options[1]: CSS selector that defines the group of conditionally required fields |
|
2599 * |
|
2600 */ |
|
2601 $.validator.addMethod( "skip_or_fill_minimum", function( value, element, options ) { |
|
2602 var $fields = $( options[ 1 ], element.form ), |
|
2603 $fieldsFirst = $fields.eq( 0 ), |
|
2604 validator = $fieldsFirst.data( "valid_skip" ) ? $fieldsFirst.data( "valid_skip" ) : $.extend( {}, this ), |
|
2605 numberFilled = $fields.filter( function() { |
|
2606 return validator.elementValue( this ); |
|
2607 } ).length, |
|
2608 isValid = numberFilled === 0 || numberFilled >= options[ 0 ]; |
|
2609 |
|
2610 // Store the cloned validator for future validation |
|
2611 $fieldsFirst.data( "valid_skip", validator ); |
|
2612 |
|
2613 // If element isn't being validated, run each skip_or_fill_minimum field's validation rules |
|
2614 if ( !$( element ).data( "being_validated" ) ) { |
|
2615 $fields.data( "being_validated", true ); |
|
2616 $fields.each( function() { |
|
2617 validator.element( this ); |
|
2618 } ); |
|
2619 $fields.data( "being_validated", false ); |
|
2620 } |
|
2621 return isValid; |
|
2622 }, $.validator.format( "Please either skip these fields or fill at least {0} of them." ) ); |
|
2623 |
|
2624 /* Validates US States and/or Territories by @jdforsythe |
|
2625 * Can be case insensitive or require capitalization - default is case insensitive |
|
2626 * Can include US Territories or not - default does not |
|
2627 * Can include US Military postal abbreviations (AA, AE, AP) - default does not |
|
2628 * |
|
2629 * Note: "States" always includes DC (District of Colombia) |
|
2630 * |
|
2631 * Usage examples: |
|
2632 * |
|
2633 * This is the default - case insensitive, no territories, no military zones |
|
2634 * stateInput: { |
|
2635 * caseSensitive: false, |
|
2636 * includeTerritories: false, |
|
2637 * includeMilitary: false |
|
2638 * } |
|
2639 * |
|
2640 * Only allow capital letters, no territories, no military zones |
|
2641 * stateInput: { |
|
2642 * caseSensitive: false |
|
2643 * } |
|
2644 * |
|
2645 * Case insensitive, include territories but not military zones |
|
2646 * stateInput: { |
|
2647 * includeTerritories: true |
|
2648 * } |
|
2649 * |
|
2650 * Only allow capital letters, include territories and military zones |
|
2651 * stateInput: { |
|
2652 * caseSensitive: true, |
|
2653 * includeTerritories: true, |
|
2654 * includeMilitary: true |
|
2655 * } |
|
2656 * |
|
2657 */ |
|
2658 $.validator.addMethod( "stateUS", function( value, element, options ) { |
|
2659 var isDefault = typeof options === "undefined", |
|
2660 caseSensitive = ( isDefault || typeof options.caseSensitive === "undefined" ) ? false : options.caseSensitive, |
|
2661 includeTerritories = ( isDefault || typeof options.includeTerritories === "undefined" ) ? false : options.includeTerritories, |
|
2662 includeMilitary = ( isDefault || typeof options.includeMilitary === "undefined" ) ? false : options.includeMilitary, |
|
2663 regex; |
|
2664 |
|
2665 if ( !includeTerritories && !includeMilitary ) { |
|
2666 regex = "^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; |
|
2667 } else if ( includeTerritories && includeMilitary ) { |
|
2668 regex = "^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; |
|
2669 } else if ( includeTerritories ) { |
|
2670 regex = "^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; |
|
2671 } else { |
|
2672 regex = "^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; |
|
2673 } |
|
2674 |
|
2675 regex = caseSensitive ? new RegExp( regex ) : new RegExp( regex, "i" ); |
|
2676 return this.optional( element ) || regex.test( value ); |
|
2677 }, "Please specify a valid state" ); |
|
2678 |
|
2679 // TODO check if value starts with <, otherwise don't try stripping anything |
|
2680 $.validator.addMethod( "strippedminlength", function( value, element, param ) { |
|
2681 return $( value ).text().length >= param; |
|
2682 }, $.validator.format( "Please enter at least {0} characters" ) ); |
|
2683 |
|
2684 $.validator.addMethod( "time", function( value, element ) { |
|
2685 return this.optional( element ) || /^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test( value ); |
|
2686 }, "Please enter a valid time, between 00:00 and 23:59" ); |
|
2687 |
|
2688 $.validator.addMethod( "time12h", function( value, element ) { |
|
2689 return this.optional( element ) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test( value ); |
|
2690 }, "Please enter a valid time in 12-hour am/pm format" ); |
|
2691 |
|
2692 // Same as url, but TLD is optional |
|
2693 $.validator.addMethod( "url2", function( value, element ) { |
|
2694 return this.optional( element ) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test( value ); |
|
2695 }, $.validator.messages.url ); |
|
2696 |
|
2697 /** |
|
2698 * Return true, if the value is a valid vehicle identification number (VIN). |
|
2699 * |
|
2700 * Works with all kind of text inputs. |
|
2701 * |
|
2702 * @example <input type="text" size="20" name="VehicleID" class="{required:true,vinUS:true}" /> |
|
2703 * @desc Declares a required input element whose value must be a valid vehicle identification number. |
|
2704 * |
|
2705 * @name $.validator.methods.vinUS |
|
2706 * @type Boolean |
|
2707 * @cat Plugins/Validate/Methods |
|
2708 */ |
|
2709 $.validator.addMethod( "vinUS", function( v ) { |
|
2710 if ( v.length !== 17 ) { |
|
2711 return false; |
|
2712 } |
|
2713 |
|
2714 var LL = [ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ], |
|
2715 VL = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9 ], |
|
2716 FL = [ 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 ], |
|
2717 rs = 0, |
|
2718 i, n, d, f, cd, cdv; |
|
2719 |
|
2720 for ( i = 0; i < 17; i++ ) { |
|
2721 f = FL[ i ]; |
|
2722 d = v.slice( i, i + 1 ); |
|
2723 if ( i === 8 ) { |
|
2724 cdv = d; |
|
2725 } |
|
2726 if ( !isNaN( d ) ) { |
|
2727 d *= f; |
|
2728 } else { |
|
2729 for ( n = 0; n < LL.length; n++ ) { |
|
2730 if ( d.toUpperCase() === LL[ n ] ) { |
|
2731 d = VL[ n ]; |
|
2732 d *= f; |
|
2733 if ( isNaN( cdv ) && n === 8 ) { |
|
2734 cdv = LL[ n ]; |
|
2735 } |
|
2736 break; |
|
2737 } |
|
2738 } |
|
2739 } |
|
2740 rs += d; |
|
2741 } |
|
2742 cd = rs % 11; |
|
2743 if ( cd === 10 ) { |
|
2744 cd = "X"; |
|
2745 } |
|
2746 if ( cd === cdv ) { |
|
2747 return true; |
|
2748 } |
|
2749 return false; |
|
2750 }, "The specified vehicle identification number (VIN) is invalid." ); |
|
2751 |
|
2752 $.validator.addMethod( "zipcodeUS", function( value, element ) { |
|
2753 return this.optional( element ) || /^\d{5}(-\d{4})?$/.test( value ); |
|
2754 }, "The specified US ZIP Code is invalid" ); |
|
2755 |
|
2756 $.validator.addMethod( "ziprange", function( value, element ) { |
|
2757 return this.optional( element ) || /^90[2-5]\d\{2\}-\d{4}$/.test( value ); |
|
2758 }, "Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx" ); |
|
2759 return $; |
|
2760 })); |
|
2761 |