var ReadyState = {};

ReadyState.Uninitialized = 0;
ReadyState.Loading = 1;
ReadyState.Loaded = 2;
ReadyState.Interactive = 3;
ReadyState.Complete  = 4;
ReadyState.Unknown = -1;

function XHR()
{
	var me = this;
	var used = false;
	var xhr = (function()
	{
		var result = null;

		if (window["XMLHttpRequest"])
			result = new XMLHttpRequest();
		else if (window["ActiveXObject"])
			result = new ActiveXObject("Microsoft.XMLHTTP");
		if (result)
			result.onreadystatechange = readystatechange;
		return (result);
	})();

	this.readyState = ReadyState.Unknown;
	this.request = new Request(xhr);
	this.response = null;

	function complete()
	{
		me.response = new Response(xhr);
		return (handler("oncomplete"));
	}

	function handler(name)
	{
		return (typeof (me[name]) != "function" || me[name]() != false);
	}

	function interactive()
	{
		return (handler("oninteractive"));
	}

	function loaded()
	{
		return (handler("onloaded"));
	}

	function loading()
	{
		return (handler("onloading"));
	}

	function readystatechange()
	{
		update();
		switch (xhr.readyState)
		{
			case ReadyState.Uninitialized :
				return (uninitialized());
			case ReadyState.Loading :
				return (loading());
			case ReadyState.Loaded :
				return (loaded());
			case ReadyState.Interactive :
				return (interactive());
			case ReadyState.Complete :
				return (complete());
		}
		handler("onreadystatechange");
	}

	function uninitialized()
	{
		return (handler("onuninitialized"));
	}

	function update()
	{
		try
		{
			me.readyState = xhr.readyState;
		}
		catch (error)
		{
		}
	}

	function Request(xhr)
	{
		this.headers = NameValueList("\n", ": ");

		this.abort = function()
		{
			abort();
			this.headers.clear();
		}

		this.open = function(method, url, async, name, password)
		{
			if (used)
				abort();
			else
				used = true;
			for (var i = 0; i < this.headers.length; ++i)
				xhr.setRequestHeader(this.headers[i].name, this.headers[i].value);
			switch (arguments.length)
			{
				case 2 :
					xhr.open(method, url);
					break;
				case 3 :
					xhr.open(method, url, async);
					break;
				case 4 :
					xhr.open(method, url, async, name);
					break;
				case 5 :
					xhr.open(method, url, async, name, password);
					break;
			}
		}

		this.send = function(data)
		{
			if (!data)
				xhr.send("");
			else
				xhr.send(data);
		}

		function abort()
		{
			xhr.abort();
			xhr.onreadystatechange = readystatechange;
			me.readyState = ReadyState.Unknown;
			me.response = null;
		}
	}

	function Response(xhr)
	{
		this.headers = NameValueList.parse(xhr.getAllResponseHeaders().replace(/\s*$/g, ""), "\n", ": ");
		this.status = {value:xhr.status, text:xhr.statusText};
		this.text = xhr.responseText;
		this.xml = xhr.responseXML;
	}
}
