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