/**
 * @filename AJAXConnector.class.js
 * @fileoverview Class to send and get data using XMLHTTPRequest
 * @author hsgries
 */


/**
 * Class to send data to and receive from server via xmlhttpquest
 * @constructor
 * @class <code><strong>Example:</strong><br />
 * var objConnect = new AJAXConnector( );<br /><br />
 * objConnect.registerDataHandler( handleRequest );<br />
 * <br />
 * function updateData( intDataId )<br />
 * {<br />
 *     objConnect.sendRequest( "updater.php", "data=" + intDataId, AJAXConnector.REQUEST_GET );<br />
 * }<br /><br />
 * function handleRequest( strRequest, intID )<br />
 * {<br />
 *     alert( strRequest );<br />
 * }</code><br /><br />
 * Note: if you need multiple concurrent requests, you must initiate a
 * new instance of AJAXConnector for any concurrent request. For Example, if you
 * want to update the content of four data tables at the same time, you need 4
 * different instances of AJAXConnector. If you want to update the tables
 * one after the other, you need only one instance of AJAXConnector.<br />
 */
function AJAXConnector( )
{
	// public methods

	/**
	 * Sends a http request to server
	 * @param String, datasource on server, e.g. data.php
	 * @param String, query string to send to server, optionally
	 * @param int, request type, possible values: REQUEST_GET, REQUEST_POST, REQUEST_XML, REQUEST_HEAD default REQUEST_GET
	 * @param mix, param, e.g. array , will be handed over to registered callback function, optionally
	 * @return Query string
	 * @type String
	 */
	this.sendRequest = function( strReqHandler, strQuery, intReqType, mixParam )
		{
			if( !this._p_funcData )
			{
				if( this._p_handleData( ) )
					return;
			}

			if( !strQuery )
			{
				strQuery = '';
			}

			// default type (0 = GET, 1 = xml, 2 = POST )
			if( isNaN( intReqType ) )
			{
				intReqType = 0; // GET
			}

			// previous request not finished yet, abort it before sending a new request

			if( this._p_xmlHttp && this._p_xmlHttp.readyState )
			{
				this._p_stopMonitor(false);
				this._p_xmlHttp = false;
			}

			// create a new instance of xmlhttprequest object
			// if it fails, return
			if( !this._p_xmlHttp )
			{
				this._p_getReqObject( );
				if( !this._p_xmlHttp )
					return;
			}
			// parse query string
			if( intReqType != 1 && ( strQuery.length && strQuery.substr( 0, 1 ) == '&' || strQuery.substr( 0, 1 ) == '?' ) )
				strQuery = strQuery.substring( 1, strQuery.length );
			// data to send using POST
			var dataReturn = strQuery ? strQuery : strReqHandler;
			switch( intReqType )
			{
				case 1:	// xml
					strQuery = "xml=" + strQuery;
				case 2: // POST
					// open the connection
					this._p_xmlHttp.open( "POST", strReqHandler, true );
					this._p_xmlHttp.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
					this._p_xmlHttp.setRequestHeader( 'Content-length', strQuery.length );
					break;
				case 3: // HEAD
					// open the connection
					this._p_xmlHttp.open( "HEAD", strReqHandler, true );
					strQuery = null;
					break;
				default: // GET
					// open the connection
					var strDataFile = strReqHandler + (strQuery ? '?' + strQuery : '' );
					this._p_xmlHttp.open( "GET", strDataFile, true );
					strQuery = null;
			}

			this._p_param = null;
			if(mixParam) {
				this._p_param = mixParam;
			}

			// set onload data event-handler
			this._p_xmlHttp.onreadystatechange = new Function( "", "window[\"" + this._p_id + "\"]._p_processResponse()" );

			// send request to server
			try
			{
				if( this._p_intTimeout )
					this._p_startMonitor( );
				this._p_xmlHttp.send( strQuery );	// param = POST data
			}
			catch( excSendError )
			{
				this._p_handleData( null, null, AJAXConnector.ERRID_SEND, ERR_SEND );
			}

			return dataReturn;
		};


	/**
	 * Given function to handle data from server
	 */
	this._p_funcData;

	/**
	 * Registers the data handler function
	 * The data handler function is given two to four parameters:<br />
	 * Param 1: the data from server. If data are type of XML, they will given
	 * as XML data, else as text data.<br />
	 * Param 2: integer indicating response is text or xml<br />
	 * Param 3: the id of this request if an id was given to the {@link #sendRequest} method<br />
	 * Param 4: the param data set with {@link #registerUserParam} method
	 * @param Function, Function to handle the requested data from server
	 */
	this.registerDataHandler		= function ( funcData ) {	this._p_funcData = funcData; };


	/**
	 * Max execution time for this request, 0 means infinite
	 */
	this._p_intTimeout				= 0;

	/**
	 * Sets max execution time for this request
	 * @param int, Time in milliseconds
	 */
	this.setMaxRequestTime			= function ( intTimeout ) { this._p_intTimeout = intTimeout; };




	// private methods

	/**
	 * An instance of the XMLHttpRequest object
	 */
	this._p_xmlHttp					= null;

	/**
	 * Instantiates a new xmlhttprequest object
	 * @return XMLHttpRequest-Object or false
	 * @type XMLHttpRequest | boolean
	 */
	this._p_getReqObject = function( )
		{

			// Mozilla, Opera und Safari
			try
			{
				this._p_xmlHttp = new XMLHttpRequest();
			}
			catch( excW3C )
			{
				// Internet Explorer
				for( var i = 5; i; i-- )
				{
					try
					{
						// loading of a newer version of msxml dll (msxml3 - msxml5) failed
						// use fallback solution
						// old style msxml version independent, deprecated
						if( i == 2 )
						{
							this._p_xmlHttp = new ActiveXObject( "Microsoft.XMLHTTP" );
						}
						// try to use the latest msxml dll
						else if( i > 2 )
						{
							this._p_xmlHttp = new ActiveXObject( "Msxml2.XMLHTTP." + i + ".0" );
						}
						// loading of xmlhttp object failed
						else
						{
							this._p_handleError( null, null, AJAXConnector.ERRID_LOAD, ERR_LOAD );
						}
						break;
					}
					catch( excNotLoadable )	{}
				}
			}
		};


	/**
	 * Process the response data from server
	 * @param int, ID of this response
	 */
	this._p_processResponse = function( )
		{
			// status 0 UNINITIALIZED open() has not been called yet.
			// status 1 LOADING send() has not been called yet.
			// status 2 LOADED send() has been called, headers and status are available.
			// status 3 INTERACTIVE Downloading, responseText holds the partial data.
			// status 4 COMPLETED Finished with all operations.

			switch( this._p_xmlHttp.readyState )
			{
				// uninitialized
				case 0:
				// loading
				case 1:
				// loaded
				case 2:
				// interactive
				case 3:
					break;
				// complete
				case 4:
					// check http status
					if( this._p_xmlHttp.status == 200 || this._p_xmlHttp.status == 304 )	// success
					{
						// stop connection monitor
						this._p_stopMonitor(false, true);
						if( this._p_xmlHttp.responseXML && this._p_xmlHttp.responseXML.childNodes.length )
							this._p_handleData( this._p_xmlHttp.responseXML, AJAXConnector.RESPONSE_XML, AJAXConnector.SUCCID_LOAD, SUCC_LOAD );
						else {
							this._p_handleData( this._p_xmlHttp.responseText, AJAXConnector.RESPONSE_TEXT, AJAXConnector.SUCCID_LOAD, SUCC_LOAD );
						}
					}
					// loading not successfull, e.g. page not available
					else
					{
						if( !this._p_xmlHttp.status )
							this._p_handleData( null, null, AJAXConnector.ERRID_SEND, ERR_SEND );
						else
							this._p_handleData( null, null, this._p_xmlHttp.status, this._p_xmlHttp.statusText );
					}
			}
		};


	/**
	 * Shows an error message or calls a given error handler
	 * @param String, data from server as xml or plain text
	 * @param String, constant for xml or plain text (AJAXConnector.RESPONSE_XML | AJAXConnector.RESPONSE_TEXT)
	 * @param int, id of current request
	 * @param mix, param set by method 'registerUserParam'
	 * @param int, error id or http status
	 * @param String, error message or http status message
	 */
	this._p_handleData = function( objResponseData, intType, intStatusID, strStatusMsg )
		{
			if( this._p_funcData )
			{
				this._p_funcData( intStatusID, objResponseData, this._p_param, intType, strStatusMsg );
			}
			else
			{
				alert( "Error-ID: " + AJAXConnector.ERRID_NOHANDLER + "\nError-Message: " + ERR_NOHANDLER );
			}
		};

	/**
	 * Starts the timeout monitor
	 */
	this._p_startMonitor = function ( ) { this._p_to = setTimeout( "window[\"" + this._p_id + "\"]._p_stopMonitor( true )", this._p_intTimeout ); };

	/**
	 * Stops the timeout monitor
	 */
	this._p_stopMonitor = function( blnIsTimeout, blnStopTimer )
		{
			if(!blnStopTimer) {
				this._p_xmlHttp.onreadystatechange = function () {}
				this._p_xmlHttp.abort( );
			}

			if( blnIsTimeout && this._p_xmlHttp && this._p_xmlHttp.readyState)
			{
				this._p_handleData( null, null, AJAXConnector.ERRID_TIMEOUT, ERR_TIMEOUT );
			}
			else if(this._p_to)
			{
				clearTimeout( this._p_to );
				this._p_to = null;
			}
		};



	// status messages and id's
	var SUCC_LOAD					= "Fetching of data successfully finished";
	var ERR_LOAD					= "Unable to instantiate XMLRequestObject";
	var ERR_NOHANDLER				= "No Data-Handler registered";
	var ERR_SEND					= "Unable to send data. Is your Computer connected to the internet?";
	var ERR_TIMEOUT					= "Operation timed out.";

	var _p_param					= null;

	// set this instance as global variable to pass to predifined functions ( e.g. event handler )
	this._p_id						= "ajax@" + Math.random( ) * Math.pow( 10, 18 );
	window[ this._p_id ]			= this;

}


