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