// implements: AJAX;

function ajaxModule(method, headers, asynchronous, onReadyStateChange, servlet)
{
	return	{
		method				: method,				// POST/GET
		headers				: headers,				// {"Header Name" : "Header Value"}
		asynchronous		: asynchronous,			// True if execution is to continue without waiting for the server's response
		onReadyStateChange	: onReadyStateChange,	// The callback function to use when the server's response changes
		servlet				: servlet				// A path to the servers script (servlet) that is to handle the request for this module.
	};
}

/*
	var xmlHttp;
	try					{ xmlHttp	= new XMLHttpRequest();									}	// Firefox, Opera 8.0+, Safari
	catch (e)
	{	// Internet Explorer
		try				{ xmlHttp	= new ActiveXObject("Msxml2.XMLHTTP");					}
		catch(e)
		{
		    try			{ xmlHttp	= new ActiveXObject("Microsoft.XMLHTTP");				}
			catch(e)	{ alert("Your browser does not support AJAX!");		return false;	}
		}
	}
 */

// Create the XMLHttpRequest object if it is not present (such as with IE) so we can prototype it.
if(typeof(XMLHttpRequest) == "undefined")
{
	XMLHttpRequest	= function()
	{
		var me					= this;
		var activeX				= undefined;
		
		try			{ activeX	= new ActiveXObject("Msxml2.XMLHTTP");				}	//"Microsoft.XMLHTTP"
		catch (e)	{ alert("Your browser does not support AJAX!");	return false;	}
		
		function abort()							{ activeX.abort.call();						}
		function getAllResponseHeaders()			{ return activeX.getAllResponseHeaders();	}
		function getResponseHeader(header)			{ return activeX.getResponseHeader(header);	}
		function open(method, url)					{ activeX.open(method, url);				}
		function overrideMimeType(mimetype)			{ activeX.overrideMimeType(mimetype);		}
		function send(body)							{ activeX.send(body);						}
		function sendAsBinary(body)					{ activeX.sendAsBinary(body);				}
		function setRequestHeader(header, value)	{ activeX.setRequestHeader(header, value);	}
		
		updateProperties			= function()
		{
			for(var p in activeX)	if(typeof(activeX[p]) != "function")	this[p]	= activeX[p];
		};
		
		activeX.onreadystatechange	= function()
		{
			me.updateProperties();
			me.onreadystatechange.call(this);
		};
		
		updateProperties();
	};
}

/**
 * Captures the browser independent Http Request object.
 * @param {Object} 		hostName	The host name of the responding server
 */
