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