1/*! 2 * jQuery Form Plugin 3 * version: 4.3.0 4 * Requires jQuery v1.7.2 or later 5 * Project repository: https://github.com/jquery-form/form 6 7 * Copyright 2017 Kevin Morris 8 * Copyright 2006 M. Alsup 9 10 * Dual licensed under the LGPL-2.1+ or MIT licenses 11 * https://github.com/jquery-form/form#license 12 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Lesser General Public 15 * License as published by the Free Software Foundation; either 16 * version 2.1 of the License, or (at your option) any later version. 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Lesser General Public License for more details. 21 */ 22/* global ActiveXObject */ 23 24(function (factory) { 25 if (typeof define === 'function' && define.amd) { 26 // AMD. Register as an anonymous module. 27 define(['jquery'], factory); 28 } else if (typeof module === 'object' && module.exports) { 29 // Node/CommonJS 30 module.exports = function( root, jQuery ) { 31 if (typeof jQuery === 'undefined') { 32 // require('jQuery') returns a factory that requires window to build a jQuery instance, we normalize how we use modules 33 // that require this pattern but the window provided is a noop if it's defined (how jquery works) 34 if (typeof window !== 'undefined') { 35 jQuery = require('jquery'); 36 } 37 else { 38 jQuery = require('jquery')(root); 39 } 40 } 41 factory(jQuery); 42 return jQuery; 43 }; 44 } else { 45 // Browser globals 46 factory(jQuery); 47 } 48 49}(function ($) { 50/* eslint-enable */ 51 'use strict'; 52 53 /* 54 Usage Note: 55 ----------- 56 Do not use both ajaxSubmit and ajaxForm on the same form. These 57 functions are mutually exclusive. Use ajaxSubmit if you want 58 to bind your own submit handler to the form. For example, 59 60 $(document).ready(function() { 61 $('#myForm').on('submit', function(e) { 62 e.preventDefault(); // <-- important 63 $(this).ajaxSubmit({ 64 target: '#output' 65 }); 66 }); 67 }); 68 69 Use ajaxForm when you want the plugin to manage all the event binding 70 for you. For example, 71 72 $(document).ready(function() { 73 $('#myForm').ajaxForm({ 74 target: '#output' 75 }); 76 }); 77 78 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 79 form does not have to exist when you invoke ajaxForm: 80 81 $('#myForm').ajaxForm({ 82 delegation: true, 83 target: '#output' 84 }); 85 86 When using ajaxForm, the ajaxSubmit function will be invoked for you 87 at the appropriate time. 88 */ 89 90 var rCRLF = /\r?\n/g; 91 92 /** 93 * Feature detection 94 */ 95 var feature = {}; 96 97 feature.fileapi = $('<input type="file">').get(0).files !== undefined; 98 feature.formdata = (typeof window.FormData !== 'undefined'); 99 100 var hasProp = !!$.fn.prop; 101 102 // attr2 uses prop when it can but checks the return type for 103 // an expected string. This accounts for the case where a form 104 // contains inputs with names like "action" or "method"; in those 105 // cases "prop" returns the element 106 $.fn.attr2 = function() { 107 if (!hasProp) { 108 return this.attr.apply(this, arguments); 109 } 110 111 var val = this.prop.apply(this, arguments); 112 113 if ((val && val.jquery) || typeof val === 'string') { 114 return val; 115 } 116 117 return this.attr.apply(this, arguments); 118 }; 119 120 /** 121 * ajaxSubmit() provides a mechanism for immediately submitting 122 * an HTML form using AJAX. 123 * 124 * @param {object|string} options jquery.form.js parameters or custom url for submission 125 * @param {object} data extraData 126 * @param {string} dataType ajax dataType 127 * @param {function} onSuccess ajax success callback function 128 */ 129 $.fn.ajaxSubmit = function(options, data, dataType, onSuccess) { 130 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 131 if (!this.length) { 132 log('ajaxSubmit: skipping submit process - no element selected'); 133 134 return this; 135 } 136 137 /* eslint consistent-this: ["error", "$form"] */ 138 var method, action, url, isMsie, iframeSrc, $form = this; 139 140 if (typeof options === 'function') { 141 options = {success: options}; 142 143 } else if (typeof options === 'string' || (options === false && arguments.length > 0)) { 144 options = { 145 'url' : options, 146 'data' : data, 147 'dataType' : dataType 148 }; 149 150 if (typeof onSuccess === 'function') { 151 options.success = onSuccess; 152 } 153 154 } else if (typeof options === 'undefined') { 155 options = {}; 156 } 157 158 method = options.method || options.type || this.attr2('method'); 159 action = options.url || this.attr2('action'); 160 161 url = (typeof action === 'string') ? action.trim() : ''; 162 url = url || window.location.href || ''; 163 if (url) { 164 // clean url (don't include hash vaue) 165 url = (url.match(/^([^#]+)/) || [])[1]; 166 } 167 // IE requires javascript:false in https, but this breaks chrome >
16783 and goes against spec. 168 // Instead of using javascript:false always, let's only apply it for IE. 169 isMsie = /(MSIE|Trident)/.test(navigator.userAgent || ''); 170 iframeSrc = (isMsie && /^https/i.test(window.location.href || '')) ? 'javascript:false' : 'about:blank'; // eslint-disable-line no-script-url 171 172 options = $.extend(true, { 173 url : url, 174 success : $.ajaxSettings.success, 175 type : method || $.ajaxSettings.type, 176 iframeSrc : iframeSrc 177 }, options); 178 179 // hook for manipulating the form data before it is extracted; 180 // convenient for use with rich editors like tinyMCE or FCKEditor 181 var veto = {}; 182 183 this.trigger('form-pre-serialize', [this, options, veto]); 184 185 if (veto.veto) { 186 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 187 188 return this; 189 } 190 191 // provide opportunity to alter form data before it is serialized 192 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 193 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 194 195 return this; 196 } 197 198 var traditional = options.traditional; 199 200 if (typeof traditional === 'undefined') { 201 traditional = $.ajaxSettings.traditional; 202 } 203 204 var elements = []; 205 var qx, a = this.formToArray(options.semantic, elements, options.filtering); 206 207 if (options.data) { 208 var optionsData = typeof(options.data) === "function" ? options.data(a) : options.data; 209 210 options.extraData = optionsData; 211 qx = $.param(optionsData, traditional); 212 } 213 214 // give pre-submit callback an opportunity to abort the submit 215 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 216 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 217 218 return this; 219 } 220 221 // fire vetoable 'validate' event 222 this.trigger('form-submit-validate', [a, this, options, veto]); 223 if (veto.veto) { 224 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 225 226 return this; 227 } 228 229 var q = $.param(a, traditional); 230 231 if (qx) { 232 q = (q ? (q + '&' + qx) : qx); 233 } 234 235 if (options.type.toUpperCase() === 'GET') { 236 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 237 options.data = null; // data is null for 'get' 238 } else { 239 options.data = q; // data is the query string for 'post' 240 } 241 242 var callbacks = []; 243 244 if (options.resetForm) { 245 callbacks.push(function() { 246 $form.resetForm(); 247 }); 248 } 249 250 if (options.clearForm) { 251 callbacks.push(function() { 252 $form.clearForm(options.includeHidden); 253 }); 254 } 255 256 // perform a load on the target only if dataType is not provided 257 if (!options.dataType && options.target) { 258 var oldSuccess = options.success || function(){}; 259 260 callbacks.push(function(data, textStatus, jqXHR) { 261 var successArguments = arguments, 262 fn = options.replaceTarget ? 'replaceWith' : 'html'; 263 264 $(options.target)[fn](data).each(function(){ 265 oldSuccess.apply(this, successArguments); 266 }); 267 }); 268 269 } else if (options.success) { 270 if (Array.isArray(options.success)) { 271 callbacks = callbacks.concat(options.success); 272 } else { 273 callbacks.push(options.success); 274 } 275 } 276 277 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 278 var context = options.context || this; // jQuery 1.4+ supports scope context 279 280 for (var i = 0, max = callbacks.length; i < max; i++) { 281 callbacks[i].apply(context, [data, status, xhr || $form, $form]); 282 } 283 }; 284 285 if (options.error) { 286 var oldError = options.error; 287 288 options.error = function(xhr, status, error) { 289 var context = options.context || this; 290 291 oldError.apply(context, [xhr, status, error, $form]); 292 }; 293 } 294 295 if (options.complete) { 296 var oldComplete = options.complete; 297 298 options.complete = function(xhr, status) { 299 var context = options.context || this; 300 301 oldComplete.apply(context, [xhr, status, $form]); 302 }; 303 } 304 305 // are there files to upload? 306 307 // [value] (issue #113), also see comment: 308 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 309 var fileInputs = $('input[type=file]:enabled', this).filter(function() { 310 return $(this).val() !== ''; 311 }); 312 var hasFileInputs = fileInputs.length > 0; 313 var mp = 'multipart/form-data'; 314 var multipart = ($form.attr('enctype') === mp || $form.attr('encoding') === mp); 315 var fileAPI = feature.fileapi && feature.formdata; 316 317 log('fileAPI :' + fileAPI); 318 319 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 320 var jqxhr; 321 322 // options.iframe allows user to force iframe mode 323 // 06-NOV-09: now defaulting to iframe mode if file input is detected 324 if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 325 // hack to fix Safari hang (thanks to Tim Molendijk for this) 326 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 327 if (options.closeKeepAlive) { 328 $.get(options.closeKeepAlive, function() { 329 jqxhr = fileUploadIframe(a); 330 }); 331 332 } else { 333 jqxhr = fileUploadIframe(a); 334 } 335 336 } else if ((hasFileInputs || multipart) && fileAPI) { 337 jqxhr = fileUploadXhr(a); 338 339 } else { 340 jqxhr = $.ajax(options); 341 } 342 343 $form.removeData('jqxhr').data('jqxhr', jqxhr); 344 345 // clear element array 346 for (var k = 0; k < elements.length; k++) { 347 elements[k] = null; 348 } 349 350 // fire 'notify' event 351 this.trigger('form-submit-notify', [this, options]); 352 353 return this; 354 355 // utility fn for deep serialization 356 function deepSerialize(extraData) { 357 var serialized = $.param(extraData, options.traditional).split('&'); 358 var len = serialized.length; 359 var result = []; 360 var i, part; 361 362 for (i = 0; i < len; i++) { 363 // #252; undo param space replacement 364 serialized[i] = serialized[i].replace(/\+/g, ' '); 365 part = serialized[i].split('='); 366 // #278; use array instead of object storage, favoring array serializations 367 result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); 368 } 369 370 return result; 371 } 372 373 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 374 function fileUploadXhr(a) { 375 var formdata = new FormData(); 376 377 for (var i = 0; i < a.length; i++) { 378 formdata.append(a[i].name, a[i].value); 379 } 380 381 if (options.extraData) { 382 var serializedData = deepSerialize(options.extraData); 383 384 for (i = 0; i < serializedData.length; i++) { 385 if (serializedData[i]) { 386 formdata.append(serializedData[i][0], serializedData[i][1]); 387 } 388 } 389 } 390 391 options.data = null; 392 393 var s = $.extend(true, {}, $.ajaxSettings, options, { 394 contentType : false, 395 processData : false, 396 cache : false, 397 type : method || 'POST' 398 }); 399 400 if (options.uploadProgress) { 401 // workaround because jqXHR does not expose upload property 402 s.xhr = function() { 403 var xhr = $.ajaxSettings.xhr(); 404 405 if (xhr.upload) { 406 xhr.upload.addEventListener('progress', function(event) { 407 var percent = 0; 408 var position = event.loaded || event.position; /* event.position is deprecated */ 409 var total = event.total; 410 411 if (event.lengthComputable) {
412 percent = Math.ceil(position / total * 100); 413 } 414 415 options.uploadProgress(event, position, total, percent); 416 }, false); 417 } 418 419 return xhr; 420 }; 421 } 422 423 s.data = null; 424 425 var beforeSend = s.beforeSend; 426 427 s.beforeSend = function(xhr, o) { 428 // Send FormData() provided by user 429 if (options.formData) { 430 o.data = options.formData; 431 } else { 432 o.data = formdata; 433 } 434 435 if (beforeSend) { 436 beforeSend.call(this, xhr, o); 437 } 438 }; 439 440 return $.ajax(s); 441 } 442 443 // private function for handling file uploads (hat tip to YAHOO!) 444 function fileUploadIframe(a) { 445 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 446 var deferred = $.Deferred(); 447 448 // #341 449 deferred.abort = function(status) { 450 xhr.abort(status); 451 }; 452 453 if (a) { 454 // ensure that every serialized input is still enabled 455 for (i = 0; i < elements.length; i++) { 456 el = $(elements[i]); 457 if (hasProp) { 458 el.prop('disabled', false); 459 } else { 460 el.removeAttr('disabled'); 461 } 462 } 463 } 464 465 s = $.extend(true, {}, $.ajaxSettings, options); 466 s.context = s.context || s; 467 id = 'jqFormIO' + new Date().getTime(); 468 var ownerDocument = form.ownerDocument; 469 var $body = $form.closest('body'); 470 471 if (s.iframeTarget) { 472 $io = $(s.iframeTarget, ownerDocument); 473 n = $io.attr2('name'); 474 if (!n) { 475 $io.attr2('name', id); 476 } else { 477 id = n; 478 } 479 480 } else { 481 $io = $('<iframe name="' + id + '" src="' + s.iframeSrc + '" />', ownerDocument); 482 $io.css({position: 'absolute', top: '-1000px', left: '-1000px'}); 483 } 484 io = $io[0]; 485 486 487 xhr = { // mock object 488 aborted : 0, 489 responseText : null, 490 responseXML : null, 491 status : 0, 492 statusText : 'n/a', 493 getAllResponseHeaders : function() {}, 494 getResponseHeader : function() {}, 495 setRequestHeader : function() {}, 496 abort : function(status) { 497 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 498 499 log('aborting upload... ' + e); 500 this.aborted = 1; 501 502 try { // #214, #257 503 if (io.contentWindow.document.execCommand) { 504 io.contentWindow.document.execCommand('Stop'); 505 } 506 } catch (ignore) {} 507 508 $io.attr('src', s.iframeSrc); // abort op in progress 509 xhr.error = e; 510 if (s.error) { 511 s.error.call(s.context, xhr, e, status); 512 } 513 514 if (g) { 515 $.event.trigger('ajaxError', [xhr, s, e]); 516 } 517 518 if (s.complete) { 519 s.complete.call(s.context, xhr, e); 520 } 521 } 522 }; 523 524 g = s.global; 525 // trigger ajax global events so that activity/block indicators work like normal 526 if (g && $.active++ === 0) { 527 $.event.trigger('ajaxStart'); 528 } 529 if (g) { 530 $.event.trigger('ajaxSend', [xhr, s]); 531 } 532 533 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 534 if (s.global) { 535 $.active--; 536 } 537 deferred.reject(); 538 539 return deferred; 540 } 541 542 if (xhr.aborted) { 543 deferred.reject(); 544 545 return deferred; 546 } 547 548 // add submitting element to data if we know it 549 sub = form.clk; 550 if (sub) { 551 n = sub.name; 552 if (n && !sub.disabled) { 553 s.extraData = s.extraData || {}; 554 s.extraData[n] = sub.value; 555 if (sub.type === 'image') { 556 s.extraData[n + '.x'] = form.clk_x; 557 s.extraData[n + '.y'] = form.clk_y; 558 } 559 } 560 } 561 562 var CLIENT_TIMEOUT_ABORT = 1; 563 var SERVER_ABORT = 2; 564 565 function getDoc(frame) { 566 /* it looks like contentWindow or contentDocument do not 567 * carry the protocol property in ie8, when running under ssl 568 * frame.document is the only valid response document, since 569 * the protocol is know but not on the other two objects. strange? 570 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy 571 */ 572 573 var doc = null; 574 575 // IE8 cascading access check 576 try { 577 if (frame.contentWindow) { 578 doc = frame.contentWindow.document; 579 } 580 } catch (err) { 581 // IE8 access denied under ssl & missing protocol 582 log('cannot get iframe.contentWindow document: ' + err); 583 } 584 585 if (doc) { // successful getting content 586 return doc; 587 } 588 589 try { // simply checking may throw in ie8 under ssl or mismatched protocol 590 doc = frame.contentDocument ? frame.contentDocument : frame.document; 591 } catch (err) { 592 // last attempt 593 log('cannot get iframe.contentDocument: ' + err); 594 doc = frame.document; 595 } 596 597 return doc; 598 } 599 600 // Rails CSRF hack (thanks to Yvan Barthelemy) 601 var csrf_token = $('meta[name=csrf-token]').attr('content'); 602 var csrf_param = $('meta[name=csrf-param]').attr('content'); 603 604 if (csrf_param && csrf_token) { 605 s.extraData = s.extraData || {}; 606 s.extraData[csrf_param] = csrf_token; 607 } 608 609 // take a breath so that pending repaints get some cpu time before the upload starts 610 function doSubmit() { 611 // make sure form attrs are set 612 var t = $form.attr2('target'), 613 a = $form.attr2('action'), 614 mp = 'multipart/form-data', 615 et = $form.attr('enctype') || $form.attr('encoding') || mp; 616 617 // update form attrs in IE friendly way 618 form.setAttribute('target', id); 619 if (!method || /post/i.test(method)) { 620 form.setAttribute('method', 'POST'); 621 } 622 if (a !== s.url) { 623 form.setAttribute('action', s.url); 624 } 625 626 // ie borks in some cases when setting encoding 627 if (!s.skipEncodingOverride && (!method || /post/i.test(method))) { 628 $form.attr({ 629 encoding : 'multipart/form-data', 630 enctype : 'multipart/form-data' 631 }); 632 } 633 634 // support timout 635 if (s.timeout) { 636 timeoutHandle = setTimeout(function() { 637 timedOut = true; cb(CLIENT_TIMEOUT_ABORT); 638 }, s.timeout); 639 } 640 641 // look for server aborts 642 function checkState() { 643 try { 644 var state = getDoc(io).readyState; 645 646 log('state = ' + state); 647 if (state && state.toLowerCase() === 'uninitialized') { 648 setTimeout(checkState, 50); 649 } 650 651 } catch (e) { 652 log('Server abort: ', e, ' (', e.name, ')'); 653 cb(SERVER_ABORT); // eslint-disable-line callback-return 654 if (timeoutHandle) { 655 clearTimeout(timeoutHandle); 656 } 657 timeoutHandle = undefined; 658 } 659 } 660 661 // add "extra" data to form if provided in options 662 var extraInputs = []; 663 664 try { 665 if (s.extraData) { 666 for (var n in s.extraData) { 667 if (s.extraData.hasOwnProperty(n)) { 668 // if using the $.param format that allows for multiple values with the same name 669 if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { 670 extraInputs.push( 671 $('<input type="hidden" name="' + s.extraData[n].name + '">', ownerDocument).val(s.extraData[n].value) 672 .appendTo(form)[0]); 673 } else { 674 extraInputs.push( 675 $('<input type="hidden" name="' + n + '">', ownerDocument).val(s.extraData[n]) 676 .appendTo(form)[0]); 677 } 678 } 679 } 680 } 681 682 if (!s.iframeTarget) { 683 // add iframe to doc and submit the form 684 $io.appendTo($body); 685 } 686 687 if (io.attachEvent) { 688 io.attachEvent('onload', cb); 689 } else { 690 io.addEventListener('load', cb, false); 691 } 692 693 setTimeout(checkState, 15); 694 695 try { 696 form.submit(); 697 698 } catch (err) { 699 // just in case form has element with name/id of 'submit' 700 var submitFn = document.createElement('form').submit; 701 702 submitFn.apply(form); 703 } 704 705 } finally { 706 // reset attrs and remove "extra" input elements 707 form.setAttribute('action', a); 708 form.setAttribute('enctype', et); // #380 709 if (t) { 710 form.setAttribute('target', t); 711 } else { 712 $form.removeAttr('target'); 713 } 714 $(extraInputs).remove(); 715 } 716 } 717 718 if (s.forceSync) { 719 doSubmit(); 720 } else { 721 setTimeout(doSubmit, 10); // this lets dom updates render 722 } 723 724 var data, doc, domCheckCount = 50, callbackProcessed; 725 726 function cb(e) { 727 if (xhr.aborted || callbackProcessed) { 728 return; 729 } 730 731 doc = getDoc(io); 732 if (!doc) { 733 log('cannot access response document'); 734 e = SERVER_ABORT; 735 } 736 if (e === CLIENT_TIMEOUT_ABORT && xhr) { 737 xhr.abort('timeout'); 738 deferred.reject(xhr, 'timeout'); 739 740 return; 741 742 } 743 if (e === SERVER_ABORT && xhr) { 744 xhr.abort('server abort'); 745 deferred.reject(xhr, 'error', 'server abort'); 746 747 return; 748 } 749 750 if (!doc || doc.location.href === s.iframeSrc) { 751 // response not received yet 752 if (!timedOut) { 753 return; 754 } 755 } 756 757 if (io.detachEvent) { 758 io.detachEvent('onload', cb); 759 } else { 760 io.removeEventListener('load', cb, false); 761 } 762 763 var status = 'success', errMsg; 764 765 try { 766 if (timedOut) { 767 throw 'timeout'; 768 } 769 770 var isXml = s.dataType === 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 771 772 log('isXml=' + isXml); 773 774 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 775 if (--domCheckCount) { 776 // in some browsers (Opera) the iframe DOM is not always traversable when 777 // the onload callback fires, so we loop a bit to accommodate 778 log('requeing onLoad callback, DOM not available'); 779 setTimeout(cb, 250); 780 781 return; 782 } 783 // let this fall through because server response could be an empty document 784 // log('Could not access iframe DOM after mutiple tries.'); 785 // throw 'DOMException: not available'; 786 } 787 788 // log('response detected'); 789 var docRoot = doc.body ? doc.body : doc.documentElement; 790 791 xhr.responseText = docRoot ? docRoot.innerHTML : null; 792 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 793 if (isXml) { 794 s.dataType = 'xml'; 795 } 796 xhr.getResponseHeader = function(header){ 797 var headers = {'content-type': s.dataType}; 798 799 return headers[header.toLowerCase()]; 800 }; 801 // support for XHR 'status' & 'statusText' emulation : 802 if (docRoot) { 803 xhr.status = Number(docRoot.getAttribute('status')) || xhr.status; 804 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 805 } 806 807 var dt = (s.dataType || '').toLowerCase(); 808 var scr = /(json|script|text)/.test(dt); 809 810 if (scr || s.textarea) { 811 // see if user embedded response in textarea 812 var ta = doc.getElementsByTagName('textarea')[0]; 813 814 if (ta) { 815 xhr.responseText = ta.value; 816 // support for XHR 'status' & 'statusText' emulation : 817 xhr.status = Number(ta.getAttribute('status')) || xhr.status; 818 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 819 820 } else if (scr) { 821 // account for browsers injecting pre around json response 822 var pre = doc.getElementsByTagName('pre')[0]; 823 var b = doc.getElementsByTagName('body')[0]; 824 825 if (pre) { 826 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 827 } else if (b) { 828 xhr.responseText = b.textContent ? b.textContent : b.innerText; 829 } 830 } 831 832 } else if (dt === 'xml' && !xhr.responseXML && xhr.responseText) { 833 xhr.responseXML = toXml(xhr.responseText); // eslint-disable-line no-use-before-define 834 } 835 836 try { 837 data = httpData(xhr, dt, s); // eslint-disable-line no-use-before-define 838 839 } catch (err) { 840 status = 'parsererror'; 841 xhr.error = errMsg = (err || status); 842 } 843 844 } catch (err) { 845 log('error caught: ', err); 846 status = 'error'; 847 xhr.error = errMsg = (err || status); 848 } 849 850 if (xhr.aborted) { 851 log('upload aborted'); 852 status = null; 853 } 854 855 if (xhr.status) { // we've set xhr.status 856 status = ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 'success' : 'error'; 857 } 858 859 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 860 if (status === 'success') { 861 if (s.success) { 862 s.success.call(s.context, data, 'success', xhr); 863 } 864 865 deferred.resolve(xhr.responseText, 'success', xhr); 866 867 if (g) {
868 $.event.trigger('ajaxSuccess', [xhr, s]); 869 } 870 871 } else if (status) { 872 if (typeof errMsg === 'undefined') { 873 errMsg = xhr.statusText; 874 } 875 if (s.error) { 876 s.error.call(s.context, xhr, status, errMsg); 877 } 878 deferred.reject(xhr, 'error', errMsg); 879 if (g) { 880 $.event.trigger('ajaxError', [xhr, s, errMsg]); 881 } 882 } 883 884 if (g) { 885 $.event.trigger('ajaxComplete', [xhr, s]); 886 } 887 888 if (g && !--$.active) { 889 $.event.trigger('ajaxStop'); 890 } 891 892 if (s.complete) { 893 s.complete.call(s.context, xhr, status); 894 } 895 896 callbackProcessed = true; 897 if (s.timeout) { 898 clearTimeout(timeoutHandle); 899 } 900 901 // clean up 902 setTimeout(function() { 903 if (!s.iframeTarget) { 904 $io.remove(); 905 } else { // adding else to clean up existing iframe response. 906 $io.attr('src', s.iframeSrc); 907 } 908 xhr.responseXML = null; 909 }, 100); 910 } 911 912 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 913 if (window.ActiveXObject) { 914 doc = new ActiveXObject('Microsoft.XMLDOM'); 915 doc.async = 'false'; 916 doc.loadXML(s); 917 918 } else { 919 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 920 } 921 922 return (doc && doc.documentElement && doc.documentElement.nodeName !== 'parsererror') ? doc : null; 923 }; 924 925 var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4 926 927 var ct = xhr.getResponseHeader('content-type') || '', 928 xml = ((type === 'xml' || !type) && ct.indexOf('xml') >= 0), 929 data = xml ? xhr.responseXML : xhr.responseText; 930 931 if (xml && data.documentElement.nodeName === 'parsererror') { 932 if ($.error) { 933 $.error('parsererror'); 934 } 935 } 936 if (s && s.dataFilter) { 937 data = s.dataFilter(data, type); 938 } 939 if (typeof data === 'string') { 940 if ((type === 'json' || !type) && ct.indexOf('json') >= 0) { 941 data = JSON.parse(data); 942 } else if ((type === 'script' || !type) && ct.indexOf('javascript') >= 0) { 943 $.globalEval(data); 944 } 945 } 946 947 return data; 948 }; 949 950 return deferred; 951 } 952 }; 953 954 /** 955 * ajaxForm() provides a mechanism for fully automating form submission. 956 * 957 * The advantages of using this method instead of ajaxSubmit() are: 958 * 959 * 1: This method will include coordinates for <input type="image"> elements (if the element 960 * is used to submit the form). 961 * 2. This method will include the submit element's name/value data (for the element that was 962 * used to submit the form). 963 * 3. This method binds the submit() method to the form for you. 964 * 965 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 966 * passes the options argument along after properly binding events for submit elements and 967 * the form itself. 968 */ 969 $.fn.ajaxForm = function(options, data, dataType, onSuccess) { 970 if (typeof options === 'string' || (options === false && arguments.length > 0)) { 971 options = { 972 'url' : options, 973 'data' : data, 974 'dataType' : dataType 975 }; 976 977 if (typeof onSuccess === 'function') { 978 options.success = onSuccess; 979 } 980 } 981 982 options = options || {}; 983 options.delegation = options.delegation && typeof $.fn.on === 'function'; 984 985 // in jQuery 1.3+ we can fix mistakes with the ready state 986 if (!options.delegation && this.length === 0) { 987 var o = {s: this.selector, c: this.context}; 988 989 if (!$.isReady && o.s) { 990 log('DOM not ready, queuing ajaxForm'); 991 $(function() { 992 $(o.s, o.c).ajaxForm(options); 993 }); 994 995 return this; 996 } 997 998 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 999 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 1000 1001 return this; 1002 } 1003 1004 if (options.delegation) { 1005 $(document) 1006 .off('submit.form-plugin', this.selector, doAjaxSubmit) 1007 .off('click.form-plugin', this.selector, captureSubmittingElement) 1008 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 1009 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 1010 1011 return this; 1012 } 1013 1014 if (options.beforeFormUnbind) { 1015 options.beforeFormUnbind(this, options); 1016 } 1017 1018 return this.ajaxFormUnbind() 1019 .on('submit.form-plugin', options, doAjaxSubmit) 1020 .on('click.form-plugin', options, captureSubmittingElement); 1021 }; 1022 1023 // private event handlers 1024 function doAjaxSubmit(e) { 1025 /* jshint validthis:true */ 1026 var options = e.data; 1027 1028 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 1029 e.preventDefault(); 1030 $(e.target).closest('form').ajaxSubmit(options); // #365 1031 } 1032 } 1033 1034 function captureSubmittingElement(e) { 1035 /* jshint validthis:true */ 1036 var target = e.target; 1037 var $el = $(target); 1038 1039 if (!$el.is('[type=submit],[type=image]')) { 1040 // is this a child element of the submit el? (ex: a span within a button) 1041 var t = $el.closest('[type=submit]'); 1042 1043 if (t.length === 0) { 1044 return; 1045 } 1046 target = t[0]; 1047 } 1048 1049 var form = target.form; 1050 1051 form.clk = target; 1052 1053 if (target.type === 'image') { 1054 if (typeof e.offsetX !== 'undefined') { 1055 form.clk_x = e.offsetX; 1056 form.clk_y = e.offsetY; 1057 1058 } else if (typeof $.fn.offset === 'function') { 1059 var offset = $el.offset(); 1060 1061 form.clk_x = e.pageX - offset.left; 1062 form.clk_y = e.pageY - offset.top; 1063 1064 } else { 1065 form.clk_x = e.pageX - target.offsetLeft; 1066 form.clk_y = e.pageY - target.offsetTop; 1067 } 1068 } 1069 // clear form vars 1070 setTimeout(function() { 1071 form.clk = form.clk_x = form.clk_y = null; 1072 }, 100); 1073 } 1074 1075 1076 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 1077 $.fn.ajaxFormUnbind = function() { 1078 return this.off('submit.form-plugin click.form-plugin'); 1079 }; 1080 1081 /** 1082 * formToArray() gathers form element data into an array of objects that can 1083 * be passed to any of the following ajax functions: $.get, $.post, or load. 1084 * Each object in the array has both a 'name' and 'value' property. An example of 1085 * an array for a simple login form might be: 1086 * 1087 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 1088 * 1089 * It is this array that is passed to pre-submit callback functions provided to the 1090 * ajaxSubmit() and ajaxForm() methods. 1091 */ 1092 $.fn.formToArray = function(semantic, elements, filtering) { 1093 var a = []; 1094 1095 if (this.length === 0) { 1096 return a; 1097 } 1098 1099 var form = this[0]; 1100 var formId = this.attr('id'); 1101 var els = (semantic || typeof form.elements === 'undefined') ? form.getElementsByTagName('*') : form.elements; 1102 var els2; 1103 1104 if (els) { 1105 els = $.makeArray(els); // convert to standard array 1106 } 1107 1108 // #386; account for inputs outside the form which use the 'form' attribute 1109 // FinesseRus: in non-IE browsers outside fields are already included in form.elements. 1110 if (formId && (semantic || /(Edge|Trident)\//.test(navigator.userAgent))) { 1111 els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet 1112 if (els2.length) { 1113 els = (els || []).concat(els2); 1114 } 1115 } 1116 1117 if (!els || !els.length) { 1118 return a; 1119 } 1120 1121 if (typeof(filtering) === "function") { 1122 els = $.map(els, filtering); 1123 } 1124 1125 var i, j, n, v, el, max, jmax; 1126 1127 for (i = 0, max = els.length; i < max; i++) { 1128 el = els[i]; 1129 n = el.name; 1130 if (!n || el.disabled) { 1131 continue; 1132 } 1133 1134 if (semantic && form.clk && el.type === 'image') { 1135 // handle image inputs on the fly when semantic == true 1136 if (form.clk === el) { 1137 a.push({name: n, value: $(el).val(), type: el.type}); 1138 a.push({name: n + '.x', value: form.clk_x}
1138, {name: n + '.y', value: form.clk_y}); 1139 } 1140 continue; 1141 } 1142 1143 v = $.fieldValue(el, true); 1144 if (v && v.constructor === Array) { 1145 if (elements) { 1146 elements.push(el); 1147 } 1148 for (j = 0, jmax = v.length; j < jmax; j++) { 1149 a.push({name: n, value: v[j]}); 1150 } 1151 1152 } else if (feature.fileapi && el.type === 'file') { 1153 if (elements) { 1154 elements.push(el); 1155 } 1156 1157 var files = el.files; 1158 1159 if (files.length) { 1160 for (j = 0; j < files.length; j++) { 1161 a.push({name: n, value: files[j], type: el.type}); 1162 } 1163 } else { 1164 // #180 1165 a.push({name: n, value: '', type: el.type}); 1166 } 1167 1168 } else if (v !== null && typeof v !== 'undefined') { 1169 if (elements) { 1170 elements.push(el); 1171 } 1172 a.push({name: n, value: v, type: el.type, required: el.required}); 1173 } 1174 } 1175 1176 if (!semantic && form.clk) { 1177 // input type=='image' are not found in elements array! handle it here 1178 var $input = $(form.clk), input = $input[0]; 1179 1180 n = input.name; 1181 1182 if (n && !input.disabled && input.type === 'image') { 1183 a.push({name: n, value: $input.val()}); 1184 a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y}); 1185 } 1186 } 1187 1188 return a; 1189 }; 1190 1191 /** 1192 * Serializes form data into a 'submittable' string. This method will return a string 1193 * in the format: name1=value1&name2=value2 1194 */ 1195 $.fn.formSerialize = function(semantic) { 1196 // hand off to jQuery.param for proper encoding 1197 return $.param(this.formToArray(semantic)); 1198 }; 1199 1200 /** 1201 * Serializes all field elements in the jQuery object into a query string. 1202 * This method will return a string in the format: name1=value1&name2=value2 1203 */ 1204 $.fn.fieldSerialize = function(successful) { 1205 var a = []; 1206 1207 this.each(function() { 1208 var n = this.name; 1209 1210 if (!n) { 1211 return; 1212 } 1213 1214 var v = $.fieldValue(this, successful); 1215 1216 if (v && v.constructor === Array) { 1217 for (var i = 0, max = v.length; i < max; i++) { 1218 a.push({name: n, value: v[i]}); 1219 } 1220 1221 } else if (v !== null && typeof v !== 'undefined') { 1222 a.push({name: this.name, value: v}); 1223 } 1224 }); 1225 1226 // hand off to jQuery.param for proper encoding 1227 return $.param(a); 1228 }; 1229 1230 /** 1231 * Returns the value(s) of the element in the matched set. For example, consider the following form: 1232 * 1233 * <form><fieldset> 1234 * <input name="A" type="text"> 1235 * <input name="A" type="text"> 1236 * <input name="B" type="checkbox" value="B1"> 1237 * <input name="B" type="checkbox" value="B2"> 1238 * <input name="C" type="radio" value="C1"> 1239 * <input name="C" type="radio" value="C2"> 1240 * </fieldset></form> 1241 * 1242 * var v = $('input[type=text]').fieldValue(); 1243 * // if no values are entered into the text inputs 1244 * v === ['',''] 1245 * // if values entered into the text inputs are 'foo' and 'bar' 1246 * v === ['foo','bar'] 1247 * 1248 * var v = $('input[type=checkbox]').fieldValue(); 1249 * // if neither checkbox is checked 1250 * v === undefined 1251 * // if both checkboxes are checked 1252 * v === ['B1', 'B2'] 1253 * 1254 * var v = $('input[type=radio]').fieldValue(); 1255 * // if neither radio is checked 1256 * v === undefined 1257 * // if first radio is checked 1258 * v === ['C1'] 1259 * 1260 * The successful argument controls whether or not the field element must be 'successful' 1261 * (per https://www.w3.org/TR/html4/interact/forms.html#successful-controls). 1262 * The default value of the successful argument is true. If this value is false the value(s) 1263 * for each element is returned. 1264 * 1265 * Note: This method *always* returns an array. If no valid value can be determined the 1266 * array will be empty, otherwise it will contain one or more values. 1267 */ 1268 $.fn.fieldValue = function(successful) { 1269 for (var val = [], i = 0, max = this.length; i < max; i++) { 1270 var el = this[i]; 1271 var v = $.fieldValue(el, successful); 1272 1273 if (v === null || typeof v === 'undefined' || (v.constructor === Array && !v.length)) { 1274 continue; 1275 } 1276 1277 if (Array.isArray(v)) { 1278 val = val.concat(v); 1279 } else { 1280 val.push(v); 1281 } 1282 } 1283 1284 return val; 1285 }; 1286 1287 /** 1288 * Returns the value of the field element. 1289 */ 1290 $.fieldValue = function(el, successful) { 1291 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 1292 1293 if (typeof successful === 'undefined') { 1294 successful = true; 1295 } 1296 1297 /* eslint-disable no-mixed-operators */ 1298 if (successful && (!n || el.disabled || t === 'reset' || t === 'button' || 1299 (t === 'checkbox' || t === 'radio') && !el.checked || 1300 (t === 'submit' || t === 'image') && el.form && el.form.clk !== el || 1301 tag === 'select' && el.selectedIndex === -1)) { 1302 /* eslint-enable no-mixed-operators */ 1303 return null; 1304 } 1305 1306 if (tag === 'select') { 1307 var index = el.selectedIndex; 1308 1309 if (index < 0) { 1310 return null; 1311 } 1312 1313 var a = [], ops = el.options; 1314 var one = (t === 'select-one'); 1315 var max = (one ? index + 1 : ops.length); 1316 1317 for (var i = (one ? index : 0); i < max; i++) { 1318 var op = ops[i]; 1319 1320 if (op.selected && !op.disabled) { 1321 var v = op.value; 1322 1323 if (!v) { // extra pain for IE... 1324 v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; 1325 } 1326 1327 if (one) { 1328 return v; 1329 } 1330 1331 a.push(v); 1332 } 1333 } 1334 1335 return a; 1336 } 1337 1338 return $(el).val().replace(rCRLF, '\r\n'); 1339 }; 1340 1341 /** 1342 * Clears the form data. Takes the following actions on the form's input fields: 1343 * - input text fields will have their 'value' property set to the empty string 1344 * - select elements will have their 'selectedIndex' property set to -1 1345 * - checkbox and radio inputs will have their 'checked' property set to false 1346 * - inputs of type submit, button, reset, and hidden will *not* be effected 1347 * - button elements will *not* be effected 1348 */ 1349 $.fn.clearForm = function(includeHidden) { 1350 return this.each(function() { 1351 $('input,select,textarea', this).clearFields(includeHidden); 1352 }); 1353 }; 1354 1355 /** 1356 * Clears the selected form elements. 1357 */ 1358 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 1359 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 1360 1361 return this.each(function() { 1362 var t = this.type, tag = this.tagName.toLowerCase(); 1363 1364 if (re.test(t) || tag === 'textarea') { 1365 this.value = ''; 1366 1367 } else if (t === 'checkbox' || t === 'radio') { 1368 this.checked = false;
1369 1370 } else if (tag === 'select') { 1371 this.selectedIndex = -1; 1372 1373 } else if (t === 'file') { 1374 if (/MSIE/.test(navigator.userAgent)) { 1375 $(this).replaceWith($(this).clone(true)); 1376 } else { 1377 $(this).val(''); 1378 } 1379 1380 } else if (includeHidden) { 1381 // includeHidden can be the value true, or it can be a selector string 1382 // indicating a special test; for example: 1383 // $('#myForm').clearForm('.special:hidden') 1384 // the above would clean hidden inputs that have the class of 'special' 1385 if ((includeHidden === true && /hidden/.test(t)) || 1386 (typeof includeHidden === 'string' && $(this).is(includeHidden))) { 1387 this.value = ''; 1388 } 1389 } 1390 }); 1391 }; 1392 1393 1394 /** 1395 * Resets the form data or individual elements. Takes the following actions 1396 * on the selected tags: 1397 * - all fields within form elements will be reset to their original value 1398 * - input / textarea / select fields will be reset to their original value 1399 * - option / optgroup fields (for multi-selects) will defaulted individually 1400 * - non-multiple options will find the right select to default 1401 * - label elements will be searched against its 'for' attribute 1402 * - all others will be searched for appropriate children to default 1403 */ 1404 $.fn.resetForm = function() { 1405 return this.each(function() { 1406 var el = $(this); 1407 var tag = this.tagName.toLowerCase(); 1408 1409 switch (tag) { 1410 case 'input': 1411 this.checked = this.defaultChecked; 1412 // fall through 1413 1414 case 'textarea': 1415 this.value = this.defaultValue; 1416 1417 return true; 1418 1419 case 'option': 1420 case 'optgroup': 1421 var select = el.parents('select'); 1422 1423 if (select.length && select[0].multiple) { 1424 if (tag === 'option') { 1425 this.selected = this.defaultSelected; 1426 } else { 1427 el.find('option').resetForm(); 1428 } 1429 } else { 1430 select.resetForm(); 1431 } 1432 1433 return true; 1434 1435 case 'select': 1436 el.find('option').each(function(i) { // eslint-disable-line consistent-return 1437 this.selected = this.defaultSelected; 1438 if (this.defaultSelected && !el[0].multiple) { 1439 el[0].selectedIndex = i; 1440 1441 return false; 1442 } 1443 }); 1444 1445 return true; 1446 1447 case 'label': 1448 var forEl = $(el.attr('for')); 1449 var list = el.find('input,select,textarea'); 1450 1451 if (forEl[0]) { 1452 list.unshift(forEl[0]); 1453 } 1454 1455 list.resetForm(); 1456 1457 return true; 1458 1459 case 'form': 1460 // guard against an input with the name of 'reset' 1461 // note that IE reports the reset function as an 'object' 1462 if (typeof this.reset === 'function' || (typeof this.reset === 'object' && !this.reset.nodeType)) { 1463 this.reset(); 1464 } 1465 1466 return true; 1467 1468 default: 1469 el.find('form,input,label,select,textarea').resetForm(); 1470 1471 return true; 1472 } 1473 }); 1474 }; 1475 1476 /** 1477 * Enables or disables any matching elements. 1478 */ 1479 $.fn.enable = function(b) { 1480 if (typeof b === 'undefined') { 1481 b = true; 1482 } 1483 1484 return this.each(function() { 1485 this.disabled = !b; 1486 }); 1487 }; 1488 1489 /** 1490 * Checks/unchecks any matching checkboxes or radio buttons and 1491 * selects/deselects and matching option elements. 1492 */ 1493 $.fn.selected = function(select) { 1494 if (typeof select === 'undefined') { 1495 select = true; 1496 } 1497 1498 return this.each(function() { 1499 var t = this.type; 1500 1501 if (t === 'checkbox' || t === 'radio') { 1502 this.checked = select; 1503 1504 } else if (this.tagName.toLowerCase() === 'option') { 1505 var $sel = $(this).parent('select'); 1506 1507 if (select && $sel[0] && $sel[0].type === 'select-one') { 1508 // deselect all other options 1509 $sel.find('option').selected(false); 1510 } 1511 1512 this.selected = select; 1513 } 1514 }); 1515 }; 1516 1517 // expose debug var 1518 $.fn.ajaxSubmit.debug = false; 1519 1520 // helper fn for console logging 1521 function log() { 1522 if (!$.fn.ajaxSubmit.debug) { 1523 return; 1524 } 1525 1526 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, ''); 1527 1528 if (window.console && window.console.log) { 1529 window.console.log(msg); 1530 1531 } else if (window.opera && window.opera.postError) { 1532 window.opera.postError(msg); 1533 } 1534 } 1535}));
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.