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