/**
 *	@fileoverview Zaxas is an Object Oriented Ajax Framework. It's built to support all the environment platforms possible to develop web applications. To integrate the Ajax functionalities in any existing web application, developer has to know very little about JavaScript.
 *	"Think Ajax, Think Zaxas!"
 *
 *  Zaxas follows the GNU General Public License (GPL).
 *	It's cross-browser supported.
 *	For more information, visit the Zaxas homepage: 
 *	http://sf.com/projects/zaxas
 *
 *	[July 2006]
 *
 *	@version 0.1.5 alpha
 *	@author Kazi G. Rabbany Sajid <kgrsajid@users.sourceforge.net>
 */

/**
 *	Creates a new instance of ZaxasRequest.
 *	@class ZaxasRequest is the class of the core Ajax engine.
 *	@constructor
 *	@return New instance of ZaxasRequest
 */
function ZaxasRequest()
{

	// Private variables of ZaxasRequest Class
	/**
	 * XmlHttpRequest object of the request
	 * @private
	 * @type XmlHttpRequest
	 */
	var _xmlHttpRequest = null;
	
	/**
	 * Place to response
	 * @private
	 * @type String
	 */
	var _responseHere = '';
	
	/**
	 * Function to to be called with response text
	 * @private
	 * @type Function
	 */
	var _responseMethod = null;
	
	/**
	 * Parameters for the request
	 * @private
	 * @type Array
	 */
	var _parameters = new Array();
	
	/**
	 * Length of the parameters
	 * @private
	 * @type Int
	 */
	var _parameterLength = 0;
	
	/**
	 * Save error messages
	 * @private
	 * @type String
	 */
	var _errors = null;
	
	/**
	 * Request and recieve Response Headers
	 * @private
	 * @type Array
	 */
	var _headers = null;
	
	/**
	 * @private
	 * Sets error messages of the request. [Privileged function]
	 * @param {String} errors The errors of the response
	 * @see #setErrors
	 */
	this.setErrors = function(errors)
	{
		this._errors = errors;
	}
	
	/**
	 * @private
	 * Set parameters length [Privileged function]
	 */
	this.setParameterLength = function(paramLength)
	{
		this._parameterLength = paramLength;
	}
	
	/** @private
	 *	Returns processed argument. Process all the parameters and make it an argument to pass with the request. [Previleged function]
	 *	@type String
	 */
	this.processArguments = function()
	{
		var args = "";
		
		for(var i in this._parameters)
		{
				if(args != "")
					args += "&";
				args += i + "=" + escape(this._parameters[i]);
		}
		
		return args;
	}
	
	/** @private
	XmlHttpRequest Initialization Function. Determines XmlHttpRequest object for the cross browser support. [Previleged function]
	Parameter: None
	Return: Initialized XmlHttpRequest object
	*/
	this.xmlHttpRequestInit = function()
	{
		var xmlHttpRequest = null;
		
		try
		{
			xmlHttpRequest = new XMLHttpRequest();
		}
		catch(thinkMicrosoft)
		{
			try
			{
				xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch(tryOldMicrosoft)
			{
				try
				{
					xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
				}
				catch(failedAnyway)
				{
					//Do Nothing!
				}
			}
		}
		
		return xmlHttpRequest;
	}
	
	/**
	@private
	Handler of XmlHttpRequest triggers on state changed and call the user defined function with the response text. [Previleged function]
	Parameter: None
	Return: None (but calls the user defined function with the response text synchronicity)
	*/
	this.stateChangedAndReturn = function()
	{
		if(this._xmlHttpRequest.readyState == 4)
		{
			if(this._xmlHttpRequest.status >= 400 && this._xmlHttpRequest.status < 500)
			{
				this.setErrors("Bad Request / Invalid Syntax");
			}
			else if(this._xmlHttpRequest.status >= 500 && this._xmlHttpRequest.status < 600)
			{
				this.setErrors("Server Error");
			}
	
			if(this.hasError())
			{
				this._responseFunction(this._xmlHttpRequest.status + ": " + this._errors);
			}
			else
			{
				this._responseFunction(this._xmlHttpRequest.responseText);
			}
		}
	}
	
	/**
	@private
	Handler of XmlHttpRequest triggers on state changed. [Previleged function]
	Parameter: None
	Return: None (but replaces the text with the response text of a prefered location with synchronicity)
	*/
	this.stateChanged = function()
	{
		if(this._xmlHttpRequest.readyState == 4)
		{
			if(this._xmlHttpRequest.status >= 400 && this._xmlHttpRequest.status < 500)
			{
				this.setErrors("Bad Request / Invalid Syntax");
			}
			else if(this._xmlHttpRequest.status >= 500 && this._xmlHttpRequest.status < 600)
			{
				this.setErrors("Server Error");
			}
	
			this.placeResponse();
		}
	}
	
	/**
	@private
	Handler of XmlHttpRequest triggers on state changed of HEAD request. [Previleged function]
	Parameter: None
	Return: None (but calls the user defined function with the response text synchronicity)
	*/
	this.stateChangedAndReturnHeaders = function()
	{
		if(this._xmlHttpRequest.readyState == 4)
		{
			if(this._headers == null || this._headers == '')
			{
				this._responseMethod(this._xmlHttpRequest.getAllResponseHeaders());
			}
			else
			{
				var responseHeaders = new Array();
				
				for(var i in this._headers)
				{
						responseHeaders[this._headers[i]] = this._xmlHttpRequest.getResponseHeader(this._headers[i]);
				}
				
				this._responseMethod(responseHeaders);
			}
		}
	}

	
	
	
	this.zaxasInit();
}

// get and set methods of the private variables
/* ***** */

/**
 * Returns the XmlHttpRequest object
 * @see #setXmlHttpRequest
 * @type XmlHttpRequest
 */
ZaxasRequest.prototype.getXmlHttpRequest = function()
{
	return this._xmlHttpRequest;
}

/**
 * Sets new XmlHttpRequest object
 * @param {XmlHttpRequest} xmlHttpRequest The XmlHttpRequest object to set
 * @see #getXmlHttpRequest
 */
ZaxasRequest.prototype.setXmlHttpRequest = function(xmlHttpRequest)
{
	this._xmlHttpRequest = xmlHttpRequest;
}

/**
 * Returns the place where to response
 * @see #setResponseHere
 * @type String
 */
ZaxasRequest.prototype.getResponseHere = function()
{
	return this._responseHere;
}

/**
 * Sets new place where to response
 * @param {String} responseHere The place to set
 * @see #getResponseHere
 */
ZaxasRequest.prototype.setResponseHere = function(responseHere)
{
	this._responseHere = responseHere;
}

/**
 * Returns the function where to response
 * @see #setResponseMethod
 * @type Function
 */
ZaxasRequest.prototype.getResponseMethod = function()
{
	return this._responseMethod;
}

/**
 * Sets a new function where to response
 * @param {Function} responseMethod The function to set
 * @see #getResponseMethod
 */
ZaxasRequest.prototype.setResponseMethod = function(responseMethod)
{
	this._responseMethod = responseMethod;
}

/**
 * Returns parameters that already set to the request
 * @see #setParameters
 * @type Array
 */
ZaxasRequest.prototype.getParameters = function()
{
	return this._parameters;
}

/**
 * Sets new parameters to the request
 * @param {Array} params The parameters to set
 * @see #getParameters
 */
ZaxasRequest.prototype.setParameters = function(params)
{
	this._parameters = params;
}

/**
 * Returns errors of the request
 * @type String
 * @see #hasError
 */
ZaxasRequest.prototype.getErrors = function()
{
	return this._errors;
}

/**
 * Returns Response Headers of the HEAD request
 * @see #setHeaders
 * @type Array
 */
ZaxasRequest.prototype.getHeaders = function()
{
	return this._headers;
}

/**
 * Sets the list of headers to the HEAD request. Set null to get all the Response Headers.
 * @param {Array} headers The list of headers to set
 * @see #getHeaders
 */
ZaxasRequest.prototype.setHeaders = function(headers)
{
	this._headers = headers;
}

/**
 * Returns the length of parameters
 * @type Int
 */
ZaxasRequest.prototype.getParameterLength = function()
{
	return this._parameterLength;
}

/* ***** */
// End of get and set methods of the private variables

/**
 * Adds a single parameter to request
 * @param {String} paramName Name of the parameter
 * @param {String} paramValue Value of the parameter
 * @see #setParameters
 */
ZaxasRequest.prototype.setSingleParameter = function(paramName, paramValue)
{
	this._parameters[paramName] = paramValue;
	this.setParameterLength(this.getParameterLength() + 1);
}

/**
 * Returns true if any error occurred during the request process
 * @see #getErrors
 * @type Boolean
*/
ZaxasRequest.prototype.hasError = function()
{
	if(this._errors != null)
		return true;
	else
		return false;
}

/**
 * Initializes ZaxasRequest object. Creates XmlHttpRequest object and alerts if failed.
*/
ZaxasRequest.prototype.zaxasInit = function()
{
	this._xmlHttpRequest = this.xmlHttpRequestInit();
	
	if(this._xmlHttpRequest == null)
	{
		alert("Your browser is not supporting Ajax!");
		return;
	}
	
	if(this._xmlHttpRequest.overrideMimeType)
	{
		this._xmlHttpRequest.overrideMimeType('text/xml');
	}
}

/**
 * Sends the request with the specified method type and reponses to the specified function.
 * @param {String} method Method type
 * @param {String} url URL where to request
 * @param {Function} responseFunction Function to be called to response
 * @param {Boolean} asynchronous Synchronicity of the request
 * @see #sendRequestAndResponse
*/
ZaxasRequest.prototype.sendRequest = function(method, url, responseFunction, asynchronous)
{
	var args = this.processArguments();
	
	if(method == 'GET')
	{
		if(this.getParameterLength() > 0)
		{
			if(url.indexOf('?') == -1)
				url += '?' + args;
			else
				url += '&' + args;
		}
	}

	this._xmlHttpRequest.open(method, url, asynchronous);
	
	var self = this;
	
	this._xmlHttpRequest.onReadyStateChange = function()
	{
		self.stateChangedAndReturn();
	}
	
	this._responseFunction = responseFunction;

 	this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

	this._xmlHttpRequest.send(args);
}

/**
 * Sends the request with the specified method type and places the response text to the specified location.
 * @param {String} method Method type
 * @param {String} url URL where to request
 * @param {String} responseHere Place to response
 * @param {Boolean} asynchronous Synchronicity of the request
 * @see #sendRequest
*/
ZaxasRequest.prototype.sendRequestAndResponse = function(method, url, responseHere, asynchronous)
{
	var args = this.processArguments();
	
	if(method == 'GET')
	{
		if(this.getParameterLength() > 0)
		{
			if(url.indexOf('?') == -1)
				url += '?' + args;
			else
				url += '&' + args;
		}
	}

	this._xmlHttpRequest.open(method, url, asynchronous);
	
	var self = this;
	
	this._xmlHttpRequest.onreadystatechange = function()
	{
		self.stateChanged();
	}
	
	this._responseHere = responseHere;

 	this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

	this._xmlHttpRequest.send(args);
}

/**
 * Sends HEAD request and responses to the specified function.
 * @param {String} url URL where to request
 * @param {Function} responseMethod Function to response
 * @param {Array} headers List of headers to request. To get all the headers, set null.
 * @param {Boolean} asynchronous Synchronicity of the request
 * @see #sendRequest
*/
ZaxasRequest.prototype.getResponseHeaders = function(url, responseMethod, headers, asynchronous)
{
	this._xmlHttpRequest.open("HEAD", url, asynchronous);
	
	var self = this;
	
	this._xmlHttpRequest.onReadyStateChange = function()
	{
		self.stateChangedAndReturnHeaders();
	}

	this._headers = headers;
	this._responseMethod = responseMethod;

	this._xmlHttpRequest.send(null);
}

/**
 * Places the response text to specified location.
 * @see #setResponseHere
*/
ZaxasRequest.prototype.placeResponse = function()
{
	if(this.hasError())
	{
		document.getElementById(this._responseHere).innerHTML = this._xmlHttpRequest.status + ": " + this._errors;
	}
	else
	{
		document.getElementById(this._responseHere).innerHTML = this._xmlHttpRequest.responseText;
	}
}

/**
 * Returns response text of the GET or POST request.
 * @type String
*/
ZaxasRequest.prototype.getResponseText = function()
{
	if(this.hasError())
	{
		return this._xmlHttpRequest.status + ": " + this._errors;
	}
	else
	{
		return this._xmlHttpRequest.responseText;
	}
}

/**
 * Sends GET request and places the response text to the specified location.
 * @param {String} url URL where to request
 * @param {String} responseHere Place to response
 * @param {Boolean} asynchronous Synchronicity of the request
 * @see #postContent
 * @see #postForm
 * @see #sendRequestAndResponse
 * @see #sendRequest
*/
ZaxasRequest.prototype.getContent = function(url, responseHere, asynchronous)
{
	this.sendRequestAndResponse("GET", url, responseHere, asynchronous);
}

/**
 * Sends POST request and places the response text to the specified location.
 * @param {String} url URL where to request
 * @param {String} responseHere Place to response
 * @param {Boolean} asynchronous Synchronicity of the request
 * @see #postForm
 * @see #getContent
 * @see #sendRequestAndResponse
 * @see #sendRequest
*/
ZaxasRequest.prototype.postContent = function(url, responseHere, asynchronous)
{
	var args = this.processArguments();

	this._xmlHttpRequest.open("POST", url, asynchronous);
	
	var self = this;
	
	this._xmlHttpRequest.onReadyStateChange = function()
	{
		self.stateChanged();
	}
	
	this._responseHere = responseHere;

 	this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

	this._xmlHttpRequest.send(args);
}

/**
 * Sends a html form to POST request and places the response text to the specified location.
 * @param {String} url URL where to request
 * @param {String} responseHere Place to response
 * @param {form} myForm Form to POST request
 * @param {Boolean} asynchronous Synchronicity of the request
 * @see #postContent
 * @see #getContent
 * @see #sendRequestAndResponse
 * @see #sendRequest
*/
ZaxasRequest.prototype.postForm = function(url, responseHere, myForm, asynchronous)
{
	var params = new Array();

	for(var i=0; i < myForm.elements.length ; i++)
	{
		params[myForm.elements[i].name] = myForm.elements[i].value;
	}
	this.setParameters(params);

	var args = this.processArguments();
	
	this.sendRequestAndResponse("POST", url, responseHere, asynchronous);
}

/**
 * Aborts the current request.
*/
ZaxasRequest.prototype.abortRequest = function()
{
	this._xmlHttpRequest.abort();
}
