File indexing completed on 2024-05-19 06:00:27
0001 /*! 0002 * jQuery Form Plugin 0003 * version: 3.51.0-2014.06.20 0004 * Requires jQuery v1.5 or later 0005 * Copyright (c) 2014 M. Alsup 0006 * Examples and documentation at: http://malsup.com/jquery/form/ 0007 * Project repository: https://github.com/malsup/form 0008 * Dual licensed under the MIT and GPL licenses. 0009 * https://github.com/malsup/form#copyright-and-license 0010 */ 0011 /*global ActiveXObject */ 0012 0013 // AMD support 0014 (function (factory) { 0015 "use strict"; 0016 if (typeof define === 'function' && define.amd) { 0017 // using AMD; register as anon module 0018 define(['jquery'], factory); 0019 } else { 0020 // no AMD; invoke directly 0021 factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto ); 0022 } 0023 } 0024 0025 (function($) { 0026 "use strict"; 0027 0028 /* 0029 Usage Note: 0030 ----------- 0031 Do not use both ajaxSubmit and ajaxForm on the same form. These 0032 functions are mutually exclusive. Use ajaxSubmit if you want 0033 to bind your own submit handler to the form. For example, 0034 0035 $(document).ready(function() { 0036 $('#myForm').on('submit', function(e) { 0037 e.preventDefault(); // <-- important 0038 $(this).ajaxSubmit({ 0039 target: '#output' 0040 }); 0041 }); 0042 }); 0043 0044 Use ajaxForm when you want the plugin to manage all the event binding 0045 for you. For example, 0046 0047 $(document).ready(function() { 0048 $('#myForm').ajaxForm({ 0049 target: '#output' 0050 }); 0051 }); 0052 0053 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 0054 form does not have to exist when you invoke ajaxForm: 0055 0056 $('#myForm').ajaxForm({ 0057 delegation: true, 0058 target: '#output' 0059 }); 0060 0061 When using ajaxForm, the ajaxSubmit function will be invoked for you 0062 at the appropriate time. 0063 */ 0064 0065 /** 0066 * Feature detection 0067 */ 0068 var feature = {}; 0069 feature.fileapi = $("<input type='file'/>").get(0).files !== undefined; 0070 feature.formdata = window.FormData !== undefined; 0071 0072 var hasProp = !!$.fn.prop; 0073 0074 // attr2 uses prop when it can but checks the return type for 0075 // an expected string. this accounts for the case where a form 0076 // contains inputs with names like "action" or "method"; in those 0077 // cases "prop" returns the element 0078 $.fn.attr2 = function() { 0079 if ( ! hasProp ) { 0080 return this.attr.apply(this, arguments); 0081 } 0082 var val = this.prop.apply(this, arguments); 0083 if ( ( val && val.jquery ) || typeof val === 'string' ) { 0084 return val; 0085 } 0086 return this.attr.apply(this, arguments); 0087 }; 0088 0089 /** 0090 * ajaxSubmit() provides a mechanism for immediately submitting 0091 * an HTML form using AJAX. 0092 */ 0093 $.fn.ajaxSubmit = function(options) { 0094 /*jshint scripturl:true */ 0095 0096 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 0097 if (!this.length) { 0098 log('ajaxSubmit: skipping submit process - no element selected'); 0099 return this; 0100 } 0101 0102 var method, action, url, $form = this; 0103 0104 if (typeof options == 'function') { 0105 options = { success: options }; 0106 } 0107 else if ( options === undefined ) { 0108 options = {}; 0109 } 0110 0111 method = options.type || this.attr2('method'); 0112 action = options.url || this.attr2('action'); 0113 0114 url = (typeof action === 'string') ? $.trim(action) : ''; 0115 url = url || window.location.href || ''; 0116 if (url) { 0117 // clean url (don't include hash vaue) 0118 url = (url.match(/^([^#]+)/)||[])[1]; 0119 } 0120 0121 options = $.extend(true, { 0122 url: url, 0123 success: $.ajaxSettings.success, 0124 type: method || $.ajaxSettings.type, 0125 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' 0126 }, options); 0127 0128 // hook for manipulating the form data before it is extracted; 0129 // convenient for use with rich editors like tinyMCE or FCKEditor 0130 var veto = {}; 0131 this.trigger('form-pre-serialize', [this, options, veto]); 0132 if (veto.veto) { 0133 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 0134 return this; 0135 } 0136 0137 // provide opportunity to alter form data before it is serialized 0138 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 0139 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 0140 return this; 0141 } 0142 0143 var traditional = options.traditional; 0144 if ( traditional === undefined ) { 0145 traditional = $.ajaxSettings.traditional; 0146 } 0147 0148 var elements = []; 0149 var qx, a = this.formToArray(options.semantic, elements); 0150 if (options.data) { 0151 options.extraData = options.data; 0152 qx = $.param(options.data, traditional); 0153 } 0154 0155 // give pre-submit callback an opportunity to abort the submit 0156 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 0157 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 0158 return this; 0159 } 0160 0161 // fire vetoable 'validate' event 0162 this.trigger('form-submit-validate', [a, this, options, veto]); 0163 if (veto.veto) { 0164 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 0165 return this; 0166 } 0167 0168 var q = $.param(a, traditional); 0169 if (qx) { 0170 q = ( q ? (q + '&' + qx) : qx ); 0171 } 0172 if (options.type.toUpperCase() == 'GET') { 0173 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 0174 options.data = null; // data is null for 'get' 0175 } 0176 else { 0177 options.data = q; // data is the query string for 'post' 0178 } 0179 0180 var callbacks = []; 0181 if (options.resetForm) { 0182 callbacks.push(function() { $form.resetForm(); }); 0183 } 0184 if (options.clearForm) { 0185 callbacks.push(function() { $form.clearForm(options.includeHidden); }); 0186 } 0187 0188 // perform a load on the target only if dataType is not provided 0189 if (!options.dataType && options.target) { 0190 var oldSuccess = options.success || function(){}; 0191 callbacks.push(function(data) { 0192 var fn = options.replaceTarget ? 'replaceWith' : 'html'; 0193 $(options.target)[fn](data).each(oldSuccess, arguments); 0194 }); 0195 } 0196 else if (options.success) { 0197 callbacks.push(options.success); 0198 } 0199 0200 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 0201 var context = options.context || this ; // jQuery 1.4+ supports scope context 0202 for (var i=0, max=callbacks.length; i < max; i++) { 0203 callbacks[i].apply(context, [data, status, xhr || $form, $form]); 0204 } 0205 }; 0206 0207 if (options.error) { 0208 var oldError = options.error; 0209 options.error = function(xhr, status, error) { 0210 var context = options.context || this; 0211 oldError.apply(context, [xhr, status, error, $form]); 0212 }; 0213 } 0214 0215 if (options.complete) { 0216 var oldComplete = options.complete; 0217 options.complete = function(xhr, status) { 0218 var context = options.context || this; 0219 oldComplete.apply(context, [xhr, status, $form]); 0220 }; 0221 } 0222 0223 // are there files to upload? 0224 0225 // [value] (issue #113), also see comment: 0226 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 0227 var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; }); 0228 0229 var hasFileInputs = fileInputs.length > 0; 0230 var mp = 'multipart/form-data'; 0231 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 0232 0233 var fileAPI = feature.fileapi && feature.formdata; 0234 log("fileAPI :" + fileAPI); 0235 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 0236 0237 var jqxhr; 0238 0239 // options.iframe allows user to force iframe mode 0240 // 06-NOV-09: now defaulting to iframe mode if file input is detected 0241 if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 0242 // hack to fix Safari hang (thanks to Tim Molendijk for this) 0243 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 0244 if (options.closeKeepAlive) { 0245 $.get(options.closeKeepAlive, function() { 0246 jqxhr = fileUploadIframe(a); 0247 }); 0248 } 0249 else { 0250 jqxhr = fileUploadIframe(a); 0251 } 0252 } 0253 else if ((hasFileInputs || multipart) && fileAPI) { 0254 jqxhr = fileUploadXhr(a); 0255 } 0256 else { 0257 jqxhr = $.ajax(options); 0258 } 0259 0260 $form.removeData('jqxhr').data('jqxhr', jqxhr); 0261 0262 // clear element array 0263 for (var k=0; k < elements.length; k++) { 0264 elements[k] = null; 0265 } 0266 0267 // fire 'notify' event 0268 this.trigger('form-submit-notify', [this, options]); 0269 return this; 0270 0271 // utility fn for deep serialization 0272 function deepSerialize(extraData){ 0273 var serialized = $.param(extraData, options.traditional).split('&'); 0274 var len = serialized.length; 0275 var result = []; 0276 var i, part; 0277 for (i=0; i < len; i++) { 0278 // #252; undo param space replacement 0279 serialized[i] = serialized[i].replace(/\+/g,' '); 0280 part = serialized[i].split('='); 0281 // #278; use array instead of object storage, favoring array serializations 0282 result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); 0283 } 0284 return result; 0285 } 0286 0287 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 0288 function fileUploadXhr(a) { 0289 var formdata = new FormData(); 0290 0291 for (var i=0; i < a.length; i++) { 0292 formdata.append(a[i].name, a[i].value); 0293 } 0294 0295 if (options.extraData) { 0296 var serializedData = deepSerialize(options.extraData); 0297 for (i=0; i < serializedData.length; i++) { 0298 if (serializedData[i]) { 0299 formdata.append(serializedData[i][0], serializedData[i][1]); 0300 } 0301 } 0302 } 0303 0304 options.data = null; 0305 0306 var s = $.extend(true, {}, $.ajaxSettings, options, { 0307 contentType: false, 0308 processData: false, 0309 cache: false, 0310 type: method || 'POST' 0311 }); 0312 0313 if (options.uploadProgress) { 0314 // workaround because jqXHR does not expose upload property 0315 s.xhr = function() { 0316 var xhr = $.ajaxSettings.xhr(); 0317 if (xhr.upload) { 0318 xhr.upload.addEventListener('progress', function(event) { 0319 var percent = 0; 0320 var position = event.loaded || event.position; /*event.position is deprecated*/ 0321 var total = event.total; 0322 if (event.lengthComputable) { 0323 percent = Math.ceil(position / total * 100); 0324 } 0325 options.uploadProgress(event, position, total, percent); 0326 }, false); 0327 } 0328 return xhr; 0329 }; 0330 } 0331 0332 s.data = null; 0333 var beforeSend = s.beforeSend; 0334 s.beforeSend = function(xhr, o) { 0335 //Send FormData() provided by user 0336 if (options.formData) { 0337 o.data = options.formData; 0338 } 0339 else { 0340 o.data = formdata; 0341 } 0342 if(beforeSend) { 0343 beforeSend.call(this, xhr, o); 0344 } 0345 }; 0346 return $.ajax(s); 0347 } 0348 0349 // private function for handling file uploads (hat tip to YAHOO!) 0350 function fileUploadIframe(a) { 0351 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 0352 var deferred = $.Deferred(); 0353 0354 // #341 0355 deferred.abort = function(status) { 0356 xhr.abort(status); 0357 }; 0358 0359 if (a) { 0360 // ensure that every serialized input is still enabled 0361 for (i=0; i < elements.length; i++) { 0362 el = $(elements[i]); 0363 if ( hasProp ) { 0364 el.prop('disabled', false); 0365 } 0366 else { 0367 el.removeAttr('disabled'); 0368 } 0369 } 0370 } 0371 0372 s = $.extend(true, {}, $.ajaxSettings, options); 0373 s.context = s.context || s; 0374 id = 'jqFormIO' + (new Date().getTime()); 0375 if (s.iframeTarget) { 0376 $io = $(s.iframeTarget); 0377 n = $io.attr2('name'); 0378 if (!n) { 0379 $io.attr2('name', id); 0380 } 0381 else { 0382 id = n; 0383 } 0384 } 0385 else { 0386 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); 0387 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 0388 } 0389 io = $io[0]; 0390 0391 0392 xhr = { // mock object 0393 aborted: 0, 0394 responseText: null, 0395 responseXML: null, 0396 status: 0, 0397 statusText: 'n/a', 0398 getAllResponseHeaders: function() {}, 0399 getResponseHeader: function() {}, 0400 setRequestHeader: function() {}, 0401 abort: function(status) { 0402 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 0403 log('aborting upload... ' + e); 0404 this.aborted = 1; 0405 0406 try { // #214, #257 0407 if (io.contentWindow.document.execCommand) { 0408 io.contentWindow.document.execCommand('Stop'); 0409 } 0410 } 0411 catch(ignore) {} 0412 0413 $io.attr('src', s.iframeSrc); // abort op in progress 0414 xhr.error = e; 0415 if (s.error) { 0416 s.error.call(s.context, xhr, e, status); 0417 } 0418 if (g) { 0419 $.event.trigger("ajaxError", [xhr, s, e]); 0420 } 0421 if (s.complete) { 0422 s.complete.call(s.context, xhr, e); 0423 } 0424 } 0425 }; 0426 0427 g = s.global; 0428 // trigger ajax global events so that activity/block indicators work like normal 0429 if (g && 0 === $.active++) { 0430 $.event.trigger("ajaxStart"); 0431 } 0432 if (g) { 0433 $.event.trigger("ajaxSend", [xhr, s]); 0434 } 0435 0436 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 0437 if (s.global) { 0438 $.active--; 0439 } 0440 deferred.reject(); 0441 return deferred; 0442 } 0443 if (xhr.aborted) { 0444 deferred.reject(); 0445 return deferred; 0446 } 0447 0448 // add submitting element to data if we know it 0449 sub = form.clk; 0450 if (sub) { 0451 n = sub.name; 0452 if (n && !sub.disabled) { 0453 s.extraData = s.extraData || {}; 0454 s.extraData[n] = sub.value; 0455 if (sub.type == "image") { 0456 s.extraData[n+'.x'] = form.clk_x; 0457 s.extraData[n+'.y'] = form.clk_y; 0458 } 0459 } 0460 } 0461 0462 var CLIENT_TIMEOUT_ABORT = 1; 0463 var SERVER_ABORT = 2; 0464 0465 function getDoc(frame) { 0466 /* it looks like contentWindow or contentDocument do not 0467 * carry the protocol property in ie8, when running under ssl 0468 * frame.document is the only valid response document, since 0469 * the protocol is know but not on the other two objects. strange? 0470 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy 0471 */ 0472 0473 var doc = null; 0474 0475 // IE8 cascading access check 0476 try { 0477 if (frame.contentWindow) { 0478 doc = frame.contentWindow.document; 0479 } 0480 } catch(err) { 0481 // IE8 access denied under ssl & missing protocol 0482 log('cannot get iframe.contentWindow document: ' + err); 0483 } 0484 0485 if (doc) { // successful getting content 0486 return doc; 0487 } 0488 0489 try { // simply checking may throw in ie8 under ssl or mismatched protocol 0490 doc = frame.contentDocument ? frame.contentDocument : frame.document; 0491 } catch(err) { 0492 // last attempt 0493 log('cannot get iframe.contentDocument: ' + err); 0494 doc = frame.document; 0495 } 0496 return doc; 0497 } 0498 0499 // Rails CSRF hack (thanks to Yvan Barthelemy) 0500 var csrf_token = $('meta[name=csrf-token]').attr('content'); 0501 var csrf_param = $('meta[name=csrf-param]').attr('content'); 0502 if (csrf_param && csrf_token) { 0503 s.extraData = s.extraData || {}; 0504 s.extraData[csrf_param] = csrf_token; 0505 } 0506 0507 // take a breath so that pending repaints get some cpu time before the upload starts 0508 function doSubmit() { 0509 // make sure form attrs are set 0510 var t = $form.attr2('target'), 0511 a = $form.attr2('action'), 0512 mp = 'multipart/form-data', 0513 et = $form.attr('enctype') || $form.attr('encoding') || mp; 0514 0515 // update form attrs in IE friendly way 0516 form.setAttribute('target',id); 0517 if (!method || /post/i.test(method) ) { 0518 form.setAttribute('method', 'POST'); 0519 } 0520 if (a != s.url) { 0521 form.setAttribute('action', s.url); 0522 } 0523 0524 // ie borks in some cases when setting encoding 0525 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) { 0526 $form.attr({ 0527 encoding: 'multipart/form-data', 0528 enctype: 'multipart/form-data' 0529 }); 0530 } 0531 0532 // support timout 0533 if (s.timeout) { 0534 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); 0535 } 0536 0537 // look for server aborts 0538 function checkState() { 0539 try { 0540 var state = getDoc(io).readyState; 0541 log('state = ' + state); 0542 if (state && state.toLowerCase() == 'uninitialized') { 0543 setTimeout(checkState,50); 0544 } 0545 } 0546 catch(e) { 0547 log('Server abort: ' , e, ' (', e.name, ')'); 0548 cb(SERVER_ABORT); 0549 if (timeoutHandle) { 0550 clearTimeout(timeoutHandle); 0551 } 0552 timeoutHandle = undefined; 0553 } 0554 } 0555 0556 // add "extra" data to form if provided in options 0557 var extraInputs = []; 0558 try { 0559 if (s.extraData) { 0560 for (var n in s.extraData) { 0561 if (s.extraData.hasOwnProperty(n)) { 0562 // if using the $.param format that allows for multiple values with the same name 0563 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { 0564 extraInputs.push( 0565 $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value) 0566 .appendTo(form)[0]); 0567 } else { 0568 extraInputs.push( 0569 $('<input type="hidden" name="'+n+'">').val(s.extraData[n]) 0570 .appendTo(form)[0]); 0571 } 0572 } 0573 } 0574 } 0575 0576 if (!s.iframeTarget) { 0577 // add iframe to doc and submit the form 0578 $io.appendTo('body'); 0579 } 0580 if (io.attachEvent) { 0581 io.attachEvent('onload', cb); 0582 } 0583 else { 0584 io.addEventListener('load', cb, false); 0585 } 0586 setTimeout(checkState,15); 0587 0588 try { 0589 form.submit(); 0590 } catch(err) { 0591 // just in case form has element with name/id of 'submit' 0592 var submitFn = document.createElement('form').submit; 0593 submitFn.apply(form); 0594 } 0595 } 0596 finally { 0597 // reset attrs and remove "extra" input elements 0598 form.setAttribute('action',a); 0599 form.setAttribute('enctype', et); // #380 0600 if(t) { 0601 form.setAttribute('target', t); 0602 } else { 0603 $form.removeAttr('target'); 0604 } 0605 $(extraInputs).remove(); 0606 } 0607 } 0608 0609 if (s.forceSync) { 0610 doSubmit(); 0611 } 0612 else { 0613 setTimeout(doSubmit, 10); // this lets dom updates render 0614 } 0615 0616 var data, doc, domCheckCount = 50, callbackProcessed; 0617 0618 function cb(e) { 0619 if (xhr.aborted || callbackProcessed) { 0620 return; 0621 } 0622 0623 doc = getDoc(io); 0624 if(!doc) { 0625 log('cannot access response document'); 0626 e = SERVER_ABORT; 0627 } 0628 if (e === CLIENT_TIMEOUT_ABORT && xhr) { 0629 xhr.abort('timeout'); 0630 deferred.reject(xhr, 'timeout'); 0631 return; 0632 } 0633 else if (e == SERVER_ABORT && xhr) { 0634 xhr.abort('server abort'); 0635 deferred.reject(xhr, 'error', 'server abort'); 0636 return; 0637 } 0638 0639 if (!doc || doc.location.href == s.iframeSrc) { 0640 // response not received yet 0641 if (!timedOut) { 0642 return; 0643 } 0644 } 0645 if (io.detachEvent) { 0646 io.detachEvent('onload', cb); 0647 } 0648 else { 0649 io.removeEventListener('load', cb, false); 0650 } 0651 0652 var status = 'success', errMsg; 0653 try { 0654 if (timedOut) { 0655 throw 'timeout'; 0656 } 0657 0658 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 0659 log('isXml='+isXml); 0660 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 0661 if (--domCheckCount) { 0662 // in some browsers (Opera) the iframe DOM is not always traversable when 0663 // the onload callback fires, so we loop a bit to accommodate 0664 log('requeing onLoad callback, DOM not available'); 0665 setTimeout(cb, 250); 0666 return; 0667 } 0668 // let this fall through because server response could be an empty document 0669 //log('Could not access iframe DOM after mutiple tries.'); 0670 //throw 'DOMException: not available'; 0671 } 0672 0673 //log('response detected'); 0674 var docRoot = doc.body ? doc.body : doc.documentElement; 0675 xhr.responseText = docRoot ? docRoot.innerHTML : null; 0676 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 0677 if (isXml) { 0678 s.dataType = 'xml'; 0679 } 0680 xhr.getResponseHeader = function(header){ 0681 var headers = {'content-type': s.dataType}; 0682 return headers[header.toLowerCase()]; 0683 }; 0684 // support for XHR 'status' & 'statusText' emulation : 0685 if (docRoot) { 0686 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status; 0687 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 0688 } 0689 0690 var dt = (s.dataType || '').toLowerCase(); 0691 var scr = /(json|script|text)/.test(dt); 0692 if (scr || s.textarea) { 0693 // see if user embedded response in textarea 0694 var ta = doc.getElementsByTagName('textarea')[0]; 0695 if (ta) { 0696 xhr.responseText = ta.value; 0697 // support for XHR 'status' & 'statusText' emulation : 0698 xhr.status = Number( ta.getAttribute('status') ) || xhr.status; 0699 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 0700 } 0701 else if (scr) { 0702 // account for browsers injecting pre around json response 0703 var pre = doc.getElementsByTagName('pre')[0]; 0704 var b = doc.getElementsByTagName('body')[0]; 0705 if (pre) { 0706 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 0707 } 0708 else if (b) { 0709 xhr.responseText = b.textContent ? b.textContent : b.innerText; 0710 } 0711 } 0712 } 0713 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { 0714 xhr.responseXML = toXml(xhr.responseText); 0715 } 0716 0717 try { 0718 data = httpData(xhr, dt, s); 0719 } 0720 catch (err) { 0721 status = 'parsererror'; 0722 xhr.error = errMsg = (err || status); 0723 } 0724 } 0725 catch (err) { 0726 log('error caught: ',err); 0727 status = 'error'; 0728 xhr.error = errMsg = (err || status); 0729 } 0730 0731 if (xhr.aborted) { 0732 log('upload aborted'); 0733 status = null; 0734 } 0735 0736 if (xhr.status) { // we've set xhr.status 0737 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; 0738 } 0739 0740 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 0741 if (status === 'success') { 0742 if (s.success) { 0743 s.success.call(s.context, data, 'success', xhr); 0744 } 0745 deferred.resolve(xhr.responseText, 'success', xhr); 0746 if (g) { 0747 $.event.trigger("ajaxSuccess", [xhr, s]); 0748 } 0749 } 0750 else if (status) { 0751 if (errMsg === undefined) { 0752 errMsg = xhr.statusText; 0753 } 0754 if (s.error) { 0755 s.error.call(s.context, xhr, status, errMsg); 0756 } 0757 deferred.reject(xhr, 'error', errMsg); 0758 if (g) { 0759 $.event.trigger("ajaxError", [xhr, s, errMsg]); 0760 } 0761 } 0762 0763 if (g) { 0764 $.event.trigger("ajaxComplete", [xhr, s]); 0765 } 0766 0767 if (g && ! --$.active) { 0768 $.event.trigger("ajaxStop"); 0769 } 0770 0771 if (s.complete) { 0772 s.complete.call(s.context, xhr, status); 0773 } 0774 0775 callbackProcessed = true; 0776 if (s.timeout) { 0777 clearTimeout(timeoutHandle); 0778 } 0779 0780 // clean up 0781 setTimeout(function() { 0782 if (!s.iframeTarget) { 0783 $io.remove(); 0784 } 0785 else { //adding else to clean up existing iframe response. 0786 $io.attr('src', s.iframeSrc); 0787 } 0788 xhr.responseXML = null; 0789 }, 100); 0790 } 0791 0792 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 0793 if (window.ActiveXObject) { 0794 doc = new ActiveXObject('Microsoft.XMLDOM'); 0795 doc.async = 'false'; 0796 doc.loadXML(s); 0797 } 0798 else { 0799 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 0800 } 0801 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; 0802 }; 0803 var parseJSON = $.parseJSON || function(s) { 0804 /*jslint evil:true */ 0805 return window['eval']('(' + s + ')'); 0806 }; 0807 0808 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 0809 0810 var ct = xhr.getResponseHeader('content-type') || '', 0811 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, 0812 data = xml ? xhr.responseXML : xhr.responseText; 0813 0814 if (xml && data.documentElement.nodeName === 'parsererror') { 0815 if ($.error) { 0816 $.error('parsererror'); 0817 } 0818 } 0819 if (s && s.dataFilter) { 0820 data = s.dataFilter(data, type); 0821 } 0822 if (typeof data === 'string') { 0823 if (type === 'json' || !type && ct.indexOf('json') >= 0) { 0824 data = parseJSON(data); 0825 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { 0826 $.globalEval(data); 0827 } 0828 } 0829 return data; 0830 }; 0831 0832 return deferred; 0833 } 0834 }; 0835 0836 /** 0837 * ajaxForm() provides a mechanism for fully automating form submission. 0838 * 0839 * The advantages of using this method instead of ajaxSubmit() are: 0840 * 0841 * 1: This method will include coordinates for <input type="image" /> elements (if the element 0842 * is used to submit the form). 0843 * 2. This method will include the submit element's name/value data (for the element that was 0844 * used to submit the form). 0845 * 3. This method binds the submit() method to the form for you. 0846 * 0847 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 0848 * passes the options argument along after properly binding events for submit elements and 0849 * the form itself. 0850 */ 0851 $.fn.ajaxForm = function(options) { 0852 options = options || {}; 0853 options.delegation = options.delegation && $.isFunction($.fn.on); 0854 0855 // in jQuery 1.3+ we can fix mistakes with the ready state 0856 if (!options.delegation && this.length === 0) { 0857 var o = { s: this.selector, c: this.context }; 0858 if (!$.isReady && o.s) { 0859 log('DOM not ready, queuing ajaxForm'); 0860 $(function() { 0861 $(o.s,o.c).ajaxForm(options); 0862 }); 0863 return this; 0864 } 0865 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 0866 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 0867 return this; 0868 } 0869 0870 if ( options.delegation ) { 0871 $(document) 0872 .off('submit.form-plugin', this.selector, doAjaxSubmit) 0873 .off('click.form-plugin', this.selector, captureSubmittingElement) 0874 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 0875 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 0876 return this; 0877 } 0878 0879 return this.ajaxFormUnbind() 0880 .bind('submit.form-plugin', options, doAjaxSubmit) 0881 .bind('click.form-plugin', options, captureSubmittingElement); 0882 }; 0883 0884 // private event handlers 0885 function doAjaxSubmit(e) { 0886 /*jshint validthis:true */ 0887 var options = e.data; 0888 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 0889 e.preventDefault(); 0890 $(e.target).ajaxSubmit(options); // #365 0891 } 0892 } 0893 0894 function captureSubmittingElement(e) { 0895 /*jshint validthis:true */ 0896 var target = e.target; 0897 var $el = $(target); 0898 if (!($el.is("[type=submit],[type=image]"))) { 0899 // is this a child element of the submit el? (ex: a span within a button) 0900 var t = $el.closest('[type=submit]'); 0901 if (t.length === 0) { 0902 return; 0903 } 0904 target = t[0]; 0905 } 0906 var form = this; 0907 form.clk = target; 0908 if (target.type == 'image') { 0909 if (e.offsetX !== undefined) { 0910 form.clk_x = e.offsetX; 0911 form.clk_y = e.offsetY; 0912 } else if (typeof $.fn.offset == 'function') { 0913 var offset = $el.offset(); 0914 form.clk_x = e.pageX - offset.left; 0915 form.clk_y = e.pageY - offset.top; 0916 } else { 0917 form.clk_x = e.pageX - target.offsetLeft; 0918 form.clk_y = e.pageY - target.offsetTop; 0919 } 0920 } 0921 // clear form vars 0922 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); 0923 } 0924 0925 0926 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 0927 $.fn.ajaxFormUnbind = function() { 0928 return this.unbind('submit.form-plugin click.form-plugin'); 0929 }; 0930 0931 /** 0932 * formToArray() gathers form element data into an array of objects that can 0933 * be passed to any of the following ajax functions: $.get, $.post, or load. 0934 * Each object in the array has both a 'name' and 'value' property. An example of 0935 * an array for a simple login form might be: 0936 * 0937 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 0938 * 0939 * It is this array that is passed to pre-submit callback functions provided to the 0940 * ajaxSubmit() and ajaxForm() methods. 0941 */ 0942 $.fn.formToArray = function(semantic, elements) { 0943 var a = []; 0944 if (this.length === 0) { 0945 return a; 0946 } 0947 0948 var form = this[0]; 0949 var formId = this.attr('id'); 0950 var els = semantic ? form.getElementsByTagName('*') : form.elements; 0951 var els2; 0952 0953 if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390 0954 els = $(els).get(); // convert to standard array 0955 } 0956 0957 // #386; account for inputs outside the form which use the 'form' attribute 0958 if ( formId ) { 0959 els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet 0960 if ( els2.length ) { 0961 els = (els || []).concat(els2); 0962 } 0963 } 0964 0965 if (!els || !els.length) { 0966 return a; 0967 } 0968 0969 var i,j,n,v,el,max,jmax; 0970 for(i=0, max=els.length; i < max; i++) { 0971 el = els[i]; 0972 n = el.name; 0973 if (!n || el.disabled) { 0974 continue; 0975 } 0976 0977 if (semantic && form.clk && el.type == "image") { 0978 // handle image inputs on the fly when semantic == true 0979 if(form.clk == el) { 0980 a.push({name: n, value: $(el).val(), type: el.type }); 0981 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 0982 } 0983 continue; 0984 } 0985 0986 v = $.fieldValue(el, true); 0987 if (v && v.constructor == Array) { 0988 if (elements) { 0989 elements.push(el); 0990 } 0991 for(j=0, jmax=v.length; j < jmax; j++) { 0992 a.push({name: n, value: v[j]}); 0993 } 0994 } 0995 else if (feature.fileapi && el.type == 'file') { 0996 if (elements) { 0997 elements.push(el); 0998 } 0999 var files = el.files; 1000 if (files.length) { 1001 for (j=0; j < files.length; j++) { 1002 a.push({name: n, value: files[j], type: el.type}); 1003 } 1004 } 1005 else { 1006 // #180 1007 a.push({ name: n, value: '', type: el.type }); 1008 } 1009 } 1010 else if (v !== null && typeof v != 'undefined') { 1011 if (elements) { 1012 elements.push(el); 1013 } 1014 a.push({name: n, value: v, type: el.type, required: el.required}); 1015 } 1016 } 1017 1018 if (!semantic && form.clk) { 1019 // input type=='image' are not found in elements array! handle it here 1020 var $input = $(form.clk), input = $input[0]; 1021 n = input.name; 1022 if (n && !input.disabled && input.type == 'image') { 1023 a.push({name: n, value: $input.val()}); 1024 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 1025 } 1026 } 1027 return a; 1028 }; 1029 1030 /** 1031 * Serializes form data into a 'submittable' string. This method will return a string 1032 * in the format: name1=value1&name2=value2 1033 */ 1034 $.fn.formSerialize = function(semantic) { 1035 //hand off to jQuery.param for proper encoding 1036 return $.param(this.formToArray(semantic)); 1037 }; 1038 1039 /** 1040 * Serializes all field elements in the jQuery object into a query string. 1041 * This method will return a string in the format: name1=value1&name2=value2 1042 */ 1043 $.fn.fieldSerialize = function(successful) { 1044 var a = []; 1045 this.each(function() { 1046 var n = this.name; 1047 if (!n) { 1048 return; 1049 } 1050 var v = $.fieldValue(this, successful); 1051 if (v && v.constructor == Array) { 1052 for (var i=0,max=v.length; i < max; i++) { 1053 a.push({name: n, value: v[i]}); 1054 } 1055 } 1056 else if (v !== null && typeof v != 'undefined') { 1057 a.push({name: this.name, value: v}); 1058 } 1059 }); 1060 //hand off to jQuery.param for proper encoding 1061 return $.param(a); 1062 }; 1063 1064 /** 1065 * Returns the value(s) of the element in the matched set. For example, consider the following form: 1066 * 1067 * <form><fieldset> 1068 * <input name="A" type="text" /> 1069 * <input name="A" type="text" /> 1070 * <input name="B" type="checkbox" value="B1" /> 1071 * <input name="B" type="checkbox" value="B2"/> 1072 * <input name="C" type="radio" value="C1" /> 1073 * <input name="C" type="radio" value="C2" /> 1074 * </fieldset></form> 1075 * 1076 * var v = $('input[type=text]').fieldValue(); 1077 * // if no values are entered into the text inputs 1078 * v == ['',''] 1079 * // if values entered into the text inputs are 'foo' and 'bar' 1080 * v == ['foo','bar'] 1081 * 1082 * var v = $('input[type=checkbox]').fieldValue(); 1083 * // if neither checkbox is checked 1084 * v === undefined 1085 * // if both checkboxes are checked 1086 * v == ['B1', 'B2'] 1087 * 1088 * var v = $('input[type=radio]').fieldValue(); 1089 * // if neither radio is checked 1090 * v === undefined 1091 * // if first radio is checked 1092 * v == ['C1'] 1093 * 1094 * The successful argument controls whether or not the field element must be 'successful' 1095 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). 1096 * The default value of the successful argument is true. If this value is false the value(s) 1097 * for each element is returned. 1098 * 1099 * Note: This method *always* returns an array. If no valid value can be determined the 1100 * array will be empty, otherwise it will contain one or more values. 1101 */ 1102 $.fn.fieldValue = function(successful) { 1103 for (var val=[], i=0, max=this.length; i < max; i++) { 1104 var el = this[i]; 1105 var v = $.fieldValue(el, successful); 1106 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { 1107 continue; 1108 } 1109 if (v.constructor == Array) { 1110 $.merge(val, v); 1111 } 1112 else { 1113 val.push(v); 1114 } 1115 } 1116 return val; 1117 }; 1118 1119 /** 1120 * Returns the value of the field element. 1121 */ 1122 $.fieldValue = function(el, successful) { 1123 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 1124 if (successful === undefined) { 1125 successful = true; 1126 } 1127 1128 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 1129 (t == 'checkbox' || t == 'radio') && !el.checked || 1130 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 1131 tag == 'select' && el.selectedIndex == -1)) { 1132 return null; 1133 } 1134 1135 if (tag == 'select') { 1136 var index = el.selectedIndex; 1137 if (index < 0) { 1138 return null; 1139 } 1140 var a = [], ops = el.options; 1141 var one = (t == 'select-one'); 1142 var max = (one ? index+1 : ops.length); 1143 for(var i=(one ? index : 0); i < max; i++) { 1144 var op = ops[i]; 1145 if (op.selected) { 1146 var v = op.value; 1147 if (!v) { // extra pain for IE... 1148 v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; 1149 } 1150 if (one) { 1151 return v; 1152 } 1153 a.push(v); 1154 } 1155 } 1156 return a; 1157 } 1158 return $(el).val(); 1159 }; 1160 1161 /** 1162 * Clears the form data. Takes the following actions on the form's input fields: 1163 * - input text fields will have their 'value' property set to the empty string 1164 * - select elements will have their 'selectedIndex' property set to -1 1165 * - checkbox and radio inputs will have their 'checked' property set to false 1166 * - inputs of type submit, button, reset, and hidden will *not* be effected 1167 * - button elements will *not* be effected 1168 */ 1169 $.fn.clearForm = function(includeHidden) { 1170 return this.each(function() { 1171 $('input,select,textarea', this).clearFields(includeHidden); 1172 }); 1173 }; 1174 1175 /** 1176 * Clears the selected form elements. 1177 */ 1178 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 1179 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 1180 return this.each(function() { 1181 var t = this.type, tag = this.tagName.toLowerCase(); 1182 if (re.test(t) || tag == 'textarea') { 1183 this.value = ''; 1184 } 1185 else if (t == 'checkbox' || t == 'radio') { 1186 this.checked = false; 1187 } 1188 else if (tag == 'select') { 1189 this.selectedIndex = -1; 1190 } 1191 else if (t == "file") { 1192 if (/MSIE/.test(navigator.userAgent)) { 1193 $(this).replaceWith($(this).clone(true)); 1194 } else { 1195 $(this).val(''); 1196 } 1197 } 1198 else if (includeHidden) { 1199 // includeHidden can be the value true, or it can be a selector string 1200 // indicating a special test; for example: 1201 // $('#myForm').clearForm('.special:hidden') 1202 // the above would clean hidden inputs that have the class of 'special' 1203 if ( (includeHidden === true && /hidden/.test(t)) || 1204 (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) { 1205 this.value = ''; 1206 } 1207 } 1208 }); 1209 }; 1210 1211 /** 1212 * Resets the form data. Causes all form elements to be reset to their original value. 1213 */ 1214 $.fn.resetForm = function() { 1215 return this.each(function() { 1216 // guard against an input with the name of 'reset' 1217 // note that IE reports the reset function as an 'object' 1218 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { 1219 this.reset(); 1220 } 1221 }); 1222 }; 1223 1224 /** 1225 * Enables or disables any matching elements. 1226 */ 1227 $.fn.enable = function(b) { 1228 if (b === undefined) { 1229 b = true; 1230 } 1231 return this.each(function() { 1232 this.disabled = !b; 1233 }); 1234 }; 1235 1236 /** 1237 * Checks/unchecks any matching checkboxes or radio buttons and 1238 * selects/deselects and matching option elements. 1239 */ 1240 $.fn.selected = function(select) { 1241 if (select === undefined) { 1242 select = true; 1243 } 1244 return this.each(function() { 1245 var t = this.type; 1246 if (t == 'checkbox' || t == 'radio') { 1247 this.checked = select; 1248 } 1249 else if (this.tagName.toLowerCase() == 'option') { 1250 var $sel = $(this).parent('select'); 1251 if (select && $sel[0] && $sel[0].type == 'select-one') { 1252 // deselect all other options 1253 $sel.find('option').selected(false); 1254 } 1255 this.selected = select; 1256 } 1257 }); 1258 }; 1259 1260 // expose debug var 1261 $.fn.ajaxSubmit.debug = false; 1262 1263 // helper fn for console logging 1264 function log() { 1265 if (!$.fn.ajaxSubmit.debug) { 1266 return; 1267 } 1268 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); 1269 if (window.console && window.console.log) { 1270 window.console.log(msg); 1271 } 1272 else if (window.opera && window.opera.postError) { 1273 window.opera.postError(msg); 1274 } 1275 } 1276 1277 }));