function ajax(hostName)
{
	// Private:
	var me						= this;						// Used for those pesky this references or lack of anyways.
	var server					= new XMLHttpRequest();
	var hostName				= hostName;					// Host name of the server script (i.e. a PHP file)
	var module;												// An object representing method, headers, asynchronous, and onreadystatechange callback
	var ready					= true;						// The status of AJAX (could use this.XMLHTTP.readyState...maybe not?)
	var que						= new Array();				// The messages are collected into this que when they overflow.

	var eventListeners			= {onReadyStateChange	: new Array()	};

	// Public:
	/**
	 * The states that the server can be.
	 * <table>
	 * <thead>
	 * 	<tr><th>State			</th><th>value	</th><th>Description</th></tr>
	 * </thead>
	 * <tbody>
	 * 	<tr><td>uninitialized	</td><td>0		</td><td>The state is not yet initialized				</td></tr>
	 * 	<tr><td>loading			</td><td>1		</td><td>content is loading								</td></tr>
	 * 	<tr><td>loaded			</td><td>2		</td><td>Content is loaded								</td></tr>
	 * 	<tr><td>interactive		</td><td>3		</td><td>Content is not yet complete but is interactive	</td></tr>
	 * 	<tr><td>complete		</td><td>4		</td><td>Content is loaded and complete					</td></tr>
	 * </tbody>
	 * </table>
	 */
	this.states					= {	uninitialized 	: 0,
									loading 		: 1,
									loaded			: 2,
									interactive		: 3,
									complete		: 4
									};

	/**
	 * <body>
	 * <tbody>
	 * 	<tr><th>Header			</th><th>Possible Values									</th><th>Description							</th></tr>
	 * 	<tr><td>Accept-Charset	</td><td>x-user-defined										</td><td>Download binary files					</td></tr>
	 * 	<tr><td>Content-Type	</td><td>application/x-www-form-urlencoded					</td><td>URL encoded							</td></tr>
	 * 	<tr><td>Content-Type	</td><td>multipart/form-data; boundary=<i>[separator]</i>	</td><td>For form data separated by a boundary	</td></tr>
	 * 	<tr><td>Connection		</td><td>close												</td><td>										</td></tr>
	 * </tbody>
	 * </body>
	 */
	this.setRequestHeader		= function(header, value)
	{
		for(var h in module.headers)
			if(h == header)
				console.warn("header, \"" + header + "\" was already set.  Replacing \"" + module.headers[h] + "\" with \"" + value + "\".");
		
		module.headers[header]	= value;
		console.debug(header + ": " + value);
	};
	
	this.getRequestHeader		= function(header)			{ return requestHeaders[header];			};
	
	this.getAllResponseHeaders	= function()				{ return server.getAllResponseHeaders();	};
	this.getResponseHeader		= function(header)			{ return server.getResponseHeader(header);	};
	this.setModule				= function(Module)			{ module				= Module;			};
	this.setAsynchronous		= function(asynchronous)	{ module.asynchronous	= asynchronous;		};
	this.addEventListener		= function(event, callback)	{ eventListeners[event].push(callback);		};

	updateProperties			= function()
	{
		this.readyState		= server.readyState;
		this.responseText	= server.responseText;
		this.responseXML	= server.responseXML;
		this.status			= server.status;
		this.statusText		= server.statusText;
	};
	updateProperties();

	// todo: Potentialy the same server parameters could be used and the method and data could vary.
	_onReadyStateChange			= function()
	{
		updateProperties();
		if(onReadyStateChange)	onReadyStateChange();

		for(var onReadyStateChangeListener in eventListeners.onReadyStateChange)
			onReadyStateChangeListener.call(me);

		if(server.readyState == this.states.complete)
		{
			ready	= true;
			if(que.length != 0)
			{
				var item	= que.shift();
				me.setModule(item.module);
				me.send(item.data);
			}
		}
	};

	/**
	 * Sometimes you just need to send content using the same configurations.  <code>reset</code> allows
	 * you to recycle the AJAX object.  This function is called before sending data each send request.
	 */
	reset						= function(GET)
	{
		// Todo: Validate GET data.  Maybe leave the "?" out if it is already present?
		
		var URI		= hostName + module.servlet + ((GET == "")? "" : ("?" + GET));
		
		console.debug("open(\"" + module.method + "\", \"" + URI + "\", " + module.asynchronous + ")");
		server.open(module.method, URI, module.asynchronous);
		
		var headers	= module.headers;
		for(header in headers)	server.setRequestHeader(header, headers[header]);
		
		server.onreadystatechange	= _onReadyStateChange;
	};

	/**
	 * Send the data to the http response server.
	 * @param {Object} data The data to be sent to the http response server
	 */
	this.send					= function(data)
	{
		if(ready)
		{
			ready	= false;
			var GET	= "";
			
			if(module.method == "GET")	{ GET = data;	data = "";					}
			
			reset(GET);													// Only uses <code>data</code> if the method is POST

			if(module.method == "POST")	this.setRequestHeader("Content-length",	data.length);

			server.send(data);
			
			if(!module.asynchronous)	{ updateProperties.call(me); ready	= true;	}
		}
		else que.push({module : module, data : data});
	};
}
