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