/*
	Title: JsoxAJAX class (jsoxAJAX.js)

	Details:
		Version			-	1.0
		Release date	-	19.03.2007
		Author			-	Lukasz Karpuc
		Contact			-	lukaszkarpuc@gmail.com
*/

/*
	Class: JsoxAJAX

		Enables easy asynchronous connection beetween HTTP client and server

	Example use:

	(start code)
	<form id="formularz">
		<input name="tekst" value="" /><br />
	</form>

	<input type="checkbox" id="ch" name="czek" /><br />

	<div id="text"></div>

	<script language="javascript" type="text/javascript">

		AXobj.post({
			url: "test.php",
			onFatalError: function( obj ) { alert('FatalError');},
			onInit: function( obj ) { alert('Initialized');},
			onComplete: function( obj ) { alert('Completed');},
			virtualFrame: 'text',
			parameters: { "tab[0]" : "a", "tab[1]" : "b" },
			elements: [ 'formularz', 'czek' ],
			formPrefix: true
		});
	</script>
	(end)
*/

var JsoxAJAX = function()
{

	/*
		Object: params

			Contains default request parameters. See <makeRequest()> for the list of available params.
	*/
	this.params = null;

	/*
		Function: elementToQuery()

			Creates query part of URL address due to HTML element passed by parameter.

		Parameters:

			obj - HTML element to be converted
			formPrefix - boolean value determining wheter prefix should be used or not
			prefix - prefix for in-query variable name

		Returns:

			Query corresponding to the element passed to function
	*/
	this.elementToQuery = function( obj, formPrefix, prefix )
	{
		prefix += '::';

		if ( obj.tagName == 'FORM' )
		{
			var el = obj.elements, tmp = new Array(), tmpel;

			for ( i = 0; i < el.length; i++ )
			{
				if ( formPrefix )
					tmpel = this.elementToQuery( el[i], formPrefix, obj.id );
				else
					tmpel = this.elementToQuery( el[i], formPrefix, '' );

				if ( tmpel != '' )
					tmp.push( tmpel );
			}

			return tmp.join( '&' );
		}
		else if ( obj.tagName == 'INPUT' )
		{
			if ( obj.type == 'checkbox' )
			{
				if ( !obj.checked )
					return '';

				return ( encodeURIComponent( prefix + obj.name ) + "=" + encodeURIComponent( obj.checked ) );
			}
			else if ( obj.type == 'radio' )
			{
				if ( !obj.checked )
					return '';

				return ( encodeURIComponent( prefix + obj.name ) + "=" + encodeURIComponent( obj.value ) );
			}
		}
		else if ( obj.tagName == 'SELECT' )
		{
			var el = obj.options;

			if ( obj.multiple )
			{
				var tmp = new Array();

				for ( var i = 0; i < el.length; i++ )
					if ( el[i].selected )
						tmp.push( encodeURIComponent( prefix + obj.name ) + '[' + i + ']' + "=" + encodeURIComponent( el[i].value ) );

				return tmp.join( '&' );
			}
			else
			{
				for ( var i = 0; i < el.length; i++ )
					if ( el[i].selected )
						return( encodeURIComponent( prefix + obj.name ) + "=" + encodeURIComponent( el[i].value ) );
			}
		}

		return ( encodeURIComponent( prefix + obj.name ) + "=" + encodeURIComponent( obj.value ) );
	}

	/*
		Function: elementsArrayToQuery()

			Creates query part of URL address from the elements (or elements' id) array.

		Parameters:

			arr - array of elements
			formPrefix - boolean value that stands for using (or not) prefices for FORM elements

		Returns:

			Query corresponding to the element passed to function
	*/
	this.elementsArrayToQuery = function( arr, formPrefix )
	{
		if ( typeof arr != 'object' )
			return '';

		var key, val, i, el, tmp = new Array(), tmpel;

		for ( key in arr )
		{
			val = arr[key];

			if ( typeof val == 'string' )
				val = document.getElementById( val );

			if ( typeof val == 'object' && val != null )
			{
				tmpel = this.elementToQuery( val, formPrefix, '' );

				if ( tmpel != '' )
					tmp.push( tmpel );
			}
		}

		return tmp.join('&');
	}

	/*
		Function: paramsArrayToQuery()

			Converts params array into url query string.

		Parameters:

			arr - array of parameters

		Returns:

			URL query string
	*/
	this.paramsArrayToQuery = function ( arr )
	{
		if ( typeof arr != 'object' )
			return '';

		var tmp = new Array();

		var key;

		for ( key in arr )
			tmp.push( encodeURIComponent( key ) + "=" + encodeURIComponent( arr[key] ) );

		return tmp.join( '&' );
	}

	/*
		Function: createAJAXObject()

			Creates XMLHttpRequest object in the browser-independent way.

		Returns:

			XMLHttpRequest object
	*/
	this.createAJAXObject = function()
	{
		var hr;

		if (window.XMLHttpRequest)
		{
		    // normalna przegladarka oraz IE7
		    hr = new XMLHttpRequest();
		}
		else if (window.ActiveXObject)
		{
		    // IE < 7
		    try
		    {
		        hr = new ActiveXObject("Msxml2.XMLHTTP");
		    }
		    catch (e)
		    {
		        try
		        {
		            hr = new ActiveXObject("Microsoft.XMLHTTP");
		        }
		        catch (e)
		        {}
		    }
		}

		return hr;
	}

	/*
		Function: makeRequest()

			Makes a request to HTTP server, using asynchronous communication

		Parameters:

			method - request method ('GET' or 'POST')
			par - request parameters

		Request parameters structure:

			Request parameters are contained by an object,
			which indices are parameters names and values are their values. For example:

			(start code)
			{
				url: "address.php",
				parameters: {  a: "1", b: "2" },
				onSuccess: function( obj ) { alert( 'ALL IS OK. Response text is: ' + obj.responseText ); }
			}
			( end)

		Available parameters:

			url 		- required; requested URL. Ex. url: 'test.php'
			parameters 	- url query parameters list; parameters will be attached to the URL.
					Ex. parameters: { first: "aaa", second: "bbb"} will produce first=aaa&second=bbb string.
					Arrays CANNOT be passed this way (they should be serialized before).
			formPrefix	- if set to true, to all form-elements will be added a prefix: the form id and ::, ex. x1::elementname.
			elements	- elements & element ids array. Values of listed elements will be attached to the URL.
					Ex.  elements: [ 'x1', document.getElementById('x2') ] attaches values (with names)
					of all elements contained by form x1 id and those contained by passed object (form with x2 id).
					If formPrefix is set and elements array is ['x1'], and there is an
					<form id="x1"><input name="y" /><input name="z" /></form> form
					on the page, the following url query will be created: x1::y=Yvalue&x1::z=Zvalue.
					The ":" should be encoded into %3F (":" code).
			virtualFrame - id of the element, that should be filled with a response text
			onFatalError - function; operations to be made when XMLHttpRequest object cannot be created.
					Takes one parameter: object with parameters used to make request. Ex.
					onFatalError: function( obj ) {alert('Request to '+obj.url+' cannot be done.');}
			onInit	- function; operations before request is sent. Takes one parameter (like all next functions):
					XMLHttpRequest object used in proccessed request. This object is extended with
					requestParameters attribute which is object with parameters used to make request.
			onSuccess	- function; operations to be made when server returns status 200. Takes one parameter (like onInit()).
			onFailure	- function; operations to be made when server returns status other than 200. Takes one parameter (like onInit()).
			onStatusXXX - function; operations to be made when server returns status XXX. Takes one parameter (like onInit()).
			onLoading	- function; operations to be made when loading status is reached by XMLHttpRequest object. Takes one parameter (like onInit()).
					Called twice on most popular browsers (Firefox, IE7).
			onLoaded	- function; operations to be made when loaded status is reached by XMLHttpRequest object. Takes one parameter (like onInit()).
					Not called on some browsers (Opera).
			onInteractive - function; operations to be made when interactive status is reached by XMLHttpRequest object. Takes one parameter (like onInit()).
			onComplete	- function; operations to be made when complete status is reached by XMLHttpRequest object. Takes one parameter (like onInit()).

		See also:

			<get()>
			<post()>
	*/
	this.makeRequest = function( method, par )
	{
		if ( par == null )
			par = new Object();

		// dopisuje domyslne parametry
		if ( this.params != null && typeof this.params == 'object' )
			for ( var i in this.params )
			{
				if ( typeof par[i] == 'undefined' )
				{
					if ( typeof this.params[i] == 'object' )
					{
						par[i] = new Object();
						for ( var j in this.params[i] )
							par[i][j] = this.params[i][j];
					}
					else
						par[i] = this.params[i];
				}
				else if ( typeof this.params[i] == 'object' )
					for ( var j in this.params[i] )
						if ( typeof par[i][j] == 'undefined' )
							par[i][j] = this.params[i][j];
			}

		if ( par.url == null || ( par.url != null && typeof par.url != "string" ) )
			return;

		var hr = this.createAJAXObject();

		if ( hr == null )
		{

			if ( typeof par.onFatalError == 'function' )
				par.onFatalError( par );

			return;
		}

		hr.onreadystatechange = function()
		{
			if ( typeof par.onEach == "function" )
				par.onEach( hr );

		    if ( hr.readyState == 4 )
		    {
				if ( typeof par.onComplete == "function" )
					par.onComplete( hr );

				if ( typeof par[ 'onStatus' + hr.status ] == 'function' )
					typeof par[ 'onStatus' + hr.status ]( hr );

		        if ( hr.status == 200 )
		        {
					if ( typeof par.onSuccess == "function" )
						par.onSuccess( hr );

					if ( typeof par.virtualFrame == "string" )
					{
						var vf = document.getElementById( par.virtualFrame );

						if ( vf != null )
							vf.innerHTML = hr.responseText;
					}
		        }
		        else
		        {
					if ( typeof par.onFailure == "function" )
						par.onFailure( hr );
		        }
		    }
			else if ( hr.readyState == 3 )
			{
				if ( typeof par.onInteractive == "function" )
					par.onInteractive( hr );
			}
			else if ( hr.readyState == 2 )
			{
				if ( typeof par.onLoaded == "function" )
					par.onLoaded( hr );
			}
			else if ( hr.readyState == 1 )
			{
				if ( typeof par.onLoading == "function" )
					par.onLoading( hr );
			}

			if ( typeof par.onEachEnd == "function" )
				par.onEachEnd( hr );
		}

		var query = ''

		if ( typeof par.elements == 'object' )
			query += this.elementsArrayToQuery( par.elements, par.formPrefix ) + '&';

		if ( typeof par.parameters == 'object' )
			query += this.paramsArrayToQuery( par.parameters );

		hr.requestParameters = par;

		if ( typeof par.onInit == "function" )
			par.onInit( hr );

		if ( method == 'GET' )
		{
			hr.open( 'GET', par.url + '?' +  query, true );
			hr.send( null );
		}
		else if ( method == 'POST' )
		{
			hr.open( 'POST', par.url, true );
			hr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			hr.send( query );
		}
	}

	/*
		Function: get()

			Makes a GET HTTP request. See <makeRequest()> description for parameters list.

		Parameters:

			par - request parameters

		See also:

			<post()>
			<makeRequest()>
	*/
	this.get = function ( par )
	{
		this.makeRequest( 'GET', par );
	}

	/*
		Function: post()

			Makes a POST HTTP request. See <makeRequest()> description for parameters list.

		Parameters:

			par - request parameters

		See also:

			<get()>
			<makeRequest()>
	*/
	this.post = function ( par )
	{
		this.makeRequest( 'POST', par );
	}
}