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