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