Merge pull request #5205 from solgenomics/topic/generic_trial_upload
[sgn.git] / js / source / legacy / jquery / form.js
blob591ad6f1fe7b5a436387870822f7e0189989cab7
1 /*!
2  * jQuery Form Plugin
3  * version: 3.51.0-2014.06.20
4  * Requires jQuery v1.5 or later
5  * Copyright (c) 2014 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 */
13 // AMD support
14 (function (factory) {
15     "use strict";
16     if (typeof define === 'function' && define.amd) {
17         // using AMD; register as anon module
18         define(['jquery'], factory);
19     } else {
20         // no AMD; invoke directly
21         factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
22     }
25 (function($) {
26 "use strict";
29     Usage Note:
30     -----------
31     Do not use both ajaxSubmit and ajaxForm on the same form.  These
32     functions are mutually exclusive.  Use ajaxSubmit if you want
33     to bind your own submit handler to the form.  For example,
35     $(document).ready(function() {
36         $('#myForm').on('submit', function(e) {
37             e.preventDefault(); // <-- important
38             $(this).ajaxSubmit({
39                 target: '#output'
40             });
41         });
42     });
44     Use ajaxForm when you want the plugin to manage all the event binding
45     for you.  For example,
47     $(document).ready(function() {
48         $('#myForm').ajaxForm({
49             target: '#output'
50         });
51     });
53     You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
54     form does not have to exist when you invoke ajaxForm:
56     $('#myForm').ajaxForm({
57         delegation: true,
58         target: '#output'
59     });
61     When using ajaxForm, the ajaxSubmit function will be invoked for you
62     at the appropriate time.
65 /**
66  * Feature detection
67  */
68 var feature = {};
69 feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
70 feature.formdata = window.FormData !== undefined;
72 var hasProp = !!$.fn.prop;
74 // attr2 uses prop when it can but checks the return type for
75 // an expected string.  this accounts for the case where a form 
76 // contains inputs with names like "action" or "method"; in those
77 // cases "prop" returns the element
78 $.fn.attr2 = function() {
79     if ( ! hasProp ) {
80         return this.attr.apply(this, arguments);
81     }
82     var val = this.prop.apply(this, arguments);
83     if ( ( val && val.jquery ) || typeof val === 'string' ) {
84         return val;
85     }
86     return this.attr.apply(this, arguments);
89 /**
90  * ajaxSubmit() provides a mechanism for immediately submitting
91  * an HTML form using AJAX.
92  */
93 $.fn.ajaxSubmit = function(options) {
94     /*jshint scripturl:true */
96     // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
97     if (!this.length) {
98         log('ajaxSubmit: skipping submit process - no element selected');
99         return this;
100     }
102     var method, action, url, $form = this;
104     if (typeof options == 'function') {
105         options = { success: options };
106     }
107     else if ( options === undefined ) {
108         options = {};
109     }
111     method = options.type || this.attr2('method');
112     action = options.url  || this.attr2('action');
114     url = (typeof action === 'string') ? $.trim(action) : '';
115     url = url || window.location.href || '';
116     if (url) {
117         // clean url (don't include hash vaue)
118         url = (url.match(/^([^#]+)/)||[])[1];
119     }
121     options = $.extend(true, {
122         url:  url,
123         success: $.ajaxSettings.success,
124         type: method || $.ajaxSettings.type,
125         iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
126     }, options);
128     // hook for manipulating the form data before it is extracted;
129     // convenient for use with rich editors like tinyMCE or FCKEditor
130     var veto = {};
131     this.trigger('form-pre-serialize', [this, options, veto]);
132     if (veto.veto) {
133         log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
134         return this;
135     }
137     // provide opportunity to alter form data before it is serialized
138     if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
139         log('ajaxSubmit: submit aborted via beforeSerialize callback');
140         return this;
141     }
143     var traditional = options.traditional;
144     if ( traditional === undefined ) {
145         traditional = $.ajaxSettings.traditional;
146     }
148     var elements = [];
149     var qx, a = this.formToArray(options.semantic, elements);
150     if (options.data) {
151         options.extraData = options.data;
152         qx = $.param(options.data, traditional);
153     }
155     // give pre-submit callback an opportunity to abort the submit
156     if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
157         log('ajaxSubmit: submit aborted via beforeSubmit callback');
158         return this;
159     }
161     // fire vetoable 'validate' event
162     this.trigger('form-submit-validate', [a, this, options, veto]);
163     if (veto.veto) {
164         log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
165         return this;
166     }
168     var q = $.param(a, traditional);
169     if (qx) {
170         q = ( q ? (q + '&' + qx) : qx );
171     }
172     if (options.type.toUpperCase() == 'GET') {
173         options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
174         options.data = null;  // data is null for 'get'
175     }
176     else {
177         options.data = q; // data is the query string for 'post'
178     }
180     var callbacks = [];
181     if (options.resetForm) {
182         callbacks.push(function() { $form.resetForm(); });
183     }
184     if (options.clearForm) {
185         callbacks.push(function() { $form.clearForm(options.includeHidden); });
186     }
188     // perform a load on the target only if dataType is not provided
189     if (!options.dataType && options.target) {
190         var oldSuccess = options.success || function(){};
191         callbacks.push(function(data) {
192             var fn = options.replaceTarget ? 'replaceWith' : 'html';
193             $(options.target)[fn](data).each(oldSuccess, arguments);
194         });
195     }
196     else if (options.success) {
197         callbacks.push(options.success);
198     }
200     options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
201         var context = options.context || this ;    // jQuery 1.4+ supports scope context
202         for (var i=0, max=callbacks.length; i < max; i++) {
203             callbacks[i].apply(context, [data, status, xhr || $form, $form]);
204         }
205     };
207     if (options.error) {
208         var oldError = options.error;
209         options.error = function(xhr, status, error) {
210             var context = options.context || this;
211             oldError.apply(context, [xhr, status, error, $form]);
212         };
213     }
215      if (options.complete) {
216         var oldComplete = options.complete;
217         options.complete = function(xhr, status) {
218             var context = options.context || this;
219             oldComplete.apply(context, [xhr, status, $form]);
220         };
221     }
223     // are there files to upload?
225     // [value] (issue #113), also see comment:
226     // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
227     var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
229     var hasFileInputs = fileInputs.length > 0;
230     var mp = 'multipart/form-data';
231     var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
233     var fileAPI = feature.fileapi && feature.formdata;
234     log("fileAPI :" + fileAPI);
235     var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
237     var jqxhr;
239     // options.iframe allows user to force iframe mode
240     // 06-NOV-09: now defaulting to iframe mode if file input is detected
241     if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
242         // hack to fix Safari hang (thanks to Tim Molendijk for this)
243         // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
244         if (options.closeKeepAlive) {
245             $.get(options.closeKeepAlive, function() {
246                 jqxhr = fileUploadIframe(a);
247             });
248         }
249         else {
250             jqxhr = fileUploadIframe(a);
251         }
252     }
253     else if ((hasFileInputs || multipart) && fileAPI) {
254         jqxhr = fileUploadXhr(a);
255     }
256     else {
257         jqxhr = $.ajax(options);
258     }
260     $form.removeData('jqxhr').data('jqxhr', jqxhr);
262     // clear element array
263     for (var k=0; k < elements.length; k++) {
264         elements[k] = null;
265     }
267     // fire 'notify' event
268     this.trigger('form-submit-notify', [this, options]);
269     return this;
271     // utility fn for deep serialization
272     function deepSerialize(extraData){
273         var serialized = $.param(extraData, options.traditional).split('&');
274         var len = serialized.length;
275         var result = [];
276         var i, part;
277         for (i=0; i < len; i++) {
278             // #252; undo param space replacement
279             serialized[i] = serialized[i].replace(/\+/g,' ');
280             part = serialized[i].split('=');
281             // #278; use array instead of object storage, favoring array serializations
282             result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
283         }
284         return result;
285     }
287      // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
288     function fileUploadXhr(a) {
289         var formdata = new FormData();
291         for (var i=0; i < a.length; i++) {
292             formdata.append(a[i].name, a[i].value);
293         }
295         if (options.extraData) {
296             var serializedData = deepSerialize(options.extraData);
297             for (i=0; i < serializedData.length; i++) {
298                 if (serializedData[i]) {
299                     formdata.append(serializedData[i][0], serializedData[i][1]);
300                 }
301             }
302         }
304         options.data = null;
306         var s = $.extend(true, {}, $.ajaxSettings, options, {
307             contentType: false,
308             processData: false,
309             cache: false,
310             type: method || 'POST'
311         });
313         if (options.uploadProgress) {
314             // workaround because jqXHR does not expose upload property
315             s.xhr = function() {
316                 var xhr = $.ajaxSettings.xhr();
317                 if (xhr.upload) {
318                     xhr.upload.addEventListener('progress', function(event) {
319                         var percent = 0;
320                         var position = event.loaded || event.position; /*event.position is deprecated*/
321                         var total = event.total;
322                         if (event.lengthComputable) {
323                             percent = Math.ceil(position / total * 100);
324                         }
325                         options.uploadProgress(event, position, total, percent);
326                     }, false);
327                 }
328                 return xhr;
329             };
330         }
332         s.data = null;
333         var beforeSend = s.beforeSend;
334         s.beforeSend = function(xhr, o) {
335             //Send FormData() provided by user
336             if (options.formData) {
337                 o.data = options.formData;
338             }
339             else {
340                 o.data = formdata;
341             }
342             if(beforeSend) {
343                 beforeSend.call(this, xhr, o);
344             }
345         };
346         return $.ajax(s);
347     }
349     // private function for handling file uploads (hat tip to YAHOO!)
350     function fileUploadIframe(a) {
351         var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
352         var deferred = $.Deferred();
354         // #341
355         deferred.abort = function(status) {
356             xhr.abort(status);
357         };
359         if (a) {
360             // ensure that every serialized input is still enabled
361             for (i=0; i < elements.length; i++) {
362                 el = $(elements[i]);
363                 if ( hasProp ) {
364                     el.prop('disabled', false);
365                 }
366                 else {
367                     el.removeAttr('disabled');
368                 }
369             }
370         }
372         s = $.extend(true, {}, $.ajaxSettings, options);
373         s.context = s.context || s;
374         id = 'jqFormIO' + (new Date().getTime());
375         if (s.iframeTarget) {
376             $io = $(s.iframeTarget);
377             n = $io.attr2('name');
378             if (!n) {
379                 $io.attr2('name', id);
380             }
381             else {
382                 id = n;
383             }
384         }
385         else {
386             $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
387             $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
388         }
389         io = $io[0];
392         xhr = { // mock object
393             aborted: 0,
394             responseText: null,
395             responseXML: null,
396             status: 0,
397             statusText: 'n/a',
398             getAllResponseHeaders: function() {},
399             getResponseHeader: function() {},
400             setRequestHeader: function() {},
401             abort: function(status) {
402                 var e = (status === 'timeout' ? 'timeout' : 'aborted');
403                 log('aborting upload... ' + e);
404                 this.aborted = 1;
406                 try { // #214, #257
407                     if (io.contentWindow.document.execCommand) {
408                         io.contentWindow.document.execCommand('Stop');
409                     }
410                 }
411                 catch(ignore) {}
413                 $io.attr('src', s.iframeSrc); // abort op in progress
414                 xhr.error = e;
415                 if (s.error) {
416                     s.error.call(s.context, xhr, e, status);
417                 }
418                 if (g) {
419                     $.event.trigger("ajaxError", [xhr, s, e]);
420                 }
421                 if (s.complete) {
422                     s.complete.call(s.context, xhr, e);
423                 }
424             }
425         };
427         g = s.global;
428         // trigger ajax global events so that activity/block indicators work like normal
429         if (g && 0 === $.active++) {
430             $.event.trigger("ajaxStart");
431         }
432         if (g) {
433             $.event.trigger("ajaxSend", [xhr, s]);
434         }
436         if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
437             if (s.global) {
438                 $.active--;
439             }
440             deferred.reject();
441             return deferred;
442         }
443         if (xhr.aborted) {
444             deferred.reject();
445             return deferred;
446         }
448         // add submitting element to data if we know it
449         sub = form.clk;
450         if (sub) {
451             n = sub.name;
452             if (n && !sub.disabled) {
453                 s.extraData = s.extraData || {};
454                 s.extraData[n] = sub.value;
455                 if (sub.type == "image") {
456                     s.extraData[n+'.x'] = form.clk_x;
457                     s.extraData[n+'.y'] = form.clk_y;
458                 }
459             }
460         }
462         var CLIENT_TIMEOUT_ABORT = 1;
463         var SERVER_ABORT = 2;
464                 
465         function getDoc(frame) {
466             /* it looks like contentWindow or contentDocument do not
467              * carry the protocol property in ie8, when running under ssl
468              * frame.document is the only valid response document, since
469              * the protocol is know but not on the other two objects. strange?
470              * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
471              */
472             
473             var doc = null;
474             
475             // IE8 cascading access check
476             try {
477                 if (frame.contentWindow) {
478                     doc = frame.contentWindow.document;
479                 }
480             } catch(err) {
481                 // IE8 access denied under ssl & missing protocol
482                 log('cannot get iframe.contentWindow document: ' + err);
483             }
485             if (doc) { // successful getting content
486                 return doc;
487             }
489             try { // simply checking may throw in ie8 under ssl or mismatched protocol
490                 doc = frame.contentDocument ? frame.contentDocument : frame.document;
491             } catch(err) {
492                 // last attempt
493                 log('cannot get iframe.contentDocument: ' + err);
494                 doc = frame.document;
495             }
496             return doc;
497         }
499         // Rails CSRF hack (thanks to Yvan Barthelemy)
500         var csrf_token = $('meta[name=csrf-token]').attr('content');
501         var csrf_param = $('meta[name=csrf-param]').attr('content');
502         if (csrf_param && csrf_token) {
503             s.extraData = s.extraData || {};
504             s.extraData[csrf_param] = csrf_token;
505         }
507         // take a breath so that pending repaints get some cpu time before the upload starts
508         function doSubmit() {
509             // make sure form attrs are set
510             var t = $form.attr2('target'), 
511                 a = $form.attr2('action'), 
512                 mp = 'multipart/form-data',
513                 et = $form.attr('enctype') || $form.attr('encoding') || mp;
515             // update form attrs in IE friendly way
516             form.setAttribute('target',id);
517             if (!method || /post/i.test(method) ) {
518                 form.setAttribute('method', 'POST');
519             }
520             if (a != s.url) {
521                 form.setAttribute('action', s.url);
522             }
524             // ie borks in some cases when setting encoding
525             if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
526                 $form.attr({
527                     encoding: 'multipart/form-data',
528                     enctype:  'multipart/form-data'
529                 });
530             }
532             // support timout
533             if (s.timeout) {
534                 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
535             }
537             // look for server aborts
538             function checkState() {
539                 try {
540                     var state = getDoc(io).readyState;
541                     log('state = ' + state);
542                     if (state && state.toLowerCase() == 'uninitialized') {
543                         setTimeout(checkState,50);
544                     }
545                 }
546                 catch(e) {
547                     log('Server abort: ' , e, ' (', e.name, ')');
548                     cb(SERVER_ABORT);
549                     if (timeoutHandle) {
550                         clearTimeout(timeoutHandle);
551                     }
552                     timeoutHandle = undefined;
553                 }
554             }
556             // add "extra" data to form if provided in options
557             var extraInputs = [];
558             try {
559                 if (s.extraData) {
560                     for (var n in s.extraData) {
561                         if (s.extraData.hasOwnProperty(n)) {
562                            // if using the $.param format that allows for multiple values with the same name
563                            if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
564                                extraInputs.push(
565                                $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
566                                    .appendTo(form)[0]);
567                            } else {
568                                extraInputs.push(
569                                $('<input type="hidden" name="'+n+'">').val(s.extraData[n])
570                                    .appendTo(form)[0]);
571                            }
572                         }
573                     }
574                 }
576                 if (!s.iframeTarget) {
577                     // add iframe to doc and submit the form
578                     $io.appendTo('body');
579                 }
580                 if (io.attachEvent) {
581                     io.attachEvent('onload', cb);
582                 }
583                 else {
584                     io.addEventListener('load', cb, false);
585                 }
586                 setTimeout(checkState,15);
588                 try {
589                     form.submit();
590                 } catch(err) {
591                     // just in case form has element with name/id of 'submit'
592                     var submitFn = document.createElement('form').submit;
593                     submitFn.apply(form);
594                 }
595             }
596             finally {
597                 // reset attrs and remove "extra" input elements
598                 form.setAttribute('action',a);
599                 form.setAttribute('enctype', et); // #380
600                 if(t) {
601                     form.setAttribute('target', t);
602                 } else {
603                     $form.removeAttr('target');
604                 }
605                 $(extraInputs).remove();
606             }
607         }
609         if (s.forceSync) {
610             doSubmit();
611         }
612         else {
613             setTimeout(doSubmit, 10); // this lets dom updates render
614         }
616         var data, doc, domCheckCount = 50, callbackProcessed;
618         function cb(e) {
619             if (xhr.aborted || callbackProcessed) {
620                 return;
621             }
622             
623             doc = getDoc(io);
624             if(!doc) {
625                 log('cannot access response document');
626                 e = SERVER_ABORT;
627             }
628             if (e === CLIENT_TIMEOUT_ABORT && xhr) {
629                 xhr.abort('timeout');
630                 deferred.reject(xhr, 'timeout');
631                 return;
632             }
633             else if (e == SERVER_ABORT && xhr) {
634                 xhr.abort('server abort');
635                 deferred.reject(xhr, 'error', 'server abort');
636                 return;
637             }
639             if (!doc || doc.location.href == s.iframeSrc) {
640                 // response not received yet
641                 if (!timedOut) {
642                     return;
643                 }
644             }
645             if (io.detachEvent) {
646                 io.detachEvent('onload', cb);
647             }
648             else {
649                 io.removeEventListener('load', cb, false);
650             }
652             var status = 'success', errMsg;
653             try {
654                 if (timedOut) {
655                     throw 'timeout';
656                 }
658                 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
659                 log('isXml='+isXml);
660                 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
661                     if (--domCheckCount) {
662                         // in some browsers (Opera) the iframe DOM is not always traversable when
663                         // the onload callback fires, so we loop a bit to accommodate
664                         log('requeing onLoad callback, DOM not available');
665                         setTimeout(cb, 250);
666                         return;
667                     }
668                     // let this fall through because server response could be an empty document
669                     //log('Could not access iframe DOM after mutiple tries.');
670                     //throw 'DOMException: not available';
671                 }
673                 //log('response detected');
674                 var docRoot = doc.body ? doc.body : doc.documentElement;
675                 xhr.responseText = docRoot ? docRoot.innerHTML : null;
676                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
677                 if (isXml) {
678                     s.dataType = 'xml';
679                 }
680                 xhr.getResponseHeader = function(header){
681                     var headers = {'content-type': s.dataType};
682                     return headers[header.toLowerCase()];
683                 };
684                 // support for XHR 'status' & 'statusText' emulation :
685                 if (docRoot) {
686                     xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
687                     xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
688                 }
690                 var dt = (s.dataType || '').toLowerCase();
691                 var scr = /(json|script|text)/.test(dt);
692                 if (scr || s.textarea) {
693                     // see if user embedded response in textarea
694                     var ta = doc.getElementsByTagName('textarea')[0];
695                     if (ta) {
696                         xhr.responseText = ta.value;
697                         // support for XHR 'status' & 'statusText' emulation :
698                         xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
699                         xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
700                     }
701                     else if (scr) {
702                         // account for browsers injecting pre around json response
703                         var pre = doc.getElementsByTagName('pre')[0];
704                         var b = doc.getElementsByTagName('body')[0];
705                         if (pre) {
706                             xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
707                         }
708                         else if (b) {
709                             xhr.responseText = b.textContent ? b.textContent : b.innerText;
710                         }
711                     }
712                 }
713                 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
714                     xhr.responseXML = toXml(xhr.responseText);
715                 }
717                 try {
718                     data = httpData(xhr, dt, s);
719                 }
720                 catch (err) {
721                     status = 'parsererror';
722                     xhr.error = errMsg = (err || status);
723                 }
724             }
725             catch (err) {
726                 log('error caught: ',err);
727                 status = 'error';
728                 xhr.error = errMsg = (err || status);
729             }
731             if (xhr.aborted) {
732                 log('upload aborted');
733                 status = null;
734             }
736             if (xhr.status) { // we've set xhr.status
737                 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
738             }
740             // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
741             if (status === 'success') {
742                 if (s.success) {
743                     s.success.call(s.context, data, 'success', xhr);
744                 }
745                 deferred.resolve(xhr.responseText, 'success', xhr);
746                 if (g) {
747                     $.event.trigger("ajaxSuccess", [xhr, s]);
748                 }
749             }
750             else if (status) {
751                 if (errMsg === undefined) {
752                     errMsg = xhr.statusText;
753                 }
754                 if (s.error) {
755                     s.error.call(s.context, xhr, status, errMsg);
756                 }
757                 deferred.reject(xhr, 'error', errMsg);
758                 if (g) {
759                     $.event.trigger("ajaxError", [xhr, s, errMsg]);
760                 }
761             }
763             if (g) {
764                 $.event.trigger("ajaxComplete", [xhr, s]);
765             }
767             if (g && ! --$.active) {
768                 $.event.trigger("ajaxStop");
769             }
771             if (s.complete) {
772                 s.complete.call(s.context, xhr, status);
773             }
775             callbackProcessed = true;
776             if (s.timeout) {
777                 clearTimeout(timeoutHandle);
778             }
780             // clean up
781             setTimeout(function() {
782                 if (!s.iframeTarget) {
783                     $io.remove();
784                 }
785                 else { //adding else to clean up existing iframe response.
786                     $io.attr('src', s.iframeSrc);
787                 }
788                 xhr.responseXML = null;
789             }, 100);
790         }
792         var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
793             if (window.ActiveXObject) {
794                 doc = new ActiveXObject('Microsoft.XMLDOM');
795                 doc.async = 'false';
796                 doc.loadXML(s);
797             }
798             else {
799                 doc = (new DOMParser()).parseFromString(s, 'text/xml');
800             }
801             return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
802         };
803         var parseJSON = $.parseJSON || function(s) {
804             /*jslint evil:true */
805             return window['eval']('(' + s + ')');
806         };
808         var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
810             var ct = xhr.getResponseHeader('content-type') || '',
811                 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
812                 data = xml ? xhr.responseXML : xhr.responseText;
814             if (xml && data.documentElement.nodeName === 'parsererror') {
815                 if ($.error) {
816                     $.error('parsererror');
817                 }
818             }
819             if (s && s.dataFilter) {
820                 data = s.dataFilter(data, type);
821             }
822             if (typeof data === 'string') {
823                 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
824                     data = parseJSON(data);
825                 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
826                     $.globalEval(data);
827                 }
828             }
829             return data;
830         };
832         return deferred;
833     }
837  * ajaxForm() provides a mechanism for fully automating form submission.
839  * The advantages of using this method instead of ajaxSubmit() are:
841  * 1: This method will include coordinates for <input type="image" /> elements (if the element
842  *    is used to submit the form).
843  * 2. This method will include the submit element's name/value data (for the element that was
844  *    used to submit the form).
845  * 3. This method binds the submit() method to the form for you.
847  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
848  * passes the options argument along after properly binding events for submit elements and
849  * the form itself.
850  */
851 $.fn.ajaxForm = function(options) {
852     options = options || {};
853     options.delegation = options.delegation && $.isFunction($.fn.on);
855     // in jQuery 1.3+ we can fix mistakes with the ready state
856     if (!options.delegation && this.length === 0) {
857         var o = { s: this.selector, c: this.context };
858         if (!$.isReady && o.s) {
859             log('DOM not ready, queuing ajaxForm');
860             $(function() {
861                 $(o.s,o.c).ajaxForm(options);
862             });
863             return this;
864         }
865         // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
866         log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
867         return this;
868     }
870     if ( options.delegation ) {
871         $(document)
872             .off('submit.form-plugin', this.selector, doAjaxSubmit)
873             .off('click.form-plugin', this.selector, captureSubmittingElement)
874             .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
875             .on('click.form-plugin', this.selector, options, captureSubmittingElement);
876         return this;
877     }
879     return this.ajaxFormUnbind()
880         .bind('submit.form-plugin', options, doAjaxSubmit)
881         .bind('click.form-plugin', options, captureSubmittingElement);
884 // private event handlers
885 function doAjaxSubmit(e) {
886     /*jshint validthis:true */
887     var options = e.data;
888     if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
889         e.preventDefault();
890         $(e.target).ajaxSubmit(options); // #365
891     }
894 function captureSubmittingElement(e) {
895     /*jshint validthis:true */
896     var target = e.target;
897     var $el = $(target);
898     if (!($el.is("[type=submit],[type=image]"))) {
899         // is this a child element of the submit el?  (ex: a span within a button)
900         var t = $el.closest('[type=submit]');
901         if (t.length === 0) {
902             return;
903         }
904         target = t[0];
905     }
906     var form = this;
907     form.clk = target;
908     if (target.type == 'image') {
909         if (e.offsetX !== undefined) {
910             form.clk_x = e.offsetX;
911             form.clk_y = e.offsetY;
912         } else if (typeof $.fn.offset == 'function') {
913             var offset = $el.offset();
914             form.clk_x = e.pageX - offset.left;
915             form.clk_y = e.pageY - offset.top;
916         } else {
917             form.clk_x = e.pageX - target.offsetLeft;
918             form.clk_y = e.pageY - target.offsetTop;
919         }
920     }
921     // clear form vars
922     setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
926 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
927 $.fn.ajaxFormUnbind = function() {
928     return this.unbind('submit.form-plugin click.form-plugin');
932  * formToArray() gathers form element data into an array of objects that can
933  * be passed to any of the following ajax functions: $.get, $.post, or load.
934  * Each object in the array has both a 'name' and 'value' property.  An example of
935  * an array for a simple login form might be:
937  * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
939  * It is this array that is passed to pre-submit callback functions provided to the
940  * ajaxSubmit() and ajaxForm() methods.
941  */
942 $.fn.formToArray = function(semantic, elements) {
943     var a = [];
944     if (this.length === 0) {
945         return a;
946     }
948     var form = this[0];
949     var formId = this.attr('id');
950     var els = semantic ? form.getElementsByTagName('*') : form.elements;
951     var els2;
953     if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
954         els = $(els).get();  // convert to standard array
955     }
957     // #386; account for inputs outside the form which use the 'form' attribute
958     if ( formId ) {
959         els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
960         if ( els2.length ) {
961             els = (els || []).concat(els2);
962         }
963     }
965     if (!els || !els.length) {
966         return a;
967     }
969     var i,j,n,v,el,max,jmax;
970     for(i=0, max=els.length; i < max; i++) {
971         el = els[i];
972         n = el.name;
973         if (!n || el.disabled) {
974             continue;
975         }
977         if (semantic && form.clk && el.type == "image") {
978             // handle image inputs on the fly when semantic == true
979             if(form.clk == el) {
980                 a.push({name: n, value: $(el).val(), type: el.type });
981                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
982             }
983             continue;
984         }
986         v = $.fieldValue(el, true);
987         if (v && v.constructor == Array) {
988             if (elements) {
989                 elements.push(el);
990             }
991             for(j=0, jmax=v.length; j < jmax; j++) {
992                 a.push({name: n, value: v[j]});
993             }
994         }
995         else if (feature.fileapi && el.type == 'file') {
996             if (elements) {
997                 elements.push(el);
998             }
999             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     }
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;
1031  * Serializes form data into a 'submittable' string. This method will return a string
1032  * in the format: name1=value1&amp;name2=value2
1033  */
1034 $.fn.formSerialize = function(semantic) {
1035     //hand off to jQuery.param for proper encoding
1036     return $.param(this.formToArray(semantic));
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&amp;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);
1065  * Returns the value(s) of the element in the matched set.  For example, consider the following form:
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>
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']
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']
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']
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.
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;
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     }
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     }
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();
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     });
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     });
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     });
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     });
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     });
1260 // expose debug var
1261 $.fn.ajaxSubmit.debug = false;
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     }
1277 }));