function define(varName,value)
{
	window[varName]=value;
}

//document.write('<script type="text/javascript" src="./eajax.cfg"></script>');//Loading config (config file should be placed in the same folder, in which there is html-doc requiring this script file OR you should manually add including the config script file)

function urlEncode(uri)
{
	return uri.toString().replace(/[^\w.\-\/]/g,function (ch) {var hex=ch.charCodeAt(0).toString(16); return '%u'+'000'.substring(0,4-hex.length)+hex;});
}

function Channel()
{
	this.free  =true;
	this.func  =null;
	this.xhr   =null;
	this.iframe=null;
	this.form  =null;
}

var sChans=new Array(),dChans=new Array();//static and dynamic channels

function res(val,wnd)
{
	if (!wnd.name) return;//stupid MSIE call this function twice when form submitted
	document.getElementById(wnd.name).channel.func(val);
}

/*  Parameters decription:

	uri:  request URI to which requests will be made; specify address of php-script, which process the request
	vars: {container | Array of containers}; container: {HTML Form Element | form name ("string") | assoc array ("object")}
	func: callback function (may be "eval" or null/undefined for JSON concept)
	channel: id Number for the channel or null/undefined for dynamic request channel creation; sending request via the sertain channel, in which prev request haven't finished, will override that prev request by stop them before starting new request
*/
function eajaxRequest(uri,vars,func,channel)
{
	if (typeof vars != 'object')
	{
		alert('eajaxRequest: Wrong parameter <vars>');
		return;
	}

	if (func==eval || func==null) func=function (val) {return eval(val)};//for stupid Opera

	if (vars.length==null || vars.elements!=null) vars=[vars];//making var Array of containers

	//Preparing query string
	var params=new Array();

	for (var i=0;i<vars.length;i++)
		switch (typeof vars[i])
		{
		case 'string':
			vars[i]=document.forms[vars[i]];
			i--;
			continue;
		case 'object':
			if (vars[i].nodeType==1 && typeof(vars[i].tagName)=='string' && vars[i].tagName.toLowerCase()=='form')//HTML Form Element
			{
				for (var j=0,els=vars[i].elements;j<els.length;j++)
					if (els[j].name) with (els[j])
						params.push({name : name,
									 value : value,
									 toString : function () {return urlEncode(this.name)+'='+urlEncode(this.value)}});
			}
			else//assoc array
			{
				for (p in vars[i])
					params.push({name : p,
								 value : vars[i][p],
								 toString : function () {return urlEncode(this.name)+'='+urlEncode(this.value)}});
			}
			break;
		default:
			alert('eajaxRequest: Wrong element in <vars> array');
			return;
		}

	var qstring=params.join('&');

	//Prepairing channel to transfer
	var chan;
	if (channel!=null)//take a static channel
	{
		if (sChans[channel]==null)
		{
			chan=new Channel();
			sChans[channel]=chan;
		}
		else
		{
			chan=sChans[channel];
			if (!chan.free && chan.xhr)
				chan.xhr.abort();
		}
	}
	else//allocate dynamic channel
	{
		for (var i=0;i<dChans.length;i++)
			if (dChans[i].free)
			{
				chan=dChans[i];
				break;
			}

		if (i==dChans.length)
		{
			chan=new Channel();
			dChans.push(chan);
		}
	}

	chan.free = false;
	chan.func = func;

	function allocIFrame()
	{
		do var r=parseInt(Math.random()*1000000);
		while (window['iframe'+r]);

		var el=document.createElement('iframe');
		el.style.display='none';
		el.id=el.name='iframe'+r;
		document.getElementsByTagName('body')[0].appendChild(el);

		setTimeout(function ()//OnLoad event should be set up just when element would be created and page 'about:blank' loaded
		{
			el.onload=el.onload_=function ()//stupid MSIE
			{
			    //Здесь this везде заменено на el, т.к. Эксплорер чё-то глючит
				el.channel.free=true;
				clearTimeout(el.timeout);

				//_-This is need no more-_
	            //if (window[this.name].location.href=='about:blank') return;

				if (window[el.name].document.getElementsByTagName('meta').length) return;//primitive test for 404 error
				
				//Check for error messages
				var mes='';
				function traverseNode(node)
				{
					switch (node.nodeType)
					{
					case 1://ELEMENT_NODE
					    switch (node.tagName.toLowerCase())
					    {
						case 'br':
							mes+='\n';
							break;
						case 'script':
						    break;
						default:
							for (var i=0;i<node.childNodes.length;i++)
								traverseNode(node.childNodes[i]);
						    break;
						}
						break;
					case 3://TEXT_NODE
						mes+=node.data;
						break;
					}
				}
				var node=window[el.name].document.documentElement;
				//Нужно на случай, когда сервер ничего не возвращает (в этом случае в Опере node=null)
				if (node) traverseNode(node);//.getElementsByTagName('body')[0]);
				if (mes) alert(mes);
			};
			el.onreadystatechange=function () {if (this.readyState=='complete') this.onload_()};//for MSIE
		},0);

		/*window[el.name].res=func;

		if (channel==null)//dynamic channel
			el.timeout=setTimeout(function () {chan.free=true;},EAJAX_REQUEST_TIMEOUT),
			el.channel=chan;*/

		return el;
	}

	if (uri.length+1+qstring.length>512)//Use XmlHttpRequest
	{
		var req = chan.xhr;//false;

		if (!req)
		{
			// branch for native XMLHttpRequest object
			if (window.XMLHttpRequest)
			{
				try {req = new XMLHttpRequest();}
				catch (e) {req = false;}
			}
			else if (window.ActiveXObject)// branch for IE/Windows ActiveX version
			{
				try {req = new ActiveXObject("Msxml2.XMLHTTP");}
				catch (e)
				{
					try {req = new ActiveXObject("Microsoft.XMLHTTP");}
					catch (e) {req = false;}
				}
			}
		}

		if (req)
		{
			chan.xhr = req;
			//req.channel = chan;
			if (channel==null)//dynamic channel
				var timeout=setTimeout(function () {req.abort(); chan.free=true;},EAJAX_REQUEST_TIMEOUT);

			req.onreadystatechange = function ()
			{
				// only if req shows "loaded"
				if (req.readyState == 4)
				{
					// only if "OK"
					if (req.status == 200)
					{
						// ...processing statements go here...
						var parent={res:func};//simple work around
						//function res(val) {this.channel.func(val)}
						var mes=req.responseText.replace(/<script>([^<]*)<\/script>/gi,function(str,code) {eval(code); return ''});
						if (mes) alert(mes.replace(/<br[^>]*>/gi,'\n'));//possibly it is errors message
						chan.free = true;
						clearTimeout(timeout);
					} else {
						alert("There was a problem retrieving the XML data:\n"+req.statusText);
					}
				}
			};
			req.open("POST", uri, true);
			if (!window.opera || req.setRequestHeader)//for Opera 8.0
				req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			//req.overrideMimeType('text/plain');
			req.send(qstring);
		}
		else//Form submit
		{
			if (!chan.iframe) chan.iframe=allocIFrame();
			setTimeout(function ()
			{
				with (window[chan.iframe.name])
					/*self.res=func,*/self.name=chan.iframe.name;
			},0);

			chan.iframe.channel=chan;
			if (channel==null)//dynamic channel
				chan.iframe.timeout=setTimeout(function () {chan.free=true;},EAJAX_REQUEST_TIMEOUT);

			if (chan.form) chan.form.parentNode.removeChild(chan.form);

			with (chan.form=document.createElement('form'))
				style.display='inline',action=uri,target=chan.iframe.name,method='post';

			for (var i=0;i<params.length;i++)
			{
				var el=document.createElement('input');
				el.type='hidden';
				el.name=params[i].name;
				el.value=params[i].value;
				chan.form.appendChild(el);
			}

			document.getElementsByTagName('body')[0].appendChild(chan.form);
			setTimeout(function () {chan.form.submit();},0);
		}
	}
	else//Use iframe.replace method, i.e. via GET
	{
		//alert(uri+'?'+qstring);
		if (!chan.iframe) chan.iframe=allocIFrame();
		setTimeout(function ()
		{
			with (window[chan.iframe.name]) {
				/*self.res=func,*/ //self.name=chan.iframe.name,location.replace(uri+'?'+qstring);
				if(uri.search(/\?/i) == -1) xlink = '?';
				else xlink = '&';
				self.name = chan.iframe.name,location.replace(uri+xlink+qstring);
			}
		},0);

		chan.iframe.channel=chan;
		if (channel==null)//dynamic channel
			chan.iframe.timeout=setTimeout(function () {chan.free=true;},EAJAX_REQUEST_TIMEOUT);
	}
}
