1/*! 2 * jQuery Form Plugin 3 * version: 3.08 (07-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 var position = event.loaded || event.position; /*event.position is deprecated*/ 244 var total = event.total; 245 if (event.lengthComputable) {
246 percent = Math.ceil(position / total * 100); 247 } 248 options.uploadProgress(event, position, total, percent); 249 }; 250 } 251 return xhr; 252 }; 253 } 254 255 s.data = null; 256 var beforeSend = s.beforeSend; 257 s.beforeSend = function(xhr, o) { 258 o.data = formdata; 259 if(beforeSend) 260 beforeSend.call(o, xhr, options); 261 }; 262 $.ajax(s); 263 } 264 265 // private function for handling file uploads (hat tip to YAHOO!) 266 function fileUploadIframe(a) { 267 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 268 var useProp = !!$.fn.prop; 269 270 if ($(':input[name=submit],:input[id=submit]', form).length) { 271 // if there is an input with a name or id of 'submit' then we won't be 272 // able to invoke the submit fn on the form (at least not x-browser) 273 alert('Error: Form elements must not have name or id of "submit".'); 274 return; 275 } 276 277 if (a) { 278 // ensure that every serialized input is still enabled 279 for (i=0; i < elements.length; i++) { 280 el = $(elements[i]); 281 if ( useProp ) 282 el.prop('disabled', false); 283 else 284 el.removeAttr('disabled'); 285 } 286 } 287 288 s = $.extend(true, {}, $.ajaxSettings, options); 289 s.context = s.context || s; 290 id = 'jqFormIO' + (new Date().getTime()); 291 if (s.iframeTarget) { 292 $io = $(s.iframeTarget); 293 n = $io.attr('name'); 294 if (!n) 295 $io.attr('name', id); 296 else 297 id = n; 298 } 299 else { 300 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); 301 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 302 } 303 io = $io[0]; 304 305 306 xhr = { // mock object 307 aborted: 0, 308 responseText: null, 309 responseXML: null, 310 status: 0, 311 statusText: 'n/a', 312 getAllResponseHeaders: function() {}, 313 getResponseHeader: function() {}, 314 setRequestHeader: function() {}, 315 abort: function(status) { 316 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 317 log('aborting upload... ' + e); 318 this.aborted = 1; 319 $io.attr('src', s.iframeSrc); // abort op in progress 320 xhr.error = e; 321 if (s.error) 322 s.error.call(s.context, xhr, e, status); 323 if (g) 324 $.event.trigger("ajaxError", [xhr, s, e]); 325 if (s.complete) 326 s.complete.call(s.context, xhr, e); 327 } 328 }; 329 330 g = s.global; 331 // trigger ajax global events so that activity/block indicators work like normal 332 if (g && 0 === $.active++) { 333 $.event.trigger("ajaxStart"); 334 } 335 if (g) { 336 $.event.trigger("ajaxSend", [xhr, s]); 337 } 338 339 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 340 if (s.global) { 341 $.active--; 342 } 343 return; 344 } 345 if (xhr.aborted) { 346 return; 347 } 348 349 // add submitting element to data if we know it 350 sub = form.clk; 351 if (sub) { 352 n = sub.name; 353 if (n && !sub.disabled) { 354 s.extraData = s.extraData || {}; 355 s.extraData[n] = sub.value; 356 if (sub.type == "image") { 357 s.extraData[n+'.x'] = form.clk_x; 358 s.extraData[n+'.y'] = form.clk_y; 359 } 360 } 361 } 362 363 var CLIENT_TIMEOUT_ABORT = 1; 364 var SERVER_ABORT = 2; 365 366 function getDoc(frame) { 367 var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document; 368 return doc; 369 } 370 371 // Rails CSRF hack (thanks to Yvan Barthelemy) 372 var csrf_token = $('meta[name=csrf-token]').attr('content'); 373 var csrf_param = $('meta[name=csrf-param]').attr('content'); 374 if (csrf_param && csrf_token) { 375 s.extraData = s.extraData || {}; 376 s.extraData[csrf_param] = csrf_token; 377 } 378 379 // take a breath so that pending repaints get some cpu time before the upload starts 380 function doSubmit() { 381 // make sure form attrs are set 382 var t = $form.attr('target'), a = $form.attr('action'); 383 384 // update form attrs in IE friendly way 385 form.setAttribute('target',id);
386 if (!method) { 387 form.setAttribute('method', 'POST'); 388 } 389 if (a != s.url) { 390 form.setAttribute('action', s.url); 391 } 392 393 // ie borks in some cases when setting encoding 394 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) { 395 $form.attr({ 396 encoding: 'multipart/form-data', 397 enctype: 'multipart/form-data' 398 }); 399 } 400 401 // support timout 402 if (s.timeout) { 403 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); 404 } 405 406 // look for server aborts 407 function checkState() { 408 try { 409 var state = getDoc(io).readyState; 410 log('state = ' + state); 411 if (state && state.toLowerCase() == 'uninitialized') 412 setTimeout(checkState,50); 413 } 414 catch(e) { 415 log('Server abort: ' , e, ' (', e.name, ')'); 416 cb(SERVER_ABORT); 417 if (timeoutHandle) 418 clearTimeout(timeoutHandle); 419 timeoutHandle = undefined; 420 } 421 } 422 423 // add "extra" data to form if provided in options 424 var extraInputs = []; 425 try { 426 if (s.extraData) { 427 for (var n in s.extraData) { 428 if (s.extraData.hasOwnProperty(n)) { 429 extraInputs.push( 430 $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n]) 431 .appendTo(form)[0]); 432 } 433 } 434 } 435 436 if (!s.iframeTarget) { 437 // add iframe to doc and submit the form 438 $io.appendTo('body'); 439 if (io.attachEvent) 440 io.attachEvent('onload', cb); 441 else 442 io.addEventListener('load', cb, false); 443 } 444 setTimeout(checkState,15); 445 form.submit(); 446 } 447 finally { 448 // reset attrs and remove "extra" input elements 449 form.setAttribute('action',a); 450 if(t) { 451 form.setAttribute('target', t); 452 } else { 453 $form.removeAttr('target'); 454 } 455 $(extraInputs).remove(); 456 } 457 } 458 459 if (s.forceSync) { 460 doSubmit(); 461 } 462 else { 463 setTimeout(doSubmit, 10); // this lets dom updates render 464 } 465 466 var data, doc, domCheckCount = 50, callbackProcessed; 467 468 function cb(e) { 469 if (xhr.aborted || callbackProcessed) { 470 return; 471 } 472 try { 473 doc = getDoc(io); 474 } 475 catch(ex) { 476 log('cannot access response document: ', ex); 477 e = SERVER_ABORT; 478 } 479 if (e === CLIENT_TIMEOUT_ABORT && xhr) { 480 xhr.abort('timeout'); 481 return; 482 } 483 else if (e == SERVER_ABORT && xhr) { 484 xhr.abort('server abort'); 485 return; 486 } 487 488 if (!doc || doc.location.href == s.iframeSrc) { 489 // response not received yet 490 if (!timedOut) 491 return; 492 } 493 if (io.detachEvent) 494 io.detachEvent('onload', cb); 495 else 496 io.removeEventListener('load', cb, false); 497 498 var status = 'success', errMsg; 499 try { 500 if (timedOut) { 501 throw 'timeout'; 502 } 503 504 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 505 log('isXml='+isXml); 506 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 507 if (--domCheckCount) { 508 // in some browsers (Opera) the iframe DOM is not always traversable when 509 // the onload callback fires, so we loop a bit to accommodate 510 log('requeing onLoad callback, DOM not available'); 511 setTimeout(cb, 250); 512 return; 513 } 514 // let this fall through because server response could be an empty document 515 //log('Could not access iframe DOM after mutiple tries.'); 516 //throw 'DOMException: not available'; 517 } 518 519 //log('response detected'); 520 var docRoot = doc.body ? doc.body : doc.documentElement; 521 xhr.responseText = docRoot ? docRoot.innerHTML : null; 522 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 523 if (isXml) 524 s.dataType = 'xml'; 525 xhr.getResponseHeader = function(header){ 526 var headers = {'content-type': s.dataType}; 527 return headers[header]; 528 }; 529 // support for XHR 'status' & 'statusText' emulation : 530 if (docRoot) { 531 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status; 532 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 533 } 534 535 var dt = (s.dataType || '').toLowerCase(); 536 var scr = /(json|script|text)/.test(dt); 537 if (scr || s.textarea) { 538 // see if user embedded response in textarea 539 var ta = doc.getElementsByTagName('textarea')[0]; 540 if (ta) { 541 xhr.responseText = ta.value; 542 // support for XHR 'status' & 'statusText' emulation : 543 xhr.status = Number( ta.getAttribute('status') ) || xhr.status; 544 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 545 } 546 else if (scr) { 547 // account for browsers injecting pre around json response 548 var pre = doc.getElementsByTagName('pre')[0]; 549 var b = doc.getElementsByTagName('body')[0]; 550 if (pre) { 551 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 552 } 553 else if (b) { 554 xhr.responseText = b.textContent ? b.textContent : b.innerText; 555 } 556 } 557 } 558 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { 559 xhr.responseXML = toXml(xhr.responseText); 560 } 561 562 try { 563 data = httpData(xhr, dt, s); 564 } 565 catch (e) { 566 status = 'parsererror'; 567 xhr.error = errMsg = (e || status); 568 } 569 } 570 catch (e) { 571 log('error caught: ',e); 572 status = 'error'; 573 xhr.error = errMsg = (e || status); 574 } 575 576 if (xhr.aborted) { 577 log('upload aborted'); 578 status = null; 579 } 580 581 if (xhr.status) { // we've set xhr.status 582 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; 583 } 584 585 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 586 if (status === 'success') { 587 if (s.success) 588 s.success.call(s.context, data, 'success', xhr); 589 if (g) 590 $.event.trigger("ajaxSuccess", [xhr, s]); 591 } 592 else if (status) { 593 if (errMsg === undefined) 594 errMsg = xhr.statusText; 595 if (s.error) 596 s.error.call(s.context, xhr, status, errMsg); 597 if (g) 598 $.event.trigger("ajaxError", [xhr, s, errMsg]); 599 } 600 601 if (g)
602 $.event.trigger("ajaxComplete", [xhr, s]); 603 604 if (g && ! --$.active) { 605 $.event.trigger("ajaxStop"); 606 } 607 608 if (s.complete) 609 s.complete.call(s.context, xhr, status); 610 611 callbackProcessed = true; 612 if (s.timeout) 613 clearTimeout(timeoutHandle); 614 615 // clean up 616 setTimeout(function() { 617 if (!s.iframeTarget) 618 $io.remove(); 619 xhr.responseXML = null; 620 }, 100); 621 } 622 623 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 624 if (window.ActiveXObject) { 625 doc = new ActiveXObject('Microsoft.XMLDOM'); 626 doc.async = 'false'; 627 doc.loadXML(s); 628 } 629 else { 630 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 631 } 632 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; 633 }; 634 var parseJSON = $.parseJSON || function(s) { 635 /*jslint evil:true */ 636 return window['eval']('(' + s + ')'); 637 }; 638 639 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 640 641 var ct = xhr.getResponseHeader('content-type') || '', 642 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, 643 data = xml ? xhr.responseXML : xhr.responseText; 644 645 if (xml && data.documentElement.nodeName === 'parsererror') { 646 if ($.error) 647 $.error('parsererror'); 648 } 649 if (s && s.dataFilter) { 650 data = s.dataFilter(data, type); 651 } 652 if (typeof data === 'string') { 653 if (type === 'json' || !type && ct.indexOf('json') >= 0) { 654 data = parseJSON(data); 655 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { 656 $.globalEval(data); 657 } 658 } 659 return data; 660 }; 661 } 662}; 663 664/** 665 * ajaxForm() provides a mechanism for fully automating form submission. 666 * 667 * The advantages of using this method instead of ajaxSubmit() are: 668 * 669 * 1: This method will include coordinates for <input type="image" /> elements (if the element 670 * is used to submit the form). 671 * 2. This method will include the submit element's name/value data (for the element that was 672 * used to submit the form). 673 * 3. This method binds the submit() method to the form for you. 674 * 675 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 676 * passes the options argument along after properly binding events for submit elements and 677 * the form itself. 678 */ 679$.fn.ajaxForm = function(options) { 680 options = options || {}; 681 options.delegation = options.delegation && $.isFunction($.fn.on); 682 683 // in jQuery 1.3+ we can fix mistakes with the ready state 684 if (!options.delegation && this.length === 0) { 685 var o = { s: this.selector, c: this.context }; 686 if (!$.isReady && o.s) { 687 log('DOM not ready, queuing ajaxForm'); 688 $(function() { 689 $(o.s,o.c).ajaxForm(options); 690 }); 691 return this; 692 } 693 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 694 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 695 return this; 696 } 697 698 if ( options.delegation ) { 699 $(document) 700 .off('submit.form-plugin', this.selector, doAjaxSubmit) 701 .off('click.form-plugin', this.selector, captureSubmittingElement) 702 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 703 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 704 return this; 705 } 706 707 return this.ajaxFormUnbind() 708 .bind('submit.form-plugin', options, doAjaxSubmit) 709 .bind('click.form-plugin', options, captureSubmittingElement); 710}; 711 712// private event handlers 713function doAjaxSubmit(e) { 714 /*jshint validthis:true */ 715 var options = e.data; 716 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 717 e.preventDefault(); 718 $(this).ajaxSubmit(options); 719 } 720} 721 722function captureSubmittingElement(e) { 723 /*jshint validthis:true */ 724 var target = e.target; 725 var $el = $(target); 726 if (!($el.is(":submit,input:image"))) { 727 // is this a child element of the submit el? (ex: a span within a button) 728 var t = $el.closest(':submit'); 729 if (t.length === 0) { 730 return; 731 } 732 target = t[0]; 733 } 734 var form = this; 735 form.clk = target; 736 if (target.type == 'image') { 737 if (e.offsetX !== undefined) { 738 form.clk_x = e.offsetX; 739 form.clk_y = e.offsetY; 740 } else if (typeof $.fn.offset == 'function') { 741 var offset = $el.offset(); 742 form.clk_x = e.pageX - offset.left; 743 form.clk_y = e.pageY - offset.top; 744 } else { 745 form.clk_x = e.pageX - target.offsetLeft; 746 form.clk_y = e.pageY - target.offsetTop; 747 } 748 } 749 // clear form vars 750 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); 751} 752 753 754// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 755$.fn.ajaxFormUnbind = function() { 756 return this.unbind('submit.form-plugin click.form-plugin'); 757}; 758 759/** 760 * formToArray() gathers form element data into an array of objects that can 761 * be passed to any of the following ajax functions: $.get, $.post, or load. 762 * Each object in the array has both a 'name' and 'value' property. An example of 763 * an array for a simple login form might be: 764 * 765 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 766 * 767 * It is this array that is passed to pre-submit callback functions provided to the 768 * ajaxSubmit() and ajaxForm() methods. 769 */ 770$.fn.formToArray = function(semantic, elements) { 771 var a = []; 772 if (this.length === 0) { 773 return a; 774 } 775 776 var form = this[0]; 777 var els = semantic ? form.getElementsByTagName('*') : form.elements; 778 if (!els) { 779 return a; 780 } 781 782 var i,j,n,v,el,max,jmax; 783 for(i=0, max=els.length; i < max; i++) { 784 el = els[i]; 785 n = el.name; 786 if (!n) { 787 continue; 788 } 789 790 if (semantic && form.clk && el.type == "image") { 791 // handle image inputs on the fly when semantic == true 792 if(!el.disabled && form.clk == el) { 793 a.push({name: n, value: $(el).val(), type: el.type }); 794 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 795 } 796 continue; 797 } 798 799 v = $.fieldValue(el, true); 800 if (v && v.constructor == Array) { 801 if (elements) 802 elements.push(el); 803 for(j=0, jmax=v.length; j < jmax; j++) { 804 a.push({name: n, value: v[j]}); 805 } 806 } 807 else if (feature.fileapi && el.type == 'file' && !el.disabled) { 808 if (elements) 809 elements.push(el); 810 var files = el.files; 811 for (j=0; j < files.length; j++) { 812 a.push({name: n, value: files[j], type: el.type}); 813 } 814 } 815 else if (v !== null && typeof v != 'undefined') { 816 if (elements) 817 elements.push(el); 818 a.push({name: n, value: v, type: el.type, required: el.required}); 819 } 820 } 821 822 if (!semantic && form.clk) { 823 // input type=='image' are not found in elements array! handle it here 824 var $input = $(form.clk), input = $input[0]; 825 n = input.name; 826 if (n && !input.disabled && input.type == 'image') { 827 a.push({name: n, value: $input.val()}); 828 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 829 } 830 } 831 return a; 832}; 833 834/** 835 * Serializes form data into a 'submittable' string. This method will return a string 836 * in the format: name1=value1&name2=value2 837 */
838$.fn.formSerialize = function(semantic) { 839 //hand off to jQuery.param for proper encoding 840 return $.param(this.formToArray(semantic)); 841}; 842 843/** 844 * Serializes all field elements in the jQuery object into a query string. 845 * This method will return a string in the format: name1=value1&name2=value2 846 */ 847$.fn.fieldSerialize = function(successful) { 848 var a = []; 849 this.each(function() { 850 var n = this.name; 851 if (!n) { 852 return; 853 } 854 var v = $.fieldValue(this, successful); 855 if (v && v.constructor == Array) { 856 for (var i=0,max=v.length; i < max; i++) { 857 a.push({name: n, value: v[i]}); 858 } 859 } 860 else if (v !== null && typeof v != 'undefined') { 861 a.push({name: this.name, value: v}); 862 } 863 }); 864 //hand off to jQuery.param for proper encoding 865 return $.param(a); 866}; 867 868/** 869 * Returns the value(s) of the element in the matched set. For example, consider the following form: 870 * 871 * <form><fieldset> 872 * <input name="A" type="text" /> 873 * <input name="A" type="text" /> 874 * <input name="B" type="checkbox" value="B1" /> 875 * <input name="B" type="checkbox" value="B2"/> 876 * <input name="C" type="radio" value="C1" /> 877 * <input name="C" type="radio" value="C2" /> 878 * </fieldset></form> 879 * 880 * var v = $(':text').fieldValue(); 881 * // if no values are entered into the text inputs 882 * v == ['',''] 883 * // if values entered into the text inputs are 'foo' and 'bar' 884 * v == ['foo','bar'] 885 * 886 * var v = $(':checkbox').fieldValue(); 887 * // if neither checkbox is checked 888 * v === undefined 889 * // if both checkboxes are checked 890 * v == ['B1', 'B2'] 891 * 892 * var v = $(':radio').fieldValue(); 893 * // if neither radio is checked 894 * v === undefined 895 * // if first radio is checked 896 * v == ['C1'] 897 * 898 * The successful argument controls whether or not the field element must be 'successful' 899 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). 900 * The default value of the successful argument is true. If this value is false the value(s) 901 * for each element is returned. 902 * 903 * Note: This method *always* returns an array. If no valid value can be determined the 904 * array will be empty, otherwise it will contain one or more values. 905 */ 906$.fn.fieldValue = function(successful) { 907 for (var val=[], i=0, max=this.length; i < max; i++) { 908 var el = this[i]; 909 var v = $.fieldValue(el, successful); 910 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { 911 continue; 912 } 913 if (v.constructor == Array) 914 $.merge(val, v); 915 else 916 val.push(v); 917 } 918 return val; 919}; 920 921/** 922 * Returns the value of the field element. 923 */ 924$.fieldValue = function(el, successful) { 925 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 926 if (successful === undefined) { 927 successful = true; 928 } 929 930 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 931 (t == 'checkbox' || t == 'radio') && !el.checked || 932 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 933 tag == 'select' && el.selectedIndex == -1)) { 934 return null; 935 } 936 937 if (tag == 'select') { 938 var index = el.selectedIndex; 939 if (index < 0) { 940 return null; 941 } 942 var a = [], ops = el.options; 943 var one = (t == 'select-one'); 944 var max = (one ? index+1 : ops.length); 945 for(var i=(one ? index : 0); i < max; i++) { 946 var op = ops[i]; 947 if (op.selected) { 948 var v = op.value; 949 if (!v) { // extra pain for IE... 950 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; 951 } 952 if (one) { 953 return v; 954 } 955 a.push(v); 956 } 957 } 958 return a; 959 } 960 return $(el).val(); 961}; 962 963/** 964 * Clears the form data. Takes the following actions on the form's input fields: 965 * - input text fields will have their 'value' property set to the empty string 966 * - select elements will have their 'selectedIndex' property set to -1 967 * - checkbox and radio inputs will have their 'checked' property set to false 968 * - inputs of type submit, button, reset, and hidden will *not* be effected 969 * - button elements will *not* be effected 970 */ 971$.fn.clearForm = function(includeHidden) { 972 return this.each(function() { 973 $('input,select,textarea', this).clearFields(includeHidden); 974 }); 975}; 976 977/** 978 * Clears the selected form elements. 979 */ 980$.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 981 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 982 return this.each(function() { 983 var t = this.type, tag = this.tagName.toLowerCase(); 984 if (re.test(t) || tag == 'textarea') { 985 this.value = ''; 986 } 987 else if (t == 'checkbox' || t == 'radio') { 988 this.checked = false;
989 } 990 else if (tag == 'select') { 991 this.selectedIndex = -1; 992 } 993 else if (includeHidden) { 994 // includeHidden can be the valud true, or it can be a selector string 995 // indicating a special test; for example: 996 // $('#myForm').clearForm('.special:hidden') 997 // the above would clean hidden inputs that have the class of 'special' 998 if ( (includeHidden === true && /hidden/.test(t)) || 999 (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) 1000 this.value = ''; 1001 } 1002 }); 1003}; 1004 1005/** 1006 * Resets the form data. Causes all form elements to be reset to their original value. 1007 */ 1008$.fn.resetForm = function() { 1009 return this.each(function() { 1010 // guard against an input with the name of 'reset' 1011 // note that IE reports the reset function as an 'object' 1012 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { 1013 this.reset(); 1014 } 1015 }); 1016}; 1017 1018/** 1019 * Enables or disables any matching elements. 1020 */ 1021$.fn.enable = function(b) { 1022 if (b === undefined) { 1023 b = true; 1024 } 1025 return this.each(function() { 1026 this.disabled = !b; 1027 }); 1028}; 1029 1030/** 1031 * Checks/unchecks any matching checkboxes or radio buttons and 1032 * selects/deselects and matching option elements. 1033 */ 1034$.fn.selected = function(select) { 1035 if (select === undefined) { 1036 select = true; 1037 } 1038 return this.each(function() { 1039 var t = this.type; 1040 if (t == 'checkbox' || t == 'radio') { 1041 this.checked = select; 1042 } 1043 else if (this.tagName.toLowerCase() == 'option') { 1044 var $sel = $(this).parent('select'); 1045 if (select && $sel[0] && $sel[0].type == 'select-one') { 1046 // deselect all other options 1047 $sel.find('option').selected(false); 1048 } 1049 this.selected = select; 1050 } 1051 }); 1052}; 1053 1054// expose debug var 1055$.fn.ajaxSubmit.debug = false; 1056 1057// helper fn for console logging 1058function log() { 1059 if (!$.fn.ajaxSubmit.debug) 1060 return; 1061 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); 1062 if (window.console && window.console.log) { 1063 window.console.log(msg); 1064 } 1065 else if (window.opera && window.opera.postError) { 1066 window.opera.postError(msg); 1067 } 1068} 1069 1070})(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.