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