PageRenderTime 115ms CodeModel.GetById 73ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/static/scripts/jquery.form.js

https://bitbucket.org/cistrome/cistrome-harvard/
JavaScript | 803 lines | 540 code | 73 blank | 190 comment | 254 complexity | 85a2e62c75ea2e629652a9b7d16aa24e MD5 | raw file
  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
791function 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);