/**
 * ajaxify_forms uses Prototype.
 * Proved to be browser compatible: Mozilla Firefox 1.5 - 2.0, 
 * Opera 9.0 (likely to work in older versions), 
 * MSIE 6.0 (likely to work in older versions)
 * Usage:
 * To make all forms on the page use AJAX for their submission, just place the following
 * on the page (typically, in the <head> element):
 * 	<script language="javascript" type="text/javascript" src="/path/to/prototype.js"></script>
 * 	<script language="javascript" type="text/javascript" src="/path/to/ajaxify_forms.js"></script>
 * After that all forms will use AJAX for their submission, but NOTICE:
 * If you'd want some form NOT to be affected by ajaxify_forms, place a "runat='client'" attribute in the form declaration.
 * (for example, <form runat="client">).
 *
 * ajaxify_forms expects the server answer to be a valid json-encoded string containing a JS object with the
 * following fields: 
 * - code: string, either "ok" or "error"
 * - error_list: array of error messages, for example, ["Username field is required", "Password field is required"]
 * - data: ANY javascript variable.
 *
 * How to make a JSON-encoded string with PHP: http://php.net/json
 *
 * After submission, ajaxify_forms will detect whether response contains an error. If so,
 * the errors will be displayed in a <ul> (unordered list) with id="displayErrorList", 
 * just above the form which triggerred submission.
 *
 * What if ajaxify_forms will find that response does not contain errors?
 * If the form has an attribute named "onSubmitDone", the JS expression in that attribute is evaluated like this:
 *		- if it has argument specification, i. e. it is in form "myFunction(myArg1[, myArg2,..])" the expression
 *			is JUST evaluated.
 *		- otherwise, the function is called with response data passed as argument.
 * If the form does not have an attribute named "onSubmitDone", ajaxify_forms will simply reload the page.
 */

/**
 * Makes every form on the page to be submitted via AJAX
 */
function ajaxifyForms() {
	var fc = document.getElementsByTagName('form')
	for (var i = 0; i < fc.length; ++i)
		ajaxifyForm(fc.item(i))
}
function ajaxifyForm(form) {
	if ('client' != form.getAttribute('runat'))
	{
		form.onsubmit = function() {
			if (isDefined(window.event))
			{
				event.returnValue = false
			}
			ajaxFormSubmit(this)
			return false
		}
	}
}
/**
 * Submit a single form with AJAX
 *
 * @param HtmlFormElement form
 * @return bool alwayse FALSE
 */
function ajaxFormSubmit(form) {
	/*try {*/
	var submissionMethod
	if ('undefined' != typeof(form.method))
	{
		submissionMethod = form.method
	}
	else
	{
		submissionMethod = form.hasAttribute('method') ? form.getAttribute('method') : 'get'
	}
	var postData = Form.serialize(form)
	Form.disable(form)
	var actionUrl = form.getAttribute('action')
	if (('undefined' == typeof(actionUrl)) || (!actionUrl) || (0 == actionUrl.length))
	{
		actionUrl = window.location.toString().replace(/#.*$/, '')
	}
	
	var sysError = ['System error: Invalid response data!']
	
	// what to do on error
	var onErrorCallback = form.getAttribute('onError')
	if (! onErrorCallback)
		onErrorCallback = 'handleError'
	
	new Ajax.Request(
		actionUrl,
		{
			method:form.getAttribute('method'),
			parameters:postData,
			onSuccess:function(r) {
//				alert(r.responseText)
				try {
					eval('var json = ' + r.responseText)
					if ('error' == json.code)
					{
						_onError(onErrorCallback, json.error_list, form)
					} else {
						removeErrorList()
						var callbackString = form.getAttribute('onSubmitDone')
						if (callbackString)
							_call(callbackString, json.data)
						else
							location.reload()
					}
				} catch (e) {
					alert(r.responseText)
					alert(e.message)
					_onError(onErrorCallback, sysError, form)
				}
			},
			onFailure:function(r) {
				_onError(onErrorCallback, ['HTTP request failed!'], form)
			}
		});
	Form.enable(form)
	return false
}

/**
 * Call a callback function specified by callbackString, with arg passed as an argument.
 * NOTE: if the user has specified arguments in callbackString, arg WON'T be passed to the callback!!!
 * callbackString examples:
 *	- "MyFunction": no parameters, arg passed as an argument
 *	- "MyFunction(myArg1,..)": parameters specified, arg WON'T be passed to the callback
 */
function _call(callbackString, arg)
{
	callbackString = _prepareCallbackString(callbackString)
	if (_eval(callbackString))
	{
		return ;
	}
	else
	{
		var callback = eval(callbackString)
		callback(arg)
	}
}

function _eval(preparedCallbackString)
{
	if (preparedCallbackString.match(/[\(\)]/))
	{// user has specified parameters
		eval(preparedCallbackString)
		return true
	}
	
	return false
}

function _prepareCallbackString(callbackString)
{
	return callbackString.replace(/\(\)$/, "")
}

function _onError(callbackString, el, form)
{
	callbackString = _prepareCallbackString(callbackString)
	if (_eval(callbackString))
	{
		return ;
	}
	else
	{
		var callback = eval(callbackString)
		callback(el, form)
	}
}

/**
 * Parse error list and display errors
 *
 * @param array el error list
 * @param HtmlFormElement form
 * @return void
 */
function handleError(el, form) {
	if ('object' != (typeof(el)).toLowerCase())
	{
		el = ['Unknown error has occured']
	}
	
	removeErrorList()

	ul = document.createElement('ul')
	ul.setAttribute('id', 'displayErrorList')
	
	for (var i = 0; i < el.length; ++i)
	{
		var li = document.createElement('li')
		li.appendChild(document.createTextNode(el[i]))
		ul.appendChild(li)
	}
	
	form.parentNode.insertBefore(ul, form)
}

function removeErrorList() {
	var ul = $('displayErrorList')
	if (isDefined(ul) && ul)
	{
		ul.parentNode.removeChild(ul)
	}
}

function isDefined(v) {
	return 'undefined' != (typeof(v))
}

/**
 * Apply AJAX forms
 */
window.onload = function() {
	ajaxifyForms();
}