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