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