/***************************************************************
 *  Copyright notice
 *
 *  (c) 2011 Andreas Thurnheer-Meier <tma@iresults.li>, iresults
 *  Daniel Corn <cod@iresults.li>, iresults
 *  
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/


/**
 * Make sure the variable console exists.
 */
if(typeof console == "undefined"){
	function Console(){
	}
	Console.prototype.log = function(){	}
	var console = new Console()
}

//function trace(){
//	if(typeof console !== "undefined"){
//		console.log.apply(arguments)
//	} else {
//		alert(arguments)
//	}
//}




/**
 * The iresults Ajax class uses jQuery to make Ajax calls.
 */
$(document).ready(function(){
	var irAjax = new Tx_Iresults_Ajax();
});



/**
 * The iresults Ajax class uses jQuery to make AJAX calls. Additionally it
 * automatically handles the transmisson of forms with the CSS classes
 * "iresults_ajax_form" and "iresults_ajax_hot_form".
 *
 * If you set "iresults_ajax_form" as the CSS class of your form it will be sent
 * via AJAX on submit.
 * 
 * If you set "iresults_ajax_hot_form" as the CSS class of your form it will
 * automatically be sent via AJAX every time you change the form.
 * 
 * @author	Daniel Corn <cod@iresults.li>
 * @package	Iresults
 * @subpackage	Iresults_Ajax
 * @version 2.0.1
 */
function Tx_Iresults_Ajax(para){
	this.url = '';
	this.targetId = '';
	this.loadingId = '';
	this.data = '';
	this.method = 'GET';
	this.dataType = 'html';
	this.sendRaw = false;
	this.eId = 'iresults_ajax';
	this.typeNum = '310004';
	this.cache = true;
	
	//this.mode = Tx_Iresults_Ajax.MODE_EID;
	this.mode = Tx_Iresults_Ajax.MODE_TYPENUM;
	
	this.developerMode = false;
	this.completeFunctionCallback = null;
	
	if(!para || !para.autoInitSubmitListener){
		this.initSubmitListener();
	}
	
	/**
	 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply
	 */
	this._loadConfigurationFromArgument.apply(this, arguments);
}

/**
 * @var {boolean} Static property to prevent Tx_Iresults_Ajax to init the submit
 * listeners multiple times.
 */
Tx_Iresults_Ajax.didInitSubmitListener = false;

/**
 * @constant {String} In eID mode the eID (this.eId) is attached to the request
 * URL.
 */
Tx_Iresults_Ajax.MODE_EID = 'MODE_EID';

/**
 * @constant {String} In typeNum mode the typeNum (this.typeNum) is attached to
 * the request URL.
 */
Tx_Iresults_Ajax.MODE_TYPENUM = 'MODE_TYPENUM';

/**
 * @constant {String} In raw mode no data is automatically attached to the
 * request. This mode has the same effect as setting this.sendRaw to TRUE.
 */
Tx_Iresults_Ajax.MODE_RAW = 'MODE_RAW';


/**
 * Registers a submit listener for each DOM object with class "iresults_ajax_form".
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.initSubmitListener = function(){
	if(!Tx_Iresults_Ajax.didInitSubmitListener){
		/*
		 * Init the listener for the normal AJAX forms.
		 */
		$("form.iresults_ajax_form").submit(this.sendForm);
		
		
		/*
		 * Init the listeners for the "hot" AJAX forms.
		 */
		$("form.iresults_ajax_hot_form input").change(this.sendForm);
		$("form.iresults_ajax_hot_form").submit(this.sendForm);
		
		Tx_Iresults_Ajax.didInitSubmitListener = true;
	}
	
	//$(".iresults_ajax_button").click(this.sendForm);
}

