github.com/apremalal/vamps-core@v1.0.1-0.20161221121535-d430b56ec174/server/webapps/app/base/plugins/jquery-validation/js/jquery.validate.js (about) 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 }));