1 | /*! |
---|
2 | * jQuery Validation Plugin v1.13.0 |
---|
3 | * |
---|
4 | * http://jqueryvalidation.org/ |
---|
5 | * |
---|
6 | * Copyright (c) 2014 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 { |
---|
13 | factory( jQuery ); |
---|
14 | } |
---|
15 | }(function( $ ) { |
---|
16 | |
---|
17 | $.extend($.fn, { |
---|
18 | // http://jqueryvalidation.org/validate/ |
---|
19 | validate: function( options ) { |
---|
20 | |
---|
21 | // if nothing is selected, return nothing; can't chain anyway |
---|
22 | if ( !this.length ) { |
---|
23 | if ( options && options.debug && window.console ) { |
---|
24 | console.warn( "Nothing selected, can't validate, returning nothing." ); |
---|
25 | } |
---|
26 | return; |
---|
27 | } |
---|
28 | |
---|
29 | // check if a validator for this form was already created |
---|
30 | var validator = $.data( this[ 0 ], "validator" ); |
---|
31 | if ( validator ) { |
---|
32 | return validator; |
---|
33 | } |
---|
34 | |
---|
35 | // Add novalidate tag if HTML5. |
---|
36 | this.attr( "novalidate", "novalidate" ); |
---|
37 | |
---|
38 | validator = new $.validator( options, this[ 0 ] ); |
---|
39 | $.data( this[ 0 ], "validator", validator ); |
---|
40 | |
---|
41 | if ( validator.settings.onsubmit ) { |
---|
42 | |
---|
43 | this.validateDelegate( ":submit", "click", function( event ) { |
---|
44 | if ( validator.settings.submitHandler ) { |
---|
45 | validator.submitButton = event.target; |
---|
46 | } |
---|
47 | // allow suppressing validation by adding a cancel class to the submit button |
---|
48 | if ( $( event.target ).hasClass( "cancel" ) ) { |
---|
49 | validator.cancelSubmit = true; |
---|
50 | } |
---|
51 | |
---|
52 | // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button |
---|
53 | if ( $( event.target ).attr( "formnovalidate" ) !== undefined ) { |
---|
54 | validator.cancelSubmit = true; |
---|
55 | } |
---|
56 | }); |
---|
57 | |
---|
58 | // validate the form on submit |
---|
59 | this.submit( function( event ) { |
---|
60 | if ( validator.settings.debug ) { |
---|
61 | // prevent form submit to be able to see console output |
---|
62 | event.preventDefault(); |
---|
63 | } |
---|
64 | function handle() { |
---|
65 | var hidden; |
---|
66 | if ( validator.settings.submitHandler ) { |
---|
67 | if ( validator.submitButton ) { |
---|
68 | // insert a hidden input as a replacement for the missing submit button |
---|
69 | hidden = $( "<input type='hidden'/>" ) |
---|
70 | .attr( "name", validator.submitButton.name ) |
---|
71 | .val( $( validator.submitButton ).val() ) |
---|
72 | .appendTo( validator.currentForm ); |
---|
73 | } |
---|
74 | validator.settings.submitHandler.call( validator, validator.currentForm, event ); |
---|
75 | if ( validator.submitButton ) { |
---|
76 | // and clean up afterwards; thanks to no-block-scope, hidden can be referenced |
---|
77 | hidden.remove(); |
---|
78 | } |
---|
79 | return false; |
---|
80 | } |
---|
81 | return true; |
---|
82 | } |
---|
83 | |
---|
84 | // prevent submit for invalid forms or custom submit handlers |
---|
85 | if ( validator.cancelSubmit ) { |
---|
86 | validator.cancelSubmit = false; |
---|
87 | return handle(); |
---|
88 | } |
---|
89 | if ( validator.form() ) { |
---|
90 | if ( validator.pendingRequest ) { |
---|
91 | validator.formSubmitted = true; |
---|
92 | return false; |
---|
93 | } |
---|
94 | return handle(); |
---|
95 | } else { |
---|
96 | validator.focusInvalid(); |
---|
97 | return false; |
---|
98 | } |
---|
99 | }); |
---|
100 | } |
---|
101 | |
---|
102 | return validator; |
---|
103 | }, |
---|
104 | // http://jqueryvalidation.org/valid/ |
---|
105 | valid: function() { |
---|
106 | var valid, validator; |
---|
107 | |
---|
108 | if ( $( this[ 0 ] ).is( "form" ) ) { |
---|
109 | valid = this.validate().form(); |
---|
110 | } else { |
---|
111 | valid = true; |
---|
112 | validator = $( this[ 0 ].form ).validate(); |
---|
113 | this.each( function() { |
---|
114 | valid = validator.element( this ) && valid; |
---|
115 | }); |
---|
116 | } |
---|
117 | return valid; |
---|
118 | }, |
---|
119 | // attributes: space separated list of attributes to retrieve and remove |
---|
120 | removeAttrs: function( attributes ) { |
---|
121 | var result = {}, |
---|
122 | $element = this; |
---|
123 | $.each( attributes.split( /\s/ ), function( index, value ) { |
---|
124 | result[ value ] = $element.attr( value ); |
---|
125 | $element.removeAttr( value ); |
---|
126 | }); |
---|
127 | return result; |
---|
128 | }, |
---|
129 | // http://jqueryvalidation.org/rules/ |
---|
130 | rules: function( command, argument ) { |
---|
131 | var element = this[ 0 ], |
---|
132 | settings, staticRules, existingRules, data, param, filtered; |
---|
133 | |
---|
134 | if ( command ) { |
---|
135 | settings = $.data( element.form, "validator" ).settings; |
---|
136 | staticRules = settings.rules; |
---|
137 | existingRules = $.validator.staticRules( element ); |
---|
138 | switch ( command ) { |
---|
139 | case "add": |
---|
140 | $.extend( existingRules, $.validator.normalizeRule( argument ) ); |
---|
141 | // remove messages from rules, but allow them to be set separately |
---|
142 | delete existingRules.messages; |
---|
143 | staticRules[ element.name ] = existingRules; |
---|
144 | if ( argument.messages ) { |
---|
145 | settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages ); |
---|
146 | } |
---|
147 | break; |
---|
148 | case "remove": |
---|
149 | if ( !argument ) { |
---|
150 | delete staticRules[ element.name ]; |
---|
151 | return existingRules; |
---|
152 | } |
---|
153 | filtered = {}; |
---|
154 | $.each( argument.split( /\s/ ), function( index, method ) { |
---|
155 | filtered[ method ] = existingRules[ method ]; |
---|
156 | delete existingRules[ method ]; |
---|
157 | if ( method === "required" ) { |
---|
158 | $( element ).removeAttr( "aria-required" ); |
---|
159 | } |
---|
160 | }); |
---|
161 | return filtered; |
---|
162 | } |
---|
163 | } |
---|
164 | |
---|
165 | data = $.validator.normalizeRules( |
---|
166 | $.extend( |
---|
167 | {}, |
---|
168 | $.validator.classRules( element ), |
---|
169 | $.validator.attributeRules( element ), |
---|
170 | $.validator.dataRules( element ), |
---|
171 | $.validator.staticRules( element ) |
---|
172 | ), element ); |
---|
173 | |
---|
174 | // make sure required is at front |
---|
175 | if ( data.required ) { |
---|
176 | param = data.required; |
---|
177 | delete data.required; |
---|
178 | data = $.extend( { required: param }, data ); |
---|
179 | $( element ).attr( "aria-required", "true" ); |
---|
180 | } |
---|
181 | |
---|
182 | // make sure remote is at back |
---|
183 | if ( data.remote ) { |
---|
184 | param = data.remote; |
---|
185 | delete data.remote; |
---|
186 | data = $.extend( data, { remote: param }); |
---|
187 | } |
---|
188 | |
---|
189 | return data; |
---|
190 | } |
---|
191 | }); |
---|
192 | |
---|
193 | // Custom selectors |
---|
194 | $.extend( $.expr[ ":" ], { |
---|
195 | // http://jqueryvalidation.org/blank-selector/ |
---|
196 | blank: function( a ) { |
---|
197 | return !$.trim( "" + $( a ).val() ); |
---|
198 | }, |
---|
199 | // http://jqueryvalidation.org/filled-selector/ |
---|
200 | filled: function( a ) { |
---|
201 | return !!$.trim( "" + $( a ).val() ); |
---|
202 | }, |
---|
203 | // http://jqueryvalidation.org/unchecked-selector/ |
---|
204 | unchecked: function( a ) { |
---|
205 | return !$( a ).prop( "checked" ); |
---|
206 | } |
---|
207 | }); |
---|
208 | |
---|
209 | // constructor for validator |
---|
210 | $.validator = function( options, form ) { |
---|
211 | this.settings = $.extend( true, {}, $.validator.defaults, options ); |
---|
212 | this.currentForm = form; |
---|
213 | this.init(); |
---|
214 | }; |
---|
215 | |
---|
216 | // http://jqueryvalidation.org/jQuery.validator.format/ |
---|
217 | $.validator.format = function( source, params ) { |
---|
218 | if ( arguments.length === 1 ) { |
---|
219 | return function() { |
---|
220 | var args = $.makeArray( arguments ); |
---|
221 | args.unshift( source ); |
---|
222 | return $.validator.format.apply( this, args ); |
---|
223 | }; |
---|
224 | } |
---|
225 | if ( arguments.length > 2 && params.constructor !== Array ) { |
---|
226 | params = $.makeArray( arguments ).slice( 1 ); |
---|
227 | } |
---|
228 | if ( params.constructor !== Array ) { |
---|
229 | params = [ params ]; |
---|
230 | } |
---|
231 | $.each( params, function( i, n ) { |
---|
232 | source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() { |
---|
233 | return n; |
---|
234 | }); |
---|
235 | }); |
---|
236 | return source; |
---|
237 | }; |
---|
238 | |
---|
239 | $.extend( $.validator, { |
---|
240 | |
---|
241 | defaults: { |
---|
242 | messages: {}, |
---|
243 | groups: {}, |
---|
244 | rules: {}, |
---|
245 | errorClass: "error", |
---|
246 | validClass: "valid", |
---|
247 | errorElement: "label", |
---|
248 | focusInvalid: true, |
---|
249 | errorContainer: $( [] ), |
---|
250 | errorLabelContainer: $( [] ), |
---|
251 | onsubmit: true, |
---|
252 | ignore: ":hidden", |
---|
253 | ignoreTitle: false, |
---|
254 | onfocusin: function( element ) { |
---|
255 | this.lastActive = element; |
---|
256 | |
---|
257 | // hide error label and remove error class on focus if enabled |
---|
258 | if ( this.settings.focusCleanup && !this.blockFocusCleanup ) { |
---|
259 | if ( this.settings.unhighlight ) { |
---|
260 | this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); |
---|
261 | } |
---|
262 | this.hideThese( this.errorsFor( element ) ); |
---|
263 | } |
---|
264 | }, |
---|
265 | onfocusout: function( element ) { |
---|
266 | if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) { |
---|
267 | this.element( element ); |
---|
268 | } |
---|
269 | }, |
---|
270 | onkeyup: function( element, event ) { |
---|
271 | if ( event.which === 9 && this.elementValue( element ) === "" ) { |
---|
272 | return; |
---|
273 | } else if ( element.name in this.submitted || element === this.lastElement ) { |
---|
274 | this.element( element ); |
---|
275 | } |
---|
276 | }, |
---|
277 | onclick: function( element ) { |
---|
278 | // click on selects, radiobuttons and checkboxes |
---|
279 | if ( element.name in this.submitted ) { |
---|
280 | this.element( element ); |
---|
281 | |
---|
282 | // or option elements, check parent select in that case |
---|
283 | } else if ( element.parentNode.name in this.submitted ) { |
---|
284 | this.element( element.parentNode ); |
---|
285 | } |
---|
286 | }, |
---|
287 | highlight: function( element, errorClass, validClass ) { |
---|
288 | if ( element.type === "radio" ) { |
---|
289 | this.findByName( element.name ).addClass( errorClass ).removeClass( validClass ); |
---|
290 | } else { |
---|
291 | $( element ).addClass( errorClass ).removeClass( validClass ); |
---|
292 | } |
---|
293 | }, |
---|
294 | unhighlight: function( element, errorClass, validClass ) { |
---|
295 | if ( element.type === "radio" ) { |
---|
296 | this.findByName( element.name ).removeClass( errorClass ).addClass( validClass ); |
---|
297 | } else { |
---|
298 | $( element ).removeClass( errorClass ).addClass( validClass ); |
---|
299 | } |
---|
300 | } |
---|
301 | }, |
---|
302 | |
---|
303 | // http://jqueryvalidation.org/jQuery.validator.setDefaults/ |
---|
304 | setDefaults: function( settings ) { |
---|
305 | $.extend( $.validator.defaults, settings ); |
---|
306 | }, |
---|
307 | |
---|
308 | messages: { |
---|
309 | required: "This field is required.", |
---|
310 | remote: "Please fix this field.", |
---|
311 | email: "Please enter a valid email address.", |
---|
312 | url: "Please enter a valid URL.", |
---|
313 | date: "Please enter a valid date.", |
---|
314 | dateISO: "Please enter a valid date ( ISO ).", |
---|
315 | number: "Please enter a valid number.", |
---|
316 | digits: "Please enter only digits.", |
---|
317 | creditcard: "Please enter a valid credit card number.", |
---|
318 | equalTo: "Please enter the same value again.", |
---|
319 | maxlength: $.validator.format( "Please enter no more than {0} characters." ), |
---|
320 | minlength: $.validator.format( "Please enter at least {0} characters." ), |
---|
321 | rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ), |
---|
322 | range: $.validator.format( "Please enter a value between {0} and {1}." ), |
---|
323 | max: $.validator.format( "Please enter a value less than or equal to {0}." ), |
---|
324 | min: $.validator.format( "Please enter a value greater than or equal to {0}." ) |
---|
325 | }, |
---|
326 | |
---|
327 | autoCreateRanges: false, |
---|
328 | |
---|
329 | prototype: { |
---|
330 | |
---|
331 | init: function() { |
---|
332 | this.labelContainer = $( this.settings.errorLabelContainer ); |
---|
333 | this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm ); |
---|
334 | this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer ); |
---|
335 | this.submitted = {}; |
---|
336 | this.valueCache = {}; |
---|
337 | this.pendingRequest = 0; |
---|
338 | this.pending = {}; |
---|
339 | this.invalid = {}; |
---|
340 | this.reset(); |
---|
341 | |
---|
342 | var groups = ( this.groups = {} ), |
---|
343 | rules; |
---|
344 | $.each( this.settings.groups, function( key, value ) { |
---|
345 | if ( typeof value === "string" ) { |
---|
346 | value = value.split( /\s/ ); |
---|
347 | } |
---|
348 | $.each( value, function( index, name ) { |
---|
349 | groups[ name ] = key; |
---|
350 | }); |
---|
351 | }); |
---|
352 | rules = this.settings.rules; |
---|
353 | $.each( rules, function( key, value ) { |
---|
354 | rules[ key ] = $.validator.normalizeRule( value ); |
---|
355 | }); |
---|
356 | |
---|
357 | function delegate( event ) { |
---|
358 | var validator = $.data( this[ 0 ].form, "validator" ), |
---|
359 | eventType = "on" + event.type.replace( /^validate/, "" ), |
---|
360 | settings = validator.settings; |
---|
361 | if ( settings[ eventType ] && !this.is( settings.ignore ) ) { |
---|
362 | settings[ eventType ].call( validator, this[ 0 ], event ); |
---|
363 | } |
---|
364 | } |
---|
365 | $( this.currentForm ) |
---|
366 | .validateDelegate( ":text, [type='password'], [type='file'], select, textarea, " + |
---|
367 | "[type='number'], [type='search'] ,[type='tel'], [type='url'], " + |
---|
368 | "[type='email'], [type='datetime'], [type='date'], [type='month'], " + |
---|
369 | "[type='week'], [type='time'], [type='datetime-local'], " + |
---|
370 | "[type='range'], [type='color'], [type='radio'], [type='checkbox']", |
---|
371 | "focusin focusout keyup", delegate) |
---|
372 | // Support: Chrome, oldIE |
---|
373 | // "select" is provided as event.target when clicking a option |
---|
374 | .validateDelegate("select, option, [type='radio'], [type='checkbox']", "click", delegate); |
---|
375 | |
---|
376 | if ( this.settings.invalidHandler ) { |
---|
377 | $( this.currentForm ).bind( "invalid-form.validate", this.settings.invalidHandler ); |
---|
378 | } |
---|
379 | |
---|
380 | // Add aria-required to any Static/Data/Class required fields before first validation |
---|
381 | // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html |
---|
382 | $( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" ); |
---|
383 | }, |
---|
384 | |
---|
385 | // http://jqueryvalidation.org/Validator.form/ |
---|
386 | form: function() { |
---|
387 | this.checkForm(); |
---|
388 | $.extend( this.submitted, this.errorMap ); |
---|
389 | this.invalid = $.extend({}, this.errorMap ); |
---|
390 | if ( !this.valid() ) { |
---|
391 | $( this.currentForm ).triggerHandler( "invalid-form", [ this ]); |
---|
392 | } |
---|
393 | this.showErrors(); |
---|
394 | return this.valid(); |
---|
395 | }, |
---|
396 | |
---|
397 | checkForm: function() { |
---|
398 | this.prepareForm(); |
---|
399 | for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) { |
---|
400 | this.check( elements[ i ] ); |
---|
401 | } |
---|
402 | return this.valid(); |
---|
403 | }, |
---|
404 | |
---|
405 | // http://jqueryvalidation.org/Validator.element/ |
---|
406 | element: function( element ) { |
---|
407 | var cleanElement = this.clean( element ), |
---|
408 | checkElement = this.validationTargetFor( cleanElement ), |
---|
409 | result = true; |
---|
410 | |
---|
411 | this.lastElement = checkElement; |
---|
412 | |
---|
413 | if ( checkElement === undefined ) { |
---|
414 | delete this.invalid[ cleanElement.name ]; |
---|
415 | } else { |
---|
416 | this.prepareElement( checkElement ); |
---|
417 | this.currentElements = $( checkElement ); |
---|
418 | |
---|
419 | result = this.check( checkElement ) !== false; |
---|
420 | if ( result ) { |
---|
421 | delete this.invalid[ checkElement.name ]; |
---|
422 | } else { |
---|
423 | this.invalid[ checkElement.name ] = true; |
---|
424 | } |
---|
425 | } |
---|
426 | // Add aria-invalid status for screen readers |
---|
427 | $( element ).attr( "aria-invalid", !result ); |
---|
428 | |
---|
429 | if ( !this.numberOfInvalids() ) { |
---|
430 | // Hide error containers on last error |
---|
431 | this.toHide = this.toHide.add( this.containers ); |
---|
432 | } |
---|
433 | this.showErrors(); |
---|
434 | return result; |
---|
435 | }, |
---|
436 | |
---|
437 | // http://jqueryvalidation.org/Validator.showErrors/ |
---|
438 | showErrors: function( errors ) { |
---|
439 | if ( errors ) { |
---|
440 | // add items to error list and map |
---|
441 | $.extend( this.errorMap, errors ); |
---|
442 | this.errorList = []; |
---|
443 | for ( var name in errors ) { |
---|
444 | this.errorList.push({ |
---|
445 | message: errors[ name ], |
---|
446 | element: this.findByName( name )[ 0 ] |
---|
447 | }); |
---|
448 | } |
---|
449 | // remove items from success list |
---|
450 | this.successList = $.grep( this.successList, function( element ) { |
---|
451 | return !( element.name in errors ); |
---|
452 | }); |
---|
453 | } |
---|
454 | if ( this.settings.showErrors ) { |
---|
455 | this.settings.showErrors.call( this, this.errorMap, this.errorList ); |
---|
456 | } else { |
---|
457 | this.defaultShowErrors(); |
---|
458 | } |
---|
459 | }, |
---|
460 | |
---|
461 | // http://jqueryvalidation.org/Validator.resetForm/ |
---|
462 | resetForm: function() { |
---|
463 | if ( $.fn.resetForm ) { |
---|
464 | $( this.currentForm ).resetForm(); |
---|
465 | } |
---|
466 | this.submitted = {}; |
---|
467 | this.lastElement = null; |
---|
468 | this.prepareForm(); |
---|
469 | this.hideErrors(); |
---|
470 | this.elements() |
---|
471 | .removeClass( this.settings.errorClass ) |
---|
472 | .removeData( "previousValue" ) |
---|
473 | .removeAttr( "aria-invalid" ); |
---|
474 | }, |
---|
475 | |
---|
476 | numberOfInvalids: function() { |
---|
477 | return this.objectLength( this.invalid ); |
---|
478 | }, |
---|
479 | |
---|
480 | objectLength: function( obj ) { |
---|
481 | /* jshint unused: false */ |
---|
482 | var count = 0, |
---|
483 | i; |
---|
484 | for ( i in obj ) { |
---|
485 | count++; |
---|
486 | } |
---|
487 | return count; |
---|
488 | }, |
---|
489 | |
---|
490 | hideErrors: function() { |
---|
491 | this.hideThese( this.toHide ); |
---|
492 | }, |
---|
493 | |
---|
494 | hideThese: function( errors ) { |
---|
495 | errors.not( this.containers ).text( "" ); |
---|
496 | this.addWrapper( errors ).hide(); |
---|
497 | }, |
---|
498 | |
---|
499 | valid: function() { |
---|
500 | return this.size() === 0; |
---|
501 | }, |
---|
502 | |
---|
503 | size: function() { |
---|
504 | return this.errorList.length; |
---|
505 | }, |
---|
506 | |
---|
507 | focusInvalid: function() { |
---|
508 | if ( this.settings.focusInvalid ) { |
---|
509 | try { |
---|
510 | $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || []) |
---|
511 | .filter( ":visible" ) |
---|
512 | .focus() |
---|
513 | // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find |
---|
514 | .trigger( "focusin" ); |
---|
515 | } catch ( e ) { |
---|
516 | // ignore IE throwing errors when focusing hidden elements |
---|
517 | } |
---|
518 | } |
---|
519 | }, |
---|
520 | |
---|
521 | findLastActive: function() { |
---|
522 | var lastActive = this.lastActive; |
---|
523 | return lastActive && $.grep( this.errorList, function( n ) { |
---|
524 | return n.element.name === lastActive.name; |
---|
525 | }).length === 1 && lastActive; |
---|
526 | }, |
---|
527 | |
---|
528 | elements: function() { |
---|
529 | var validator = this, |
---|
530 | rulesCache = {}; |
---|
531 | |
---|
532 | // select all valid inputs inside the form (no submit or reset buttons) |
---|
533 | return $( this.currentForm ) |
---|
534 | .find( "input, select, textarea" ) |
---|
535 | .not( ":submit, :reset, :image, [disabled]" ) |
---|
536 | .not( this.settings.ignore ) |
---|
537 | .filter( function() { |
---|
538 | if ( !this.name && validator.settings.debug && window.console ) { |
---|
539 | console.error( "%o has no name assigned", this ); |
---|
540 | } |
---|
541 | |
---|
542 | // select only the first element for each name, and only those with rules specified |
---|
543 | if ( this.name in rulesCache || !validator.objectLength( $( this ).rules() ) ) { |
---|
544 | return false; |
---|
545 | } |
---|
546 | |
---|
547 | rulesCache[ this.name ] = true; |
---|
548 | return true; |
---|
549 | }); |
---|
550 | }, |
---|
551 | |
---|
552 | clean: function( selector ) { |
---|
553 | return $( selector )[ 0 ]; |
---|
554 | }, |
---|
555 | |
---|
556 | errors: function() { |
---|
557 | var errorClass = this.settings.errorClass.split( " " ).join( "." ); |
---|
558 | return $( this.settings.errorElement + "." + errorClass, this.errorContext ); |
---|
559 | }, |
---|
560 | |
---|
561 | reset: function() { |
---|
562 | this.successList = []; |
---|
563 | this.errorList = []; |
---|
564 | this.errorMap = {}; |
---|
565 | this.toShow = $( [] ); |
---|
566 | this.toHide = $( [] ); |
---|
567 | this.currentElements = $( [] ); |
---|
568 | }, |
---|
569 | |
---|
570 | prepareForm: function() { |
---|
571 | this.reset(); |
---|
572 | this.toHide = this.errors().add( this.containers ); |
---|
573 | }, |
---|
574 | |
---|
575 | prepareElement: function( element ) { |
---|
576 | this.reset(); |
---|
577 | this.toHide = this.errorsFor( element ); |
---|
578 | }, |
---|
579 | |
---|
580 | elementValue: function( element ) { |
---|
581 | var val, |
---|
582 | $element = $( element ), |
---|
583 | type = element.type; |
---|
584 | |
---|
585 | if ( type === "radio" || type === "checkbox" ) { |
---|
586 | return $( "input[name='" + element.name + "']:checked" ).val(); |
---|
587 | } else if ( type === "number" && typeof element.validity !== "undefined" ) { |
---|
588 | return element.validity.badInput ? false : $element.val(); |
---|
589 | } |
---|
590 | |
---|
591 | val = $element.val(); |
---|
592 | if ( typeof val === "string" ) { |
---|
593 | return val.replace(/\r/g, "" ); |
---|
594 | } |
---|
595 | return val; |
---|
596 | }, |
---|
597 | |
---|
598 | check: function( element ) { |
---|
599 | element = this.validationTargetFor( this.clean( element ) ); |
---|
600 | |
---|
601 | var rules = $( element ).rules(), |
---|
602 | rulesCount = $.map( rules, function( n, i ) { |
---|
603 | return i; |
---|
604 | }).length, |
---|
605 | dependencyMismatch = false, |
---|
606 | val = this.elementValue( element ), |
---|
607 | result, method, rule; |
---|
608 | |
---|
609 | for ( method in rules ) { |
---|
610 | rule = { method: method, parameters: rules[ method ] }; |
---|
611 | try { |
---|
612 | |
---|
613 | result = $.validator.methods[ method ].call( this, val, element, rule.parameters ); |
---|
614 | |
---|
615 | // if a method indicates that the field is optional and therefore valid, |
---|
616 | // don't mark it as valid when there are no other rules |
---|
617 | if ( result === "dependency-mismatch" && rulesCount === 1 ) { |
---|
618 | dependencyMismatch = true; |
---|
619 | continue; |
---|
620 | } |
---|
621 | dependencyMismatch = false; |
---|
622 | |
---|
623 | if ( result === "pending" ) { |
---|
624 | this.toHide = this.toHide.not( this.errorsFor( element ) ); |
---|
625 | return; |
---|
626 | } |
---|
627 | |
---|
628 | if ( !result ) { |
---|
629 | this.formatAndAdd( element, rule ); |
---|
630 | return false; |
---|
631 | } |
---|
632 | } catch ( e ) { |
---|
633 | if ( this.settings.debug && window.console ) { |
---|
634 | console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); |
---|
635 | } |
---|
636 | throw e; |
---|
637 | } |
---|
638 | } |
---|
639 | if ( dependencyMismatch ) { |
---|
640 | return; |
---|
641 | } |
---|
642 | if ( this.objectLength( rules ) ) { |
---|
643 | this.successList.push( element ); |
---|
644 | } |
---|
645 | return true; |
---|
646 | }, |
---|
647 | |
---|
648 | // return the custom message for the given element and validation method |
---|
649 | // specified in the element's HTML5 data attribute |
---|
650 | // return the generic message if present and no method specific message is present |
---|
651 | customDataMessage: function( element, method ) { |
---|
652 | return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() + |
---|
653 | method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" ); |
---|
654 | }, |
---|
655 | |
---|
656 | // return the custom message for the given element name and validation method |
---|
657 | customMessage: function( name, method ) { |
---|
658 | var m = this.settings.messages[ name ]; |
---|
659 | return m && ( m.constructor === String ? m : m[ method ]); |
---|
660 | }, |
---|
661 | |
---|
662 | // return the first defined argument, allowing empty strings |
---|
663 | findDefined: function() { |
---|
664 | for ( var i = 0; i < arguments.length; i++) { |
---|
665 | if ( arguments[ i ] !== undefined ) { |
---|
666 | return arguments[ i ]; |
---|
667 | } |
---|
668 | } |
---|
669 | return undefined; |
---|
670 | }, |
---|
671 | |
---|
672 | defaultMessage: function( element, method ) { |
---|
673 | return this.findDefined( |
---|
674 | this.customMessage( element.name, method ), |
---|
675 | this.customDataMessage( element, method ), |
---|
676 | // title is never undefined, so handle empty string as undefined |
---|
677 | !this.settings.ignoreTitle && element.title || undefined, |
---|
678 | $.validator.messages[ method ], |
---|
679 | "<strong>Warning: No message defined for " + element.name + "</strong>" |
---|
680 | ); |
---|
681 | }, |
---|
682 | |
---|
683 | formatAndAdd: function( element, rule ) { |
---|
684 | var message = this.defaultMessage( element, rule.method ), |
---|
685 | theregex = /\$?\{(\d+)\}/g; |
---|
686 | if ( typeof message === "function" ) { |
---|
687 | message = message.call( this, rule.parameters, element ); |
---|
688 | } else if ( theregex.test( message ) ) { |
---|
689 | message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters ); |
---|
690 | } |
---|
691 | this.errorList.push({ |
---|
692 | message: message, |
---|
693 | element: element, |
---|
694 | method: rule.method |
---|
695 | }); |
---|
696 | |
---|
697 | this.errorMap[ element.name ] = message; |
---|
698 | this.submitted[ element.name ] = message; |
---|
699 | }, |
---|
700 | |
---|
701 | addWrapper: function( toToggle ) { |
---|
702 | if ( this.settings.wrapper ) { |
---|
703 | toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); |
---|
704 | } |
---|
705 | return toToggle; |
---|
706 | }, |
---|
707 | |
---|
708 | defaultShowErrors: function() { |
---|
709 | var i, elements, error; |
---|
710 | for ( i = 0; this.errorList[ i ]; i++ ) { |
---|
711 | error = this.errorList[ i ]; |
---|
712 | if ( this.settings.highlight ) { |
---|
713 | this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); |
---|
714 | } |
---|
715 | this.showLabel( error.element, error.message ); |
---|
716 | } |
---|
717 | if ( this.errorList.length ) { |
---|
718 | this.toShow = this.toShow.add( this.containers ); |
---|
719 | } |
---|
720 | if ( this.settings.success ) { |
---|
721 | for ( i = 0; this.successList[ i ]; i++ ) { |
---|
722 | this.showLabel( this.successList[ i ] ); |
---|
723 | } |
---|
724 | } |
---|
725 | if ( this.settings.unhighlight ) { |
---|
726 | for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) { |
---|
727 | this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass ); |
---|
728 | } |
---|
729 | } |
---|
730 | this.toHide = this.toHide.not( this.toShow ); |
---|
731 | this.hideErrors(); |
---|
732 | this.addWrapper( this.toShow ).show(); |
---|
733 | }, |
---|
734 | |
---|
735 | validElements: function() { |
---|
736 | return this.currentElements.not( this.invalidElements() ); |
---|
737 | }, |
---|
738 | |
---|
739 | invalidElements: function() { |
---|
740 | return $( this.errorList ).map(function() { |
---|
741 | return this.element; |
---|
742 | }); |
---|
743 | }, |
---|
744 | |
---|
745 | showLabel: function( element, message ) { |
---|
746 | var place, group, errorID, |
---|
747 | error = this.errorsFor( element ), |
---|
748 | elementID = this.idOrName( element ), |
---|
749 | describedBy = $( element ).attr( "aria-describedby" ); |
---|
750 | if ( error.length ) { |
---|
751 | // refresh error/success class |
---|
752 | error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); |
---|
753 | // replace message on existing label |
---|
754 | error.html( message ); |
---|
755 | } else { |
---|
756 | // create error element |
---|
757 | error = $( "<" + this.settings.errorElement + ">" ) |
---|
758 | .attr( "id", elementID + "-error" ) |
---|
759 | .addClass( this.settings.errorClass ) |
---|
760 | .html( message || "" ); |
---|
761 | |
---|
762 | // Maintain reference to the element to be placed into the DOM |
---|
763 | place = error; |
---|
764 | if ( this.settings.wrapper ) { |
---|
765 | // make sure the element is visible, even in IE |
---|
766 | // actually showing the wrapped element is handled elsewhere |
---|
767 | place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent(); |
---|
768 | } |
---|
769 | if ( this.labelContainer.length ) { |
---|
770 | this.labelContainer.append( place ); |
---|
771 | } else if ( this.settings.errorPlacement ) { |
---|
772 | this.settings.errorPlacement( place, $( element ) ); |
---|
773 | } else { |
---|
774 | place.insertAfter( element ); |
---|
775 | } |
---|
776 | |
---|
777 | // Link error back to the element |
---|
778 | if ( error.is( "label" ) ) { |
---|
779 | // If the error is a label, then associate using 'for' |
---|
780 | error.attr( "for", elementID ); |
---|
781 | } else if ( error.parents( "label[for='" + elementID + "']" ).length === 0 ) { |
---|
782 | // If the element is not a child of an associated label, then it's necessary |
---|
783 | // to explicitly apply aria-describedby |
---|
784 | |
---|
785 | errorID = error.attr( "id" ); |
---|
786 | // Respect existing non-error aria-describedby |
---|
787 | if ( !describedBy ) { |
---|
788 | describedBy = errorID; |
---|
789 | } else if ( !describedBy.match( new RegExp( "\b" + errorID + "\b" ) ) ) { |
---|
790 | // Add to end of list if not already present |
---|
791 | describedBy += " " + errorID; |
---|
792 | } |
---|
793 | $( element ).attr( "aria-describedby", describedBy ); |
---|
794 | |
---|
795 | // If this element is grouped, then assign to all elements in the same group |
---|
796 | group = this.groups[ element.name ]; |
---|
797 | if ( group ) { |
---|
798 | $.each( this.groups, function( name, testgroup ) { |
---|
799 | if ( testgroup === group ) { |
---|
800 | $( "[name='" + name + "']", this.currentForm ) |
---|
801 | .attr( "aria-describedby", error.attr( "id" ) ); |
---|
802 | } |
---|
803 | }); |
---|
804 | } |
---|
805 | } |
---|
806 | } |
---|
807 | if ( !message && this.settings.success ) { |
---|
808 | error.text( "" ); |
---|
809 | if ( typeof this.settings.success === "string" ) { |
---|
810 | error.addClass( this.settings.success ); |
---|
811 | } else { |
---|
812 | this.settings.success( error, element ); |
---|
813 | } |
---|
814 | } |
---|
815 | this.toShow = this.toShow.add( error ); |
---|
816 | }, |
---|
817 | |
---|
818 | errorsFor: function( element ) { |
---|
819 | var name = this.idOrName( element ), |
---|
820 | describer = $( element ).attr( "aria-describedby" ), |
---|
821 | selector = "label[for='" + name + "'], label[for='" + name + "'] *"; |
---|
822 | // aria-describedby should directly reference the error element |
---|
823 | if ( describer ) { |
---|
824 | selector = selector + ", #" + describer.replace( /\s+/g, ", #" ); |
---|
825 | } |
---|
826 | return this |
---|
827 | .errors() |
---|
828 | .filter( selector ); |
---|
829 | }, |
---|
830 | |
---|
831 | idOrName: function( element ) { |
---|
832 | return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name ); |
---|
833 | }, |
---|
834 | |
---|
835 | validationTargetFor: function( element ) { |
---|
836 | // if radio/checkbox, validate first element in group instead |
---|
837 | if ( this.checkable( element ) ) { |
---|
838 | element = this.findByName( element.name ).not( this.settings.ignore )[ 0 ]; |
---|
839 | } |
---|
840 | return element; |
---|
841 | }, |
---|
842 | |
---|
843 | checkable: function( element ) { |
---|
844 | return ( /radio|checkbox/i ).test( element.type ); |
---|
845 | }, |
---|
846 | |
---|
847 | findByName: function( name ) { |
---|
848 | return $( this.currentForm ).find( "[name='" + name + "']" ); |
---|
849 | }, |
---|
850 | |
---|
851 | getLength: function( value, element ) { |
---|
852 | switch ( element.nodeName.toLowerCase() ) { |
---|
853 | case "select": |
---|
854 | return $( "option:selected", element ).length; |
---|
855 | case "input": |
---|
856 | if ( this.checkable( element ) ) { |
---|
857 | return this.findByName( element.name ).filter( ":checked" ).length; |
---|
858 | } |
---|
859 | } |
---|
860 | return value.length; |
---|
861 | }, |
---|
862 | |
---|
863 | depend: function( param, element ) { |
---|
864 | return this.dependTypes[typeof param] ? this.dependTypes[typeof param]( param, element ) : true; |
---|
865 | }, |
---|
866 | |
---|
867 | dependTypes: { |
---|
868 | "boolean": function( param ) { |
---|
869 | return param; |
---|
870 | }, |
---|
871 | "string": function( param, element ) { |
---|
872 | return !!$( param, element.form ).length; |
---|
873 | }, |
---|
874 | "function": function( param, element ) { |
---|
875 | return param( element ); |
---|
876 | } |
---|
877 | }, |
---|
878 | |
---|
879 | optional: function( element ) { |
---|
880 | var val = this.elementValue( element ); |
---|
881 | return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch"; |
---|
882 | }, |
---|
883 | |
---|
884 | startRequest: function( element ) { |
---|
885 | if ( !this.pending[ element.name ] ) { |
---|
886 | this.pendingRequest++; |
---|
887 | this.pending[ element.name ] = true; |
---|
888 | } |
---|
889 | }, |
---|
890 | |
---|
891 | stopRequest: function( element, valid ) { |
---|
892 | this.pendingRequest--; |
---|
893 | // sometimes synchronization fails, make sure pendingRequest is never < 0 |
---|
894 | if ( this.pendingRequest < 0 ) { |
---|
895 | this.pendingRequest = 0; |
---|
896 | } |
---|
897 | delete this.pending[ element.name ]; |
---|
898 | if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) { |
---|
899 | $( this.currentForm ).submit(); |
---|
900 | this.formSubmitted = false; |
---|
901 | } else if (!valid && this.pendingRequest === 0 && this.formSubmitted ) { |
---|
902 | $( this.currentForm ).triggerHandler( "invalid-form", [ this ]); |
---|
903 | this.formSubmitted = false; |
---|
904 | } |
---|
905 | }, |
---|
906 | |
---|
907 | previousValue: function( element ) { |
---|
908 | return $.data( element, "previousValue" ) || $.data( element, "previousValue", { |
---|
909 | old: null, |
---|
910 | valid: true, |
---|
911 | message: this.defaultMessage( element, "remote" ) |
---|
912 | }); |
---|
913 | } |
---|
914 | |
---|
915 | }, |
---|
916 | |
---|
917 | classRuleSettings: { |
---|
918 | required: { required: true }, |
---|
919 | email: { email: true }, |
---|
920 | url: { url: true }, |
---|
921 | date: { date: true }, |
---|
922 | dateISO: { dateISO: true }, |
---|
923 | number: { number: true }, |
---|
924 | digits: { digits: true }, |
---|
925 | creditcard: { creditcard: true } |
---|
926 | }, |
---|
927 | |
---|
928 | addClassRules: function( className, rules ) { |
---|
929 | if ( className.constructor === String ) { |
---|
930 | this.classRuleSettings[ className ] = rules; |
---|
931 | } else { |
---|
932 | $.extend( this.classRuleSettings, className ); |
---|
933 | } |
---|
934 | }, |
---|
935 | |
---|
936 | classRules: function( element ) { |
---|
937 | var rules = {}, |
---|
938 | classes = $( element ).attr( "class" ); |
---|
939 | |
---|
940 | if ( classes ) { |
---|
941 | $.each( classes.split( " " ), function() { |
---|
942 | if ( this in $.validator.classRuleSettings ) { |
---|
943 | $.extend( rules, $.validator.classRuleSettings[ this ]); |
---|
944 | } |
---|
945 | }); |
---|
946 | } |
---|
947 | return rules; |
---|
948 | }, |
---|
949 | |
---|
950 | attributeRules: function( element ) { |
---|
951 | var rules = {}, |
---|
952 | $element = $( element ), |
---|
953 | type = element.getAttribute( "type" ), |
---|
954 | method, value; |
---|
955 | |
---|
956 | for ( method in $.validator.methods ) { |
---|
957 | |
---|
958 | // support for <input required> in both html5 and older browsers |
---|
959 | if ( method === "required" ) { |
---|
960 | value = element.getAttribute( method ); |
---|
961 | // Some browsers return an empty string for the required attribute |
---|
962 | // and non-HTML5 browsers might have required="" markup |
---|
963 | if ( value === "" ) { |
---|
964 | value = true; |
---|
965 | } |
---|
966 | // force non-HTML5 browsers to return bool |
---|
967 | value = !!value; |
---|
968 | } else { |
---|
969 | value = $element.attr( method ); |
---|
970 | } |
---|
971 | |
---|
972 | // convert the value to a number for number inputs, and for text for backwards compability |
---|
973 | // allows type="date" and others to be compared as strings |
---|
974 | if ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) { |
---|
975 | value = Number( value ); |
---|
976 | } |
---|
977 | |
---|
978 | if ( value || value === 0 ) { |
---|
979 | rules[ method ] = value; |
---|
980 | } else if ( type === method && type !== "range" ) { |
---|
981 | // exception: the jquery validate 'range' method |
---|
982 | // does not test for the html5 'range' type |
---|
983 | rules[ method ] = true; |
---|
984 | } |
---|
985 | } |
---|
986 | |
---|
987 | // maxlength may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs |
---|
988 | if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) { |
---|
989 | delete rules.maxlength; |
---|
990 | } |
---|
991 | |
---|
992 | return rules; |
---|
993 | }, |
---|
994 | |
---|
995 | dataRules: function( element ) { |
---|
996 | var method, value, |
---|
997 | rules = {}, $element = $( element ); |
---|
998 | for ( method in $.validator.methods ) { |
---|
999 | value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() ); |
---|
1000 | if ( value !== undefined ) { |
---|
1001 | rules[ method ] = value; |
---|
1002 | } |
---|
1003 | } |
---|
1004 | return rules; |
---|
1005 | }, |
---|
1006 | |
---|
1007 | staticRules: function( element ) { |
---|
1008 | var rules = {}, |
---|
1009 | validator = $.data( element.form, "validator" ); |
---|
1010 | |
---|
1011 | if ( validator.settings.rules ) { |
---|
1012 | rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {}; |
---|
1013 | } |
---|
1014 | return rules; |
---|
1015 | }, |
---|
1016 | |
---|
1017 | normalizeRules: function( rules, element ) { |
---|
1018 | // handle dependency check |
---|
1019 | $.each( rules, function( prop, val ) { |
---|
1020 | // ignore rule when param is explicitly false, eg. required:false |
---|
1021 | if ( val === false ) { |
---|
1022 | delete rules[ prop ]; |
---|
1023 | return; |
---|
1024 | } |
---|
1025 | if ( val.param || val.depends ) { |
---|
1026 | var keepRule = true; |
---|
1027 | switch ( typeof val.depends ) { |
---|
1028 | case "string": |
---|
1029 | keepRule = !!$( val.depends, element.form ).length; |
---|
1030 | break; |
---|
1031 | case "function": |
---|
1032 | keepRule = val.depends.call( element, element ); |
---|
1033 | break; |
---|
1034 | } |
---|
1035 | if ( keepRule ) { |
---|
1036 | rules[ prop ] = val.param !== undefined ? val.param : true; |
---|
1037 | } else { |
---|
1038 | delete rules[ prop ]; |
---|
1039 | } |
---|
1040 | } |
---|
1041 | }); |
---|
1042 | |
---|
1043 | // evaluate parameters |
---|
1044 | $.each( rules, function( rule, parameter ) { |
---|
1045 | rules[ rule ] = $.isFunction( parameter ) ? parameter( element ) : parameter; |
---|
1046 | }); |
---|
1047 | |
---|
1048 | // clean number parameters |
---|
1049 | $.each([ "minlength", "maxlength" ], function() { |
---|
1050 | if ( rules[ this ] ) { |
---|
1051 | rules[ this ] = Number( rules[ this ] ); |
---|
1052 | } |
---|
1053 | }); |
---|
1054 | $.each([ "rangelength", "range" ], function() { |
---|
1055 | var parts; |
---|
1056 | if ( rules[ this ] ) { |
---|
1057 | if ( $.isArray( rules[ this ] ) ) { |
---|
1058 | rules[ this ] = [ Number( rules[ this ][ 0 ]), Number( rules[ this ][ 1 ] ) ]; |
---|
1059 | } else if ( typeof rules[ this ] === "string" ) { |
---|
1060 | parts = rules[ this ].replace(/[\[\]]/g, "" ).split( /[\s,]+/ ); |
---|
1061 | rules[ this ] = [ Number( parts[ 0 ]), Number( parts[ 1 ] ) ]; |
---|
1062 | } |
---|
1063 | } |
---|
1064 | }); |
---|
1065 | |
---|
1066 | if ( $.validator.autoCreateRanges ) { |
---|
1067 | // auto-create ranges |
---|
1068 | if ( rules.min && rules.max ) { |
---|
1069 | rules.range = [ rules.min, rules.max ]; |
---|
1070 | delete rules.min; |
---|
1071 | delete rules.max; |
---|
1072 | } |
---|
1073 | if ( rules.minlength && rules.maxlength ) { |
---|
1074 | rules.rangelength = [ rules.minlength, rules.maxlength ]; |
---|
1075 | delete rules.minlength; |
---|
1076 | delete rules.maxlength; |
---|
1077 | } |
---|
1078 | } |
---|
1079 | |
---|
1080 | return rules; |
---|
1081 | }, |
---|
1082 | |
---|
1083 | // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} |
---|
1084 | normalizeRule: function( data ) { |
---|
1085 | if ( typeof data === "string" ) { |
---|
1086 | var transformed = {}; |
---|
1087 | $.each( data.split( /\s/ ), function() { |
---|
1088 | transformed[ this ] = true; |
---|
1089 | }); |
---|
1090 | data = transformed; |
---|
1091 | } |
---|
1092 | return data; |
---|
1093 | }, |
---|
1094 | |
---|
1095 | // http://jqueryvalidation.org/jQuery.validator.addMethod/ |
---|
1096 | addMethod: function( name, method, message ) { |
---|
1097 | $.validator.methods[ name ] = method; |
---|
1098 | $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ]; |
---|
1099 | if ( method.length < 3 ) { |
---|
1100 | $.validator.addClassRules( name, $.validator.normalizeRule( name ) ); |
---|
1101 | } |
---|
1102 | }, |
---|
1103 | |
---|
1104 | methods: { |
---|
1105 | |
---|
1106 | // http://jqueryvalidation.org/required-method/ |
---|
1107 | required: function( value, element, param ) { |
---|
1108 | // check if dependency is met |
---|
1109 | if ( !this.depend( param, element ) ) { |
---|
1110 | return "dependency-mismatch"; |
---|
1111 | } |
---|
1112 | if ( element.nodeName.toLowerCase() === "select" ) { |
---|
1113 | // could be an array for select-multiple or a string, both are fine this way |
---|
1114 | var val = $( element ).val(); |
---|
1115 | return val && val.length > 0; |
---|
1116 | } |
---|
1117 | if ( this.checkable( element ) ) { |
---|
1118 | return this.getLength( value, element ) > 0; |
---|
1119 | } |
---|
1120 | return $.trim( value ).length > 0; |
---|
1121 | }, |
---|
1122 | |
---|
1123 | // http://jqueryvalidation.org/email-method/ |
---|
1124 | email: function( value, element ) { |
---|
1125 | // From http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#e-mail-state-%28type=email%29 |
---|
1126 | // Retrieved 2014-01-14 |
---|
1127 | // If you have a problem with this implementation, report a bug against the above spec |
---|
1128 | // Or use custom methods to implement your own email validation |
---|
1129 | 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 ); |
---|
1130 | }, |
---|
1131 | |
---|
1132 | // http://jqueryvalidation.org/url-method/ |
---|
1133 | url: function( value, element ) { |
---|
1134 | // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ |
---|
1135 | return this.optional( element ) || /^(https?|s?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 ); |
---|
1136 | }, |
---|
1137 | |
---|
1138 | // http://jqueryvalidation.org/date-method/ |
---|
1139 | date: function( value, element ) { |
---|
1140 | return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() ); |
---|
1141 | }, |
---|
1142 | |
---|
1143 | // http://jqueryvalidation.org/dateISO-method/ |
---|
1144 | dateISO: function( value, element ) { |
---|
1145 | return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value ); |
---|
1146 | }, |
---|
1147 | |
---|
1148 | // http://jqueryvalidation.org/number-method/ |
---|
1149 | number: function( value, element ) { |
---|
1150 | return this.optional( element ) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value ); |
---|
1151 | }, |
---|
1152 | |
---|
1153 | // http://jqueryvalidation.org/digits-method/ |
---|
1154 | digits: function( value, element ) { |
---|
1155 | return this.optional( element ) || /^\d+$/.test( value ); |
---|
1156 | }, |
---|
1157 | |
---|
1158 | // http://jqueryvalidation.org/creditcard-method/ |
---|
1159 | // based on http://en.wikipedia.org/wiki/Luhn/ |
---|
1160 | creditcard: function( value, element ) { |
---|
1161 | if ( this.optional( element ) ) { |
---|
1162 | return "dependency-mismatch"; |
---|
1163 | } |
---|
1164 | // accept only spaces, digits and dashes |
---|
1165 | if ( /[^0-9 \-]+/.test( value ) ) { |
---|
1166 | return false; |
---|
1167 | } |
---|
1168 | var nCheck = 0, |
---|
1169 | nDigit = 0, |
---|
1170 | bEven = false, |
---|
1171 | n, cDigit; |
---|
1172 | |
---|
1173 | value = value.replace( /\D/g, "" ); |
---|
1174 | |
---|
1175 | // Basing min and max length on |
---|
1176 | // http://developer.ean.com/general_info/Valid_Credit_Card_Types |
---|
1177 | if ( value.length < 13 || value.length > 19 ) { |
---|
1178 | return false; |
---|
1179 | } |
---|
1180 | |
---|
1181 | for ( n = value.length - 1; n >= 0; n--) { |
---|
1182 | cDigit = value.charAt( n ); |
---|
1183 | nDigit = parseInt( cDigit, 10 ); |
---|
1184 | if ( bEven ) { |
---|
1185 | if ( ( nDigit *= 2 ) > 9 ) { |
---|
1186 | nDigit -= 9; |
---|
1187 | } |
---|
1188 | } |
---|
1189 | nCheck += nDigit; |
---|
1190 | bEven = !bEven; |
---|
1191 | } |
---|
1192 | |
---|
1193 | return ( nCheck % 10 ) === 0; |
---|
1194 | }, |
---|
1195 | |
---|
1196 | // http://jqueryvalidation.org/minlength-method/ |
---|
1197 | minlength: function( value, element, param ) { |
---|
1198 | var length = $.isArray( value ) ? value.length : this.getLength( $.trim( value ), element ); |
---|
1199 | return this.optional( element ) || length >= param; |
---|
1200 | }, |
---|
1201 | |
---|
1202 | // http://jqueryvalidation.org/maxlength-method/ |
---|
1203 | maxlength: function( value, element, param ) { |
---|
1204 | var length = $.isArray( value ) ? value.length : this.getLength( $.trim( value ), element ); |
---|
1205 | return this.optional( element ) || length <= param; |
---|
1206 | }, |
---|
1207 | |
---|
1208 | // http://jqueryvalidation.org/rangelength-method/ |
---|
1209 | rangelength: function( value, element, param ) { |
---|
1210 | var length = $.isArray( value ) ? value.length : this.getLength( $.trim( value ), element ); |
---|
1211 | return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] ); |
---|
1212 | }, |
---|
1213 | |
---|
1214 | // http://jqueryvalidation.org/min-method/ |
---|
1215 | min: function( value, element, param ) { |
---|
1216 | return this.optional( element ) || value >= param; |
---|
1217 | }, |
---|
1218 | |
---|
1219 | // http://jqueryvalidation.org/max-method/ |
---|
1220 | max: function( value, element, param ) { |
---|
1221 | return this.optional( element ) || value <= param; |
---|
1222 | }, |
---|
1223 | |
---|
1224 | // http://jqueryvalidation.org/range-method/ |
---|
1225 | range: function( value, element, param ) { |
---|
1226 | return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] ); |
---|
1227 | }, |
---|
1228 | |
---|
1229 | // http://jqueryvalidation.org/equalTo-method/ |
---|
1230 | equalTo: function( value, element, param ) { |
---|
1231 | // bind to the blur event of the target in order to revalidate whenever the target field is updated |
---|
1232 | // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead |
---|
1233 | var target = $( param ); |
---|
1234 | if ( this.settings.onfocusout ) { |
---|
1235 | target.unbind( ".validate-equalTo" ).bind( "blur.validate-equalTo", function() { |
---|
1236 | $( element ).valid(); |
---|
1237 | }); |
---|
1238 | } |
---|
1239 | return value === target.val(); |
---|
1240 | }, |
---|
1241 | |
---|
1242 | // http://jqueryvalidation.org/remote-method/ |
---|
1243 | remote: function( value, element, param ) { |
---|
1244 | if ( this.optional( element ) ) { |
---|
1245 | return "dependency-mismatch"; |
---|
1246 | } |
---|
1247 | |
---|
1248 | var previous = this.previousValue( element ), |
---|
1249 | validator, data; |
---|
1250 | |
---|
1251 | if (!this.settings.messages[ element.name ] ) { |
---|
1252 | this.settings.messages[ element.name ] = {}; |
---|
1253 | } |
---|
1254 | previous.originalMessage = this.settings.messages[ element.name ].remote; |
---|
1255 | this.settings.messages[ element.name ].remote = previous.message; |
---|
1256 | |
---|
1257 | param = typeof param === "string" && { url: param } || param; |
---|
1258 | |
---|
1259 | if ( previous.old === value ) { |
---|
1260 | return previous.valid; |
---|
1261 | } |
---|
1262 | |
---|
1263 | previous.old = value; |
---|
1264 | validator = this; |
---|
1265 | this.startRequest( element ); |
---|
1266 | data = {}; |
---|
1267 | data[ element.name ] = value; |
---|
1268 | $.ajax( $.extend( true, { |
---|
1269 | url: param, |
---|
1270 | mode: "abort", |
---|
1271 | port: "validate" + element.name, |
---|
1272 | dataType: "json", |
---|
1273 | data: data, |
---|
1274 | context: validator.currentForm, |
---|
1275 | success: function( response ) { |
---|
1276 | var valid = response === true || response === "true", |
---|
1277 | errors, message, submitted; |
---|
1278 | |
---|
1279 | validator.settings.messages[ element.name ].remote = previous.originalMessage; |
---|
1280 | if ( valid ) { |
---|
1281 | submitted = validator.formSubmitted; |
---|
1282 | validator.prepareElement( element ); |
---|
1283 | validator.formSubmitted = submitted; |
---|
1284 | validator.successList.push( element ); |
---|
1285 | delete validator.invalid[ element.name ]; |
---|
1286 | validator.showErrors(); |
---|
1287 | } else { |
---|
1288 | errors = {}; |
---|
1289 | message = response || validator.defaultMessage( element, "remote" ); |
---|
1290 | errors[ element.name ] = previous.message = $.isFunction( message ) ? message( value ) : message; |
---|
1291 | validator.invalid[ element.name ] = true; |
---|
1292 | validator.showErrors( errors ); |
---|
1293 | } |
---|
1294 | previous.valid = valid; |
---|
1295 | validator.stopRequest( element, valid ); |
---|
1296 | } |
---|
1297 | }, param ) ); |
---|
1298 | return "pending"; |
---|
1299 | } |
---|
1300 | |
---|
1301 | } |
---|
1302 | |
---|
1303 | }); |
---|
1304 | |
---|
1305 | $.format = function deprecated() { |
---|
1306 | throw "$.format has been deprecated. Please use $.validator.format instead."; |
---|
1307 | }; |
---|
1308 | |
---|
1309 | // ajax mode: abort |
---|
1310 | // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); |
---|
1311 | // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() |
---|
1312 | |
---|
1313 | var pendingRequests = {}, |
---|
1314 | ajax; |
---|
1315 | // Use a prefilter if available (1.5+) |
---|
1316 | if ( $.ajaxPrefilter ) { |
---|
1317 | $.ajaxPrefilter(function( settings, _, xhr ) { |
---|
1318 | var port = settings.port; |
---|
1319 | if ( settings.mode === "abort" ) { |
---|
1320 | if ( pendingRequests[port] ) { |
---|
1321 | pendingRequests[port].abort(); |
---|
1322 | } |
---|
1323 | pendingRequests[port] = xhr; |
---|
1324 | } |
---|
1325 | }); |
---|
1326 | } else { |
---|
1327 | // Proxy ajax |
---|
1328 | ajax = $.ajax; |
---|
1329 | $.ajax = function( settings ) { |
---|
1330 | var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, |
---|
1331 | port = ( "port" in settings ? settings : $.ajaxSettings ).port; |
---|
1332 | if ( mode === "abort" ) { |
---|
1333 | if ( pendingRequests[port] ) { |
---|
1334 | pendingRequests[port].abort(); |
---|
1335 | } |
---|
1336 | pendingRequests[port] = ajax.apply(this, arguments); |
---|
1337 | return pendingRequests[port]; |
---|
1338 | } |
---|
1339 | return ajax.apply(this, arguments); |
---|
1340 | }; |
---|
1341 | } |
---|
1342 | |
---|
1343 | // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation |
---|
1344 | // handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target |
---|
1345 | |
---|
1346 | $.extend($.fn, { |
---|
1347 | validateDelegate: function( delegate, type, handler ) { |
---|
1348 | return this.bind(type, function( event ) { |
---|
1349 | var target = $(event.target); |
---|
1350 | if ( target.is(delegate) ) { |
---|
1351 | return handler.apply(target, arguments); |
---|
1352 | } |
---|
1353 | }); |
---|
1354 | } |
---|
1355 | }); |
---|
1356 | |
---|
1357 | })); |
---|