/*****************************************************
 *                    AJAXcon                        *
 *****************************************************
 * Class for ajax XML processing                     *
 *                                                   *
 * Handles XML files in tickler's XML format:        *
 * Valid XML file should contain a "status" tag      *
 * <status code="[status_code]">[status_msg]</stats> *
 * and all returned information should be sent as    *
 * <var name="[name]" type="[type]">[value]</var>    *
 * where all variables are enclosed by a             *
 * <variables>[list of var tags]</variables> tags.   *
 *                                                   *
 * Use tickler's PHP-based AJAX-XML creator to       *
 * make the XML files in the most reliable way.      *
 *****************************************************
 * (C) Created by tickler (sp.tickler@gmail.com)     *
 *     All Rights Reserved                           *
 *****************************************************/

function AJAXcon (d_url, d_vars, d_onload, d_asyn, d_uname, d_upass, d_headers)
{
	if( d_url == undefined )	d_url     = "";
	if( d_asyn == undefined )	d_asyn    = true;
	if( d_uname == undefined )	d_uname   = "";
	if( d_upass == undefined )	d_upass   = "";

	var xmlHttp = null;
	try
	{	// IE 7+, FF, Opera 8+, Safari
		xmlHttp = new XMLHttpRequest();
	}
	catch( e )
	{
		try
		{	// IE5, IE6
			xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch( e )
		{
			try
			{	// Some old browsers
				xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch( e )
			{	// sorry...
				alert("Your browser doesn't support AJAX");
			}
		}
	}
	this.xmlHttp = xmlHttp;

	this.is_busy = false;


	this.Get = function (url, vars, onload, asyn, uname, upass, headers)
	{
		var xmlHttp = this.xmlHttp;

		if( url == undefined )      url     = d_url;
		if( vars == undefined )     vars    = d_vars;
		if( onload == undefined )   onload  = d_onload;
		if( asyn == undefined )     asyn    = d_asyn;
		if( uname == undefined )    uname   = d_uname;
		if( upass == undefined )    upass   = d_upass;
		if( headers == undefined )  headers = d_headers;

		var params = "";
		for( var i in vars )
			params += (params==""?"":"&")+URLencode(i)+"="+URLencode(vars[i]);

		url = url + ( url.indexOf("?")==-1 ? "?" : "&") + params;

		if( this.is_busy )
		{
			xmlHttp.onreadystatechange = function(){};
			xmlHttp.abort();
		}
		this.is_busy = true;

		xmlHttp.open( "GET", url, asyn, uname, upass );

		if( headers == undefined )
			headers = defaultHeaders( params );
		if( headers != undefined )
			for( var i in headers )
				xmlHttp.setRequestHeader( i, headers[i] );

		var handler = makeHandler( onload );
		if( asyn )
			xmlHttp.onreadystatechange = handler;

		xmlHttp.send( null );

		if( !asyn )
			handler();
	};


	this.Post = function (url, vars, onload, asyn, uname, upass, headers)
	{
		var xmlHttp = this.xmlHttp;

		if( url == undefined )      url     = d_url;
		if( vars == undefined )     vars    = d_vars;
		if( onload == undefined )   onload  = d_onload;
		if( asyn == undefined )     asyn    = d_asyn;
		if( uname == undefined )    uname   = d_uname;
		if( upass == undefined )    upass   = d_upass;
		if( headers == undefined )  headers = d_headers;

		var params = "";
		for( var i in vars )
			params += (params==""?"":"&") + URLencode(i) + "=" + URLencode(vars[i]) ;

		if( this.is_busy )
		{
			xmlHttp.onreadystatechange = function(){};
			xmlHttp.abort();
		}
		this.is_busy = true;

		xmlHttp.open( "POST", url, asyn, uname, upass );

		if( headers == undefined )
			headers = defaultHeaders( params );
		if( headers != undefined )
			for( var i in headers )
				xmlHttp.setRequestHeader( i, headers[i] );

		var handler = makeHandler( onload );
		if( asyn )
			xmlHttp.onreadystatechange = handler;

		xmlHttp.send( params );

		if( !asyn )
			handler();
	};


	defaultHeaders = function (vars)
	{
		var a = new Array();
		a["Content-Type"]   = "application/x-www-form-urlencoded; charset=utf-8";
//		a["Content-Length"] = vars.length;
//		a["Connection"]     = "close";
		return a;
	};


	function makeHandler (onload)
	{
		return function ()
		{
			switch( xmlHttp.readyState )
			{
				case 0: // not initialized
					break;

				case 1: // ready
					break;

				case 2: // sent
					break;

				case 3: // in process
					break;

				case 4: // complete
					this.is_busy = false;
					if( onload )
						onload( new Response(xmlHttp) );
					break;

				default: // should never happen
					alert( "Critical failure" );
			}
		}
	}
}


function URLencode (str)
{
	return encodeURIComponent(str);
}
function URLdecode (str)
{
	return decodeURIComponent(str);
}



function Response (xmlHttp)
{
	this.isOK = function ()
	{
		return this.status == 0;
	}
	this.isXML = function ()
	{
		return this.xml;
	}
	this.getStatus = function ()
	{
		return this.status;
	}
	this.getMSG = function ()
	{
		return this.msg;
	}
	this.getVars = function ()
	{
		return this.vars;
	}
	this.getVar = function (name)
	{
		return this.vars[name];
	}
	this.getHeader = function (name)
	{
		return this.headers[name];
	}
	this.getFullText = function ()
	{
		return this.text;
	}


	if( !xmlHttp )
	{
		this.status  = 1;
		this.msg     = "No xmlHttpRequest Object";
		this.xml     = false;
		this.vars    = new Array();
		this.headers = new Array();
		this.text    = "";
		return this;
	}

	this.status  = 0;
	this.msg     = "Status not specified";
	this.xml     = false;
	this.vars    = new Array();
	try
	{
		this.headers = getHeaders( xmlHttp.getAllResponseHeaders() );
	}
	catch( e )
	{
		this.status = 0;
		this.msg = "Connection aborted";
		this.headers = new Array();
		this.text = "";
		return this;
	}
	this.text    = xmlHttp.responseText;

	if( xmlHttp.status != 200 )
	{
		this.status = xmlHttp.status;
		this.msg    = xmlHttp.statusText;
		return this;
	}

	var doc = xmlHttp.responseXML;

	if( !doc )
	{
		this.status = 0;
		this.msg    = "Response is not a valid XML document";
		return this;
	}

	this.xml = true;

	var stat = doc.getElementsByTagName("status")[0];
	if( stat )
	{
		this.status = parseInt( stat.getAttribute("code") );
		this.msg    = getText( stat );
	}

	this.vars = getVars( doc );


	/*
	 * Helper Functions
	 ******************
	 */

	function getVars (doc)
	{
		var var_objs = getChildren( doc.getElementsByTagName("variables")[0] );
		var vars = new Array();

		for( var i=0; i<var_objs.length; i++ )
		{
			v = var_objs[i];
			if( v.tagName == "var" )
				vars[ v.getAttribute("name") ] = makeVarType( v );
		}

		return vars;
	}

	function getHeaders (str)
	{
		if( str == null )
			return new Array();

		var arr = str.split( "\r\n" );
		var headers = new Array();

		for( var i=0; i<arr.length; i++ )
		{
			var sep  = arr[i].indexOf(": ");
			var name = arr[i].substr( 0, sep );
			var val  = arr[i].substr( sep+2 );
			headers[ name ] = val;
		}

		return headers;
	}

	function getChildren (obj)
	{
		if( !obj )
			return new Array();

		var arr = new Array();
		var children = obj.childNodes;

		for( var i=0; i<children.length; i++ )
			if( children[i].nodeType == 1 )
				arr.push( children[i] );

		return arr;
	}

	function getText (obj)
	{
		var text = obj.text;
		var textCont = obj.textContent
		if( typeof(textCont) == "undefined" || textCont == null )
			return text;
		return textCont;
	}

	function makeVarType (v)
	{
		var type = v.getAttribute("type").toLowerCase();
		var str  = getText(v);
		switch( type )
		{
			case "array":
				var arr = new Array();
				var children = getChildren( v );
				for( var i=0; i<children.length; i++ )
				{
					var child = children[i];
					var name = child.getAttribute("name");
					if( child.tagName == "var" )
						arr[ name ] = makeVarType( child );
				}
				return arr;

			case "int":
				return parseInt( str );

			case "bool":
				return str!="" && str!="0" && str.toLowerCase()!="false";

			case "null":
				return null;

			case "string":
				try{
					str = URLdecode( str );
				}catch(e){alert( "AJAX check: " + str);}

			default:
				return str;
		}
	}
}

