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