/**
 * Contains the class Sitebox.Framework.Asynchronous.
 *
 * @copyright	CARE Internet Services B.V. All Rights Reserved
 * @internal	$Id: Asynchronous.js 888 2010-01-26 14:38:18Z niek $
 * @internal	$Date: 2010-01-26 15:38:18 +0100 (Tue, 26 Jan 2010) $
 * @author		$Author: niek $
 * @version		$Revision: 888 $
 */

/**
 * Class for an asynchrone request.
 */
Sitebox.Framework.Asynchronous = function()
{
	this._request = getXMLHttpRequest();
	this._method = 'GET';
	this._url = null;
	this._headers = new Array();
	/* Callbacks */
	this._callbacks = new Array();
	this._callbacks['before'] = new Array();
	this._callbacks['loading'] = new Array();
	this._callbacks['loaded'] = new Array();
	this._callbacks['interactive'] = new Array();
	this._callbacks['complete'] = new Array();
	this._callbacks['success'] = new Array();
	this._callbacks['failure'] = new Array();
	this._callbacks['notmodified'] = new Array();
	this._callbacks['notfound'] = new Array();
	this._callbacks['after'] = new Array();
}; // class Sitebox.Framework.Asynchronous

/**
 * Sends the XMLHttpRequest.
 *
 * @param string url
 * @param string method
 * @param string content (optional)
 */
Sitebox.Framework.Asynchronous.prototype.call = function(url, method, content)
{
	/* Execute the callbacks for before the request is made. */
	for (var i = 0; i < this._callbacks['before'].length; i++)
	{
		this._callbacks['before'][i](this._request);
	}

	if (method == 'GET' || method == 'POST' || method == 'PUT')
	{
		this._method = method;
	}
	this._url = url;
	this._request.open(this._method, this._url);
	Sitebox.logDebug('Framework.Asynchronous.call(): setting request header X-Requested-With: XMLHttpRequest');
	this._request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
	Sitebox.logDebug('Framework.Asynchronous.call(): setting request header Referer: ' + location);
	this._request.setRequestHeader('Referer', location);
	if (method == 'POST')
	{
		/* log the request */
		Sitebox.logDebug('Framework.Asynchronous.call(): setting request header Content-Type: application/x-www-form-urlencoded');
		this._request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	}
	for (i = 0; i < this._headers.length; i++)
	{
		/* log the request */
		Sitebox.logDebug('Framework.Asynchronous.call(): setting request header ' + this._headers[i].header + ': ' + this._headers[i].content);
		this._request.setRequestHeader(this._headers[i].header, this._headers[i].content);
	}
	this._request.onreadystatechange = bind(this._onStateChange, this);
	/* Only allow content to be send with POST and PUT requests. */
	if (method == 'GET' || !content)
	{
		content = null;
	}
	this._request.setRequestHeader('Connection', 'close');
	/* log the request */
	Sitebox.logDebug('Framework.Asynchronous.call(): executing ' + this._method + ' to ' + this._url);
	this._request.send(content);
}; // function Sitebox.Framework.Asynchronous::call

/**
 * Adds a new header to the request.
 *
 * @param	string	header
 * @param	string	content
 */
Sitebox.Framework.Asynchronous.prototype.addHeader = function(header, content)
{
	this._headers.push({'header': header, 'content': content});
}; // function Sitebox.Framework.Asynchronous::addHeader

/**
 * Handles the callback calls.
 */
Sitebox.Framework.Asynchronous.prototype._onStateChange = function()
{
	var i;
	switch (this._request.readyState)
	{
		/* Ready state 1: loading */
		case 1:
			for (i = 0; i < this._callbacks['loading'].length; i++)
			{
				this._callbacks['loading'][i](this._request);
			}
			break;
		/* Ready state 2: loaded */
		case 2:
			for (i = 0; i < this._callbacks['loaded'].length; i++)
			{
				this._callbacks['loaded'][i](this._request);
			}
			break;
		/* Ready state 3: interactive */
		case 3:
			for (i = 0; i < this._callbacks['interactive'].length; i++)
			{
				this._callbacks['interactive'][i](this._request);
			}
			break;
		/* Ready state 4: complete */
		case 4:
			for (i = 0; i < this._callbacks['complete'].length; i++)
			{
				this._callbacks['complete'][i](this._request);
			}
			/* Response status 200: success */
			if (this._request.status == 200)
			{
				for (i = 0; i < this._callbacks['success'].length; i++)
				{
					this._callbacks['success'][i](this._request);
				}
			}
			/* Response status 304: not modified */
			else if (this._request.status == 304)
			{
				for (i = 0; i < this._callbacks['notmodified'].length; i++)
				{
					this._callbacks['notmodified'][i](this._request);
				}
			}
			/* Response status 404: not found */
			else if (this._request.status == 404)
			{
				for (i = 0; i < this._callbacks['notfound'].length; i++)
				{
					this._callbacks['notfound'][i](this._request);
				}
			}
			/* Other response status: failure */
			else
			{
				for (i = 0; i < this._callbacks['failure'].length; i++)
				{
					this._callbacks['failure'][i](this._request);
				}
			}
			/* Execute the callbacks for when the request is completely
			 * finished (including all other callbacks).
			 */
			for (i = 0; i < this._callbacks['after'].length; i++)
			{
				this._callbacks['after'][i](this._request);
			}
			break;
		default:
			/* There are only four possible states. */
			break;
	}
}; // function Sitebox.Framework.Asynchronous::_onStateChange

/**
 * Adds a callback for a specific state. Multiple callbacks possible.
 *
 * All callbacks take 1 argument: the XMLHttpRequest object.
 *
 * @param	string		state
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallback = function(state, callback)
{
	if (this._callbacks[state])
	{
		this._callbacks[state].push(callback);
	}
}; // function Sitebox.Framework.Asynchronous::addCallback

/**
 * Shortcut function for adding a 'before' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackBefore = function(callback)
{
	this.addCallback('before', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackBefore

/**
 * Shortcut function for adding a 'loading' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackLoading = function(callback)
{
	this.addCallback('loading', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackLoading

/**
 * Shortcut function for adding a 'loaded' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackLoaded = function(callback)
{
	this.addCallback('loaded', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackLoaded

/**
 * Shortcut function for adding a 'interactive' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackInteractive= function(callback)
{
	this.addCallback('interactive', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackInteractive

/**
 * Shortcut function for adding a 'complete' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackComplete = function(callback)
{
	this.addCallback('complete', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackComplete

/**
 * Shortcut function for adding a 'success' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackSuccess = function(callback)
{
	this.addCallback('success', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackSuccess

/**
 * Shortcut function for adding a 'failure' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackFailure = function(callback)
{
	this.addCallback('failure', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackFailure

/**
 * Shortcut function for adding a 'notmodified' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackNotModified = function(callback)
{
	this.addCallback('notmodified', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackNotModified

/**
 * Shortcut function for adding a 'notfound' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackNotFound = function(callback)
{
	this.addCallback('notfound', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackNotFound

/**
 * Shortcut function for adding a 'after' callback
 *
 * @param	function	callback
 */
Sitebox.Framework.Asynchronous.prototype.addCallbackAfter = function(callback)
{
	this.addCallback('after', callback);
}; // function Sitebox.Framework.Asynchronous::addCallbackAfter