epic-s6ts
[kivitendo-erp.git] / js / jquery.form.js
1 /*!
2  * jQuery Form Plugin
3  * version: 2.52 (07-DEC-2010)
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($) {
12
13 /*
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,
19
20         $(document).ready(function() {
21                 $('#myForm').bind('submit', function(e) {
22                         e.preventDefault(); // <-- important
23                         $(this).ajaxSubmit({
24                                 target: '#output'
25                         });
26                 });
27         });
28
29         Use ajaxForm when you want the plugin to manage all the event binding
30         for you.  For example,
31
32         $(document).ready(function() {
33                 $('#myForm').ajaxForm({
34                         target: '#output'
35                 });
36         });
37
38         When using ajaxForm, the ajaxSubmit function will be invoked for you
39         at the appropriate time.
40 */
41
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         if (typeof options == 'function') {
54                 options = { success: options };
55         }
56
57         var action = this.attr('action');
58         var url = (typeof action === 'string') ? $.trim(action) : '';
59         if (url) {
60                 // clean url (don't include hash vaue)
61                 url = (url.match(/^([^#]+)/)||[])[1];
62         }
63         url = url || window.location.href || '';
64
65         options = $.extend(true, {
66                 url:  url,
67                 type: this.attr('method') || 'GET',
68                 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
69         }, options);
70
71         // hook for manipulating the form data before it is extracted;
72         // convenient for use with rich editors like tinyMCE or FCKEditor
73         var veto = {};
74         this.trigger('form-pre-serialize', [this, options, veto]);
75         if (veto.veto) {
76                 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
77                 return this;
78         }
79
80         // provide opportunity to alter form data before it is serialized
81         if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
82                 log('ajaxSubmit: submit aborted via beforeSerialize callback');
83                 return this;
84         }
85
86         var n,v,a = this.formToArray(options.semantic);
87         if (options.data) {
88                 options.extraData = options.data;
89                 for (n in options.data) {
90                         if(options.data[n] instanceof Array) {
91                                 for (var k in options.data[n]) {
92                                         a.push( { name: n, value: options.data[n][k] } );
93                                 }
94                         }
95                         else {
96                                 v = options.data[n];
97                                 v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
98                                 a.push( { name: n, value: v } );
99                         }
100                 }
101         }
102
103         // give pre-submit callback an opportunity to abort the submit
104         if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
105                 log('ajaxSubmit: submit aborted via beforeSubmit callback');
106                 return this;
107         }
108
109         // fire vetoable 'validate' event
110         this.trigger('form-submit-validate', [a, this, options, veto]);
111         if (veto.veto) {
112                 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
113                 return this;
114         }
115
116         var q = $.param(a);
117
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         }
125
126         var $form = this, callbacks = [];
127         if (options.resetForm) {
128                 callbacks.push(function() { $form.resetForm(); });
129         }
130         if (options.clearForm) {
131                 callbacks.push(function() { $form.clearForm(); });
132         }
133
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         }
145
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         };
152
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);
157
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, fileUpload);
165                 }
166            else {
167                    fileUpload();
168                 }
169    }
170    else {
171            $.ajax(options);
172    }
173
174         // fire 'notify' event
175         this.trigger('form-submit-notify', [this, options]);
176         return this;
177
178
179         // private function for handling file uploads (hat tip to YAHOO!)
180         function fileUpload() {
181                 var form = $form[0];
182
183                 if ($(':input[name=submit],:input[id=submit]', form).length) {
184                         // if there is an input with a name or id of 'submit' then we won't be
185                         // able to invoke the submit fn on the form (at least not x-browser)
186                         alert('Error: Form elements must not have name or id of "submit".');
187                         return;
188                 }
189                 
190                 var s = $.extend(true, {}, $.ajaxSettings, options);
191                 s.context = s.context || s;
192                 var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
193                 window[fn] = function() {
194                         var f = $io.data('form-plugin-onload');
195                         if (f) {
196                                 f();
197                                 window[fn] = undefined;
198                                 try { delete window[fn]; } catch(e){}
199                         }
200                 }
201                 var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" onload="window[\'_\'+this.id]()" />');
202                 var io = $io[0];
203
204                 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
205
206                 var xhr = { // mock object
207                         aborted: 0,
208                         responseText: null,
209                         responseXML: null,
210                         status: 0,
211                         statusText: 'n/a',
212                         getAllResponseHeaders: function() {},
213                         getResponseHeader: function() {},
214                         setRequestHeader: function() {},
215                         abort: function() {
216                                 this.aborted = 1;
217                                 $io.attr('src', s.iframeSrc); // abort op in progress
218                         }
219                 };
220
221                 var g = s.global;
222                 // trigger ajax global events so that activity/block indicators work like normal
223                 if (g && ! $.active++) {
224                         $.event.trigger("ajaxStart");
225                 }
226                 if (g) {
227                         $.event.trigger("ajaxSend", [xhr, s]);
228                 }
229
230                 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
231                         if (s.global) { 
232                                 $.active--;
233                         }
234                         return;
235                 }
236                 if (xhr.aborted) {
237                         return;
238                 }
239
240                 var cbInvoked = false;
241                 var timedOut = 0;
242
243                 // add submitting element to data if we know it
244                 var sub = form.clk;
245                 if (sub) {
246                         var n = sub.name;
247                         if (n && !sub.disabled) {
248                                 s.extraData = s.extraData || {};
249                                 s.extraData[n] = sub.value;
250                                 if (sub.type == "image") {
251                                         s.extraData[n+'.x'] = form.clk_x;
252                                         s.extraData[n+'.y'] = form.clk_y;
253                                 }
254                         }
255                 }
256
257                 // take a breath so that pending repaints get some cpu time before the upload starts
258                 function doSubmit() {
259                         // make sure form attrs are set
260                         var t = $form.attr('target'), a = $form.attr('action');
261
262                         // update form attrs in IE friendly way
263                         form.setAttribute('target',id);
264                         if (form.getAttribute('method') != 'POST') {
265                                 form.setAttribute('method', 'POST');
266                         }
267                         if (form.getAttribute('action') != s.url) {
268                                 form.setAttribute('action', s.url);
269                         }
270
271                         // ie borks in some cases when setting encoding
272                         if (! s.skipEncodingOverride) {
273                                 $form.attr({
274                                         encoding: 'multipart/form-data',
275                                         enctype:  'multipart/form-data'
276                                 });
277                         }
278
279                         // support timout
280                         if (s.timeout) {
281                                 setTimeout(function() { timedOut = true; cb(); }, s.timeout);
282                         }
283
284                         // add "extra" data to form if provided in options
285                         var extraInputs = [];
286                         try {
287                                 if (s.extraData) {
288                                         for (var n in s.extraData) {
289                                                 extraInputs.push(
290                                                         $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
291                                                                 .appendTo(form)[0]);
292                                         }
293                                 }
294
295                                 // add iframe to doc and submit the form
296                                 $io.appendTo('body');
297                                 $io.data('form-plugin-onload', cb);
298                                 form.submit();
299                         }
300                         finally {
301                                 // reset attrs and remove "extra" input elements
302                                 form.setAttribute('action',a);
303                                 if(t) {
304                                         form.setAttribute('target', t);
305                                 } else {
306                                         $form.removeAttr('target');
307                                 }
308                                 $(extraInputs).remove();
309                         }
310                 }
311
312                 if (s.forceSync) {
313                         doSubmit();
314                 }
315                 else {
316                         setTimeout(doSubmit, 10); // this lets dom updates render
317                 }
318         
319                 var data, doc, domCheckCount = 50;
320
321                 function cb() {
322                         if (cbInvoked) {
323                                 return;
324                         }
325
326                         $io.removeData('form-plugin-onload');
327                         
328                         var ok = true;
329                         try {
330                                 if (timedOut) {
331                                         throw 'timeout';
332                                 }
333                                 // extract the server response from the iframe
334                                 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
335                                 
336                                 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
337                                 log('isXml='+isXml);
338                                 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
339                                         if (--domCheckCount) {
340                                                 // in some browsers (Opera) the iframe DOM is not always traversable when
341                                                 // the onload callback fires, so we loop a bit to accommodate
342                                                 log('requeing onLoad callback, DOM not available');
343                                                 setTimeout(cb, 250);
344                                                 return;
345                                         }
346                                         // let this fall through because server response could be an empty document
347                                         //log('Could not access iframe DOM after mutiple tries.');
348                                         //throw 'DOMException: not available';
349                                 }
350
351                                 //log('response detected');
352                                 cbInvoked = true;
353                                 xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null; 
354                                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
355                                 xhr.getResponseHeader = function(header){
356                                         var headers = {'content-type': s.dataType};
357                                         return headers[header];
358                                 };
359
360                                 var scr = /(json|script)/.test(s.dataType);
361                                 if (scr || s.textarea) {
362                                         // see if user embedded response in textarea
363                                         var ta = doc.getElementsByTagName('textarea')[0];
364                                         if (ta) {
365                                                 xhr.responseText = ta.value;
366                                         }
367                                         else if (scr) {
368                                                 // account for browsers injecting pre around json response
369                                                 var pre = doc.getElementsByTagName('pre')[0];
370                                                 var b = doc.getElementsByTagName('body')[0];
371                                                 if (pre) {
372                                                         xhr.responseText = pre.textContent;
373                                                 }
374                                                 else if (b) {
375                                                         xhr.responseText = b.innerHTML;
376                                                 }
377                                         }                         
378                                 }
379                                 else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
380                                         xhr.responseXML = toXml(xhr.responseText);
381                                 }
382                                 data = $.httpData(xhr, s.dataType);
383                         }
384                         catch(e){
385                                 log('error caught:',e);
386                                 ok = false;
387                                 xhr.error = e;
388                                 $.handleError(s, xhr, 'error', e);
389                         }
390                         
391                         if (xhr.aborted) {
392                                 log('upload aborted');
393                                 ok = false;
394                         }
395
396                         // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
397                         if (ok) {
398                                 s.success.call(s.context, data, 'success', xhr);
399                                 if (g) {
400                                         $.event.trigger("ajaxSuccess", [xhr, s]);
401                                 }
402                         }
403                         if (g) {
404                                 $.event.trigger("ajaxComplete", [xhr, s]);
405                         }
406                         if (g && ! --$.active) {
407                                 $.event.trigger("ajaxStop");
408                         }
409                         if (s.complete) {
410                                 s.complete.call(s.context, xhr, ok ? 'success' : 'error');
411                         }
412
413                         // clean up
414                         setTimeout(function() {
415                                 $io.removeData('form-plugin-onload');
416                                 $io.remove();
417                                 xhr.responseXML = null;
418                         }, 100);
419                 }
420
421                 function toXml(s, doc) {
422                         if (window.ActiveXObject) {
423                                 doc = new ActiveXObject('Microsoft.XMLDOM');
424                                 doc.async = 'false';
425                                 doc.loadXML(s);
426                         }
427                         else {
428                                 doc = (new DOMParser()).parseFromString(s, 'text/xml');
429                         }
430                         return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
431                 }
432         }
433 };
434
435 /**
436  * ajaxForm() provides a mechanism for fully automating form submission.
437  *
438  * The advantages of using this method instead of ajaxSubmit() are:
439  *
440  * 1: This method will include coordinates for <input type="image" /> elements (if the element
441  *      is used to submit the form).
442  * 2. This method will include the submit element's name/value data (for the element that was
443  *      used to submit the form).
444  * 3. This method binds the submit() method to the form for you.
445  *
446  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
447  * passes the options argument along after properly binding events for submit elements and
448  * the form itself.
449  */
450 $.fn.ajaxForm = function(options) {
451         // in jQuery 1.3+ we can fix mistakes with the ready state
452         if (this.length === 0) {
453                 var o = { s: this.selector, c: this.context };
454                 if (!$.isReady && o.s) {
455                         log('DOM not ready, queuing ajaxForm');
456                         $(function() {
457                                 $(o.s,o.c).ajaxForm(options);
458                         });
459                         return this;
460                 }
461                 // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
462                 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
463                 return this;
464         }
465         
466         return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
467                 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
468                         e.preventDefault();
469                         $(this).ajaxSubmit(options);
470                 }
471         }).bind('click.form-plugin', function(e) {
472                 var target = e.target;
473                 var $el = $(target);
474                 if (!($el.is(":submit,input:image"))) {
475                         // is this a child element of the submit el?  (ex: a span within a button)
476                         var t = $el.closest(':submit');
477                         if (t.length == 0) {
478                                 return;
479                         }
480                         target = t[0];
481                 }
482                 var form = this;
483                 form.clk = target;
484                 if (target.type == 'image') {
485                         if (e.offsetX != undefined) {
486                                 form.clk_x = e.offsetX;
487                                 form.clk_y = e.offsetY;
488                         } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
489                                 var offset = $el.offset();
490                                 form.clk_x = e.pageX - offset.left;
491                                 form.clk_y = e.pageY - offset.top;
492                         } else {
493                                 form.clk_x = e.pageX - target.offsetLeft;
494                                 form.clk_y = e.pageY - target.offsetTop;
495                         }
496                 }
497                 // clear form vars
498                 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
499         });
500 };
501
502 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
503 $.fn.ajaxFormUnbind = function() {
504         return this.unbind('submit.form-plugin click.form-plugin');
505 };
506
507 /**
508  * formToArray() gathers form element data into an array of objects that can
509  * be passed to any of the following ajax functions: $.get, $.post, or load.
510  * Each object in the array has both a 'name' and 'value' property.  An example of
511  * an array for a simple login form might be:
512  *
513  * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
514  *
515  * It is this array that is passed to pre-submit callback functions provided to the
516  * ajaxSubmit() and ajaxForm() methods.
517  */
518 $.fn.formToArray = function(semantic) {
519         var a = [];
520         if (this.length === 0) {
521                 return a;
522         }
523
524         var form = this[0];
525         var els = semantic ? form.getElementsByTagName('*') : form.elements;
526         if (!els) {
527                 return a;
528         }
529         
530         var i,j,n,v,el,max,jmax;
531         for(i=0, max=els.length; i < max; i++) {
532                 el = els[i];
533                 n = el.name;
534                 if (!n) {
535                         continue;
536                 }
537
538                 if (semantic && form.clk && el.type == "image") {
539                         // handle image inputs on the fly when semantic == true
540                         if(!el.disabled && form.clk == el) {
541                                 a.push({name: n, value: $(el).val()});
542                                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
543                         }
544                         continue;
545                 }
546
547                 v = $.fieldValue(el, true);
548                 if (v && v.constructor == Array) {
549                         for(j=0, jmax=v.length; j < jmax; j++) {
550                                 a.push({name: n, value: v[j]});
551                         }
552                 }
553                 else if (v !== null && typeof v != 'undefined') {
554                         a.push({name: n, value: v});
555                 }
556         }
557
558         if (!semantic && form.clk) {
559                 // input type=='image' are not found in elements array! handle it here
560                 var $input = $(form.clk), input = $input[0];
561                 n = input.name;
562                 if (n && !input.disabled && input.type == 'image') {
563                         a.push({name: n, value: $input.val()});
564                         a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
565                 }
566         }
567         return a;
568 };
569
570 /**
571  * Serializes form data into a 'submittable' string. This method will return a string
572  * in the format: name1=value1&amp;name2=value2
573  */
574 $.fn.formSerialize = function(semantic) {
575         //hand off to jQuery.param for proper encoding
576         return $.param(this.formToArray(semantic));
577 };
578
579 /**
580  * Serializes all field elements in the jQuery object into a query string.
581  * This method will return a string in the format: name1=value1&amp;name2=value2
582  */
583 $.fn.fieldSerialize = function(successful) {
584         var a = [];
585         this.each(function() {
586                 var n = this.name;
587                 if (!n) {
588                         return;
589                 }
590                 var v = $.fieldValue(this, successful);
591                 if (v && v.constructor == Array) {
592                         for (var i=0,max=v.length; i < max; i++) {
593                                 a.push({name: n, value: v[i]});
594                         }
595                 }
596                 else if (v !== null && typeof v != 'undefined') {
597                         a.push({name: this.name, value: v});
598                 }
599         });
600         //hand off to jQuery.param for proper encoding
601         return $.param(a);
602 };
603
604 /**
605  * Returns the value(s) of the element in the matched set.  For example, consider the following form:
606  *
607  *  <form><fieldset>
608  *        <input name="A" type="text" />
609  *        <input name="A" type="text" />
610  *        <input name="B" type="checkbox" value="B1" />
611  *        <input name="B" type="checkbox" value="B2"/>
612  *        <input name="C" type="radio" value="C1" />
613  *        <input name="C" type="radio" value="C2" />
614  *  </fieldset></form>
615  *
616  *  var v = $(':text').fieldValue();
617  *  // if no values are entered into the text inputs
618  *  v == ['','']
619  *  // if values entered into the text inputs are 'foo' and 'bar'
620  *  v == ['foo','bar']
621  *
622  *  var v = $(':checkbox').fieldValue();
623  *  // if neither checkbox is checked
624  *  v === undefined
625  *  // if both checkboxes are checked
626  *  v == ['B1', 'B2']
627  *
628  *  var v = $(':radio').fieldValue();
629  *  // if neither radio is checked
630  *  v === undefined
631  *  // if first radio is checked
632  *  v == ['C1']
633  *
634  * The successful argument controls whether or not the field element must be 'successful'
635  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
636  * The default value of the successful argument is true.  If this value is false the value(s)
637  * for each element is returned.
638  *
639  * Note: This method *always* returns an array.  If no valid value can be determined the
640  *         array will be empty, otherwise it will contain one or more values.
641  */
642 $.fn.fieldValue = function(successful) {
643         for (var val=[], i=0, max=this.length; i < max; i++) {
644                 var el = this[i];
645                 var v = $.fieldValue(el, successful);
646                 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
647                         continue;
648                 }
649                 v.constructor == Array ? $.merge(val, v) : val.push(v);
650         }
651         return val;
652 };
653
654 /**
655  * Returns the value of the field element.
656  */
657 $.fieldValue = function(el, successful) {
658         var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
659         if (successful === undefined) {
660                 successful = true;
661         }
662
663         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
664                 (t == 'checkbox' || t == 'radio') && !el.checked ||
665                 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
666                 tag == 'select' && el.selectedIndex == -1)) {
667                         return null;
668         }
669
670         if (tag == 'select') {
671                 var index = el.selectedIndex;
672                 if (index < 0) {
673                         return null;
674                 }
675                 var a = [], ops = el.options;
676                 var one = (t == 'select-one');
677                 var max = (one ? index+1 : ops.length);
678                 for(var i=(one ? index : 0); i < max; i++) {
679                         var op = ops[i];
680                         if (op.selected) {
681                                 var v = op.value;
682                                 if (!v) { // extra pain for IE...
683                                         v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
684                                 }
685                                 if (one) {
686                                         return v;
687                                 }
688                                 a.push(v);
689                         }
690                 }
691                 return a;
692         }
693         return $(el).val();
694 };
695
696 /**
697  * Clears the form data.  Takes the following actions on the form's input fields:
698  *  - input text fields will have their 'value' property set to the empty string
699  *  - select elements will have their 'selectedIndex' property set to -1
700  *  - checkbox and radio inputs will have their 'checked' property set to false
701  *  - inputs of type submit, button, reset, and hidden will *not* be effected
702  *  - button elements will *not* be effected
703  */
704 $.fn.clearForm = function() {
705         return this.each(function() {
706                 $('input,select,textarea', this).clearFields();
707         });
708 };
709
710 /**
711  * Clears the selected form elements.
712  */
713 $.fn.clearFields = $.fn.clearInputs = function() {
714         return this.each(function() {
715                 var t = this.type, tag = this.tagName.toLowerCase();
716                 if (t == 'text' || t == 'password' || tag == 'textarea') {
717                         this.value = '';
718                 }
719                 else if (t == 'checkbox' || t == 'radio') {
720                         this.checked = false;
721                 }
722                 else if (tag == 'select') {
723                         this.selectedIndex = -1;
724                 }
725         });
726 };
727
728 /**
729  * Resets the form data.  Causes all form elements to be reset to their original value.
730  */
731 $.fn.resetForm = function() {
732         return this.each(function() {
733                 // guard against an input with the name of 'reset'
734                 // note that IE reports the reset function as an 'object'
735                 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
736                         this.reset();
737                 }
738         });
739 };
740
741 /**
742  * Enables or disables any matching elements.
743  */
744 $.fn.enable = function(b) {
745         if (b === undefined) {
746                 b = true;
747         }
748         return this.each(function() {
749                 this.disabled = !b;
750         });
751 };
752
753 /**
754  * Checks/unchecks any matching checkboxes or radio buttons and
755  * selects/deselects and matching option elements.
756  */
757 $.fn.selected = function(select) {
758         if (select === undefined) {
759                 select = true;
760         }
761         return this.each(function() {
762                 var t = this.type;
763                 if (t == 'checkbox' || t == 'radio') {
764                         this.checked = select;
765                 }
766                 else if (this.tagName.toLowerCase() == 'option') {
767                         var $sel = $(this).parent('select');
768                         if (select && $sel[0] && $sel[0].type == 'select-one') {
769                                 // deselect all other options
770                                 $sel.find('option').selected(false);
771                         }
772                         this.selected = select;
773                 }
774         });
775 };
776
777 // helper fn for console logging
778 // set $.fn.ajaxSubmit.debug to true to enable debug logging
779 function log() {
780         if ($.fn.ajaxSubmit.debug) {
781                 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
782                 if (window.console && window.console.log) {
783                         window.console.log(msg);
784                 }
785                 else if (window.opera && window.opera.postError) {
786                         window.opera.postError(msg);
787                 }
788         }
789 };
790
791 })(jQuery);