// Constants

/**
 * Constant indicates that data may be send using the get method
 * @type int
 * @final
 */
AJAXConnector.REQUEST_GET	= 0;

/**
 * Constant indicates that data may be send using the post method
 * @type int
 * @final
 */
AJAXConnector.REQEST_POST	= 2;

/**
 * Constant indicates that data may be send using the post method
 * this may be used to get only the modified date of a file
 * @type int
 * @final
 */
AJAXConnector.REQUEST_HEAD	= 1;

/**
 * Constant indicates that xml data may be send
 * Param name 'xml' will be added by program, so query string may be: '?xml=&lt;sender&gt;Dr.Watson&lt;/sender&gt;...'
 * @type int
 * @final
 */
AJAXConnector.REQUEST_XML	= 3;

/**
 * Constant indicates that response type is xml
 * @type int
 * @final
 */
AJAXConnector.RESPONSE_XML	= 1;

/**
 * Constant indicates that response type is text
 * @type int
 * @final
 */
AJAXConnector.RESPONSE_TEXT	= 2;


/**
 * Request successfully finished
 * @type int
 * @final
 */
AJAXConnector.SUCCID_LOAD					= 0;

/**
 * XMLRequestObject could not be instantiated
 * @type int
 * @final
 */
AJAXConnector.ERRID_LOAD					= 1;

/**
 * No data handler registered
 * @type int
 * @final
 */
AJAXConnector.ERRID_NOHANDLER				= 2;

/**
 * Data could not be sent to server
 * @type int
 * @final
 */
AJAXConnector.ERRID_SEND					= 3;

/**
 * Request last too long
 * @type int
 * @final
 */
AJAXConnector.ERRID_TIMEOUT				= 4;


