vendor: 48,157 bytes, lines 1-1606
1/*! 2 * jQuery Validation Plugin v1.15.0 3 * 4 * http://jqueryvalidation.org/ 5 * 6 * Copyright (c) 2016 Jörn Zaefferer 7 * Released under the MIT license 8 */ 9(function( factory ) { 10 if ( typeof define === "function" && define.amd ) { 11 define( ["jquery"], factory ); 12 } else if (typeof module === "object" && module.exports) { 13 module.exports = factory( require( "jquery" ) ); 14 } else { 15 factory( jQuery ); 16 } 17}(function( $ ) { 18 19$.extend( $.fn, { 20 21 // http://jqueryvalidation.org/validate/ 22 validate: function( options ) { 23 24 // If nothing is selected, return nothing; can't chain anyway 25 if ( !this.length ) { 26 if ( options && options.debug && window.console ) { 27 console.warn( "Nothing selected, can't validate, returning nothing." ); 28 } 29 return; 30 } 31 32 // Check if a validator for this form was already created 33 var validator = $.data( this[ 0 ], "validator" ); 34 if ( validator ) { 35 return validator; 36 } 37 38 // Add novalidate tag if HTML5. 39 this.attr( "novalidate", "novalidate" ); 40 41 validator = new $.validator( options, this[ 0 ] ); 42 $.data( this[ 0 ], "validator", validator ); 43 44 if ( validator.settings.onsubmit ) { 45 46 this.on( "click.validate", ":submit", function( event ) { 47 if ( validator.settings.submitHandler ) { 48 validator.submitButton = event.target; 49 } 50 51 // Allow suppressing validation by adding a cancel class to the submit button 52 if ( $( this ).hasClass( "cancel" ) ) { 53 validator.cancelSubmit = true; 54 } 55 56 // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button 57 if ( $( this ).attr( "formnovalidate" ) !== undefined ) { 58 validator.cancelSubmit = true; 59 } 60 } ); 61 62 // Validate the form on submit 63 this.on( "submit.validate", function( event ) { 64 if ( validator.settings.debug ) { 65 66 // Prevent form submit to be able to see console output 67 event.preventDefault(); 68 } 69 function handle() { 70 var hidden, result; 71 if ( validator.settings.submitHandler ) { 72 if ( validator.submitButton ) { 73 74 // Insert a hidden input as a replacement for the missing submit button 75 hidden = $( "<input type='hidden'/>" ) 76 .attr( "name", validator.submitButton.name ) 77 .val( $( validator.submitButton ).val() ) 78 .appendTo( validator.currentForm ); 79 } 80 result = validator.settings.submitHandler.call( validator, validator.currentForm, event ); 81 if ( validator.submitButton ) { 82 83 // And clean up afterwards; thanks to no-block-scope, hidden can be referenced 84 hidden.remove(); 85 } 86 if ( result !== undefined ) { 87 return result; 88 } 89 return false; 90 } 91 return true; 92 } 93 94 // Prevent submit for invalid forms or custom submit handlers 95 if ( validator.cancelSubmit ) { 96 validator.cancelSubmit = false; 97 return handle(); 98 } 99 if ( validator.form() ) { 100 if ( validator.pendingRequest ) { 101 validator.formSubmitted = true; 102 return false; 103 } 104 return handle(); 105 } else { 106 validator.focusInvalid(); 107 return false; 108 } 109 } ); 110 } 111 112 return validator; 113 }, 114 115 // http://jqueryvalidation.org/valid/ 116 valid: function() { 117 var valid, validator, errorList; 118 119 if ( $( this[ 0 ] ).is( "form" ) ) { 120 valid = this.validate().form(); 121 } else { 122 errorList = []; 123 valid = true; 124 validator = $( this[ 0 ].form ).validate(); 125 this.each( function() { 126 valid = validator.element( this ) && valid; 127 if ( !valid ) { 128 errorList = errorList.concat( validator.errorList ); 129 } 130 } ); 131 validator.errorList = errorList; 132 } 133 return valid; 134 }, 135 136 // http://jqueryvalidation.org/rules/ 137 rules: function( command, argument ) { 138 139 // If nothing is selected, return nothing; can't chain anyway 140 if ( !this.length ) { 141 return; 142 } 143 144 var element = this[ 0 ], 145 settings, staticRules, existingRules, data, param, filtered; 146 147 if ( command ) { 148 settings = $.data( element.form, "validator" ).settings; 149 staticRules = settings.rules; 150 existingRules = $.validator.staticRules( element ); 151 switch ( command ) { 152 case "add": 153 $.extend( existingRules, $.validator.normalizeRule( argument ) ); 154 155 // Remove messages from rules, but allow them to be set separately 156 delete existingRules.messages; 157 staticRules[ element.name ] = existingRules; 158 if ( argument.messages ) { 159 settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages ); 160 } 161 break; 162 case "remove": 163 if ( !argument ) { 164 delete staticRules[ element.name ]; 165 return existingRules; 166 } 167 filtered = {}; 168 $.each( argument.split( /\s/ ), function( index, method ) { 169 filtered[ method ] = existingRules[ method ]; 170 delete existingRules[ method ]; 171 if ( method === "required" ) { 172 $( element ).removeAttr( "aria-required" ); 173 } 174 } ); 175 return filtered; 176 } 177 } 178 179 data = $.validator.normalizeRules( 180 $.extend( 181 {}, 182 $.validator.classRules( element ), 183 $.validator.attributeRules( element ), 184 $.validator.dataRules( element ), 185 $.validator.staticRules( element ) 186 ), element ); 187 188 // Make sure required is at front 189 if ( data.required ) { 190 param = data.required; 191 delete data.required; 192 data = $.extend( { required: param }, data ); 193 $( element ).attr( "aria-required", "true" ); 194 } 195 196 // Make sure remote is at back 197 if ( data.remote ) { 198 param = data.remote; 199 delete data.remote; 200 data = $.extend( data, { remote: param } ); 201 } 202 203 return data; 204 } 205} ); 206 207// Custom selectors 208$.extend( $.expr[ ":" ], { 209 210 // http://jqueryvalidation.org/blank-selector/ 211 blank: function( a ) { 212 return !$.trim( "" + $( a ).val() ); 213 }, 214 215 // http://jqueryvalidation.org/filled-selector/ 216 filled: function( a ) { 217 var val = $( a ).val(); 218 return val !== null && !!$.trim( "" + val ); 219 }, 220 221 // http://jqueryvalidation.org/unchecked-selector/ 222 unchecked: function( a ) { 223 return !$( a ).prop( "checked" ); 224 } 225} ); 226 227// Constructor for validator 228$.validator = function( options, form ) { 229 this.settings = $.extend( true, {}, $.validator.defaults, options ); 230 this.currentForm = form; 231 this.init(); 232}; 233 234// http://jqueryvalidation.org/jQuery.validator.format/ 235$.validator.format = function( source, params ) { 236 if ( arguments.length === 1 ) { 237 return function() { 238 var args = $.makeArray( arguments ); 239 args.unshift( source ); 240 return $.validator.format.apply( this, args ); 241 }; 242 } 243 if ( params === undefined ) { 244 return source; 245 } 246 if ( arguments.length > 2 && params.constructor !== Array ) { 247 params = $.makeArray( arguments ).slice( 1 ); 248 } 249 if ( params.constructor !== Array ) { 250 params = [ params ]; 251 } 252 $.each( params, function( i, n ) { 253 source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() { 254 return n; 255 } ); 256 } ); 257 return source; 258}; 259 260$.extend( $.validator, { 261 262 defaults: { 263 messages: {}, 264 groups: {}, 265 rules: {}, 266 errorClass: "error", 267 pendingClass: "pending", 268 validClass: "valid", 269 errorElement: "label", 270 focusCleanup: false, 271 focusInvalid: true, 272 errorContainer: $( [] ), 273 errorLabelContainer: $( [] ), 274 onsubmit: true, 275 ignore: ":hidden", 276 ignoreTitle: false, 277 onfocusin: function( element ) { 278 this.lastActive = element; 279 280 // Hide error label and remove error class on focus if enabled 281 if ( this.settings.focusCleanup ) { 282 if ( this.settings.unhighlight ) { 283 this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); 284 } 285 this.hideThese( this.errorsFor( element ) ); 286 } 287 }, 288 onfocusout: function( element ) { 289 if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) { 290 this.element( element ); 291 } 292 }, 293 onkeyup: function( element, event ) { 294 295 // Avoid revalidate the field when pressing one of the following keys 296 // Shift => 16 297 // Ctrl => 17 298 // Alt => 18 299 // Caps lock => 20 300 // End => 35 301 // Home => 36 302 // Left arrow => 37 303 // Up arrow => 38 304 // Right arrow => 39 305 // Down arrow => 40 306 // Insert => 45 307 // Num lock => 144 308 // AltGr key => 225 309 var excludedKeys = [ 310 16, 17, 18, 20, 35, 36, 37, 311 38, 39, 40, 45, 144, 225 312 ]; 313 314 if ( event.which === 9 && this.elementValue( element ) === "" || $.inArray( event.keyCode, excludedKeys ) !== -1 ) { 315 return; 316 } else if ( element.name in this.submitted || element.name in this.invalid ) { 317 this.element( element ); 318 } 319 }, 320 onclick: function( element ) { 321 322 // Click on selects, radiobuttons and checkboxes 323 if ( element.name in this.submitted ) { 324 this.element( element ); 325 326 // Or option elements, check parent select in that case 327 } else if ( element.parentNode.name in this.submitted ) { 328 this.element( element.parentNode ); 329 } 330 }, 331 highlight: function( element, errorClass, validClass ) { 332 if ( element.type === "radio" ) { 333 this.findByName( element.name ).addClass( errorClass ).removeClass( validClass ); 334 } else { 335 $( element ).addClass( errorClass ).removeClass( validClass ); 336 } 337 }, 338 unhighlight: function( element, errorClass, validClass ) { 339 if ( element.type === "radio" ) { 340 this.findByName( element.name ).removeClass( errorClass ).addClass( validClass ); 341 } else { 342 $( element ).removeClass( errorClass ).addClass( validClass ); 343 } 344 } 345 }, 346 347 // http://jqueryvalidation.org/jQuery.validator.setDefaults/ 348 setDefaults: function( settings ) { 349 $.extend( $.validator.defaults, settings ); 350 }, 351 352 messages: { 353 required: "This field is required.", 354 remote: "Please fix this field.", 355 email: "Please enter a valid email address.", 356 url: "Please enter a valid URL.", 357 date: "Please enter a valid date.", 358 dateISO: "Please enter a valid date ( ISO ).", 359 number: "Please enter a valid number.", 360 digits: "Please enter only digits.", 361 equalTo: "Please enter the same value again.", 362 maxlength: $.validator.format( "Please enter no more than {0} characters." ), 363 minlength: $.validator.format( "Please enter at least {0} characters." ), 364 rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ), 365 range: $.validator.format( "Please enter a value between {0} and {1}." ), 366 max: $.validator.format( "Please enter a value less than or equal to {0}." ), 367 min: $.validator.format( "Please enter a value greater than or equal to {0}." ), 368 step: $.validator.format( "Please enter a multiple of {0}." ) 369 }, 370 371 autoCreateRanges: false, 372 373 prototype: { 374 375 init: function() { 376 this.labelContainer = $( this.settings.errorLabelContainer ); 377 this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm ); 378 this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer ); 379 this.submitted = {}; 380 this.valueCache = {}; 381 this.pendingRequest = 0; 382 this.pending = {}; 383 this.invalid = {}; 384 this.reset(); 385 386 var groups = ( this.groups = {} ), 387 rules; 388 $.each( this.settings.groups, function( key, value ) { 389 if ( typeof value === "string" ) { 390 value = value.split( /\s/ ); 391 } 392 $.each( value, function( index, name ) { 393 groups[ name ] = key; 394 } ); 395 } ); 396 rules = this.settings.rules; 397 $.each( rules, function( key, value ) { 398 rules[ key ] = $.validator.normalizeRule( value ); 399 } ); 400 401 function delegate( event ) { 402 var validator = $.data( this.form, "validator" ), 403 eventType = "on" + event.type.replace( /^validate/, "" ), 404 settings = validator.settings; 405 if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) { 406 settings[ eventType ].call( validator, this, event ); 407 } 408 } 409 410 $( this.currentForm ) 411 .on( "focusin.validate focusout.validate keyup.validate", 412 ":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " + 413 "[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " + 414 "[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " + 415 "[type='radio'], [type='checkbox'], [contenteditable]", delegate ) 416 417 // Support: Chrome, oldIE 418 // "select" is provided as event.target when clicking a option 419 .on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate ); 420 421 if ( this.settings.invalidHandler ) { 422 $( this.currentForm ).on( "invalid-form.validate", this.settings.invalidHandler ); 423 } 424 425 // Add aria-required to any Static/Data/Class required fields before first validation 426 // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html 427 $( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" ); 428 }, 429 430 // http://jqueryvalidation.org/Validator.form/ 431 form: function() { 432 this.checkForm(); 433 $.extend( this.submitted, this.errorMap ); 434 this.invalid = $.extend( {}, this.errorMap ); 435 if ( !this.valid() ) { 436 $( this.currentForm ).triggerHandler( "invalid-form", [ this ] ); 437 } 438 this.showErrors(); 439 return this.valid(); 440 }, 441 442 checkForm: function() { 443 this.prepareForm(); 444 for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) { 445 this.check( elements[ i ] ); 446 } 447 return this.valid(); 448 }, 449 450 // http://jqueryvalidation.org/Validator.element/ 451 element: function( element ) { 452 var cleanElement = this.clean( element ), 453 checkElement = this.validationTargetFor( cleanElement ), 454 v = this, 455 result = true, 456 rs, group; 457 458 if ( checkElement === undefined ) { 459 delete this.invalid[ cleanElement.name ]; 460 } else { 461 this.prepareElement( checkElement ); 462 this.currentElements = $( checkElement ); 463 464 // If this element is grouped, then validate all group elements already 465 // containing a value 466 group = this.groups[ checkElement.name ]; 467 if ( group ) { 468 $.each( this.groups, function( name, testgroup ) { 469 if ( testgroup === group && name !== checkElement.name ) { 470 cleanElement = v.validationTargetFor( v.clean( v.findByName( name ) ) ); 471 if ( cleanElement && cleanElement.name in v.invalid ) { 472 v.currentElements.push( cleanElement ); 473 result = result && v.check( cleanElement ); 474 } 475 } 476 } ); 477 } 478 479 rs = this.check( checkElement ) !== false; 480 result = result && rs; 481 if ( rs ) { 482 this.invalid[ checkElement.name ] = false; 483 } else { 484 this.invalid[ checkElement.name ] = true; 485 } 486 487 if ( !this.numberOfInvalids() ) { 488 489 // Hide error containers on last error 490 this.toHide = this.toHide.add( this.containers ); 491 } 492 this.showErrors(); 493 494 // Add aria-invalid status for screen readers 495 $( element ).attr( "aria-invalid", !rs ); 496 } 497 498 return result; 499 }, 500 501 // http://jqueryvalidation.org/Validator.showErrors/ 502 showErrors: function( errors ) { 503 if ( errors ) { 504 var validator = this; 505 506 // Add items to error list and map 507 $.extend( this.errorMap, errors ); 508 this.errorList = $.map( this.errorMap, function( message, name ) { 509 return { 510 message: message, 511 element: validator.findByName( name )[ 0 ] 512 }; 513 } ); 514 515 // Remove items from success list 516 this.successList = $.grep( this.successList, function( element ) { 517 return !( element.name in errors ); 518 } ); 519 } 520 if ( this.settings.showErrors ) { 521 this.settings.showErrors.call( this, this.errorMap, this.errorList ); 522 } else { 523 this.defaultShowErrors(); 524 } 525 }, 526 527 // http://jqueryvalidation.org/Validator.resetForm/ 528 resetForm: function() { 529 if ( $.fn.resetForm ) { 530 $( this.currentForm ).resetForm(); 531 } 532 this.invalid = {}; 533 this.submitted = {}; 534 this.prepareForm(); 535 this.hideErrors(); 536 var elements = this.elements() 537 .removeData( "previousValue" ) 538 .removeAttr( "aria-invalid" ); 539 540 this.resetElements( elements ); 541 }, 542 543 resetElements: function( elements ) { 544 var i; 545 546 if ( this.settings.unhighlight ) { 547 for ( i = 0; elements[ i ]; i++ ) { 548 this.settings.unhighlight.call( this, elements[ i ], 549 this.settings.errorClass, "" ); 550 this.findByName( elements[ i ].name ).removeClass( this.settings.validClass ); 551 } 552 } else { 553 elements 554 .removeClass( this.settings.errorClass ) 555 .removeClass( this.settings.validClass ); 556 } 557 }, 558 559 numberOfInvalids: function() { 560 return this.objectLength( this.invalid ); 561 }, 562 563 objectLength: function( obj ) { 564 /* jshint unused: false */ 565 var count = 0, 566 i; 567 for ( i in obj ) { 568 if ( obj[ i ] ) { 569 count++; 570 } 571 } 572 return count; 573 }, 574 575 hideErrors: function() { 576 this.hideThese( this.toHide ); 577 }, 578 579 hideThese: function( errors ) { 580 errors.not( this.containers ).text( "" ); 581 this.addWrapper( errors ).hide(); 582 }, 583 584 valid: function() { 585 return this.size() === 0; 586 }, 587 588 size: function() { 589 return this.errorList.length; 590 }, 591 592 focusInvalid: function() { 593 if ( this.settings.focusInvalid ) { 594 try { 595 $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || [] ) 596 .filter( ":visible" ) 597 .focus() 598 599 // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find 600 .trigger( "focusin" ); 601 } catch ( e ) { 602 603 // Ignore IE throwing errors when focusing hidden elements 604 } 605 } 606 }, 607 608 findLastActive: function() { 609 var lastActive = this.lastActive; 610 return lastActive && $.grep( this.errorList, function( n ) { 611 return n.element.name === lastActive.name; 612 } ).length === 1 && lastActive; 613 }, 614 615 elements: function() { 616 var validator = this, 617 rulesCache = {}; 618 619 // Select all valid inputs inside the form (no submit or reset buttons) 620 return $( this.currentForm ) 621 .find( "input, select, textarea, [contenteditable]" ) 622 .not( ":submit, :reset, :image, :disabled" ) 623 .not( this.settings.ignore ) 624 .filter( function() { 625 var name = this.name || $( this ).attr( "name" ); // For contenteditable 626 if ( !name && validator.settings.debug && window.console ) { 627 console.error( "%o has no name assigned", this ); 628 } 629 630 // Set form expando on contenteditable 631 if ( this.hasAttribute( "contenteditable" ) ) { 632 this.form = $( this ).closest( "form" )[ 0 ]; 633 } 634 635 // Select only the first element for each name, and only those with rules specified 636 if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) { 637 return false; 638 } 639 640 rulesCache[ name ] = true; 641 return true; 642 } ); 643 }, 644 645 clean: function( selector ) { 646 return $( selector )[ 0 ]; 647 }, 648 649 errors: function() { 650 var errorClass = this.settings.errorClass.split( " " ).join( "." ); 651 return $( this.settings.errorElement + "." + errorClass, this.errorContext ); 652 }, 653 654 resetInternals: function() { 655 this.successList = []; 656 this.errorList = []; 657 this.errorMap = {}; 658 this.toShow = $( [] ); 659 this.toHide = $( [] ); 660 }, 661 662 reset: function() { 663 this.resetInternals(); 664 this.currentElements = $( [] ); 665 }, 666 667 prepareForm: function() { 668 this.reset(); 669 this.toHide = this.errors().add( this.containers ); 670 }, 671 672 prepareElement: function( element ) { 673 this.reset(); 674 this.toHide = this.errorsFor( element ); 675 }, 676 677 elementValue: function( element ) { 678 var $element = $( element ), 679 type = element.type, 680 val, idx; 681 682 if ( type === "radio" || type === "checkbox" ) { 683 return this.findByName( element.name ).filter( ":checked" ).val(); 684 } else if ( type === "number" && typeof element.validity !== "undefined" ) { 685 return element.validity.badInput ? "NaN" : $element.val(); 686 } 687 688 if ( element.hasAttribute( "contenteditable" ) ) { 689 val = $element.text(); 690 } else { 691 val = $element.val(); 692 } 693 694 if ( type === "file" ) { 695 696 // Modern browser (chrome & safari) 697 if ( val.substr( 0, 12 ) === "C:\\fakepath\\" ) { 698 return val.substr( 12 ); 699 } 700 701 // Legacy browsers 702 // Unix-based path 703 idx = val.lastIndexOf( "/" ); 704 if ( idx >= 0 ) { 705 return val.substr( idx + 1 ); 706 } 707 708 // Windows-based path 709 idx = val.lastIndexOf( "\\" ); 710 if ( idx >= 0 ) { 711 return val.substr( idx + 1 ); 712 } 713 714 // Just the file name 715 return val; 716 } 717 718 if ( typeof val === "string" ) { 719 return val.replace( /\r/g, "" ); 720 } 721 return val; 722 }, 723 724 check: function( element ) { 725 element = this.validationTargetFor( this.clean( element ) ); 726 727 var rules = $( element ).rules(), 728 rulesCount = $.map( rules, function( n, i ) { 729 return i; 730 } ).length, 731 dependencyMismatch = false, 732 val = this.elementValue( element ), 733 result, method, rule; 734 735 // If a normalizer is defined for this element, then 736 // call it to retreive the changed value instead 737 // of using the real one. 738 // Note that `this` in the normalizer is `element`. 739 if ( typeof rules.normalizer === "function" ) { 740 val = rules.normalizer.call( element, val ); 741 742 if ( typeof val !== "string" ) { 743 throw new TypeError( "The normalizer should return a string value." ); 744 } 745 746 // Delete the normalizer from rules to avoid treating 747 // it as a pre-defined method. 748 delete rules.normalizer; 749 } 750 751 for ( method in rules ) { 752 rule = { method: method, parameters: rules[ method ] }; 753 try { 754 result = $.validator.methods[ method ].call( this, val, element, rule.parameters ); 755 756 // If a method indicates that the field is optional and therefore valid, 757 // don't mark it as valid when there are no other rules 758 if ( result === "dependency-mismatch" && rulesCount === 1 ) { 759 dependencyMismatch = true; 760 continue; 761 } 762 dependencyMismatch = false; 763 764 if ( result === "pending" ) { 765 this.toHide = this.toHide.not( this.errorsFor( element ) ); 766 return; 767 } 768 769 if ( !result ) { 770 this.formatAndAdd( element, rule ); 771 return false; 772 } 773 } catch ( e ) { 774 if ( this.settings.debug && window.console ) { 775 console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); 776 } 777 if ( e instanceof TypeError ) { 778 e.message += ". Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method."; 779 } 780 781 throw e; 782 } 783 } 784 if ( dependencyMismatch ) { 785 return; 786 } 787 if ( this.objectLength( rules ) ) { 788 this.successList.push( element ); 789 } 790 return true; 791 }, 792 793 // Return the custom message for the given element and validation method 794 // specified in the element's HTML5 data attribute 795 // return the generic message if present and no method specific message is present 796 customDataMessage: function( element, method ) { 797 return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() + 798 method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" ); 799 }, 800 801 // Return the custom message for the given element name and validation method 802 customMessage: function( name, method ) { 803 var m = this.settings.messages[ name ]; 804 return m && ( m.constructor === String ? m : m[ method ] ); 805 }, 806 807 // Return the first defined argument, allowing empty strings 808 findDefined: function() { 809 for ( var i = 0; i < arguments.length; i++ ) { 810 if ( arguments[ i ] !== undefined ) { 811 return arguments[ i ]; 812 } 813 } 814 return undefined; 815 }, 816 817 defaultMessage: function( element, rule ) { 818 var message = this.findDefined( 819 this.customMessage( element.name, rule.method ), 820 this.customDataMessage( element, rule.method ), 821 822 // 'title' is never undefined, so handle empty string as undefined 823 !this.settings.ignoreTitle && element.title || undefined, 824 $.validator.messages[ rule.method ], 825 "<strong>Warning: No message defined for " + element.name + "</strong>" 826 ), 827 theregex = /\$?\{(\d+)\}/g; 828 if ( typeof message === "function" ) { 829 message = message.call( this, rule.parameters, element ); 830 } else if ( theregex.test( message ) ) { 831 message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters ); 832 } 833 834 return message; 835 }, 836 837 formatAndAdd: function( element, rule ) { 838 var message = this.defaultMessage( element, rule ); 839 840 this.errorList.push( { 841 message: message, 842 element: element, 843 method: rule.method 844 } ); 845 846 this.errorMap[ element.name ] = message; 847 this.submitted[ element.name ] = message; 848 }, 849 850 addWrapper: function( toToggle ) { 851 if ( this.settings.wrapper ) { 852 toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); 853 } 854 return toToggle; 855 }, 856 857 defaultShowErrors: function() { 858 var i, elements, error; 859 for ( i = 0; this.errorList[ i ]; i++ ) { 860 error = this.errorList[ i ]; 861 if ( this.settings.highlight ) { 862 this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); 863 } 864 this.showLabel( error.element, error.message ); 865 } 866 if ( this.errorList.length ) { 867 this.toShow = this.toShow.add( this.containers ); 868 } 869 if ( this.settings.success ) { 870 for ( i = 0; this.successList[ i ]; i++ ) { 871 this.showLabel( this.successList[ i ] ); 872 } 873 } 874 if ( this.settings.unhighlight ) { 875 for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) { 876 this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass ); 877 } 878 } 879 this.toHide = this.toHide.not( this.toShow ); 880 this.hideErrors(); 881 this.addWrapper( this.toShow ).show(); 882 }, 883 884 validElements: function() { 885 return this.currentElements.not( this.invalidElements() ); 886 }, 887 888 invalidElements: function() { 889 return $( this.errorList ).map( function() { 890 return this.element; 891 } ); 892 }, 893 894 showLabel: function( element, message ) { 895 var place, group, errorID, v, 896 error = this.errorsFor( element ), 897 elementID = this.idOrName( element ), 898 describedBy = $( element ).attr( "aria-describedby" ); 899 900 if ( error.length ) { 901 902 // Refresh error/success class 903 error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); 904 905 // Replace message on existing label 906 error.html( message ); 907 } else { 908 909 // Create error element 910 error = $( "<" + this.settings.errorElement + ">" ) 911 .attr( "id", elementID + "-error" ) 912 .addClass( this.settings.errorClass ) 913 .html( message || "" ); 914 915 // Maintain reference to the element to be placed into the DOM 916 place = error; 917 if ( this.settings.wrapper ) { 918 919 // Make sure the element is visible, even in IE 920 // actually showing the wrapped element is handled elsewhere 921 place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent(); 922 } 923 if ( this.labelContainer.length ) { 924 this.labelContainer.append( place ); 925 } else if ( this.settings.errorPlacement ) { 926 this.settings.errorPlacement( place, $( element ) ); 927 } else { 928 place.insertAfter( element ); 929 } 930 931 // Link error back to the element 932 if ( error.is( "label" ) ) { 933 934 // If the error is a label, then associate using 'for' 935 error.attr( "for", elementID ); 936 937 // If the element is not a child of an associated label, then it's necessary 938 // to explicitly apply aria-describedby 939 } else if ( error.parents( "label[for='" + this.escapeCssMeta( elementID ) + "']" ).length === 0 ) { 940 errorID = error.attr( "id" ); 941 942 // Respect existing non-error aria-describedby 943 if ( !describedBy ) { 944 describedBy = errorID; 945 } else if ( !describedBy.match( new RegExp( "\\b" + this.escapeCssMeta( errorID ) + "\\b" ) ) ) { 946 947 // Add to end of list if not already present 948 describedBy += " " + errorID; 949 } 950 $( element ).attr( "aria-describedby", describedBy ); 951 952 // If this element is grouped, then assign to all elements in the same group 953 group = this.groups[ element.name ]; 954 if ( group ) { 955 v = this; 956 $.each( v.groups, function( name, testgroup ) { 957 if ( testgroup === group ) { 958 $( "[name='" + v.escapeCssMeta( name ) + "']", v.currentForm ) 959 .attr( "aria-describedby", error.attr( "id" ) ); 960 } 961 } ); 962 } 963 } 964 } 965 if ( !message && this.settings.success ) { 966 error.text( "" ); 967 if ( typeof this.settings.success === "string" ) { 968 error.addClass( this.settings.success ); 969 } else { 970 this.settings.success( error, element ); 971 } 972 } 973 this.toShow = this.toShow.add( error ); 974 }, 975 976 errorsFor: function( element ) { 977 var name = this.escapeCssMeta( this.idOrName( element ) ), 978 describer = $( element ).attr( "aria-describedby" ), 979 selector = "label[for='" + name + "'], label[for='" + name + "'] *"; 980 981 // 'aria-describedby' should directly reference the error element 982 if ( describer ) { 983 selector = selector + ", #" + this.escapeCssMeta( describer ) 984 .replace( /\s+/g, ", #" ); 985 } 986 987 return this 988 .errors() 989 .filter( selector ); 990 }, 991 992 // See https://api.jquery.com/category/selectors/, for CSS 993 // meta-characters that should be escaped in order to be used with JQuery 994 // as a literal part of a name/id or any selector. 995 escapeCssMeta: function( string ) { 996 return string.replace( /([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1" ); 997 }, 998 999 idOrName: function( element ) { 1000 return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.id || element.name ); 1001 }, 1002 1003 validationTargetFor: function( element ) { 1004 1005 // If radio/checkbox, validate first element in group instead 1006 if ( this.checkable( element ) ) { 1007 element = this.findByName( element.name ); 1008 } 1009 1010 // Always apply ignore filter 1011 return $( element ).not( this.settings.ignore )[ 0 ]; 1012 }, 1013 1014 checkable: function( element ) { 1015 return ( /radio|checkbox/i ).test( element.type ); 1016 }, 1017 1018 findByName: function( name ) { 1019 return $( this.currentForm ).find( "[name='" + this.escapeCssMeta( name ) + "']" ); 1020 }, 1021 1022 getLength: function( value, element ) { 1023 switch ( element.nodeName.toLowerCase() ) { 1024 case "select": 1025 return $( "option:selected", element ).length; 1026 case "input": 1027 if ( this.checkable( element ) ) { 1028 return this.findByName( element.name ).filter( ":checked" ).length; 1029 } 1030 } 1031 return value.length; 1032 }, 1033 1034 depend: function( param, element ) { 1035 return this.dependTypes[ typeof param ] ? this.dependTypes[ typeof param ]( param, element ) : true; 1036 }, 1037 1038 dependTypes: { 1039 "boolean": function( param ) { 1040 return param; 1041 }, 1042 "string": function( param, element ) { 1043 return !!$( param, element.form ).length; 1044 }, 1045 "function": function( param, element ) { 1046 return param( element ); 1047 } 1048 }, 1049 1050 optional: function( element ) { 1051 var val = this.elementValue( element ); 1052 return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch"; 1053 }, 1054 1055 startRequest: function( element ) { 1056 if ( !this.pending[ element.name ] ) { 1057 this.pendingRequest++; 1058 $( element ).addClass( this.settings.pendingClass ); 1059 this.pending[ element.name ] = true; 1060 } 1061 }, 1062 1063 stopRequest: function( element, valid ) { 1064 this.pendingRequest--; 1065 1066 // Sometimes synchronization fails, make sure pendingRequest is never < 0 1067 if ( this.pendingRequest < 0 ) { 1068 this.pendingRequest = 0; 1069 } 1070 delete this.pending[ element.name ]; 1071 $( element ).removeClass( this.settings.pendingClass ); 1072 if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) { 1073 $( this.currentForm ).submit(); 1074 this.formSubmitted = false; 1075 } else if ( !valid && this.pendingRequest === 0 && this.formSubmitted ) { 1076 $( this.currentForm ).triggerHandler( "invalid-form", [ this ] ); 1077 this.formSubmitted = false; 1078 } 1079 }, 1080 1081 previousValue: function( element, method ) { 1082 return $.data( element, "previousValue" ) || $.data( element, "previousValue", { 1083 old: null, 1084 valid: true, 1085 message: this.defaultMessage( element, { method: method } ) 1086 } ); 1087 }, 1088 1089 // Cleans up all forms and elements, removes validator-specific events 1090 destroy: function() { 1091 this.resetForm(); 1092 1093 $( this.currentForm ) 1094 .off( ".validate" ) 1095 .removeData( "validator" ) 1096 .find( ".validate-equalTo-blur" ) 1097 .off( ".validate-equalTo" ) 1098 .removeClass( "validate-equalTo-blur" ); 1099 } 1100 1101 }, 1102 1103 classRuleSettings: { 1104 required: { required: true }, 1105 email: { email: true }, 1106 url: { url: true }, 1107 date: { date: true }, 1108 dateISO: { dateISO: true }, 1109 number: { number: true }, 1110 digits: { digits: true }, 1111 creditcard: { creditcard: true } 1112 }, 1113 1114 addClassRules: function( className, rules ) { 1115 if ( className.constructor === String ) { 1116 this.classRuleSettings[ className ] = rules; 1117 } else { 1118 $.extend( this.classRuleSettings, className ); 1119 } 1120 }, 1121 1122 classRules: function( element ) { 1123 var rules = {}, 1124 classes = $( element ).attr( "class" ); 1125 1126 if ( classes ) { 1127 $.each( classes.split( " " ), function() { 1128 if ( this in $.validator.classRuleSettings ) { 1129 $.extend( rules, $.validator.classRuleSettings[ this ] ); 1130 } 1131 } ); 1132 } 1133 return rules; 1134 }, 1135 1136 normalizeAttributeRule: function( rules, type, method, value ) { 1137 1138 // Convert the value to a number for number inputs, and for text for backwards compability 1139 // allows type="date" and others to be compared as strings 1140 if ( /min|max|step/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) { 1141 value = Number( value ); 1142 1143 // Support Opera Mini, which returns NaN for undefined minlength 1144 if ( isNaN( value ) ) { 1145 value = undefined; 1146 } 1147 } 1148 1149 if ( value || value === 0 ) { 1150 rules[ method ] = value; 1151 } else if ( type === method && type !== "range" ) { 1152 1153 // Exception: the jquery validate 'range' method 1154 // does not test for the html5 'range' type 1155 rules[ method ] = true; 1156 } 1157 }, 1158 1159 attributeRules: function( element ) { 1160 var rules = {}, 1161 $element = $( element ), 1162 type = element.getAttribute( "type" ), 1163 method, value; 1164 1165 for ( method in $.validator.methods ) { 1166 1167 // Support for <input required> in both html5 and older browsers 1168 if ( method === "required" ) { 1169 value = element.getAttribute( method ); 1170 1171 // Some browsers return an empty string for the required attribute 1172 // and non-HTML5 browsers might have required="" markup 1173 if ( value === "" ) { 1174 value = true; 1175 } 1176 1177 // Force non-HTML5 browsers to return bool 1178 value = !!value; 1179 } else { 1180 value = $element.attr( method ); 1181 } 1182 1183 this.normalizeAttributeRule( rules, type, method, value ); 1184 } 1185 1186 // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs 1187 if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) { 1188 delete rules.maxlength; 1189 } 1190 1191 return rules; 1192 }, 1193 1194 dataRules: function( element ) { 1195 var rules = {}, 1196 $element = $( element ), 1197 type = element.getAttribute( "type" ), 1198 method, value; 1199 1200 for ( method in $.validator.methods ) { 1201 value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() ); 1202 this.normalizeAttributeRule( rules, type, method, value ); 1203 } 1204 return rules; 1205 }, 1206 1207 staticRules: function( element ) { 1208 var rules = {}, 1209 validator = $.data( element.form, "validator" ); 1210 1211 if ( validator.settings.rules ) { 1212 rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {}; 1213 } 1214 return rules; 1215 }, 1216 1217 normalizeRules: function( rules, element ) { 1218 1219 // Handle dependency check 1220 $.each( rules, function( prop, val ) { 1221 1222 // Ignore rule when param is explicitly false, eg. required:false 1223 if ( val === false ) { 1224 delete rules[ prop ]; 1225 return; 1226 } 1227 if ( val.param || val.depends ) { 1228 var keepRule = true; 1229 switch ( typeof val.depends ) { 1230 case "string": 1231 keepRule = !!$( val.depends, element.form ).length; 1232 break; 1233 case "function": 1234 keepRule = val.depends.call( element, element ); 1235 break; 1236 } 1237 if ( keepRule ) { 1238 rules[ prop ] = val.param !== undefined ? val.param : true; 1239 } else { 1240 $.data( element.form, "validator" ).resetElements( $( element ) ); 1241 delete rules[ prop ]; 1242 } 1243 } 1244 } ); 1245 1246 // Evaluate parameters 1247 $.each( rules, function( rule, parameter ) { 1248 rules[ rule ] = $.isFunction( parameter ) && rule !== "normalizer" ? parameter( element ) : parameter; 1249 } ); 1250 1251 // Clean number parameters 1252 $.each( [ "minlength", "maxlength" ], function() { 1253 if ( rules[ this ] ) { 1254 rules[ this ] = Number( rules[ this ] ); 1255 } 1256 } ); 1257 $.each( [ "rangelength", "range" ], function() { 1258 var parts; 1259 if ( rules[ this ] ) { 1260 if ( $.isArray( rules[ this ] ) ) { 1261 rules[ this ] = [ Number( rules[ this ][ 0 ] ), Number( rules[ this ][ 1 ] ) ]; 1262 } else if ( typeof rules[ this ] === "string" ) { 1263 parts = rules[ this ].replace( /[\[\]]/g, "" ).split( /[\s,]+/ ); 1264 rules[ this ] = [ Number( parts[ 0 ] ), Number( parts[ 1 ] ) ]; 1265 } 1266 } 1267 } ); 1268 1269 if ( $.validator.autoCreateRanges ) { 1270 1271 // Auto-create ranges 1272 if ( rules.min != null && rules.max != null ) { 1273 rules.range = [ rules.min, rules.max ]; 1274 delete rules.min; 1275 delete rules.max; 1276 } 1277 if ( rules.minlength != null && rules.maxlength != null ) { 1278 rules.rangelength = [ rules.minlength, rules.maxlength ]; 1279 delete rules.minlength; 1280 delete rules.maxlength; 1281 } 1282 } 1283 1284 return rules; 1285 }, 1286 1287 // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} 1288 normalizeRule: function( data ) { 1289 if ( typeof data === "string" ) { 1290 var transformed = {}; 1291 $.each( data.split( /\s/ ), function() { 1292 transformed[ this ] = true; 1293 } ); 1294 data = transformed; 1295 } 1296 return data; 1297 }, 1298 1299 // http://jqueryvalidation.org/jQuery.validator.addMethod/ 1300 addMethod: function( name, method, message ) { 1301 $.validator.methods[ name ] = method; 1302 $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ]; 1303 if ( method.length < 3 ) { 1304 $.validator.addClassRules( name, $.validator.normalizeRule( name ) ); 1305 } 1306 }, 1307 1308 // http://jqueryvalidation.org/jQuery.validator.methods/ 1309 methods: { 1310 1311 // http://jqueryvalidation.org/required-method/ 1312 required: function( value, element, param ) { 1313 1314 // Check if dependency is met 1315 if ( !this.depend( param, element ) ) { 1316 return "dependency-mismatch"; 1317 } 1318 if ( element.nodeName.toLowerCase() === "select" ) { 1319 1320 // Could be an array for select-multiple or a string, both are fine this way 1321 var val = $( element ).val(); 1322 return val && val.length > 0; 1323 } 1324 if ( this.checkable( element ) ) { 1325 return this.getLength( value, element ) > 0; 1326 } 1327 return value.length > 0; 1328 }, 1329 1330 // http://jqueryvalidation.org/email-method/ 1331 email: function( value, element ) { 1332 1333 // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address 1334 // Retrieved 2014-01-14 1335 // If you have a problem with this implementation, report a bug against the above spec 1336 // Or use custom methods to implement your own email validation 1337 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 ); 1338 }, 1339 1340 // http://jqueryvalidation.org/url-method/ 1341 url: function( value, element ) { 1342 1343 // Copyright (c) 2010-2013 Diego Perini, MIT licensed 1344 // https://gist.github.com/dperini/729294 1345 // see also https://mathiasbynens.be/demo/url-regex 1346 // modified to allow protocol-relative URLs 1347 return this.optional( element ) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value ); 1348 }, 1349 1350 // http://jqueryvalidation.org/date-method/ 1351 date: function( value, element ) { 1352 return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() ); 1353 }, 1354 1355 // http://jqueryvalidation.org/dateISO-method/ 1356 dateISO: function( value, element ) { 1357 return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value ); 1358 }, 1359 1360 // http://jqueryvalidation.org/number-method/ 1361 number: function( value, element ) { 1362 return this.optional( element ) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value ); 1363 }, 1364 1365 // http://jqueryvalidation.org/digits-method/ 1366 digits: function( value, element ) { 1367 return this.optional( element ) || /^\d+$/.test( value ); 1368 }, 1369 1370 // http://jqueryvalidation.org/minlength-method/ 1371 minlength: function( value, element, param ) { 1372 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); 1373 return this.optional( element ) || length >= param; 1374 }, 1375 1376 // http://jqueryvalidation.org/maxlength-method/ 1377 maxlength: function( value, element, param ) { 1378 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); 1379 return this.optional( element ) || length <= param; 1380 }, 1381 1382 // http://jqueryvalidation.org/rangelength-method/ 1383 rangelength: function( value, element, param ) { 1384 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); 1385 return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] ); 1386 }, 1387 1388 // http://jqueryvalidation.org/min-method/ 1389 min: function( value, element, param ) { 1390 return this.optional( element ) || value >= param; 1391 }, 1392 1393 // http://jqueryvalidation.org/max-method/ 1394 max: function( value, element, param ) { 1395 return this.optional( element ) || value <= param; 1396 }, 1397 1398 // http://jqueryvalidation.org/range-method/ 1399 range: function( value, element, param ) { 1400 return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] ); 1401 }, 1402 1403 // http://jqueryvalidation.org/step-method/ 1404 step: function( value, element, param ) { 1405 var type = $( element ).attr( "type" ), 1406 errorMessage = "Step attribute on input type " + type + " is not supported.", 1407 supportedTypes = [ "text", "number", "range" ], 1408 re = new RegExp( "\\b" + type + "\\b" ), 1409 notSupported = type && !re.test( supportedTypes.join() ); 1410 1411 // Works only for text, number and range input types 1412 // TODO find a way to support input types date, datetime, datetime-local, month, time and week 1413 if ( notSupported ) { 1414 throw new Error( errorMessage ); 1415 } 1416 return this.optional( element ) || ( value % param === 0 ); 1417 }, 1418 1419 // http://jqueryvalidation.org/equalTo-method/ 1420 equalTo: function( value, element, param ) { 1421 1422 // Bind to the blur event of the target in order to revalidate whenever the target field is updated 1423 var target = $( param ); 1424 if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) { 1425 target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() { 1426 $( element ).valid(); 1427 } ); 1428 } 1429 return value === target.val(); 1430 }, 1431 1432 // http://jqueryvalidation.org/remote-method/ 1433 remote: function( value, element, param, method ) { 1434 if ( this.optional( element ) ) { 1435 return "dependency-mismatch"; 1436 } 1437 1438 method = typeof method === "string" && method || "remote"; 1439 1440 var previous = this.previousValue( element, method ), 1441 validator, data, optionDataString; 1442 1443 if ( !this.settings.messages[ element.name ] ) { 1444 this.settings.messages[ element.name ] = {}; 1445 } 1446 previous.originalMessage = previous.originalMessage || this.settings.messages[ element.name ][ method ]; 1447 this.settings.messages[ element.name ][ method ] = previous.message; 1448 1449 param = typeof param === "string" && { url: param } || param; 1450 optionDataString = $.param( $.extend( { data: value }, param.data ) ); 1451 if ( previous.old === optionDataString ) { 1452 return previous.valid; 1453 } 1454 1455 previous.old = optionDataString; 1456 validator = this; 1457 this.startRequest( element ); 1458 data = {}; 1459 data[ element.name ] = value; 1460 $.ajax( $.extend( true, { 1461 mode: "abort", 1462 port: "validate" + element.name, 1463 dataType: "json", 1464 data: data, 1465 context: validator.currentForm, 1466 success: function( response ) { 1467 var valid = response === true || response === "true", 1468 errors, message, submitted; 1469 1470 validator.settings.messages[ element.name ][ method ] = previous.originalMessage; 1471 if ( valid ) { 1472 submitted = validator.formSubmitted; 1473 validator.resetInternals(); 1474 validator.toHide = validator.errorsFor( element ); 1475 validator.formSubmitted = submitted; 1476 validator.successList.push( element ); 1477 validator.invalid[ element.name ] = false; 1478 validator.showErrors(); 1479 } else { 1480 errors = {}; 1481 message = response || validator.defaultMessage( element, { method: method, parameters: value } ); 1482 errors[ element.name ] = previous.message = message; 1483 validator.invalid[ element.name ] = true; 1484 validator.showErrors( errors ); 1485 } 1486 previous.valid = valid; 1487 validator.stopRequest( element, valid ); 1488 } 1489 }, param ) ); 1490 return "pending"; 1491 } 1492 } 1493 1494} ); 1495 1496// Ajax mode: abort 1497// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); 1498// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() 1499 1500var pendingRequests = {}, 1501 ajax; 1502 1503// Use a prefilter if available (1.5+) 1504if ( $.ajaxPrefilter ) { 1505 $.ajaxPrefilter( function( settings, _, xhr ) { 1506 var port = settings.port; 1507 if ( settings.mode === "abort" ) { 1508 if ( pendingRequests[ port ] ) { 1509 pendingRequests[ port ].abort(); 1510 } 1511 pendingRequests[ port ] = xhr; 1512 } 1513 } ); 1514} else { 1515 1516 // Proxy ajax 1517 ajax = $.ajax; 1518 $.ajax = function( settings ) { 1519 var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, 1520 port = ( "port" in settings ? settings : $.ajaxSettings ).port; 1521 if ( mode === "abort" ) { 1522 if ( pendingRequests[ port ] ) { 1523 pendingRequests[ port ].abort(); 1524 } 1525 pendingRequests[ port ] = ajax.apply( this, arguments ); 1526 return pendingRequests[ port ]; 1527 } 1528 return ajax.apply( this, arguments ); 1529 }; 1530} 1531 1532})); 1533 1534 1535 1536 1537 1538 1539/*! 1540 * jQuery Form Plugin 1541 * version: 3.20 (20-NOV-2012) 1542 * @requires jQuery v1.5 or later 1543 * 1544 * Examples and documentation at: http://malsup.com/jquery/form/ 1545 * Project repository: https://github.com/malsup/form 1546 * Dual licensed under the MIT and GPL licenses: 1547 * http://malsup.github.com/mit-license.txt 1548 * http://malsup.github.com/gpl-license-v2.txt 1549 */ 1550/*global ActiveXObject alert */ 1551;(function($) { 1552"use strict"; 1553 1554/* 1555 Usage Note: 1556 ----------- 1557 Do not use both ajaxSubmit and ajaxForm on the same form. These 1558 functions are mutually exclusive. Use ajaxSubmit if you want 1559 to bind your own submit handler to the form. For example, 1560 1561 $(document).ready(function() { 1562 $('#myForm').on('submit', function(e) { 1563 e.preventDefault(); // <-- important 1564 $(this).ajaxSubmit({ 1565 target: '#output' 1566 }); 1567 }); 1568 }); 1569 1570 Use ajaxForm when you want the plugin to manage all the event binding 1571 for you. For example, 1572 1573 $(document).ready(function() { 1574 $('#myForm').ajaxForm({ 1575 target: '#output' 1576 }); 1577 }); 1578 1579 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 1580 form does not have to exist when you invoke ajaxForm: 1581 1582 $('#myForm').ajaxForm({ 1583 delegation: true, 1584 target: '#output' 1585 }); 1586 1587 When using ajaxForm, the ajaxSubmit function will be invoked for you 1588 at the appropriate time. 1589*/ 1590 1591/** 1592 * Feature detection 1593 */ 1594var feature = {}; 1595feature.fileapi = $("<input type='file'/>").get(0).files !== undefined; 1596feature.formdata = window.FormData !== undefined; 1597 1598/** 1599 * ajaxSubmit() provides a mechanism for immediately submitting 1600 * an HTML form using AJAX. 1601 */ 1602$.fn.ajaxSubmit = function(options) { 1603 /*jshint scripturl:true */ 1604 1605 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 1606 if (!this.length) {
1607 log('ajaxSubmit: skipping submit process - no element selected'); 1608 return this; 1609 } 1610 1611 var method, action, url, $form = this; 1612 1613 if (typeof options == 'function') { 1614 options = { success: options }; 1615 } 1616 1617 method = this.attr('method'); 1618 action = this.attr('action'); 1619 url = (typeof action === 'string') ? $.trim(action) : ''; 1620 url = url || window.location.href || ''; 1621 if (url) { 1622 // clean url (don't include hash vaue) 1623 url = (url.match(/^([^#]+)/)||[])[1]; 1624 } 1625 1626 options = $.extend(true, { 1627 url: url, 1628 success: $.ajaxSettings.success, 1629 type: method || 'GET', 1630 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' 1631 }, options); 1632 1633 // hook for manipulating the form data before it is extracted; 1634 // convenient for use with rich editors like tinyMCE or FCKEditor 1635 var veto = {}; 1636 this.trigger('form-pre-serialize', [this, options, veto]); 1637 if (veto.veto) { 1638 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 1639 return this; 1640 } 1641 1642 // provide opportunity to alter form data before it is serialized 1643 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 1644 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 1645 return this; 1646 } 1647 1648 var traditional = options.traditional; 1649 if ( traditional === undefined ) { 1650 traditional = $.ajaxSettings.traditional; 1651 } 1652 1653 var elements = []; 1654 var qx, a = this.formToArray(options.semantic, elements); 1655 if (options.data) { 1656 options.extraData = options.data; 1657 qx = $.param(options.data, traditional); 1658 } 1659 1660 // give pre-submit callback an opportunity to abort the submit 1661 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 1662 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 1663 return this; 1664 } 1665 1666 // fire vetoable 'validate' event 1667 this.trigger('form-submit-validate', [a, this, options, veto]); 1668 if (veto.veto) { 1669 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 1670 return this; 1671 } 1672 1673 var q = $.param(a, traditional); 1674 if (qx) { 1675 q = ( q ? (q + '&' + qx) : qx ); 1676 } 1677 if (options.type.toUpperCase() == 'GET') { 1678 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 1679 options.data = null; // data is null for 'get' 1680 } 1681 else { 1682 options.data = q; // data is the query string for 'post' 1683 } 1684 1685 var callbacks = []; 1686 if (options.resetForm) { 1687 callbacks.push(function() { $form.resetForm(); }); 1688 } 1689 if (options.clearForm) { 1690 callbacks.push(function() { $form.clearForm(options.includeHidden); }); 1691 } 1692 1693 // perform a load on the target only if dataType is not provided 1694 if (!options.dataType && options.target) { 1695 var oldSuccess = options.success || function(){}; 1696 callbacks.push(function(data) { 1697 var fn = options.replaceTarget ? 'replaceWith' : 'html'; 1698 $(options.target)[fn](data).each(oldSuccess, arguments); 1699 }); 1700 } 1701 else if (options.success) { 1702 callbacks.push(options.success); 1703 } 1704 1705 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 1706 var context = options.context || this ; // jQuery 1.4+ supports scope context 1707 for (var i=0, max=callbacks.length; i < max; i++) { 1708 callbacks[i].apply(context, [data, status, xhr || $form, $form]); 1709 } 1710 }; 1711 1712 // are there files to upload? 1713 1714 // [value] (issue #113), also see comment: 1715 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 1716 var fileInputs = $('input[type=file]:enabled[value!=""]', this); 1717 1718 var hasFileInputs = fileInputs.length > 0; 1719 var mp = 'multipart/form-data'; 1720 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 1721 1722 var fileAPI = feature.fileapi && feature.formdata; 1723 log("fileAPI :" + fileAPI); 1724 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 1725 1726 var jqxhr; 1727 1728 // options.iframe allows user to force iframe mode 1729 // 06-NOV-09: now defaulting to iframe mode if file input is detected 1730 if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 1731 // hack to fix Safari hang (thanks to Tim Molendijk for this) 1732 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 1733 if (options.closeKeepAlive) { 1734 $.get(options.closeKeepAlive, function() { 1735 jqxhr = fileUploadIframe(a); 1736 }); 1737 } 1738 else { 1739 jqxhr = fileUploadIframe(a); 1740 } 1741 } 1742 else if ((hasFileInputs || multipart) && fileAPI) { 1743 jqxhr = fileUploadXhr(a); 1744 } 1745 else { 1746 jqxhr = $.ajax(options); 1747 } 1748 1749 $form.removeData('jqxhr').data('jqxhr', jqxhr); 1750 1751 // clear element array 1752 for (var k=0; k < elements.length; k++) 1753 elements[k] = null; 1754 1755 // fire 'notify' event 1756 this.trigger('form-submit-notify', [this, options]); 1757 return this; 1758 1759 // utility fn for deep serialization 1760 function deepSerialize(extraData){ 1761 var serialized = $.param(extraData).split('&'); 1762 var len = serialized.length; 1763 var result = {}; 1764 var i, part; 1765 for (i=0; i < len; i++) { 1766 part = serialized[i].split('=');
1767 result[decodeURIComponent(part[0])] = decodeURIComponent(part[1]); 1768 } 1769 return result; 1770 } 1771 1772 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 1773 function fileUploadXhr(a) { 1774 var formdata = new FormData(); 1775 1776 for (var i=0; i < a.length; i++) { 1777 formdata.append(a[i].name, a[i].value); 1778 } 1779 1780 if (options.extraData) { 1781 var serializedData = deepSerialize(options.extraData); 1782 for (var p in serializedData) 1783 if (serializedData.hasOwnProperty(p)) 1784 formdata.append(p, serializedData[p]); 1785 } 1786 1787 options.data = null; 1788 1789 var s = $.extend(true, {}, $.ajaxSettings, options, { 1790 contentType: false, 1791 processData: false, 1792 cache: false, 1793 type: method || 'POST' 1794 }); 1795 1796 if (options.uploadProgress) { 1797 // workaround because jqXHR does not expose upload property 1798 s.xhr = function() { 1799 var xhr = jQuery.ajaxSettings.xhr(); 1800 if (xhr.upload) { 1801 xhr.upload.onprogress = function(event) { 1802 var percent = 0; 1803 var position = event.loaded || event.position; /*event.position is deprecated*/ 1804 var total = event.total; 1805 if (event.lengthComputable) { 1806 percent = Math.ceil(position / total * 100); 1807 } 1808 options.uploadProgress(event, position, total, percent); 1809 }; 1810 } 1811 return xhr; 1812 }; 1813 } 1814 1815 s.data = null; 1816 var beforeSend = s.beforeSend; 1817 s.beforeSend = function(xhr, o) { 1818 o.data = formdata; 1819 if(beforeSend) 1820 beforeSend.call(this, xhr, o); 1821 }; 1822 return $.ajax(s); 1823 } 1824 1825 // private function for handling file uploads (hat tip to YAHOO!) 1826 function fileUploadIframe(a) { 1827 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 1828 var useProp = !!$.fn.prop; 1829 var deferred = $.Deferred(); 1830 1831 if ($('[name=submit],[id=submit]', form).length) { 1832 // if there is an input with a name or id of 'submit' then we won't be 1833 // able to invoke the submit fn on the form (at least not x-browser) 1834 alert('Error: Form elements must not have name or id of "submit".'); 1835 deferred.reject(); 1836 return deferred; 1837 } 1838 1839 if (a) { 1840 // ensure that every serialized input is still enabled 1841 for (i=0; i < elements.length; i++) { 1842 el = $(elements[i]); 1843 if ( useProp ) 1844 el.prop('disabled', false); 1845 else 1846 el.removeAttr('disabled'); 1847 } 1848 } 1849 1850 s = $.extend(true, {}, $.ajaxSettings, options); 1851 s.context = s.context || s; 1852 id = 'jqFormIO' + (new Date().getTime()); 1853 if (s.iframeTarget) { 1854 $io = $(s.iframeTarget); 1855 n = $io.attr('name'); 1856 if (!n) 1857 $io.attr('name', id); 1858 else 1859 id = n; 1860 } 1861 else { 1862 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); 1863 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 1864 } 1865 io = $io[0]; 1866 1867 1868 xhr = { // mock object 1869 aborted: 0, 1870 responseText: null, 1871 responseXML: null, 1872 status: 0, 1873 statusText: 'n/a', 1874 getAllResponseHeaders: function() {}, 1875 getResponseHeader: function() {}, 1876 setRequestHeader: function() {}, 1877 abort: function(status) { 1878 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 1879 log('aborting upload... ' + e); 1880 this.aborted = 1; 1881 // #214 1882 if (io.contentWindow.document.execCommand) { 1883 try { // #214 1884 io.contentWindow.document.execCommand('Stop'); 1885 } catch(ignore) {} 1886 } 1887 $io.attr('src', s.iframeSrc); // abort op in progress 1888 xhr.error = e; 1889 if (s.error) 1890 s.error.call(s.context, xhr, e, status); 1891 if (g) 1892 $.event.trigger("ajaxError", [xhr, s, e]); 1893 if (s.complete) 1894 s.complete.call(s.context, xhr, e); 1895 } 1896 }; 1897 1898 g = s.global;
1899 // trigger ajax global events so that activity/block indicators work like normal 1900 if (g && 0 === $.active++) { 1901 $.event.trigger("ajaxStart"); 1902 } 1903 if (g) { 1904 $.event.trigger("ajaxSend", [xhr, s]); 1905 } 1906 1907 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 1908 if (s.global) { 1909 $.active--; 1910 } 1911 deferred.reject(); 1912 return deferred; 1913 } 1914 if (xhr.aborted) { 1915 deferred.reject(); 1916 return deferred; 1917 } 1918 1919 // add submitting element to data if we know it 1920 sub = form.clk; 1921 if (sub) { 1922 n = sub.name; 1923 if (n && !sub.disabled) { 1924 s.extraData = s.extraData || {}; 1925 s.extraData[n] = sub.value; 1926 if (sub.type == "image") { 1927 s.extraData[n+'.x'] = form.clk_x; 1928 s.extraData[n+'.y'] = form.clk_y; 1929 } 1930 } 1931 } 1932 1933 var CLIENT_TIMEOUT_ABORT = 1; 1934 var SERVER_ABORT = 2; 1935 1936 function getDoc(frame) { 1937 var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document; 1938 return doc; 1939 } 1940 1941 // Rails CSRF hack (thanks to Yvan Barthelemy) 1942 var csrf_token = $('meta[name=csrf-token]').attr('content'); 1943 var csrf_param = $('meta[name=csrf-param]').attr('content'); 1944 if (csrf_param && csrf_token) { 1945 s.extraData = s.extraData || {}; 1946 s.extraData[csrf_param] = csrf_token; 1947 } 1948 1949 // take a breath so that pending repaints get some cpu time before the upload starts 1950 function doSubmit() { 1951 // make sure form attrs are set 1952 var t = $form.attr('target'), a = $form.attr('action'); 1953 1954 // update form attrs in IE friendly way 1955 form.setAttribute('target',id); 1956 if (!method) { 1957 form.setAttribute('method', 'POST'); 1958 } 1959 if (a != s.url) { 1960 form.setAttribute('action', s.url); 1961 } 1962 1963 // ie borks in some cases when setting encoding 1964 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) { 1965 $form.attr({ 1966 encoding: 'multipart/form-data', 1967 enctype: 'multipart/form-data' 1968 }); 1969 } 1970 1971 // support timout 1972 if (s.timeout) { 1973 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); 1974 } 1975 1976 // look for server aborts 1977 function checkState() { 1978 try { 1979 var state = getDoc(io).readyState; 1980 log('state = ' + state); 1981 if (state && state.toLowerCase() == 'uninitialized') 1982 setTimeout(checkState,50); 1983 } 1984 catch(e) { 1985 log('Server abort: ' , e, ' (', e.name, ')'); 1986 cb(SERVER_ABORT); 1987 if (timeoutHandle) 1988 clearTimeout(timeoutHandle); 1989 timeoutHandle = undefined; 1990 } 1991 } 1992 1993 // add "extra" data to form if provided in options 1994 var extraInputs = []; 1995 try { 1996 if (s.extraData) { 1997 for (var n in s.extraData) { 1998 if (s.extraData.hasOwnProperty(n)) { 1999 // if using the $.param format that allows for multiple values with the same name 2000 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { 2001 extraInputs.push( 2002 $('<input type="hidden" name="'+s.extraData[n].name+'">').attr('value',s.extraData[n].value) 2003 .appendTo(form)[0]); 2004 } else { 2005 extraInputs.push( 2006 $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n]) 2007 .appendTo(form)[0]); 2008 } 2009 } 2010 } 2011 } 2012 2013 if (!s.iframeTarget) { 2014 // add iframe to doc and submit the form 2015 $io.appendTo('body'); 2016 if (io.attachEvent) 2017 io.attachEvent('onload', cb); 2018 else 2019 io.addEventListener('load', cb, false); 2020 } 2021 setTimeout(checkState,15); 2022 form.submit(); 2023 } 2024 finally { 2025 // reset attrs and remove "extra" input elements 2026 form.setAttribute('action',a); 2027 if(t) { 2028 form.setAttribute('target', t); 2029 } else { 2030 $form.removeAttr('target'); 2031 } 2032 $(extraInputs).remove(); 2033 } 2034 } 2035 2036 if (s.forceSync) { 2037 doSubmit(); 2038 } 2039 else { 2040 setTimeout(doSubmit, 10); // this lets dom updates render 2041 } 2042 2043 var data, doc, domCheckCount = 50, callbackProcessed; 2044 2045 function cb(e) { 2046 if (xhr.aborted || callbackProcessed) { 2047 return; 2048 } 2049 try { 2050 doc = getDoc(io); 2051 } 2052 catch(ex) { 2053 log('cannot access response document: ', ex); 2054 e = SERVER_ABORT; 2055 } 2056 if (e === CLIENT_TIMEOUT_ABORT && xhr) { 2057 xhr.abort('timeout'); 2058 deferred.reject(xhr, 'timeout'); 2059 return; 2060 } 2061 else if (e == SERVER_ABORT && xhr) { 2062 xhr.abort('server abort'); 2063 deferred.reject(xhr, 'error', 'server abort'); 2064 return; 2065 } 2066 2067 if (!doc || doc.location.href == s.iframeSrc) { 2068 // response not received yet 2069 if (!timedOut) 2070 return; 2071 } 2072 if (io.detachEvent) 2073 io.detachEvent('onload', cb); 2074 else 2075 io.removeEventListener('load', cb, false); 2076 2077 var status = 'success', errMsg; 2078 try { 2079 if (timedOut) { 2080 throw 'timeout'; 2081 } 2082 2083 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 2084 log('isXml='+isXml); 2085 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 2086 if (--domCheckCount) { 2087 // in some browsers (Opera) the iframe DOM is not always traversable when 2088 // the onload callback fires, so we loop a bit to accommodate 2089 log('requeing onLoad callback, DOM not available'); 2090 setTimeout(cb, 250); 2091 return; 2092 } 2093 // let this fall through because server response could be an empty document 2094 //log('Could not access iframe DOM after mutiple tries.'); 2095 //throw 'DOMException: not available'; 2096 } 2097 2098 //log('response detected'); 2099 var docRoot = doc.body ? doc.body : doc.documentElement; 2100 xhr.responseText = docRoot ? docRoot.innerHTML : null; 2101 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 2102 if (isXml) 2103 s.dataType = 'xml'; 2104 xhr.getResponseHeader = function(header){ 2105 var headers = {'content-type': s.dataType}; 2106 return headers[header]; 2107 }; 2108 // support for XHR 'status' & 'statusText' emulation : 2109 if (docRoot) { 2110 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status; 2111 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 2112 } 2113 2114 var dt = (s.dataType || '').toLowerCase(); 2115 var scr = /(json|script|text)/.test(dt); 2116 if (scr || s.textarea) { 2117 // see if user embedded response in textarea 2118 var ta = doc.getElementsByTagName('textarea')[0]; 2119 if (ta) { 2120 xhr.responseText = ta.value; 2121 // support for XHR 'status' & 'statusText' emulation : 2122 xhr.status = Number( ta.getAttribute('status') ) || xhr.status; 2123 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 2124 } 2125 else if (scr) { 2126 // account for browsers injecting pre around json response 2127 var pre = doc.getElementsByTagName('pre')[0]; 2128 var b = doc.getElementsByTagName('body')[0]; 2129 if (pre) { 2130 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 2131 } 2132 else if (b) { 2133 xhr.responseText = b.textContent ? b.textContent : b.innerText; 2134 } 2135 } 2136 } 2137 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { 2138 xhr.responseXML = toXml(xhr.responseText); 2139 } 2140 2141 try { 2142 data = httpData(xhr, dt, s); 2143 } 2144 catch (e) { 2145 status = 'parsererror'; 2146 xhr.error = errMsg = (e || status); 2147 } 2148 } 2149 catch (e) { 2150 log('error caught: ',e); 2151 status = 'error'; 2152 xhr.error = errMsg = (e || status); 2153 } 2154 2155 if (xhr.aborted) { 2156 log('upload aborted'); 2157 status = null; 2158 } 2159 2160 if (xhr.status) { // we've set xhr.status 2161 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; 2162 } 2163 2164 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 2165 if (status === 'success') { 2166 if (s.success) 2167 s.success.call(s.context, data, 'success', xhr); 2168 deferred.resolve(xhr.responseText, 'success', xhr); 2169 if (g) 2170 $.event.trigger("ajaxSuccess", [xhr, s]); 2171 } 2172 else if (status) { 2173 if (errMsg === undefined) 2174 errMsg = xhr.statusText; 2175 if (s.error) 2176 s.error.call(s.context, xhr, status, errMsg); 2177 deferred.reject(xhr, 'error', errMsg); 2178 if (g) 2179 $.event.trigger("ajaxError", [xhr, s, errMsg]); 2180 } 2181 2182 if (g)
2183 $.event.trigger("ajaxComplete", [xhr, s]); 2184 2185 if (g && ! --$.active) { 2186 $.event.trigger("ajaxStop"); 2187 } 2188 2189 if (s.complete) 2190 s.complete.call(s.context, xhr, status); 2191 2192 callbackProcessed = true; 2193 if (s.timeout) 2194 clearTimeout(timeoutHandle); 2195 2196 // clean up 2197 setTimeout(function() { 2198 if (!s.iframeTarget) 2199 $io.remove(); 2200 xhr.responseXML = null; 2201 }, 100); 2202 } 2203 2204 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 2205 if (window.ActiveXObject) { 2206 doc = new ActiveXObject('Microsoft.XMLDOM'); 2207 doc.async = 'false'; 2208 doc.loadXML(s); 2209 } 2210 else { 2211 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 2212 } 2213 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; 2214 }; 2215 var parseJSON = $.parseJSON || function(s) { 2216 /*jslint evil:true */ 2217 return window['eval']('(' + s + ')'); 2218 }; 2219 2220 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 2221 2222 var ct = xhr.getResponseHeader('content-type') || '', 2223 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, 2224 data = xml ? xhr.responseXML : xhr.responseText; 2225 2226 if (xml && data.documentElement.nodeName === 'parsererror') { 2227 if ($.error) 2228 $.error('parsererror'); 2229 } 2230 if (s && s.dataFilter) { 2231 data = s.dataFilter(data, type); 2232 } 2233 if (typeof data === 'string') { 2234 if (type === 'json' || !type && ct.indexOf('json') >= 0) { 2235 data = parseJSON(data); 2236 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { 2237 $.globalEval(data); 2238 } 2239 } 2240 return data; 2241 }; 2242 2243 return deferred; 2244 } 2245}; 2246 2247/** 2248 * ajaxForm() provides a mechanism for fully automating form submission. 2249 * 2250 * The advantages of using this method instead of ajaxSubmit() are: 2251 * 2252 * 1: This method will include coordinates for <input type="image" /> elements (if the element 2253 * is used to submit the form). 2254 * 2. This method will include the submit element's name/value data (for the element that was 2255 * used to submit the form). 2256 * 3. This method binds the submit() method to the form for you. 2257 * 2258 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 2259 * passes the options argument along after properly binding events for submit elements and 2260 * the form itself. 2261 */ 2262$.fn.ajaxForm = function(options) { 2263 options = options || {}; 2264 options.delegation = options.delegation && $.isFunction($.fn.on); 2265 2266 // in jQuery 1.3+ we can fix mistakes with the ready state 2267 if (!options.delegation && this.length === 0) { 2268 var o = { s: this.selector, c: this.context }; 2269 if (!$.isReady && o.s) { 2270 log('DOM not ready, queuing ajaxForm'); 2271 $(function() { 2272 $(o.s,o.c).ajaxForm(options); 2273 }); 2274 return this; 2275 } 2276 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 2277 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 2278 return this; 2279 } 2280 2281 if ( options.delegation ) { 2282 $(document) 2283 .off('submit.form-plugin', this.selector, doAjaxSubmit) 2284 .off('click.form-plugin', this.selector, captureSubmittingElement) 2285 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 2286 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 2287 return this; 2288 } 2289 2290 return this.ajaxFormUnbind() 2291 .bind('submit.form-plugin', options, doAjaxSubmit) 2292 .bind('click.form-plugin', options, captureSubmittingElement); 2293}; 2294 2295// private event handlers 2296function doAjaxSubmit(e) { 2297 /*jshint validthis:true */ 2298 var options = e.data; 2299 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 2300 e.preventDefault(); 2301 $(this).ajaxSubmit(options); 2302 } 2303} 2304 2305function captureSubmittingElement(e) { 2306 /*jshint validthis:true */ 2307 var target = e.target; 2308 var $el = $(target); 2309 if (!($el.is("[type=submit],[type=image]"))) { 2310 // is this a child element of the submit el? (ex: a span within a button) 2311 var t = $el.closest('[type=submit]'); 2312 if (t.length === 0) { 2313 return; 2314 } 2315 target = t[0]; 2316 } 2317 var form = this; 2318 form.clk = target; 2319 if (target.type == 'image') { 2320 if (e.offsetX !== undefined) { 2321 form.clk_x = e.offsetX; 2322 form.clk_y = e.offsetY; 2323 } else if (typeof $.fn.offset == 'function') { 2324 var offset = $el.offset(); 2325 form.clk_x = e.pageX - offset.left; 2326 form.clk_y = e.pageY - offset.top; 2327 } else { 2328 form.clk_x = e.pageX - target.offsetLeft; 2329 form.clk_y = e.pageY - target.offsetTop; 2330 } 2331 } 2332 // clear form vars 2333 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); 2334} 2335 2336 2337// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 2338$.fn.ajaxFormUnbind = function() { 2339 return this.unbind('submit.form-plugin click.form-plugin'); 2340}; 2341 2342/** 2343 * formToArray() gathers form element data into an array of objects that can 2344 * be passed to any of the following ajax functions: $.get, $.post, or load. 2345 * Each object in the array has both a 'name' and 'value' property. An example of 2346 * an array for a simple login form might be: 2347 * 2348 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 2349 * 2350 * It is this array that is passed to pre-submit callback functions provided to the 2351 * ajaxSubmit() and ajaxForm() methods. 2352 */ 2353$.fn.formToArray = function(semantic, elements) { 2354 var a = []; 2355 if (this.length === 0) { 2356 return a; 2357 } 2358 2359 var form = this[0]; 2360 var els = semantic ? form.getElementsByTagName('*') : form.elements; 2361 if (!els) { 2362 return a; 2363 } 2364 2365 var i,j,n,v,el,max,jmax; 2366 for(i=0, max=els.length; i < max; i++) { 2367 el = els[i]; 2368 n = el.name; 2369 if (!n) { 2370 continue; 2371 } 2372 2373 if (semantic && form.clk && el.type == "image") { 2374 // handle image inputs on the fly when semantic == true 2375 if(!el.disabled && form.clk == el) { 2376 a.push({name: n, value: $(el).val(), type: el.type }); 2377 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 2378 } 2379 continue; 2380 } 2381 2382 v = $.fieldValue(el, true); 2383 if (v && v.constructor == Array) { 2384 if (elements) 2385 elements.push(el); 2386 for(j=0, jmax=v.length; j < jmax; j++) { 2387 a.push({name: n, value: v[j]}); 2388 } 2389 } 2390 else if (feature.fileapi && el.type == 'file' && !el.disabled) { 2391 if (elements) 2392 elements.push(el); 2393 var files = el.files; 2394 if (files.length) { 2395 for (j=0; j < files.length; j++) { 2396 a.push({name: n, value: files[j], type: el.type}); 2397 } 2398 } 2399 else { 2400 // #180 2401 a.push({ name: n, value: '', type: el.type }); 2402 } 2403 } 2404 else if (v !== null && typeof v != 'undefined') { 2405 if (elements) 2406 elements.push(el); 2407 a.push({name: n, value: v, type: el.type, required: el.required}); 2408 } 2409 } 2410 2411 if (!semantic && form.clk) { 2412 // input type=='image' are not found in elements array! handle it here 2413 var $input = $(form.clk), input = $input[0]; 2414 n = input.name; 2415 if (n && !input.disabled && input.type == 'image') { 2416 a.push({name: n, value: $input.val()}); 2417 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 2418 } 2419 } 2420 return a; 2421}; 2422 2423/** 2424 * Serializes form data into a 'submittable' string. This method will return a string 2425 * in the format: name1=value1&name2=value2 2426 */
2427$.fn.formSerialize = function(semantic) { 2428 //hand off to jQuery.param for proper encoding 2429 return $.param(this.formToArray(semantic)); 2430}; 2431 2432/** 2433 * Serializes all field elements in the jQuery object into a query string. 2434 * This method will return a string in the format: name1=value1&name2=value2 2435 */ 2436$.fn.fieldSerialize = function(successful) { 2437 var a = []; 2438 this.each(function() { 2439 var n = this.name; 2440 if (!n) { 2441 return; 2442 } 2443 var v = $.fieldValue(this, successful); 2444 if (v && v.constructor == Array) { 2445 for (var i=0,max=v.length; i < max; i++) { 2446 a.push({name: n, value: v[i]}); 2447 } 2448 } 2449 else if (v !== null && typeof v != 'undefined') { 2450 a.push({name: this.name, value: v}); 2451 } 2452 }); 2453 //hand off to jQuery.param for proper encoding 2454 return $.param(a); 2455}; 2456 2457/** 2458 * Returns the value(s) of the element in the matched set. For example, consider the following form: 2459 * 2460 * <form><fieldset> 2461 * <input name="A" type="text" /> 2462 * <input name="A" type="text" /> 2463 * <input name="B" type="checkbox" value="B1" /> 2464 * <input name="B" type="checkbox" value="B2"/> 2465 * <input name="C" type="radio" value="C1" /> 2466 * <input name="C" type="radio" value="C2" /> 2467 * </fieldset></form> 2468 * 2469 * var v = $('input[type=text]').fieldValue(); 2470 * // if no values are entered into the text inputs 2471 * v == ['',''] 2472 * // if values entered into the text inputs are 'foo' and 'bar' 2473 * v == ['foo','bar'] 2474 * 2475 * var v = $('input[type=checkbox]').fieldValue(); 2476 * // if neither checkbox is checked 2477 * v === undefined 2478 * // if both checkboxes are checked 2479 * v == ['B1', 'B2'] 2480 * 2481 * var v = $('input[type=radio]').fieldValue(); 2482 * // if neither radio is checked 2483 * v === undefined 2484 * // if first radio is checked 2485 * v == ['C1'] 2486 * 2487 * The successful argument controls whether or not the field element must be 'successful' 2488 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). 2489 * The default value of the successful argument is true. If this value is false the value(s) 2490 * for each element is returned. 2491 * 2492 * Note: This method *always* returns an array. If no valid value can be determined the 2493 * array will be empty, otherwise it will contain one or more values. 2494 */ 2495$.fn.fieldValue = function(successful) { 2496 for (var val=[], i=0, max=this.length; i < max; i++) { 2497 var el = this[i]; 2498 var v = $.fieldValue(el, successful); 2499 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { 2500 continue; 2501 } 2502 if (v.constructor == Array) 2503 $.merge(val, v); 2504 else 2505 val.push(v); 2506 } 2507 return val; 2508}; 2509 2510/** 2511 * Returns the value of the field element. 2512 */ 2513$.fieldValue = function(el, successful) { 2514 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 2515 if (successful === undefined) { 2516 successful = true; 2517 } 2518 2519 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 2520 (t == 'checkbox' || t == 'radio') && !el.checked || 2521 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 2522 tag == 'select' && el.selectedIndex == -1)) { 2523 return null; 2524 } 2525 2526 if (tag == 'select') { 2527 var index = el.selectedIndex; 2528 if (index < 0) { 2529 return null; 2530 } 2531 var a = [], ops = el.options; 2532 var one = (t == 'select-one'); 2533 var max = (one ? index+1 : ops.length); 2534 for(var i=(one ? index : 0); i < max; i++) { 2535 var op = ops[i]; 2536 if (op.selected) { 2537 var v = op.value; 2538 if (!v) { // extra pain for IE... 2539 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; 2540 } 2541 if (one) { 2542 return v; 2543 } 2544 a.push(v); 2545 } 2546 } 2547 return a; 2548 } 2549 return $(el).val(); 2550}; 2551 2552/** 2553 * Clears the form data. Takes the following actions on the form's input fields: 2554 * - input text fields will have their 'value' property set to the empty string 2555 * - select elements will have their 'selectedIndex' property set to -1 2556 * - checkbox and radio inputs will have their 'checked' property set to false 2557 * - inputs of type submit, button, reset, and hidden will *not* be effected 2558 * - button elements will *not* be effected 2559 */ 2560$.fn.clearForm = function(includeHidden) { 2561 return this.each(function() { 2562 $('input,select,textarea', this).clearFields(includeHidden); 2563 }); 2564}; 2565 2566/** 2567 * Clears the selected form elements. 2568 */ 2569$.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 2570 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 2571 return this.each(function() { 2572 var t = this.type, tag = this.tagName.toLowerCase(); 2573 if (re.test(t) || tag == 'textarea') { 2574 this.value = ''; 2575 } 2576 else if (t == 'checkbox' || t == 'radio') { 2577 this.checked = false;
2578 } 2579 else if (tag == 'select') { 2580 this.selectedIndex = -1; 2581 } 2582 else if (includeHidden) { 2583 // includeHidden can be the value true, or it can be a selector string 2584 // indicating a special test; for example: 2585 // $('#myForm').clearForm('.special:hidden') 2586 // the above would clean hidden inputs that have the class of 'special' 2587 if ( (includeHidden === true && /hidden/.test(t)) || 2588 (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) 2589 this.value = ''; 2590 } 2591 }); 2592}; 2593 2594/** 2595 * Resets the form data. Causes all form elements to be reset to their original value. 2596 */ 2597$.fn.resetForm = function() { 2598 return this.each(function() { 2599 // guard against an input with the name of 'reset' 2600 // note that IE reports the reset function as an 'object' 2601 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { 2602 this.reset(); 2603 } 2604 }); 2605}; 2606 2607/** 2608 * Enables or disables any matching elements. 2609 */ 2610$.fn.enable = function(b) { 2611 if (b === undefined) { 2612 b = true; 2613 } 2614 return this.each(function() { 2615 this.disabled = !b; 2616 }); 2617}; 2618 2619/** 2620 * Checks/unchecks any matching checkboxes or radio buttons and 2621 * selects/deselects and matching option elements. 2622 */ 2623$.fn.selected = function(select) { 2624 if (select === undefined) { 2625 select = true; 2626 } 2627 return this.each(function() { 2628 var t = this.type; 2629 if (t == 'checkbox' || t == 'radio') { 2630 this.checked = select; 2631 } 2632 else if (this.tagName.toLowerCase() == 'option') { 2633 var $sel = $(this).parent('select'); 2634 if (select && $sel[0] && $sel[0].type == 'select-one') { 2635 // deselect all other options 2636 $sel.find('option').selected(false); 2637 } 2638 this.selected = select; 2639 } 2640 }); 2641}; 2642 2643// expose debug var 2644$.fn.ajaxSubmit.debug = false; 2645 2646// helper fn for console logging 2647function log() { 2648 if (!$.fn.ajaxSubmit.debug) 2649 return; 2650 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); 2651 if (window.console && window.console.log) { 2652 window.console.log(msg); 2653 } 2654 else if (window.opera && window.opera.postError) { 2655 window.opera.postError(msg); 2656 } 2657} 2658 2659})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.