Protoform = function (form, success) {
	this.form = $(form);
	this.onSuccess = success;
	this.reactions = {};
	
	this.busy = false;

	this.highlightClassName = 'protoformHighlight';
	this.enableMeClassName = 'protoformEnableMe';

	this.onSubmit = function (e) {
		if (e) { Event.stop(e); }
		if (this.busy)
			return;
		this.clearHighlights();
		this.clearMessages();
		this.disableForm();
		this.lastRequest = this.form.request({onComplete:this.onComplete.bind(this)});
	};

	this.disableFormActual = function () {
		var element;
		for (var i = this.form.elements.length; i--;)
		{
			element = this.form.elements[i];
			if (!element.disabled)
			{
				element.disabled = true;
				element._protoform_disabled = true;
			}
		}
	};

	this.disableForm = function () {
		this.busy = true;
		window.setTimeout(this.disableFormActual.bind(this), 1);
	};

	this.clearHighlights = function () {
		for (var i = this.form.elements.length; i--;)
			$(this.form.elements[i]).removeClassName(this.highlightClassName);
	};

	this.clearMessages = function () {
		var x, node;
		if (node = $(this.form.id +'_message'))
			node.update();
		for (x in this.reactions)
			if (node = $(this.form.id +'_'+ x +'_message'))
				node.update();
	};

	this.onComplete = function (request) {
		this.enableForm();

		var response = request.responseText.evalJSON(true);
		if (response.success)
			this.onSuccess.call(this, request, response);
		else
			this.onFailure.call(this, request, response);
	};

	this.onFailure = function (request, response) {
		var node;

		if (response.message)
			if (node = $(this.form.id +'_message'))
				node.update(response.message)
			else
				alert(response.message);
			
		this.reactions = response.reactions;

		var x, element;
		for (x in response.reactions)
		{
			var reaction = response.reactions[x];
			
			if (reaction.highlight && (element = this.form.elements[x]))
				$(element).addClassName(this.highlightClassName);
			
			if (node = $(this.form.id +'_'+ x +'_message'))
				node.update(reaction.message||"");
		}

		for (var i = this.form.elements.length; i--;)
		{
			var element = this.form.elements[i];
			var reaction = response.reactions[element.name];

			if (reaction && reaction.highlight)
				$(element).addClassName(this.highlightClassName);

			node = $(this.form.id +'_'+ element.name +'_message');
			if (reaction && node)
				node.update(reaction.message ? reaction.message : "");
		}
	};

	this.enableForm = function () {
		var element;
		for (var i = this.form.elements.length; i--;)
		{
			element = this.form.elements[i];
			if (element._protoform_disabled)
			{
				element.disabled = false;
				element._protoform_disabled = undefined;
			}
		}
		this.busy = false;
	};
	
	for (var i = this.form.elements.length; i--;)
		if ($(this.form.elements[i]).hasClassName(this.enableMeClassName))
		{
			this.form.elements[i].disabled=false;
			this.form.elements[i].removeClassName(this.enableMeClassName);
		}

	this.form.observe('submit', this.onSubmit.bind(this));
};