/**
 * Make a AJAX request and replace the DOM object with the given ID with the data
 * returned from the server.
 * 
 * @param {String} url		The URL to call.
 * @param {String} targetId The ID of the DOM object to replace.
 * @param {mixed} data		Optional data to pass to the server.
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.update = function(para){
	/**
	 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply
	 */
	if(!this._loadConfigurationFromArgument.apply(this, arguments)){
	//	return;
	}
	
	
	/*
	 * Prepare the URL
	 */
	if(!this.url){
		this.url = document.URL;
	}
	
	if(this.getMode() == Tx_Iresults_Ajax.MODE_RAW){
		this.sendRaw = true;
	}
	
	var addition = '?';
	if(!this.sendRaw && this.url.search(/\?/) != -1){
		addition = '&';
	}
	
	/*
	 * Add the eID to the request.
	 */
	if(!this.sendRaw &&
	   this.getMode() == Tx_Iresults_Ajax.MODE_EID &&
	   this.eId && 
	   this.url.search("eID=") == -1){
		this.url = this.url + addition + "eID=" + this.eId;
	}
	
	/*
	 * Add the typeNum to the request.
	 */
	if(!this.sendRaw &&
	   this.getMode() == Tx_Iresults_Ajax.MODE_TYPENUM &&
	   this.typeNum &&
	   this.url.search("type=") == -1){
		this.url = this.url + addition + "type=" + this.typeNum;
	}
	
	/*
	 * Show the loading object.
	 */
	if(this.loadingId) this.showLoading()
	
	
	/*
	 * Add the current PID to the URL and set the developerMode
	 */
	if(typeof Tx_Iresults_Config !== 'undefined'){
		this.url = this.url + '&iresults_ajax_pid=' + Tx_Iresults_Config.pid;
		this.developerMode = Tx_Iresults_Config.debug;
	}
	
	/*
	 * Add the current language
	 */
	if(!this.sendRaw && typeof Tx_Iresults_Config !== 'undefined' && this.url.search("sys_language_uid=") == -1 && this.url.search("L=") == -1){
		this.url = this.url + '&sys_language_uid=' + Tx_Iresults_Config.sys_language_uid +
		'&L=' + Tx_Iresults_Config.sys_language_uid;
	}
	
	/*
	 * Create the AJAX parameter object.
	 * @type object ajaxParameter
	 */
	var ajaxParameter = this._createAjaxParameterObject();
	
	// Die Ajax-Anfrage senden
	try{
		var ajaxAufruf = jQuery.ajax(ajaxParameter);
	} catch(e){
		console.log("Error while trying to make AJAX call", e);
		if(this.developerMode){
			throw e
		}
	}
	return;
}

/**
 * Sends the last request again.
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.refresh = function(){
	if(!this.url) return;
	
	/*
	 * Create the AJAX parameter object.
	 * @type object ajaxParameter
	 */
	var ajaxParameter = this._createAjaxParameterObject();
	
	// Die Ajax-Anfrage senden
	try{
		var ajaxAufruf = jQuery.ajax(ajaxParameter);
	} catch(e){
		console.log("Error while trying to make AJAX call", e);
		if(this.developerMode){
			throw e
		}
	}
	
	return;
	
}



/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* FORM AND HELPER METHODS   MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/**
 * Serializes and sends a form.
 * 
 * @return {boolean} false Returns false to prevent the page from reloading.
 */
Tx_Iresults_Ajax.prototype.sendForm = function(){
	/*
	 * Get the form (if this is not one)
	 */
	var form = this;
	var formsJQ = null;
	if(this.nodeName !== 'FORM'){
		formsJQ = $(this).closest("form")
		if(formsJQ.length > 0) form = formsJQ[0];
	} else {
		formsJQ = $(this)
	}
	var data = formsJQ.serialize();
	var irAjax = new Tx_Iresults_Ajax();
	
	try{
		irAjax.update({
			url: form.action,
			targetId: form.id + "_output",
			data: data,
			method: form.method,
			loadingId: form.id + "_loading"
		});	
	} catch(e){
		console.log("Error while sending form", e)
	}
	
	/*
	 * Return TRUE if this is anything other than a form.
	 * This is needed by Internet Explorer i.e. to keep the checkbox checked.
	 */
	if(this.nodeName !== 'FORM'){
		return true;
	}
	return false;
}

/**
 * Toggle the visibility of the loading element.
 * 
 * @returns {void}
 */
Tx_Iresults_Ajax.prototype.toggleLoading = function(){
	/*
	 * Toggle the loading object.
	 */
	if(this.loadingId && $("#" + this.loadingId)){
		$("#" + this.loadingId).toggle();
	}
}

