Transmission: update from 2.42 to 2.50
[tomato.git] / release / src / router / transmission / web / javascript / jquery / jquery.form.js
blob4c044b4604a15925f40531d3288b73a05cccb46e
1 /*!
2  * jQuery Form Plugin
3  * version: 2.87 (20-OCT-2011)
4  * @requires jQuery v1.3.2 or later
5  *
6  * Examples and documentation at: http://malsup.com/jquery/form/
7  * Dual licensed under the MIT and GPL licenses:
8  *   http://www.opensource.org/licenses/mit-license.php
9  *   http://www.gnu.org/licenses/gpl.html
10  */
11 ;(function($) {
14         Usage Note:
15         -----------
16         Do not use both ajaxSubmit and ajaxForm on the same form.  These
17         functions are intended to be exclusive.  Use ajaxSubmit if you want
18         to bind your own submit handler to the form.  For example,
20         $(document).ready(function() {
21                 $('#myForm').bind('submit', function(e) {
22                         e.preventDefault(); // <-- important
23                         $(this).ajaxSubmit({
24                                 target: '#output'
25                         });
26                 });
27         });
29         Use ajaxForm when you want the plugin to manage all the event binding
30         for you.  For example,
32         $(document).ready(function() {
33                 $('#myForm').ajaxForm({
34                         target: '#output'
35                 });
36         });
38         When using ajaxForm, the ajaxSubmit function will be invoked for you
39         at the appropriate time.
42 /**
43  * ajaxSubmit() provides a mechanism for immediately submitting
44  * an HTML form using AJAX.
45  */
46 $.fn.ajaxSubmit = function(options) {
47         // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
48         if (!this.length) {
49                 log('ajaxSubmit: skipping submit process - no element selected');
50                 return this;
51         }
52         
53         var method, action, url, $form = this;
55         if (typeof options == 'function') {
56                 options = { success: options };
57         }
59         method = this.attr('method');
60         action = this.attr('action');
61         url = (typeof action === 'string') ? $.trim(action) : '';
62         url = url || window.location.href || '';
63         if (url) {
64                 // clean url (don't include hash vaue)
65                 url = (url.match(/^([^#]+)/)||[])[1];
66         }
68         options = $.extend(true, {
69                 url:  url,
70                 success: $.ajaxSettings.success,
71                 type: method || 'GET',
72                 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
73         }, options);
75         // hook for manipulating the form data before it is extracted;
76         // convenient for use with rich editors like tinyMCE or FCKEditor
77         var veto = {};
78         this.trigger('form-pre-serialize', [this, options, veto]);
79         if (veto.veto) {
80                 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
81                 return this;
82         }
84         // provide opportunity to alter form data before it is serialized
85         if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
86                 log('ajaxSubmit: submit aborted via beforeSerialize callback');
87                 return this;
88         }
90    var traditional = options.traditional;
91    if ( traditional === undefined ) {
92       traditional = $.ajaxSettings.traditional;
93    }
94    
95         var qx,n,v,a = this.formToArray(options.semantic);
96         if (options.data) {
97                 options.extraData = options.data;
98       qx = $.param(options.data, traditional);
99         }
101         // give pre-submit callback an opportunity to abort the submit
102         if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
103                 log('ajaxSubmit: submit aborted via beforeSubmit callback');
104                 return this;
105         }
107         // fire vetoable 'validate' event
108         this.trigger('form-submit-validate', [a, this, options, veto]);
109         if (veto.veto) {
110                 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
111                 return this;
112         }
114         var q = $.param(a, traditional);
115    if (qx)
116       q = ( q ? (q + '&' + qx) : qx );
118         if (options.type.toUpperCase() == 'GET') {
119                 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
120                 options.data = null;  // data is null for 'get'
121         }
122         else {
123                 options.data = q; // data is the query string for 'post'
124         }
126         var callbacks = [];
127         if (options.resetForm) {
128                 callbacks.push(function() { $form.resetForm(); });
129         }
130         if (options.clearForm) {
131                 callbacks.push(function() { $form.clearForm(options.includeHidden); });
132         }
134         // perform a load on the target only if dataType is not provided
135         if (!options.dataType && options.target) {
136                 var oldSuccess = options.success || function(){};
137                 callbacks.push(function(data) {
138                         var fn = options.replaceTarget ? 'replaceWith' : 'html';
139                         $(options.target)[fn](data).each(oldSuccess, arguments);
140                 });
141         }
142         else if (options.success) {
143                 callbacks.push(options.success);
144         }
146         options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
147                 var context = options.context || options;   // jQuery 1.4+ supports scope context 
148                 for (var i=0, max=callbacks.length; i < max; i++) {
149                         callbacks[i].apply(context, [data, status, xhr || $form, $form]);
150                 }
151         };
153         // are there files to upload?
154         var fileInputs = $('input:file', this).length > 0;
155         var mp = 'multipart/form-data';
156         var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
158         // options.iframe allows user to force iframe mode
159         // 06-NOV-09: now defaulting to iframe mode if file input is detected
160    if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
161            // hack to fix Safari hang (thanks to Tim Molendijk for this)
162            // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
163            if (options.closeKeepAlive) {
164                    $.get(options.closeKeepAlive, function() { fileUpload(a); });
165                 }
166            else {
167                    fileUpload(a);
168                 }
169    }
170    else {
171                 // IE7 massage (see issue 57)
172                 if ($.browser.msie && method == 'get' && typeof options.type === "undefined") {
173                         var ieMeth = $form[0].getAttribute('method');
174                         if (typeof ieMeth === 'string')
175                                 options.type = ieMeth;
176                 }
177                 $.ajax(options);
178    }
180         // fire 'notify' event
181         this.trigger('form-submit-notify', [this, options]);
182         return this;
185         // private function for handling file uploads (hat tip to YAHOO!)
186         function fileUpload(a) {
187                 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
188         var useProp = !!$.fn.prop;
190         if (a) {
191             if ( useProp ) {
192                 // ensure that every serialized input is still enabled
193                 for (i=0; i < a.length; i++) {
194                     el = $(form[a[i].name]);
195                     el.prop('disabled', false);
196                 }
197             } else {
198                 for (i=0; i < a.length; i++) {
199                     el = $(form[a[i].name]);
200                     el.removeAttr('disabled');
201                 }
202             };
203         }
205                 if ($(':input[name=submit],:input[id=submit]', form).length) {
206                         // if there is an input with a name or id of 'submit' then we won't be
207                         // able to invoke the submit fn on the form (at least not x-browser)
208                         alert('Error: Form elements must not have name or id of "submit".');
209                         return;
210                 }
211                 
212                 s = $.extend(true, {}, $.ajaxSettings, options);
213                 s.context = s.context || s;
214                 id = 'jqFormIO' + (new Date().getTime());
215                 if (s.iframeTarget) {
216                         $io = $(s.iframeTarget);
217                         n = $io.attr('name');
218                         if (n == null)
219                                 $io.attr('name', id);
220                         else
221                                 id = n;
222                 }
223                 else {
224                         $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
225                         $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
226                 }
227                 io = $io[0];
230                 xhr = { // mock object
231                         aborted: 0,
232                         responseText: null,
233                         responseXML: null,
234                         status: 0,
235                         statusText: 'n/a',
236                         getAllResponseHeaders: function() {},
237                         getResponseHeader: function() {},
238                         setRequestHeader: function() {},
239                         abort: function(status) {
240                                 var e = (status === 'timeout' ? 'timeout' : 'aborted');
241                                 log('aborting upload... ' + e);
242                                 this.aborted = 1;
243                                 $io.attr('src', s.iframeSrc); // abort op in progress
244                                 xhr.error = e;
245                                 s.error && s.error.call(s.context, xhr, e, status);
246                                 g && $.event.trigger("ajaxError", [xhr, s, e]);
247                                 s.complete && s.complete.call(s.context, xhr, e);
248                         }
249                 };
251                 g = s.global;
252                 // trigger ajax global events so that activity/block indicators work like normal
253                 if (g && ! $.active++) {
254                         $.event.trigger("ajaxStart");
255                 }
256                 if (g) {
257                         $.event.trigger("ajaxSend", [xhr, s]);
258                 }
260                 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
261                         if (s.global) {
262                                 $.active--;
263                         }
264                         return;
265                 }
266                 if (xhr.aborted) {
267                         return;
268                 }
270                 // add submitting element to data if we know it
271                 sub = form.clk;
272                 if (sub) {
273                         n = sub.name;
274                         if (n && !sub.disabled) {
275                                 s.extraData = s.extraData || {};
276                                 s.extraData[n] = sub.value;
277                                 if (sub.type == "image") {
278                                         s.extraData[n+'.x'] = form.clk_x;
279                                         s.extraData[n+'.y'] = form.clk_y;
280                                 }
281                         }
282                 }
283                 
284                 var CLIENT_TIMEOUT_ABORT = 1;
285                 var SERVER_ABORT = 2;
287                 function getDoc(frame) {
288                         var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
289                         return doc;
290                 }
291                 
292                 // take a breath so that pending repaints get some cpu time before the upload starts
293                 function doSubmit() {
294                         // make sure form attrs are set
295                         var t = $form.attr('target'), a = $form.attr('action');
297                         // update form attrs in IE friendly way
298                         form.setAttribute('target',id);
299                         if (!method) {
300                                 form.setAttribute('method', 'POST');
301                         }
302                         if (a != s.url) {
303                                 form.setAttribute('action', s.url);
304                         }
306                         // ie borks in some cases when setting encoding
307                         if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
308                                 $form.attr({
309                                         encoding: 'multipart/form-data',
310                                         enctype:  'multipart/form-data'
311                                 });
312                         }
314                         // support timout
315                         if (s.timeout) {
316                                 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
317                         }
318                         
319                         // look for server aborts
320                         function checkState() {
321                                 try {
322                                         var state = getDoc(io).readyState;
323                                         log('state = ' + state);
324                                         if (state.toLowerCase() == 'uninitialized')
325                                                 setTimeout(checkState,50);
326                                 }
327                                 catch(e) {
328                                         log('Server abort: ' , e, ' (', e.name, ')');
329                                         cb(SERVER_ABORT);
330                                         timeoutHandle && clearTimeout(timeoutHandle);
331                                         timeoutHandle = undefined;
332                                 }
333                         }
335                         // add "extra" data to form if provided in options
336                         var extraInputs = [];
337                         try {
338                                 if (s.extraData) {
339                                         for (var n in s.extraData) {
340                                                 extraInputs.push(
341                                                         $('<input type="hidden" name="'+n+'" />').attr('value',s.extraData[n])
342                                                                 .appendTo(form)[0]);
343                                         }
344                                 }
346                                 if (!s.iframeTarget) {
347                                         // add iframe to doc and submit the form
348                                         $io.appendTo('body');
349                         io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
350                                 }
351                                 setTimeout(checkState,15);
352                                 form.submit();
353                         }
354                         finally {
355                                 // reset attrs and remove "extra" input elements
356                                 form.setAttribute('action',a);
357                                 if(t) {
358                                         form.setAttribute('target', t);
359                                 } else {
360                                         $form.removeAttr('target');
361                                 }
362                                 $(extraInputs).remove();
363                         }
364                 }
366                 if (s.forceSync) {
367                         doSubmit();
368                 }
369                 else {
370                         setTimeout(doSubmit, 10); // this lets dom updates render
371                 }
373                 var data, doc, domCheckCount = 50, callbackProcessed;
375                 function cb(e) {
376                         if (xhr.aborted || callbackProcessed) {
377                                 return;
378                         }
379                         try {
380                                 doc = getDoc(io);
381                         }
382                         catch(ex) {
383                                 log('cannot access response document: ', ex);
384                                 e = SERVER_ABORT;
385                         }
386                         if (e === CLIENT_TIMEOUT_ABORT && xhr) {
387                                 xhr.abort('timeout');
388                                 return;
389                         }
390                         else if (e == SERVER_ABORT && xhr) {
391                                 xhr.abort('server abort');
392                                 return;
393                         }
395                         if (!doc || doc.location.href == s.iframeSrc) {
396                                 // response not received yet
397                                 if (!timedOut)
398                                         return;
399                         }
400             io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
402                         var status = 'success', errMsg;
403                         try {
404                                 if (timedOut) {
405                                         throw 'timeout';
406                                 }
408                                 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
409                                 log('isXml='+isXml);
410                                 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
411                                         if (--domCheckCount) {
412                                                 // in some browsers (Opera) the iframe DOM is not always traversable when
413                                                 // the onload callback fires, so we loop a bit to accommodate
414                                                 log('requeing onLoad callback, DOM not available');
415                                                 setTimeout(cb, 250);
416                                                 return;
417                                         }
418                                         // let this fall through because server response could be an empty document
419                                         //log('Could not access iframe DOM after mutiple tries.');
420                                         //throw 'DOMException: not available';
421                                 }
423                                 //log('response detected');
424                 var docRoot = doc.body ? doc.body : doc.documentElement;
425                 xhr.responseText = docRoot ? docRoot.innerHTML : null;
426                                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
427                                 if (isXml)
428                                         s.dataType = 'xml';
429                                 xhr.getResponseHeader = function(header){
430                                         var headers = {'content-type': s.dataType};
431                                         return headers[header];
432                                 };
433                 // support for XHR 'status' & 'statusText' emulation :
434                 if (docRoot) {
435                     xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
436                     xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
437                 }
439                                 var dt = (s.dataType || '').toLowerCase();
440                                 var scr = /(json|script|text)/.test(dt);
441                                 if (scr || s.textarea) {
442                                         // see if user embedded response in textarea
443                                         var ta = doc.getElementsByTagName('textarea')[0];
444                                         if (ta) {
445                                                 xhr.responseText = ta.value;
446                         // support for XHR 'status' & 'statusText' emulation :
447                         xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
448                         xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
449                                         }
450                                         else if (scr) {
451                                                 // account for browsers injecting pre around json response
452                                                 var pre = doc.getElementsByTagName('pre')[0];
453                                                 var b = doc.getElementsByTagName('body')[0];
454                                                 if (pre) {
455                                                         xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
456                                                 }
457                                                 else if (b) {
458                                                         xhr.responseText = b.textContent ? b.textContent : b.innerText;
459                                                 }
460                                         }
461                                 }
462                                 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText != null) {
463                                         xhr.responseXML = toXml(xhr.responseText);
464                                 }
466                 try {
467                     data = httpData(xhr, dt, s);
468                 }
469                 catch (e) {
470                     status = 'parsererror';
471                     xhr.error = errMsg = (e || status);
472                 }
473                         }
474                         catch (e) {
475                                 log('error caught: ',e);
476                                 status = 'error';
477                 xhr.error = errMsg = (e || status);
478                         }
480                         if (xhr.aborted) {
481                                 log('upload aborted');
482                                 status = null;
483                         }
485             if (xhr.status) { // we've set xhr.status
486                 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
487             }
489                         // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
490                         if (status === 'success') {
491                                 s.success && s.success.call(s.context, data, 'success', xhr);
492                                 g && $.event.trigger("ajaxSuccess", [xhr, s]);
493                         }
494             else if (status) {
495                                 if (errMsg == undefined)
496                                         errMsg = xhr.statusText;
497                                 s.error && s.error.call(s.context, xhr, status, errMsg);
498                                 g && $.event.trigger("ajaxError", [xhr, s, errMsg]);
499             }
501                         g && $.event.trigger("ajaxComplete", [xhr, s]);
503                         if (g && ! --$.active) {
504                                 $.event.trigger("ajaxStop");
505                         }
507                         s.complete && s.complete.call(s.context, xhr, status);
509                         callbackProcessed = true;
510                         if (s.timeout)
511                                 clearTimeout(timeoutHandle);
513                         // clean up
514                         setTimeout(function() {
515                                 if (!s.iframeTarget)
516                                         $io.remove();
517                                 xhr.responseXML = null;
518                         }, 100);
519                 }
521                 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
522                         if (window.ActiveXObject) {
523                                 doc = new ActiveXObject('Microsoft.XMLDOM');
524                                 doc.async = 'false';
525                                 doc.loadXML(s);
526                         }
527                         else {
528                                 doc = (new DOMParser()).parseFromString(s, 'text/xml');
529                         }
530                         return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
531                 };
532                 var parseJSON = $.parseJSON || function(s) {
533                         return window['eval']('(' + s + ')');
534                 };
536                 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
538                         var ct = xhr.getResponseHeader('content-type') || '',
539                                 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
540                                 data = xml ? xhr.responseXML : xhr.responseText;
542                         if (xml && data.documentElement.nodeName === 'parsererror') {
543                                 $.error && $.error('parsererror');
544                         }
545                         if (s && s.dataFilter) {
546                                 data = s.dataFilter(data, type);
547                         }
548                         if (typeof data === 'string') {
549                                 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
550                                         data = parseJSON(data);
551                                 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
552                                         $.globalEval(data);
553                                 }
554                         }
555                         return data;
556                 };
557         }
561  * ajaxForm() provides a mechanism for fully automating form submission.
563  * The advantages of using this method instead of ajaxSubmit() are:
565  * 1: This method will include coordinates for <input type="image" /> elements (if the element
566  *      is used to submit the form).
567  * 2. This method will include the submit element's name/value data (for the element that was
568  *      used to submit the form).
569  * 3. This method binds the submit() method to the form for you.
571  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
572  * passes the options argument along after properly binding events for submit elements and
573  * the form itself.
574  */
575 $.fn.ajaxForm = function(options) {
576         // in jQuery 1.3+ we can fix mistakes with the ready state
577         if (this.length === 0) {
578                 var o = { s: this.selector, c: this.context };
579                 if (!$.isReady && o.s) {
580                         log('DOM not ready, queuing ajaxForm');
581                         $(function() {
582                                 $(o.s,o.c).ajaxForm(options);
583                         });
584                         return this;
585                 }
586                 // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
587                 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
588                 return this;
589         }
591         return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
592                 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
593                         e.preventDefault();
594                         $(this).ajaxSubmit(options);
595                 }
596         }).bind('click.form-plugin', function(e) {
597                 var target = e.target;
598                 var $el = $(target);
599                 if (!($el.is(":submit,input:image"))) {
600                         // is this a child element of the submit el?  (ex: a span within a button)
601                         var t = $el.closest(':submit');
602                         if (t.length == 0) {
603                                 return;
604                         }
605                         target = t[0];
606                 }
607                 var form = this;
608                 form.clk = target;
609                 if (target.type == 'image') {
610                         if (e.offsetX != undefined) {
611                                 form.clk_x = e.offsetX;
612                                 form.clk_y = e.offsetY;
613                         } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
614                                 var offset = $el.offset();
615                                 form.clk_x = e.pageX - offset.left;
616                                 form.clk_y = e.pageY - offset.top;
617                         } else {
618                                 form.clk_x = e.pageX - target.offsetLeft;
619                                 form.clk_y = e.pageY - target.offsetTop;
620                         }
621                 }
622                 // clear form vars
623                 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
624         });
627 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
628 $.fn.ajaxFormUnbind = function() {
629         return this.unbind('submit.form-plugin click.form-plugin');
633  * formToArray() gathers form element data into an array of objects that can
634  * be passed to any of the following ajax functions: $.get, $.post, or load.
635  * Each object in the array has both a 'name' and 'value' property.  An example of
636  * an array for a simple login form might be:
638  * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
640  * It is this array that is passed to pre-submit callback functions provided to the
641  * ajaxSubmit() and ajaxForm() methods.
642  */
643 $.fn.formToArray = function(semantic) {
644         var a = [];
645         if (this.length === 0) {
646                 return a;
647         }
649         var form = this[0];
650         var els = semantic ? form.getElementsByTagName('*') : form.elements;
651         if (!els) {
652                 return a;
653         }
655         var i,j,n,v,el,max,jmax;
656         for(i=0, max=els.length; i < max; i++) {
657                 el = els[i];
658                 n = el.name;
659                 if (!n) {
660                         continue;
661                 }
663                 if (semantic && form.clk && el.type == "image") {
664                         // handle image inputs on the fly when semantic == true
665                         if(!el.disabled && form.clk == el) {
666                                 a.push({name: n, value: $(el).val()});
667                                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
668                         }
669                         continue;
670                 }
672                 v = $.fieldValue(el, true);
673                 if (v && v.constructor == Array) {
674                         for(j=0, jmax=v.length; j < jmax; j++) {
675                                 a.push({name: n, value: v[j]});
676                         }
677                 }
678                 else if (v !== null && typeof v != 'undefined') {
679                         a.push({name: n, value: v});
680                 }
681         }
683         if (!semantic && form.clk) {
684                 // input type=='image' are not found in elements array! handle it here
685                 var $input = $(form.clk), input = $input[0];
686                 n = input.name;
687                 if (n && !input.disabled && input.type == 'image') {
688                         a.push({name: n, value: $input.val()});
689                         a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
690                 }
691         }
692         return a;
696  * Serializes form data into a 'submittable' string. This method will return a string
697  * in the format: name1=value1&amp;name2=value2
698  */
699 $.fn.formSerialize = function(semantic) {
700         //hand off to jQuery.param for proper encoding
701         return $.param(this.formToArray(semantic));
705  * Serializes all field elements in the jQuery object into a query string.
706  * This method will return a string in the format: name1=value1&amp;name2=value2
707  */
708 $.fn.fieldSerialize = function(successful) {
709         var a = [];
710         this.each(function() {
711                 var n = this.name;
712                 if (!n) {
713                         return;
714                 }
715                 var v = $.fieldValue(this, successful);
716                 if (v && v.constructor == Array) {
717                         for (var i=0,max=v.length; i < max; i++) {
718                                 a.push({name: n, value: v[i]});
719                         }
720                 }
721                 else if (v !== null && typeof v != 'undefined') {
722                         a.push({name: this.name, value: v});
723                 }
724         });
725         //hand off to jQuery.param for proper encoding
726         return $.param(a);
730  * Returns the value(s) of the element in the matched set.  For example, consider the following form:
732  *  <form><fieldset>
733  *        <input name="A" type="text" />
734  *        <input name="A" type="text" />
735  *        <input name="B" type="checkbox" value="B1" />
736  *        <input name="B" type="checkbox" value="B2"/>
737  *        <input name="C" type="radio" value="C1" />
738  *        <input name="C" type="radio" value="C2" />
739  *  </fieldset></form>
741  *  var v = $(':text').fieldValue();
742  *  // if no values are entered into the text inputs
743  *  v == ['','']
744  *  // if values entered into the text inputs are 'foo' and 'bar'
745  *  v == ['foo','bar']
747  *  var v = $(':checkbox').fieldValue();
748  *  // if neither checkbox is checked
749  *  v === undefined
750  *  // if both checkboxes are checked
751  *  v == ['B1', 'B2']
753  *  var v = $(':radio').fieldValue();
754  *  // if neither radio is checked
755  *  v === undefined
756  *  // if first radio is checked
757  *  v == ['C1']
759  * The successful argument controls whether or not the field element must be 'successful'
760  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
761  * The default value of the successful argument is true.  If this value is false the value(s)
762  * for each element is returned.
764  * Note: This method *always* returns an array.  If no valid value can be determined the
765  *         array will be empty, otherwise it will contain one or more values.
766  */
767 $.fn.fieldValue = function(successful) {
768         for (var val=[], i=0, max=this.length; i < max; i++) {
769                 var el = this[i];
770                 var v = $.fieldValue(el, successful);
771                 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
772                         continue;
773                 }
774                 v.constructor == Array ? $.merge(val, v) : val.push(v);
775         }
776         return val;
780  * Returns the value of the field element.
781  */
782 $.fieldValue = function(el, successful) {
783         var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
784         if (successful === undefined) {
785                 successful = true;
786         }
788         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
789                 (t == 'checkbox' || t == 'radio') && !el.checked ||
790                 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
791                 tag == 'select' && el.selectedIndex == -1)) {
792                         return null;
793         }
795         if (tag == 'select') {
796                 var index = el.selectedIndex;
797                 if (index < 0) {
798                         return null;
799                 }
800                 var a = [], ops = el.options;
801                 var one = (t == 'select-one');
802                 var max = (one ? index+1 : ops.length);
803                 for(var i=(one ? index : 0); i < max; i++) {
804                         var op = ops[i];
805                         if (op.selected) {
806                                 var v = op.value;
807                                 if (!v) { // extra pain for IE...
808                                         v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
809                                 }
810                                 if (one) {
811                                         return v;
812                                 }
813                                 a.push(v);
814                         }
815                 }
816                 return a;
817         }
818         return $(el).val();
822  * Clears the form data.  Takes the following actions on the form's input fields:
823  *  - input text fields will have their 'value' property set to the empty string
824  *  - select elements will have their 'selectedIndex' property set to -1
825  *  - checkbox and radio inputs will have their 'checked' property set to false
826  *  - inputs of type submit, button, reset, and hidden will *not* be effected
827  *  - button elements will *not* be effected
828  */
829 $.fn.clearForm = function(includeHidden) {
830         return this.each(function() {
831                 $('input,select,textarea', this).clearFields(includeHidden);
832         });
836  * Clears the selected form elements.
837  */
838 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
839         var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
840         return this.each(function() {
841                 var t = this.type, tag = this.tagName.toLowerCase();
842                 if (re.test(t) || tag == 'textarea' || (includeHidden && /hidden/.test(t)) ) {
843                         this.value = '';
844                 }
845                 else if (t == 'checkbox' || t == 'radio') {
846                         this.checked = false;
847                 }
848                 else if (tag == 'select') {
849                         this.selectedIndex = -1;
850                 }
851         });
855  * Resets the form data.  Causes all form elements to be reset to their original value.
856  */
857 $.fn.resetForm = function() {
858         return this.each(function() {
859                 // guard against an input with the name of 'reset'
860                 // note that IE reports the reset function as an 'object'
861                 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
862                         this.reset();
863                 }
864         });
868  * Enables or disables any matching elements.
869  */
870 $.fn.enable = function(b) {
871         if (b === undefined) {
872                 b = true;
873         }
874         return this.each(function() {
875                 this.disabled = !b;
876         });
880  * Checks/unchecks any matching checkboxes or radio buttons and
881  * selects/deselects and matching option elements.
882  */
883 $.fn.selected = function(select) {
884         if (select === undefined) {
885                 select = true;
886         }
887         return this.each(function() {
888                 var t = this.type;
889                 if (t == 'checkbox' || t == 'radio') {
890                         this.checked = select;
891                 }
892                 else if (this.tagName.toLowerCase() == 'option') {
893                         var $sel = $(this).parent('select');
894                         if (select && $sel[0] && $sel[0].type == 'select-one') {
895                                 // deselect all other options
896                                 $sel.find('option').selected(false);
897                         }
898                         this.selected = select;
899                 }
900         });
903 // expose debug var
904 $.fn.ajaxSubmit.debug = false;
906 // helper fn for console logging
907 function log() {
908         if (!$.fn.ajaxSubmit.debug) 
909                 return;
910         var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
911         if (window.console && window.console.log) {
912                 window.console.log(msg);
913         }
914         else if (window.opera && window.opera.postError) {
915                 window.opera.postError(msg);
916         }
919 })(jQuery);