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