/**
 * Hide the loading element.
 * 
 * @returns {void}
 */
Tx_Iresults_Ajax.prototype.hideLoading = function(){
	/*
	 * Toggle the loading object.
	 */
	if(this.loadingId && $("#" + this.loadingId)){
		$("#" + this.loadingId).hide();
	}
}

/**
 * Show the loading element.
 * 
 * @returns {void}
 */
Tx_Iresults_Ajax.prototype.showLoading = function(){
	/*
	 * Toggle the loading object.
	 */
	if(this.loadingId && $("#" + this.loadingId)){
		$("#" + this.loadingId).show();
	}
}



/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* CALLBACKS            WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/**
 * The standard success callback invokes the display method.
 * 
 * @param	{mixed} data The data returned from the server, formatted according to the dataType parameter.
 * @param	{String} textStatus A string describing the status.
 * @param	{XMLHttpRequest} jqXHR
 * @param   {Objekt} ajaxParameterObject		The AJAX parameter object.
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.success = function(data, textStatus, jqXHR, ajaxParameterObject){
	return this.caller.display(data, textStatus, jqXHR);
}

/**
 * The success callback replaces the HTML-content of the target-ID object.
 * 
 * @param {mixed} data The data returned from the server, formatted according to the dataType parameter.
 * @param {String} textStatus A string describing the status.
 * @param {XMLHttpRequest} jqXHR 
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.display = function(data, textStatus,jqXHR){
	// this zeigt auf das Ajax-Objekt
	
	/*
	 * Display the new data.
	 */
	if(this.targetId){
		$("#"+this.targetId).html(data);
	}
	return;
}

/**
 * The error callback is called if the request fails.
 * 
 * @param   {XMLHttpRequest} jqXHR
 * @param   {String} textStatus		A string describing the type of error that occurred.
 * @param   {mixed} errorThrown		An optional exception object, if one occurred.
 * @param   {Objekt} ajaxParameterObject		The AJAX parameter object.
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.error = function(jqXHR, textStatus, errorThrown, ajaxParameterObject){
	// Do nothing
	if(this.developerMode){
		alert("AJAX ERROR:\n" + jqXHR +" \n "+textStatus+" \n "+errorThrown)
	}
	$("#"+this.targetId).html(jqXHR.responseText)
}

/**
 * The complete callback is called when the request completed.
 * 
 * @param   {XMLHttpRequest} jqXHR
 * @param   {String} textStatus		A string describing the status.
 * @param   {Objekt} ajaxParameterObject		The AJAX parameter object.
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.completeFunction = function(jqXHR, textStatus, ajaxParameterObject){
	this.hideLoading();
}



/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* PROTECTED METHODS    WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/**
 * Returns the AJAX parameter object for the current configuration.
 * 
 * @returns {Object} The AJAX parameter object.
 */
Tx_Iresults_Ajax.prototype._createAjaxParameterObject = function(){
	/*
	 * Build the function callbacks.
	 */
	var successFunctionCallback 	= function(data, textStatus,jqXHR){this.caller.success(data, textStatus,jqXHR, this)}
	var completeFunctionCallback 	= function(jqXHR, textStatus){this.caller.completeFunction(jqXHR, textStatus, this)}
	var errorFunctionCallback 		= function(jqXHR, textStatus, errorThrown){this.caller.error(jqXHR, textStatus, errorThrown, this)}
	
	/*
	 * Create the AJAX parameter object.
	 * @type object ajaxParameter
	 */
	var ajaxParameter = {
		processData: true,
		targetId: this.targetId,
		loadingId: this.loadingId,
		url: this.url,
		type: this.method,
		data: this.data,
		cache: this.cache,
		dataType: this.dataType,
		error: errorFunctionCallback,
		success: successFunctionCallback,
		complete: completeFunctionCallback,
		caller: this
	}
	return ajaxParameter;
}

/**
 * Load the configuration.
 * 
 * @return {boolean} Returns TRUE if configuration was loaded, otherwise FALSE.
 */
