//
//	REDDForm - Lightweight AJAX-style form handling
//	Copyright (c) 2006 REDD Media Pty. Ltd.
//
//	Tested under modern IE/Firefox browsers. For use with REDD websites only.
//
//	Use by any party or on any website without the express written permission of REDD Media is strictly prohibited.
//
//	Please see your doctor if symptoms persist.

//
//	REDDForm class
//	Handles background request sending and response parsing, form disabling and element highlighting

function REDDForm (argForm, argReceiveHandler, argSubmitHandler)
{
	//	------------------------------------
	//	PRIVATE

	var self = this;

	var http = null;
	var httpInitFailed = false;

	//
	//	initialise the http object and save the result
	//	alert the user if it fails

	var initHttp = function ()
	{
		if (httpInitFailed)
			return false;

		if (http !== null)
			return true;

		var err;

		try
		{
			if (typeof XMLHttpRequest != "undefined")
				http = new XMLHttpRequest();
			else if (typeof ActiveXObject != "undefined")
				http = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch (err) { }

		if (http === null)
		{
			alert("Your browser does not support XMLHttpRequest");
			httpInitFailed = true;
			return false;
		}

		return true;
	}

	//
	//	enable or disable all form elements

	var enableDisableForm = function (disableElements)
	{
		var e;

		for (var i = self.form.elements.length; i--;)
		{
			e = self.form.elements[i];

			if (disableElements && e.disabled)
			{
				e.__reddform_dontenable = true;
				continue;
			}

			if (!disableElements && e.__reddform_dontenable)
			{
				e.__reddform_dontenable = null;
				continue;
			}

			self.form.elements[i].disabled = disableElements;
		}
	}

	//
	//	handle the response from the server

	var onReadyStateChange = function ()
	{
		//
		//	only process readyState 4

		if (http.readyState != 4)
			return;

		//
		//	re-enable the form

		if (!http.responseXML || !http.responseXML.documentElement)
		{
			if (confirm("Server returned an invalid XML response.\n\nInspect response text in a new window?"))
			{
				var w = false;

				while (!w)
				{
					w = window.open();

					if (w)
					{
						w.document.write(http.responseText);
						w.document.close();
						w.focus();
					}
					else
					{
						if (!confirm("Failed to open window. Maybe you are using a popup blocker?\n\nTry holding CTRL and click OK to try again, otherwise click Cancel to give up."))
							break;
					}
				}

				if (!w)
				{
					alert("You'll need to bypass your popup blocker to see the error.");
				}
			}
		}
		else
		{
			var paramNodes = http.responseXML.documentElement.getElementsByTagName("param");
			var paramCount = paramNodes.length;

			var result = {};

			var i, paramNode, paramType, paramString;
			var j, itemNodes, itemNode, itemString;

			for (i = 0; i < paramCount; i++)
			{
				paramNode = paramNodes.item(i);

				paramName = paramNode.attributes.getNamedItem("name").nodeValue;
				paramType = paramNode.attributes.getNamedItem("type").nodeValue;

				if (paramType == "array")
				{
					paramValue = [];

					itemNodes = paramNode.getElementsByTagName("item");
					itemCount = itemNodes.length;
					paramValue = [];

					for (j = 0; j < itemCount; j++)
					{
						itemNode = itemNodes.item(j);
						paramValue.push(itemNode.firstChild.data.toString());
					}

					result[paramName] = paramValue;
				}
				else
				{
					paramString = paramNode.firstChild.data.toString();

					switch (paramType)
					{
						case "boolean":
							paramValue = (paramString == "true");
							break;

						case "date":
							paramValue = new Date(paramString);
							break;

						case "function":
							eval('paramValue = '+ paramString +';');
							break;

						case "number":
							paramValue = parseFloat(paramString);
							break;

						default:
							paramValue = paramString;
							break;
					}

					result[paramName] = paramValue;
				}
			}

			var f,i,e,s;
			f = self.form;

			for (i = f.elements.length; i--;)
			{
				e = f.elements[i];
				REDDForm.ClearIssueStyle(e);
			}

			if (result.issues)
			{
				for (i = result.issues.length; i--;)
				{
					e = f.elements[result.issues[i]];

					if (!e)
						continue;

					switch (e.type)
					{
						case "hidden":
							break;

						default:
							REDDForm.ApplyIssueStyle(e);
							break;
					}
				}
			}

			if (self.responseHandler)
				self.responseHandler(self, result);
		}

		delete http['onreadystatechange'];
		http = null;
		self.enableForm();
	}

	//
	//	send the form

	var sendForm = function ()
	{
		if (!initHttp())
			return false;

		var data = [];
		var el, i, o, j;

		//
		//	TODO - Create a REDDForm-specific data class

		for (i = 0; i < self.form.elements.length; i++)
		{
			el = self.form.elements[i];

			//
			//	ignore unnamed fields
			//
			//	TODO - Check RFCs to see if we can POST unnamed elements

			if (!el.name)
				continue;

			switch (el.type)
			{

				//
				//	element types to ignore

				case "submit":	// the onsubmit event doesn't seem provide enough info to find which element was 'clicked' to submit the form in all browsers, so we won't rely on it
				case "image":	// as above
				case "button":	// as above
				case "file":	// javascript alone cannot do file uploads without special permissions or applets, so this script is not scoped for handling uploads
					continue;
					break;

				//
				//	radio buttons and checkboxes can be parsed in the same way

				case "radio":
				case "checkbox":
					if (el.checked)
						data.push(escape(el.name) +"="+ (el.value ? escape(el.value) : "on"));
					break;

				//
				//	special handler for select-multiple fields

				case "select-multiple":
					for (j = 0; j < el.options.length; j++)
					{
						o = el.options[j];
						if (o.selected && o.value)
							data.push(escape(el.name) +"="+ escape(o.value));
					}
					break;

				//
				//	all other basic fields such as text, password, textarea, select-one, hidden, etc.

				default:
					data.push(escape(el.name) +"="+ escape(el.value))
					break;
			}
		}

		//
		//	TODO - Routines for handling GET requests, for now we'll just make a POST
		self.method = "POST";

		//
		//	add some variables about the client-side state of the form

		if (self.form.name)
			data.push(escape("__reddform_form_name") +"="+ escape(self.form.name));

		if (self.form.action)
			data.push(escape("__reddform_form_action") +"="+ escape(self.form.action));

		if (self.form.method)
			data.push(escape("__reddform_form_method") +"="+ escape(self.form.method));

		if (self.form.encoding)
			data.push(escape("__reddform_form_encoding") +"="+ escape(self.form.encoding));

		//
		//	join data in a POST-body friendly delimiter

		data = data.join("&");

		//
		//	disable the form to indicate request-in-progress

		self.disableForm();

		//
		//	setup the http request

		http.open(self.method, self.action, true);
		http.setRequestHeader("Content-Type", self.encoding);

		//
		//	setup the change handler

		http.onreadystatechange = onReadyStateChange;

		//
		//	and finally send the data

		http.send(data);
	}

	//
	//	object constructor

	var constructor = function ()
	{
		if (typeof argForm == "string")
			self.form = document.forms[argForm];
		else
			self.form = argForm;

		if (typeof argReceiveHandler == "function")
			self.responseHandler = argReceiveHandler;

		if (typeof argSubmitHandler == "function")
			self.submitHandler = argSubmitHandler;

		self.method = self.form.method ? self.form.method : REDDForm.defaultMethod;
		self.action = self.form.action ? self.form.action : REDDForm.defaultAction;
		self.encoding = self.form.encoding ? self.form.encoding : REDDForm.defaultEncoding;
	}

	//	------------------------------------
	//	PUBLIC

	this.form = null;

	this.method = null;
	this.action = null;
	this.encoding = null;

	this.responseHandler = null;
	this.submitHandler = null;

	//
	//	enables all form elements

	this.enableForm = function ()
	{
		enableDisableForm(false);
	}

	//
	//	disables all form elements

	this.disableForm = function ()
	{
		enableDisableForm(true);
	}

	//
	//	check if the http readystate indicates the form is currently submitting

	this.busy = function ()
	{
		//
		//	http object is not initialised

		if (!http)
			return false;

		//
		//	readystate 2 or 3 indicates the http object is busy

		switch (http.readyState)
		{
			case 2:
			case 3:
				return true;
				break;
		}

		return false;
	}

	//
	//	submit handler for piping <form> submits via backround HTTP requests
	//	note: this function should always return false

	this.submit = function ()
	{
		//
		//	abort if a submit is already in progress on this form

		if (this.busy())
			return false;

		//
		//	execute the submit handler if one is defined, abort if negative result returned

		if (this.submitHandler && !this.submitHandler(this))
			return false;

		//
		//	send form data

		sendForm();

		return false;
	}

	//
	//	call the constructor method

	constructor();
}

//
//	issue highlighting

REDDForm.ClearIssueStyle = function (e)
{
	var s = e.style;
	s.backgroundColor = "";
	s.borderColor = "";
	s.borderStyle = "";
}

REDDForm.ApplyIssueStyle = function (e)
{
	var s = e.style;
	s.backgroundColor = "#FFF0F0";
	s.borderColor = "#FF6666";
	s.borderStyle = "dashed";
}

//
//	default values

REDDForm.defaultMethod = "POST";
REDDForm.defaultAction = "submit.asp";
REDDForm.defaultEncoding = "application/x-www-form-urlencoded";

//
//	generic handlers

REDDForm.ResponseGenericHandler = function (reddForm, result)
{
	if (result.alert)
		alert(result.alert);

	if (result.redirect)
		window.location.href = result.redirect;
	else if (result.reload)
		window.location.reload();
	else if (result.reset)
		reddForm.form.reset();
}

REDDForm.ResponseAlertHandler = REDDForm.ResponseGenericHandler;
REDDForm.ResponseRedirectHandler = REDDForm.ResponseGenericHandler;
REDDForm.ResponseReloadHandler = REDDForm.ResponseGenericHandler;

//
//	ie/ff-friendly event attacher

REDDForm.AttachEvent = function (obj, name, handler)
{
	if (typeof obj.addEventListener != "undefined")
		obj.addEventListener(name, handler, false);
	else if (typeof obj.attachEvent != "undefined")
		obj.attachEvent('on'+ name, handler);
}

