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 var parseJSON = $.parseJSON || function(s) { 925 /* jslint evil:true */ 926 return window['eval']('(' + s + ')'); // eslint-disable-line dot-notation 927 }; 928 929 var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4 930 931 var ct = xhr.getResponseHeader('content-type') || '', 932 xml = ((type === 'xml' || !type) && ct.indexOf('xml') >= 0), 933 data = xml ? xhr.responseXML : xhr.responseText; 934 935 if (xml && data.documentElement.nodeName === 'parsererror') { 936 if ($.error) { 937 $.error('parsererror'); 938 } 939 } 940 if (s && s.dataFilter) { 941 data = s.dataFilter(data, type); 942 } 943 if (typeof data === 'string') { 944 if ((type === 'json' || !type) && ct.indexOf('json') >= 0) { 945 data = parseJSON(data); 946 } else if ((type === 'script' || !type) && ct.indexOf('javascript') >= 0) { 947 $.globalEval(data); 948 } 949 } 950 951 return data; 952 }; 953 954 return deferred; 955 } 956 }; 957 958 /** 959 * ajaxForm() provides a mechanism for fully automating form submission. 960 * 961 * The advantages of using this method instead of ajaxSubmit() are: 962 * 963 * 1: This method will include coordinates for <input type="image"> elements (if the element 964 * is used to submit the form). 965 * 2. This method will include the submit element's name/value data (for the element that was 966 * used to submit the form). 967 * 3. This method binds the submit() method to the form for you. 968 * 969 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 970 * passes the options argument along after properly binding events for submit elements and 971 * the form itself. 972 */ 973 $.fn.ajaxForm = function(options, data, dataType, onSuccess) { 974 if (typeof options === 'string' || (options === false && arguments.length > 0)) { 975 options = { 976 'url' : options, 977 'data' : data, 978 'dataType' : dataType 979 }; 980 981 if (typeof onSuccess === 'function') { 982 options.success = onSuccess; 983 } 984 } 985 986 options = options || {}; 987 options.delegation = options.delegation && typeof $.fn.on === 'function'; 988 989 // in jQuery 1.3+ we can fix mistakes with the ready state 990 if (!options.delegation && this.length === 0) { 991 var o = {s: this.selector, c: this.context}; 992 993 if (!$.isReady && o.s) { 994 log('DOM not ready, queuing ajaxForm'); 995 $(function() { 996 $(o.s, o.c).ajaxForm(options); 997 }); 998 999 return this; 1000 } 1001 1002 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 1003 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 1004 1005 return this; 1006 } 1007 1008 if (options.delegation) { 1009 $(document) 1010 .off('submit.form-plugin', this.selector, doAjaxSubmit) 1011 .off('click.form-plugin', this.selector, captureSubmittingElement) 1012 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 1013 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 1014 1015 return this; 1016 } 1017 1018 if (options.beforeFormUnbind) { 1019 options.beforeFormUnbind(this, options); 1020 } 1021 1022 return this.ajaxFormUnbind() 1023 .on('submit.form-plugin', options, doAjaxSubmit) 1024 .on('click.form-plugin', options, captureSubmittingElement); 1025 }; 1026 1027 // private event handlers 1028 function doAjaxSubmit(e) { 1029 /* jshint validthis:true */ 1030 var options = e.data; 1031 1032 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 1033 e.preventDefault(); 1034 $(e.target).closest('form').ajaxSubmit(options); // #365 1035 } 1036 } 1037 1038 function captureSubmittingElement(e) { 1039 /* jshint validthis:true */ 1040 var target = e.target; 1041 var $el = $(target); 1042 1043 if (!$el.is('[type=submit],[type=image]')) { 1044 // is this a child element of the submit el? (ex: a span within a button) 1045 var t = $el.closest('[type=submit]'); 1046 1047 if (t.length === 0) { 1048 return; 1049 } 1050 target = t[0]; 1051 } 1052 1053 var form = target.form; 1054 1055 form.clk = target; 1056 1057 if (target.type === 'image') { 1058 if (typeof e.offsetX !== 'undefined') { 1059 form.clk_x = e.offsetX; 1060 form.clk_y = e.offsetY; 1061 1062 } else if (typeof $.fn.offset === 'function') { 1063 var offset = $el.offset(); 1064 1065 form.clk_x = e.pageX - offset.left; 1066 form.clk_y = e.pageY - offset.top; 1067 1068 } else { 1069 form.clk_x = e.pageX - target.offsetLeft; 1070 form.clk_y = e.pageY - target.offsetTop; 1071 } 1072 } 1073 // clear form vars 1074 setTimeout(function() { 1075 form.clk = form.clk_x = form.clk_y = null; 1076 }, 100); 1077 } 1078 1079 1080 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 1081 $.fn.ajaxFormUnbind = function() { 1082 return this.off('submit.form-plugin click.form-plugin'); 1083 }; 1084 1085 /** 1086 * formToArray() gathers form element data into an array of objects that can 1087 * be passed to any of the following ajax functions: $.get, $.post, or load. 1088 * Each object in the array has both a 'name' and 'value' property. An example of 1089 * an array for a simple login form might be: 1090 * 1091 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 1092 * 1093 * It is this array that is passed to pre-submit callback functions provided to the 1094 * ajaxSubmit() and ajaxForm() methods. 1095 */ 1096 $.fn.formToArray = function(semantic, elements, filtering) { 1097 var a = []; 1098 1099 if (this.length === 0) { 1100 return a; 1101 } 1102 1103 var form = this[0]; 1104 var formId = this.attr('id'); 1105 var els = (semantic || typeof form.elements === 'undefined') ? form.getElementsByTagName('*') : form.elements; 1106 var els2; 1107 1108 if (els) { 1109 els = $.makeArray(els); // convert to standard array 1110 } 1111 1112 // #386; account for inputs outside the form which use the 'form' attribute 1113 // FinesseRus: in non-IE browsers outside fields are already included in form.elements. 1114 if (formId && (semantic || /(Edge|Trident)\//.test(navigator.userAgent))) { 1115 els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet 1116 if (els2.length) { 1117 els = (els || []).concat(els2); 1118 } 1119 } 1120 1121 if (!els || !els.length) { 1122 return a; 1123 } 1124 1125 if (typeof(filtering) === "function") { 1126 els = $.map(els, filtering); 1127 } 1128 1129 var i, j, n, v, el, max, jmax; 1130 1131 for (i = 0, max = els.length; i < max; i++) { 1132 el = els[i]; 1133 n = el.name; 1134 if (!n || el.disabled) { 1135 continue; 1136 } 1137 1138 if (semantic && form.clk && el.type === 'image') { 1139 // handle image inputs on the fly when semantic == true 1140 if (form.clk === el) { 1141 a.push({name: n, value: $(el).val(), type: el.type}); 1142 a.push({name: n + '.x', value: form.clk_x}
1142, {name: n + '.y', value: form.clk_y}); 1143 } 1144 continue; 1145 } 1146 1147 v = $.fieldValue(el, true); 1148 if (v && v.constructor === Array) { 1149 if (elements) { 1150 elements.push(el); 1151 } 1152 for (j = 0, jmax = v.length; j < jmax; j++) { 1153 a.push({name: n, value: v[j]}); 1154 } 1155 1156 } else if (feature.fileapi && el.type === 'file') { 1157 if (elements) { 1158 elements.push(el); 1159 } 1160 1161 var files = el.files; 1162 1163 if (files.length) { 1164 for (j = 0; j < files.length; j++) { 1165 a.push({name: n, value: files[j], type: el.type}); 1166 } 1167 } else { 1168 // #180 1169 a.push({name: n, value: '', type: el.type}); 1170 } 1171 1172 } else if (v !== null && typeof v !== 'undefined') { 1173 if (elements) { 1174 elements.push(el); 1175 } 1176 a.push({name: n, value: v, type: el.type, required: el.required}); 1177 } 1178 } 1179 1180 if (!semantic && form.clk) { 1181 // input type=='image' are not found in elements array! handle it here 1182 var $input = $(form.clk), input = $input[0]; 1183 1184 n = input.name; 1185 1186 if (n && !input.disabled && input.type === 'image') { 1187 a.push({name: n, value: $input.val()}); 1188 a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y}); 1189 } 1190 } 1191 1192 return a; 1193 }; 1194 1195 /** 1196 * Serializes form data into a 'submittable' string. This method will return a string 1197 * in the format: name1=value1&name2=value2 1198 */ 1199 $.fn.formSerialize = function(semantic) { 1200 // hand off to jQuery.param for proper encoding 1201 return $.param(this.formToArray(semantic)); 1202 }; 1203 1204 /** 1205 * Serializes all field elements in the jQuery object into a query string. 1206 * This method will return a string in the format: name1=value1&name2=value2 1207 */ 1208 $.fn.fieldSerialize = function(successful) { 1209 var a = []; 1210 1211 this.each(function() { 1212 var n = this.name; 1213 1214 if (!n) { 1215 return; 1216 } 1217 1218 var v = $.fieldValue(this, successful); 1219 1220 if (v && v.constructor === Array) { 1221 for (var i = 0, max = v.length; i < max; i++) { 1222 a.push({name: n, value: v[i]}); 1223 } 1224 1225 } else if (v !== null && typeof v !== 'undefined') { 1226 a.push({name: this.name, value: v}); 1227 } 1228 }); 1229 1230 // hand off to jQuery.param for proper encoding 1231 return $.param(a); 1232 }; 1233 1234 /** 1235 * Returns the value(s) of the element in the matched set. For example, consider the following form: 1236 * 1237 * <form><fieldset> 1238 * <input name="A" type="text"> 1239 * <input name="A" type="text"> 1240 * <input name="B" type="checkbox" value="B1"> 1241 * <input name="B" type="checkbox" value="B2"> 1242 * <input name="C" type="radio" value="C1"> 1243 * <input name="C" type="radio" value="C2"> 1244 * </fieldset></form> 1245 * 1246 * var v = $('input[type=text]').fieldValue(); 1247 * // if no values are entered into the text inputs 1248 * v === ['',''] 1249 * // if values entered into the text inputs are 'foo' and 'bar' 1250 * v === ['foo','bar'] 1251 * 1252 * var v = $('input[type=checkbox]').fieldValue(); 1253 * // if neither checkbox is checked 1254 * v === undefined 1255 * // if both checkboxes are checked 1256 * v === ['B1', 'B2'] 1257 * 1258 * var v = $('input[type=radio]').fieldValue(); 1259 * // if neither radio is checked 1260 * v === undefined 1261 * // if first radio is checked 1262 * v === ['C1'] 1263 * 1264 * The successful argument controls whether or not the field element must be 'successful' 1265 * (per https://www.w3.org/TR/html4/interact/forms.html#successful-controls). 1266 * The default value of the successful argument is true. If this value is false the value(s) 1267 * for each element is returned. 1268 * 1269 * Note: This method *always* returns an array. If no valid value can be determined the 1270 * array will be empty, otherwise it will contain one or more values. 1271 */ 1272 $.fn.fieldValue = function(successful) { 1273 for (var val = [], i = 0, max = this.length; i < max; i++) { 1274 var el = this[i]; 1275 var v = $.fieldValue(el, successful); 1276 1277 if (v === null || typeof v === 'undefined' || (v.constructor === Array && !v.length)) { 1278 continue; 1279 } 1280 1281 if (Array.isArray(v)) { 1282 val = val.concat(v); 1283 } else { 1284 val.push(v); 1285 } 1286 } 1287 1288 return val; 1289 }; 1290 1291 /** 1292 * Returns the value of the field element. 1293 */ 1294 $.fieldValue = function(el, successful) { 1295 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 1296 1297 if (typeof successful === 'undefined') { 1298 successful = true; 1299 } 1300 1301 /* eslint-disable no-mixed-operators */ 1302 if (successful && (!n || el.disabled || t === 'reset' || t === 'button' || 1303 (t === 'checkbox' || t === 'radio') && !el.checked || 1304 (t === 'submit' || t === 'image') && el.form && el.form.clk !== el || 1305 tag === 'select' && el.selectedIndex === -1)) { 1306 /* eslint-enable no-mixed-operators */ 1307 return null; 1308 } 1309 1310 if (tag === 'select') { 1311 var index = el.selectedIndex; 1312 1313 if (index < 0) { 1314 return null; 1315 } 1316 1317 var a = [], ops = el.options; 1318 var one = (t === 'select-one'); 1319 var max = (one ? index + 1 : ops.length); 1320 1321 for (var i = (one ? index : 0); i < max; i++) { 1322 var op = ops[i]; 1323 1324 if (op.selected && !op.disabled) { 1325 var v = op.value; 1326 1327 if (!v) { // extra pain for IE... 1328 v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; 1329 } 1330 1331 if (one) { 1332 return v; 1333 } 1334 1335 a.push(v); 1336 } 1337 } 1338 1339 return a; 1340 } 1341 1342 return $(el).val().replace(rCRLF, '\r\n'); 1343 }; 1344 1345 /** 1346 * Clears the form data. Takes the following actions on the form's input fields: 1347 * - input text fields will have their 'value' property set to the empty string 1348 * - select elements will have their 'selectedIndex' property set to -1 1349 * - checkbox and radio inputs will have their 'checked' property set to false 1350 * - inputs of type submit, button, reset, and hidden will *not* be effected 1351 * - button elements will *not* be effected 1352 */ 1353 $.fn.clearForm = function(includeHidden) { 1354 return this.each(function() { 1355 $('input,select,textarea', this).clearFields(includeHidden); 1356 }); 1357 }; 1358 1359 /** 1360 * Clears the selected form elements. 1361 */ 1362 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 1363 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 1364 1365 return this.each(function() { 1366 var t = this.type, tag = this.tagName.toLowerCase(); 1367 1368 if (re.test(t) || tag === 'textarea') { 1369 this.value = ''; 1370 1371 } else if (t === 'checkbox' || t === 'radio') { 1372 this.checked = false;
1373 1374 } else if (tag === 'select') { 1375 this.selectedIndex = -1; 1376 1377 } else if (t === 'file') { 1378 if (/MSIE/.test(navigator.userAgent)) { 1379 $(this).replaceWith($(this).clone(true)); 1380 } else { 1381 $(this).val(''); 1382 } 1383 1384 } else if (includeHidden) { 1385 // includeHidden can be the value true, or it can be a selector string 1386 // indicating a special test; for example: 1387 // $('#myForm').clearForm('.special:hidden') 1388 // the above would clean hidden inputs that have the class of 'special' 1389 if ((includeHidden === true && /hidden/.test(t)) || 1390 (typeof includeHidden === 'string' && $(this).is(includeHidden))) { 1391 this.value = ''; 1392 } 1393 } 1394 }); 1395 }; 1396 1397 1398 /** 1399 * Resets the form data or individual elements. Takes the following actions 1400 * on the selected tags: 1401 * - all fields within form elements will be reset to their original value 1402 * - input / textarea / select fields will be reset to their original value 1403 * - option / optgroup fields (for multi-selects) will defaulted individually 1404 * - non-multiple options will find the right select to default 1405 * - label elements will be searched against its 'for' attribute 1406 * - all others will be searched for appropriate children to default 1407 */ 1408 $.fn.resetForm = function() { 1409 return this.each(function() { 1410 var el = $(this); 1411 var tag = this.tagName.toLowerCase(); 1412 1413 switch (tag) { 1414 case 'input': 1415 this.checked = this.defaultChecked; 1416 // fall through 1417 1418 case 'textarea': 1419 this.value = this.defaultValue; 1420 1421 return true; 1422 1423 case 'option': 1424 case 'optgroup': 1425 var select = el.parents('select'); 1426 1427 if (select.length && select[0].multiple) { 1428 if (tag === 'option') { 1429 this.selected = this.defaultSelected; 1430 } else { 1431 el.find('option').resetForm(); 1432 } 1433 } else { 1434 select.resetForm(); 1435 } 1436 1437 return true; 1438 1439 case 'select': 1440 el.find('option').each(function(i) { // eslint-disable-line consistent-return 1441 this.selected = this.defaultSelected; 1442 if (this.defaultSelected && !el[0].multiple) { 1443 el[0].selectedIndex = i; 1444 1445 return false; 1446 } 1447 }); 1448 1449 return true; 1450 1451 case 'label': 1452 var forEl = $(el.attr('for')); 1453 var list = el.find('input,select,textarea'); 1454 1455 if (forEl[0]) { 1456 list.unshift(forEl[0]); 1457 } 1458 1459 list.resetForm(); 1460 1461 return true; 1462 1463 case 'form': 1464 // guard against an input with the name of 'reset' 1465 // note that IE reports the reset function as an 'object' 1466 if (typeof this.reset === 'function' || (typeof this.reset === 'object' && !this.reset.nodeType)) { 1467 this.reset(); 1468 } 1469 1470 return true; 1471 1472 default: 1473 el.find('form,input,label,select,textarea').resetForm(); 1474 1475 return true; 1476 } 1477 }); 1478 }; 1479 1480 /** 1481 * Enables or disables any matching elements. 1482 */ 1483 $.fn.enable = function(b) { 1484 if (typeof b === 'undefined') { 1485 b = true; 1486 } 1487 1488 return this.each(function() { 1489 this.disabled = !b; 1490 }); 1491 }; 1492 1493 /** 1494 * Checks/unchecks any matching checkboxes or radio buttons and 1495 * selects/deselects and matching option elements. 1496 */ 1497 $.fn.selected = function(select) { 1498 if (typeof select === 'undefined') { 1499 select = true; 1500 } 1501 1502 return this.each(function() { 1503 var t = this.type; 1504 1505 if (t === 'checkbox' || t === 'radio') { 1506 this.checked = select; 1507 1508 } else if (this.tagName.toLowerCase() === 'option') { 1509 var $sel = $(this).parent('select'); 1510 1511 if (select && $sel[0] && $sel[0].type === 'select-one') { 1512 // deselect all other options 1513 $sel.find('option').selected(false); 1514 } 1515 1516 this.selected = select; 1517 } 1518 }); 1519 }; 1520 1521 // expose debug var 1522 $.fn.ajaxSubmit.debug = false; 1523 1524 // helper fn for console logging 1525 function log() { 1526 if (!$.fn.ajaxSubmit.debug) { 1527 return; 1528 } 1529 1530 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, ''); 1531 1532 if (window.console && window.console.log) { 1533 window.console.log(msg); 1534 1535 } else if (window.opera && window.opera.postError) { 1536 window.opera.postError(msg); 1537 } 1538 } 1539}));
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.