// Check for jQuery
if ((typeof jQuery == 'undefined') && (typeof shownAjaxWarning == 'undefined'))
{
	alert('AJAX Save requires the jQuery library');
	shownAjaxWarning = true;
}

/**
 * Submit a FormFactory using AJAX
 *
 * The callback PHP file should return its results by instantiating an AjaxSaveResponse object
 * and printing AjaxSaveResponse->getJSON().
 *
 * @param string The form name, plus '_form'
 * @param string The ID of an HTML element containing the loading message.  This is shown while the AJAX call is in progress.  Empty string for none.
 * @param string The ID of an HTML element containing the result message.  This is shown when the AJAX call completes.  Empty string for none.
 * @param string The URL of the callback handler.
 * @param string The ID of an HTML element to contain the HTML data returned by the callback.  This is set to the HTML in the 'data' member of the AJAX result.  Empty string for none.
 * @param Function A function to call on submit, before the AJAX call is made.  This can be used for javascript validation of the form.  The form will not be submitted if this function returns false.
 */
function FFAjaxSave(form, loadingElm, messageElm, handlerUrl, targetElm, onSubmit)
{
	this.speed = 300;
	this.form = jQuery('#' + form);

	if (!this.form || !this.form.length || this.form.get(0).tagName.toLowerCase() != 'form')
		alert("FFAjaxSave(): Invalid form element.  The first parameter should be your form factory name plus '_form'.");
	
	this.loadingElm = jQuery('#' + loadingElm);
	this.messageElm = jQuery('#' + messageElm);
	this.handlerUrl = handlerUrl;
	this.targetElm = jQuery('#' + targetElm);
	this.onSubmit = onSubmit;
	this.timer = false;
	
	// Set up the form's "submit" callback to call the FFAjaxSave.save() method
	var self = this;
	this.form.submit(function() {
		return self.save();
	});
	
	// Initialise the loading & message elements:
	this.loadingElm.addClass('ui-widget ui-hydrogen-message')
		.css('display', 'none').wrapInner('<div class="ui-state-highlight ui-corner-all" style="padding: 0 .7em;"><p></p></div>');
	this.loadingElm.find('p').prepend('<span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>');
	
	this.messageElm.addClass('ui-widget ui-hydrogen-message')
		.css('display', 'none').html('<div class="ui-state-error ui-corner-all" style="padding: 20px .7em;">'
			+ '<span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>'
			+ '<div class="ui-hydrogen-message-contents"></div>'
			+ '</div>');
}

FFAjaxSave.prototype.save = function(event)
{
	// See if the form has an onSubmit handler registered
	if (typeof this.onSubmit == 'function')
	{
		// Call the onSubmit handler, and abort if it returns false
		if (!this.onSubmit(event))
			return false;
	}
	
	// We have to call updateElement on all the CKeditors
	if (typeof CKEDITOR != 'undefined')
	{
		jQuery.each(CKEDITOR.instances, function(key, value)
		{
			value.updateElement();
		});
	}
	
	var self = this;
	var options = 
	{
		data: this.form.serialize(),
		dataType: 'json',
		error: function(jqXhr, textStatus, errorThrown)
		{
			return self.processFailure(jqXhr, textStatus, errorThrown);
		},
		success: function(data, textStatus, jqXhr)
		{
			try
			{
				// Eval the javascript member of the data
				(new Function(data.javascript))();
			}
			catch (e)
			{
				return self.processFailure(jqXhr, textStatus, 'Exception: ' + e.message);
			}
			
			return self.processSuccess(data, textStatus);
		},
		type: 'post',
		url: this.handlerUrl
	}
	
	if (this.timer)
	{
		clearTimeout(this.timer);
		this.timer = false;
	}
	
	this.messageElm.hide();
	
	// Display the loading message
	this.loadingElm.slideDown(this.speed);
	
	jQuery.ajax(options);
	
	return false;
}


FFAjaxSave.prototype.processSuccess = function(data, textStatus)
{
	
	if (typeof data == 'string')
		var json = {message: data, values: [], errors: [], success: false};
	else
		var json = data;
	
	
	var self = this;
	
	// slideUp doesn't work if a parent is hidden so call hide at the end
	this.loadingElm.slideUp(this.speed, function() { jQuery(this).hide(); if (json.message) self.showMessage(json.message, json.success ? 'info' : 'error', 10000); });
	
	jQuery.each(json.values, function(key, value)
	{
		// If the key is empty then it's probably an FFHTML or FFContainedHTML
		if (key == '')
			return;
		
		FF['set' + json.types[key]](json.name + '_' + key, value);
		
		// Remove all the error messages
		jQuery('#' + json.name + '_' + key + '_error').removeClass('FFHasError').empty();
	});
	
	var errors = json.errors;
	
	// Some of the errors come back as arrays (e.g. FFAddress) - copy them into the main array
	jQuery.each(json.errors, function(key, value)
	{
		if (typeof value != 'object')
			return;
		
		jQuery.each(value, function(key2, value2)
		{
			errors[key + '_' + key2] = value2;
		});
	});
	
	jQuery.each(errors, function(key, value)
	{
		var $error = jQuery('#' + json.name + '_' + key + '_error');
		
		if (value === false)
			$error.html('').removeClass('FFHasError');
		else
			$error.html(value).addClass('FFHasError');
	});
	
	if (this.targetElm && (json.data !== null))
		this.targetElm.html(json.data);
}

FFAjaxSave.prototype.processFailure = function(XMLHttpRequest, textStatus, errorThrown)
{
	// slideUp doesn't work if a parent is hidden so call hide at the end
	var str = 'An error has occurred!<br>'
		+ 'The server returned status ' + XMLHttpRequest.status + ' (' + XMLHttpRequest.statusText + ')<br><br>'
		+ errorThrown;
	
	var self = this;
	
	this.loadingElm.slideUp(this.speed, function() { jQuery(this).hide(); self.showMessage(str, 'error', 10000); });
}

FFAjaxSave.prototype.showMessage = function(str, className, lifetime)
{
	this.messageElm.find('.ui-hydrogen-message-contents').html(str);
	this.messageElm.get(0).className = className;
	this.messageElm.slideDown(this.speed);
	
	var self = this;
	
	if (lifetime)
	{
		this.timer = setTimeout(function()
		{
			// slideUp doesn't work if a parent is hidden so call hide at the end
			self.messageElm.slideUp(self.speed, function() { jQuery(this).hide(); });
		}, lifetime);
	}
}

FFAjaxSave.prototype.submit = function()
{
	this.form.submit();
}

