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