;
(function($) {
	$.fn.ajaxSubmit = function(options) {
		if (!this.length) {
			log('ajaxSubmit: skipping submit process - no element selected');
			return this;
		}
		if (typeof options == 'function')
			options = {
				success : options
			};
		options = $.extend( {
			url : this.attr('action') || window.location.toString(),
			type : this.attr('method') || 'GET'
		}, options || {});
		var veto = {};
		this.trigger('form-pre-serialize', [ this, options, veto ]);
		if (veto.veto) {
			log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
			return this;
		}
		if (options.beforeSerialize
				&& options.beforeSerialize(this, options) === false) {
			log('ajaxSubmit: submit aborted via beforeSerialize callback');
			return this;
		}
		var a = this.formToArray(options.semantic);
		if (options.data) {
			options.extraData = options.data;
			for ( var n in options.data) {
				if (options.data[n] instanceof Array) {
					for ( var k in options.data[n])
						a.push( {
							name : n,
							value : options.data[n][k]
						})
				} else
					a.push( {
						name : n,
						value : options.data[n]
					});
			}
		}
		if (options.beforeSubmit
				&& options.beforeSubmit(a, this, options) === false) {
			log('ajaxSubmit: submit aborted via beforeSubmit callback');
			return this;
		}
		this.trigger('form-submit-validate', [ a, this, options, veto ]);
		if (veto.veto) {
			log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
			return this;
		}
		var q = $.param(a);
		if (options.type.toUpperCase() == 'GET') {
			options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
			options.data = null;
		} else
			options.data = q;
		var $form = this, callbacks = [];
		if (options.resetForm)
			callbacks.push(function() {
				$form.resetForm();
			});
		if (options.clearForm)
			callbacks.push(function() {
				$form.clearForm();
			});
		if (!options.dataType && options.target) {
			var oldSuccess = options.success || function() {
			};
			callbacks.push(function(data) {
				$(options.target).html(data).each(oldSuccess, arguments);
			});
		} else if (options.success)
			callbacks.push(options.success);
		options.success = function(data, status) {
			for ( var i = 0, max = callbacks.length; i < max; i++)
				callbacks[i].apply(options, [ data, status, $form ]);
		};
		var files = $('input:file', this).fieldValue();
		var found = false;
		for ( var j = 0; j < files.length; j++)
			if (files[j])
				found = true;
		if (options.iframe || found) {
			if ($.browser.safari && options.closeKeepAlive)
				$.get(options.closeKeepAlive, fileUpload);
			else
				fileUpload();
		} else
			$.ajax(options);
		this.trigger('form-submit-notify', [ this, options ]);
		return this;
		function fileUpload() {
			var form = $form[0];
			if ($(':input[name=submit]', form).length) {
				alert('Error: Form elements must not be named "submit".');
				return;
			}
			var opts = $.extend( {}, $.ajaxSettings, options);
			var s = jQuery.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
			var id = 'jqFormIO' + (new Date().getTime());
			var $io = $('<iframe id="' + id + '" name="' + id + '" />');
			var io = $io[0];
			if ($.browser.msie || $.browser.opera)
				io.src = 'javascript:false;document.write("");';
			$io.css( {
				position : 'absolute',
				top : '-1000px',
				left : '-1000px'
			});
			var xhr = {
				aborted : 0,
				responseText : null,
				responseXML : null,
				status : 0,
				statusText : 'n/a',
				getAllResponseHeaders : function() {
				},
				getResponseHeader : function() {
				},
				setRequestHeader : function() {
				},
				abort : function() {
					this.aborted = 1;
					$io.attr('src', 'about:blank');
				}
			};
			var g = opts.global;
			if (g && !$.active++)
				$.event.trigger("ajaxStart");
			if (g)
				$.event.trigger("ajaxSend", [ xhr, opts ]);
			if (s.beforeSend && s.beforeSend(xhr, s) === false) {
				s.global && jQuery.active--;
				return;
			}
			if (xhr.aborted)
				return;
			var cbInvoked = 0;
			var timedOut = 0;
			var sub = form.clk;
			if (sub) {
				var n = sub.name;
				if (n && !sub.disabled) {
					options.extraData = options.extraData || {};
					options.extraData[n] = sub.value;
					if (sub.type == "image") {
						options.extraData[name + '.x'] = form.clk_x;
						options.extraData[name + '.y'] = form.clk_y;
					}
				}
			}
			setTimeout(function() {
				var t = $form.attr('target'), a = $form.attr('action');
				$form.attr( {
					target : id,
					method : 'POST',
					action : opts.url
				});
				if (!options.skipEncodingOverride) {
					$form.attr( {
						encoding : 'multipart/form-data',
						enctype : 'multipart/form-data'
					});
				}
				if (opts.timeout)
					setTimeout(function() {
						timedOut = true;
						cb();
					}, opts.timeout);
				var extraInputs = [];
				try {
					if (options.extraData)
						for ( var n in options.extraData)
							extraInputs.push($(
									'<input type="hidden" name="' + n + '" value="'
											+ options.extraData[n] + '" />').appendTo(form)[0]);
					$io.appendTo('body');
					io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener(
							'load', cb, false);
					form.submit();
				} finally {
					$form.attr('action', a);
					t ? $form.attr('target', t) : $form.removeAttr('target');
					$(extraInputs).remove();
				}
			}, 10);
			function cb() {
				if (cbInvoked++)
					return;
				io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener(
						'load', cb, false);
				var operaHack = 0;
				var ok = true;
				try {
					if (timedOut)
						throw 'timeout';
					var data, doc;
					doc = io.contentWindow ? io.contentWindow.document
							: io.contentDocument ? io.contentDocument : io.document;
					if (doc.body == null && !operaHack && $.browser.opera) {
						operaHack = 1;
						cbInvoked--;
						setTimeout(cb, 100);
						return;
					}
					xhr.responseText = doc.body ? doc.body.innerHTML : null;
					xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
					xhr.getResponseHeader = function(header) {
						var headers = {
							'content-type' : opts.dataType
						};
						return headers[header];
					};
					if (opts.dataType == 'json' || opts.dataType == 'script') {
						var ta = doc.getElementsByTagName('textarea')[0];
						xhr.responseText = ta ? ta.value : xhr.responseText;
					} else if (opts.dataType == 'xml' && !xhr.responseXML
							&& xhr.responseText != null) {
						xhr.responseXML = toXml(xhr.responseText);
					}
					data = $.httpData(xhr, opts.dataType);
				} catch (e) {
					ok = false;
					$.handleError(opts, xhr, 'error', e);
				}
				if (ok) {
					opts.success(data, 'success');
					if (g)
						$.event.trigger("ajaxSuccess", [ xhr, opts ]);
				}
				if (g)
					$.event.trigger("ajaxComplete", [ xhr, opts ]);
				if (g && !--$.active)
					$.event.trigger("ajaxStop");
				if (opts.complete)
					opts.complete(xhr, ok ? 'success' : 'error');
				setTimeout(function() {
					$io.remove();
					xhr.responseXML = null;
				}, 100);
			}
			;
			function toXml(s, doc) {
				if (window.ActiveXObject) {
					doc = new ActiveXObject('Microsoft.XMLDOM');
					doc.async = 'false';
					doc.loadXML(s);
				} else
					doc = (new DOMParser()).parseFromString(s, 'text/xml');
				return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc
						: null;
			}
			;
		}
		;
	};
	$.fn.ajaxForm = function(options) {
		return this.ajaxFormUnbind().bind('submit.form-plugin', function() {
			$(this).ajaxSubmit(options);
			return false;
		}).each(function() {
			$(":submit,input:image", this).bind('click.form-plugin', function(e) {
				var form = this.form;
				form.clk = this;
				if (this.type == 'image') {
					if (e.offsetX != undefined) {
						form.clk_x = e.offsetX;
						form.clk_y = e.offsetY;
					} else if (typeof $.fn.offset == 'function') {
						var offset = $(this).offset();
						form.clk_x = e.pageX - offset.left;
						form.clk_y = e.pageY - offset.top;
					} else {
						form.clk_x = e.pageX - this.offsetLeft;
						form.clk_y = e.pageY - this.offsetTop;
					}
				}
				setTimeout(function() {
					form.clk = form.clk_x = form.clk_y = null;
				}, 10);
			});
		});
	};
	$.fn.ajaxFormUnbind = function() {
		this.unbind('submit.form-plugin');
		return this.each(function() {
			$(":submit,input:image", this).unbind('click.form-plugin');
		});
	};
	$.fn.formToArray = function(semantic) {
		var a = [];
		if (this.length == 0)
			return a;
		var form = this[0];
		var els = semantic ? form.getElementsByTagName('*') : form.elements;
		if (!els)
			return a;
		for ( var i = 0, max = els.length; i < max; i++) {
			var el = els[i];
			var n = el.name;
			if (!n)
				continue;
			if (semantic && form.clk && el.type == "image") {
				if (!el.disabled && form.clk == el)
					a.push( {
						name : n + '.x',
						value : form.clk_x
					}, {
						name : n + '.y',
						value : form.clk_y
					});
				continue;
			}
			var v = $.fieldValue(el, true);
			if (v && v.constructor == Array) {
				for ( var j = 0, jmax = v.length; j < jmax; j++)
					a.push( {
						name : n,
						value : v[j]
					});
			} else if (v !== null && typeof v != 'undefined')
				a.push( {
					name : n,
					value : v
				});
		}
		if (!semantic && form.clk) {
			var inputs = form.getElementsByTagName("input");
			for ( var i = 0, max = inputs.length; i < max; i++) {
				var input = inputs[i];
				var n = input.name;
				if (n && !input.disabled && input.type == "image" && form.clk == input)
					a.push( {
						name : n + '.x',
						value : form.clk_x
					}, {
						name : n + '.y',
						value : form.clk_y
					});
			}
		}
		return a;
	};
	$.fn.formSerialize = function(semantic) {
		return $.param(this.formToArray(semantic));
	};
	$.fn.fieldSerialize = function(successful) {
		var a = [];
		this.each(function() {
			var n = this.name;
			if (!n)
				return;
			var v = $.fieldValue(this, successful);
			if (v && v.constructor == Array) {
				for ( var i = 0, max = v.length; i < max; i++)
					a.push( {
						name : n,
						value : v[i]
					});
			} else if (v !== null && typeof v != 'undefined')
				a.push( {
					name : this.name,
					value : v
				});
		});
		return $.param(a);
	};
	$.fn.fieldValue = function(successful) {
		for ( var val = [], i = 0, max = this.length; i < max; i++) {
			var el = this[i];
			var v = $.fieldValue(el, successful);
			if (v === null || typeof v == 'undefined'
					|| (v.constructor == Array && !v.length))
				continue;
			v.constructor == Array ? $.merge(val, v) : val.push(v);
		}
		return val;
	};
	$.fieldValue = function(el, successful) {
		var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
		if (typeof successful == 'undefined')
			successful = true;
		if (successful
				&& (!n || el.disabled || t == 'reset' || t == 'button'
						|| (t == 'checkbox' || t == 'radio') && !el.checked
						|| (t == 'submit' || t == 'image') && el.form && el.form.clk != el || tag == 'select'
						&& el.selectedIndex == -1))
			return null;
		if (tag == 'select') {
			var index = el.selectedIndex;
			if (index < 0)
				return null;
			var a = [], ops = el.options;
			var one = (t == 'select-one');
			var max = (one ? index + 1 : ops.length);
			for ( var i = (one ? index : 0); i < max; i++) {
				var op = ops[i];
				if (op.selected) {
					var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text
							: op.value;
					if (one)
						return v;
					a.push(v);
				}
			}
			return a;
		}
		return el.value;
	};
	$.fn.clearForm = function() {
		return this.each(function() {
			$('input,select,textarea', this).clearFields();
		});
	};
	$.fn.clearFields = $.fn.clearInputs = function() {
		return this.each(function() {
			var t = this.type, tag = this.tagName.toLowerCase();
			if (t == 'text' || t == 'password' || tag == 'textarea')
				this.value = '';
			else if (t == 'checkbox' || t == 'radio')
				this.checked = false;
			else if (tag == 'select')
				this.selectedIndex = -1;
		});
	};
	$.fn.resetForm = function() {
		return this.each(function() {
			if (typeof this.reset == 'function'
					|| (typeof this.reset == 'object' && !this.reset.nodeType))
				this.reset();
		});
	};
	$.fn.enable = function(b) {
		if (b == undefined)
			b = true;
		return this.each(function() {
			this.disabled = !b
		});
	};
	$.fn.selected = function(select) {
		if (select == undefined)
			select = true;
		return this.each(function() {
			var t = this.type;
			if (t == 'checkbox' || t == 'radio')
				this.checked = select;
			else if (this.tagName.toLowerCase() == 'option') {
				var $sel = $(this).parent('select');
				if (select && $sel[0] && $sel[0].type == 'select-one') {
					$sel.find('option').selected(false);
				}
				this.selected = select;
			}
		});
	};
	function log() {
		if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
			window.console.log('[jquery.form] ' + Array.prototype.join.call(
					arguments, ''));
	}
	;
})(jQuery);