Tx_Iresults_Ajax.prototype._loadConfigurationFromArgument = function(para){
	var result = false;
	
	/**
	 * Check if multiple arguments have been passed or an object
	 */
	if(arguments.length >= 2){ // Multiple arguments
		var argArray = arguments;
		this.url 		= argArray[0];
		this.targetId 	= argArray[1];
		this.data 		= argArray[2];
		result = true;
	} else if(arguments.length == 1 && typeof para == 'object' ){ // Object as argument
		this.url 		= para.url;
		this.targetId 	= para.targetId;
		this.data 		= para.data;
		this.loadingId 	= para.loadingId;
		
		if(para.dataType)	this.dataType = para.dataType;
		if(para.method) 	this.method = para.method;
		if(para.sendRaw) 	this.sendRaw = para.sendRaw;
		if(para.mode) 		this.mode = para.mode;
		if(para.completeFunction)	this.completeFunction = para.completeFunction;
		result = true;
	} else if(arguments.length == 1){ /* The only argument passed is assumed to
									    be the target ID */
		this.targetId 	= para;
		result = true;
	}
	
	return result;
}


/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* ACCESSOR METHODS    MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/* MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM */
/**
 * Returns the current mode of the AJAX object.
 * 
 * @return {String} The current mode as one of the Tx_Iresults_Ajax.MODE
 * constants.
 */
Tx_Iresults_Ajax.prototype.getMode = function(){
	return this.mode;
}

/**
 * Sets the current mode of the AJAX object.
 * 
 * @param   {String} The new mode as one of the Tx_Iresults_Ajax.MODE constants.
 * 
 * @return {void}
 */
Tx_Iresults_Ajax.prototype.setMode = function(newMode){
	this.mode = newMode;
}

/**
 * Returns if the AJAX request should use the browser cache.
 *
 * @return boolean
 */
Tx_Iresults_Ajax.prototype.getCache = function(){
	return this.cache;
}

/**
 * Set if the AJAX request should use the browser cache.
 *
 * @param boolean newValue The new value to set.
 *
 * @return void
 */
Tx_Iresults_Ajax.prototype.setCache = function(newValue){
	this.cache = newValue;
}

/**
 * Returns the ID of the DOM object to show while requesting the data.
 *
 * @return string
 */
Tx_Iresults_Ajax.prototype.getLoadingId = function(){
	return this.loadingId;
}

/**
 * Set the ID of the DOM object to show while requesting the data.
 *
 * @param string newValue The new value.
 *
 * @return void
 */
Tx_Iresults_Ajax.prototype.setLoadingId = function(newValue){
	this.loadingId = newValue;
}

/**
 * Returns the complete callback function.
 *
 * @return Function
 */
Tx_Iresults_Ajax.prototype.getCompleteFunction = function(){
	return this.completeFunction;
}

/**
 * Setter for the complete callback function.
 *
 * @param Function callback The new value to set.
 *
 * @return void
 */
Tx_Iresults_Ajax.prototype.setCompleteFunction = function(callback){
	//var completeFunctionCallback = function(jqXHR, textStatus){callback(jqXHR, textStatus, this)}
	//this.completeFunction = completeFunctionCallback;
	this.completeFunction = callback;
}

/**
 * Returns the success callback function.
 *
 * @return Function
 */
Tx_Iresults_Ajax.prototype.getSuccessFunction = function(){
	return this.success;
}

/**
 * Setter for the success callback function.
 *
 * @param Function callback The new value to set.
 *
 * @return void
 */
Tx_Iresults_Ajax.prototype.setSuccessFunction = function(callback){
	//var successFunctionCallback = function(data, textStatus,jqXHR){callback(data, textStatus,jqXHR, this)}
	//this.success = successFunctionCallback;
	this.success = callback;
}

/**
 * Returns the error callback function.
 *
 * @return Function
 */
Tx_Iresults_Ajax.prototype.getErrorFunction = function(){
	return this.error;
}

/**
 * Setter for returns the error callback function.
 *
 * @param Function callback The new value to set.
 *
 * @return void
 */
Tx_Iresults_Ajax.prototype.setErrorFunction = function(callback){
	//var errorFunctionCallback = function(jqXHR, textStatus, errorThrown){callback(jqXHR, textStatus, errorThrown, this)}
	//this.error = errorFunctionCallback;
	this.error = callback;
}



