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