/**
 * Create loader to request data from server
 *
 * @param handler Object providing clear()/render() function when data arrives
 */
function it_loader(handler)
{
	/* Clear cache etc. if completely new data */
	this.req = null;
	this.handler = handler;
	this.instance = it_loader.instances++;
	this.callid = 0;
	this.scrpt = [];
	this.clear();
}

/* Methods */
it_loader.prototype =
{

/* Clear cache and initialize handler */
clear: function()
{
	/* Clear cache etc. if completely new data */
	this.entry = new Array();
	this.start = this.end = 0;
	this.attr = { num: 0, loadtime: 0 };
	this.method = "GET";
	this.post_data = null;

	if (this.handler.clear)
		this.handler.clear();
},

load: function(baseurl, pos, num, query_volatile, retry)
{
	/* Convert to int */
	pos -= 0;
	num -= 0;

	if (isNaN(retry))
		retry = 0;

	if (baseurl != this.baseurl)
	{
		this.clear();
		this.baseurl = baseurl;
		this.start = this.end = pos;
	}

	this.pos = pos;
	this.num = num;
	this.query_volatile = query_volatile;

	this.stop();

	while ((num > 0) && (typeof this.entry[pos] != "undefined"))
	{
		pos++;
		num--;
	}

	if (this.attr.eof)
		num = Math.min(num, this.end - pos);

	if (num > 0)
	{
		this.req = null;
		var samehost = (baseurl.indexOf('http://') < 0 || baseurl.indexOf(window.location.hostname) > 0);

		if (retry)
			baseurl += (baseurl.match(/\?/) ? "&" : "?") + "retry=" + retry;

		if (samehost) // use XMLHTTP request only if on same host
		{
			try
			{
				this.req = new XMLHttpRequest();
			}
			catch (e)
			{
				var classnames = [ 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ];

				for (var i in classnames)
				{
					try
					{
						this.req = new ActiveXObject(classnames[i]);
						break;
					}
					catch (e) { }
				}
			}

			try
			{
				this.req.open(this.method, baseurl + (baseurl.match(/\?/) ? "&" : "?") + "pos=" + pos + "&num=" + num + (query_volatile ? query_volatile : ""));
				var me = this;
				this.req.onreadystatechange = function() { me.readyStateChanged(); }
				var workingxmlhttp = this.req.onreadystatechange;

				if (!workingxmlhttp)    /* Old Konqueror */
					this.req = null;
			}
			catch (e) { }
		}

		this.starttime = new Date().getTime();
		this.retry = retry;

		if (this.req)
		{
			if (this.method == "POST")
				this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

			this.req.send(this.post_data);
		}
		else
		{
			var req_url = baseurl + (baseurl.match(/\?/) ? "&" : "?") + "pos=" + pos + "&num=" + num + (query_volatile ? query_volatile : "") + (this.post_data ? '&' + this.post_data : "") + "&itjs_call=it_loader.__inst" + this.instance + "&itjs_callid=" + ++this.callid;

			if (samehost || (window.opera && !window.XMLHttpRequest))  // Opera 7 only works with iframes
			{
				var scrpt = document.createElement("iframe");
				scrpt.style.width = scrpt.style.height = 1;
				req_url += "&itjs_iframe=1";
			}
			else
				var scrpt = document.createElement("script");

			it_loader['__inst' + this.instance] = this;
			this.req = { starttime: this.starttime, retry: retry };
			try
			{
				this.scrpt[this.callid] = scrpt;
				if (!document.all) scrpt.src = req_url;
				document.body.appendChild(scrpt);
				if (document.all) scrpt.src = req_url;
			}
			catch (e) { return false; }
		}
	}
	else
		this.handler.render(this);

	return true;
},

post: function(baseurl, data)
{
	this.clear();
	this.method = "POST";
	this.baseurl = baseurl;
	this.start = this.end = 10;
	this.post_data = "";

	if (typeof data == 'object')
	{
		for (var k in data)
			this.post_data += (this.post_data ? "&" : "") + k + "=" + it_url_encode(data[k]);
	}
	else
		this.post_data = data;

	this.load(baseurl, 0, 1, "");
},

readyStateChanged: function()
{
	var req = this.req;	// Avoid race conditions
	it_loader.sequence += "r";

	if (req && (req.readyState == 4))
	{
		var data = null;

		try
		{
			if (req.responseText != "")
				data = eval("(" + req.responseText + ")");
		}
		catch (e)
		{
			var retry = this.retry + 1;

			if (retry < 10)
				it_timer({ object: this, method: "retryload", timeout: Math.pow(5, Math.min(retry, 5)), baseurl: this.baseurl, pos: this.pos, num: this.num, query_volatile: this.query_volatile, retry: retry });
			else
				ED(e, req.responseText);
		}

		if (data)
			this.dataReady(data, this.callid);
	}
},

retryload: function(p)
{
	this.load(p.baseurl, p.pos, p.num, p.query_volatile, p.retry);
},

dataReady: function(data, callid)
{
	var fixkonqueror33gcbug = this.req;
	var loadtime = new Date().getTime() - this.starttime;
	it_loader.sequence += "e";

	this.req = null;

	if ((typeof data == "object") && (this.callid == callid))
	{
		this.attr = { loadtime: loadtime };

		for (var key in data)
		{
			var value = data[key];
			var id = key - 0;

			if (!isNaN(id))
			{
				this.start = Math.min(this.start, id);
				this.end = Math.max(this.end, id + 1);
				this.entry[id] = data[key];
			}
			else
				this.attr[key] = data[key];
		}

		if (this.attr.eof)
			this.attr.num = this.end;	/* Fix bogus # of result value */

		this.handler.render(this);

		if (!this.attr.eof && (this.end < this.pos + this.num))
			this.load(this.baseurl, this.end, this.pos + this.num - this.end);

		it_loader.sequence += "h";
	}

	this.unlink(callid);
},

stop: function()
{
	try
	{
		this.req.abort();
	}
	catch (e) { }

	this.unlink(this.callid);
},

unlink: function(callid)
{
	if (it_loader['__inst' + this.instance] && callid == this.callid)
		it_loader['__inst' + this.instance] = null;

	if (this.req)
		this.req = null;

	if (this.scrpt[callid])
	{
		if (!(document.all && String(navigator.userAgent).indexOf('MSIE 5.0') > 0))
			document.body.removeChild(this.scrpt[callid]);
		this.scrpt[callid] = null;
	}

	it_loader.sequence = "";
} /* NO COMMA */

}

// static properties
it_loader.instances = 0;
it_loader.sequence = "";