/*
 ----------------------------------------------------------------------------------------------------------------------------------------------
  XML HTTP REQUEST JAVASCRIPT WRAPPER!
 ==============================================================================================================================================
  VERSION 4.7
  LAST MODIFICATION ON 13.08.2009
  
  DEVELOPMENT NOTES:
  
  1.0   CONSTRUCTION OF A USABLE INSTANCE OF THE 'XML HTTP REQUEST' OBJECT:
        ADDED THE xhrCreateInstance METHOD ( THAT CAN HANDLE AROUND 66% OF THE WEB BROWSERS MARKET SHARE ).
        ADDED THE xhrSend AND xhrRead METHODS TO EASILY SEND AND RECEIVE DATA FROM THE WEB SERVER ( IN SYNCHRONOUS MODE ONLY ).
  1.1   FIXED THE MSIE 5.0 & 6.0 'XML HTTP REQUEST' OBJECT UNSUPPORT THROUGH 'ACTIVE X' ( HANDLE 23% MORE OF THE WEB BROWSERS MARKET SHARE ).
  
  2.0   CONSTRUCTION OF A WRAPPER TO SUPPORT 'XML HTTP REQUEST' OBJECT PROPERTIES ADDITION:
        ADDED SUPPORT OF THE ASYNCHRONOUS CONNECTIONS ( NOW AS DEFAULT ).
        ADDED THE xhrInbox METHOD TO ENABLE DATA DETECTION FOR ASYNCHRONOUS CONNECTIONS.
        ADDED THE json PROPERTY TO STORE INCOMING DATA ( PERFORMED VIA THE onreadystatechange PROPERTY ).
        IMPLEMENTED A SINGLETON METHOD xhrGetInstance BASED ON xhrCreateInstance ( MERGED INTO THE SINGLETON ).
  2.1   FIXED THE BUG THAT MADE MSIE REQUESTING FROM ITS INTERNAL CACHE ON 'GET' REQUESTS.
  2.2   FIXED THE BUG THAT MADE FIREFOX DROPPING SUPPORT OF THE onreadystatechange PROPERTY ( FOR SYNCHRONOUS CONNECTIONS ONLY ).
  
  3.0   CONSTRUCTION OF A WORKAROUND THROUGH A TIMER TO SIMULATE THE __defineSetter__ FUNCTION ( ONLY AVAILABLE ON THE LATEST WEB BROWSERS ):
        ADDED THE xhrDefineTimer AND xhrCancelTimer METHODS.
        DROPPED THE xhrInbox METHOD ( xhrRead AND xhrDefineTimer, ONCE LINKED TOGETHER, CAN HANDLE BOTH DATA DETECTION AND DATA ANALYSIS ).
        DROPPED ENTIRELY THE json STRING OBJECT ( IT IS NOW POSSIBLE TO USE INTERACTIVELY THE responseText PROPERTY THROUGH xhrRead ).
        DROPPED SUPPORT OF THE onreadystatechange PROPERTY.
        CHANGED THE xhrRead METHOD TO SUPPORT DATA ANALYSIS, INDEPENDENTLY OF THE KIND OF CONNECTION MODE ( SYNCHRONOUS OR ASYNCHRONOUS ).
  3.1   MERGED THE xhrDefineTimer AND xhrCancelTimer METHODS INTO ONE: xhrTrigger.
        CHANGED THE TIMER EXECUTION MODE FROM 'Interval' TO 'Timeout' ( SEE xhrTrigger NOTES FOR FURTHER EXPLANATIONS ).
  
  4.0   xhrWrapper CLASS PARTIALLY REWRITTEN FROM SCRATCH ( CODE WEIGHT DIVIDED BY 50% ):
        DROPPED THE xhrGetInstance METHOD THAT WAS FACING STRONG SCOPE LOSS WHEN COUPLED TO TRIGGERS.
        ADDED THE xhrThread METHOD THAT RETURN NEW INSTANCES OF THE 'XML HTTP REQUEST' OBJECT ( HANDLE 92% OF THE WEB BROWSERS MARKET SHARE ).
        CHANGED THE mpid PROPERTY TO INITIALIZE ITSELF WITH THE xhrThread METHOD ( NO LONGER DECLARED AS null ).
        MOVED THE xhrSend AND xhrRead METHODS INTO xhrSender AND xhrReader.
        DROPPED THE xhrTrigger METHOD ( FACING, AS xhrGetInstance AND xhrRead, SCOPE LOSS ).
        FIXED THE TRIGGERS SCOPE LOSS ( BEHAVIOUR TOTALLY REWRITTEN ) BUT THE TRIGGERS CAN NO LONGER BE INTEGRATED INTO THE xhrWrapper CLASS...
  4.1   IMPLEMENTED THE TRIGGERS WITHIN THE xhrWrapper CLASS.
  4.2   RESTORED THE onreadystatechange PROPERTY ( FROM VERSION 2.2 ) AND DROPPED THE xhrTrigger METHOD ( DEPRECATED BUT KEPT FOR HISTORY ).
        FIXED THE SCOPE ISSUE OF THE onreadystatechange PROPERTY VIA A DERIVATE OF VERSION 4.0 & 4.1 TRIGGERS.
  4.3   *** RELEASED AS "GOLD MASTER" ***
        CHANGED THE DATA FORMAT EXCHANGE WITH THE PARSER INTO 'Array' RATHER THAN 'String'.
        ADDED A PHYSICAL LINK BETWEEN THE LIBRARY ( THE xhrWrapper CLASS ) AND THE EXTERNAL PARSER!
  4.4   CHANGED THE LIBRARY LINK TO ABSTRACT. THE xhrWrapper CLASS CAN NOW HANDLE MORE THAN ONE EXTERNAL PARSER ( VIA THE callback ARGUMENT ).
  4.5   ADDED A DYNAMIC RESOURCE LOCATOR THROUGH THE TWO language AND resource PROPERTIES.
        FIXED AN ERROR ON THE 'POST' SWITCH-CASE WHERE THE data SENT WAS ALWAYS null ( xhrSender METHOD ).
  4.6   FIXED ANOTHER BUG THAT MADE MSIE REQUESTING FROM ITS INTERNAL CACHE ON 'GET' REQUESTS.
  4.7   CHANGED THE PARSING ALGORITHM FOR 'GET' REQUESTS WITHIN xhrSender ( IT NOW TAKES DOUBLE ARRAYS AS ARGS.: [[lbl,val],[lbl2,val2]] ).
        CHANGED SYNCHRONOUS AND ASYNCHRONOUS HANDLING IN BOTH xhrSender AND xhrReader METHODS ( SHOULD BE MORE ROBUST ).
 ----------------------------------------------------------------------------------------------------------------------------------------------
 */

/** -------------------------------------------------------------------------------------------------------------------------------------------
  * RETURN A NEW INSTANCE OF THE 'XML HTTP REQUEST' OBJECT.
  * object xhrThread( void )
  * 
  * DEPENDING ON THE CLIENT WEB BROWSER, THE OBJECT MUST BE DECLARED DIFFERENTLY! AS STATED BELOW, WITHIN THE CLASS DOCUMENTATION, FOR MSIE
  * 5.0 & 6.0 IT MUST BE IMPLEMENTED THROUGH AN 'ACTIVE X' WHILE FOR ALL THE OTHERS IT MUST BE DONE THROUGH THE 'XML HTTP REQUEST' OBJECT.
  * -------------------------------------------------------------------------------------------------------------------------------------------
  */
function xhrThread( )
{
	try { return new XMLHttpRequest( ); } // ... FOR FIREFOX, SAFARI, KONQUEROR, OPERA, NETSCAPE, ETC.!
	
	catch ( e ) // ... FOR INTERNET EXPLORER 5.0 & 6.0!
	{
		var brw = [ 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 
					'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 
					'Msxml2.XMLHTTP'    , 'Microsoft.XMLHTTP' ]; // ... STORE ALL THE MSIE 'ACTIVE X' XML HTTP REQUEST ID. USUALLY AVAILABLE!
		
		for ( var i in brw ) // ... FIX: TRY TO GET A VALID 'ACTIVE X' OBJECT. OTHERWISE TRY TO GET IT ON THE NEXT ATTEMPT.
			try { return new ActiveXObject( brw[i] ); }
			
			catch ( e ) { }
		
		// ... IF THIS POINT IS REACHED, THEN IT MEANS THE CLIENT WEB BROWSER DOES NOT SUPPORT THE 'XML HTTP REQUEST' OBJECT! ( PRIOR TO 1998 )
		return null;
	}
}

/** -------------------------------------------------------------------------------------------------------------------------------------------
  * CREATE AN OBJECT WRAPPING THE 'XML HTTP REQUEST' OBJECT. ** THIS IS THE JAVASCRIPT CLASS DEFINITION!!! **
  * object xhrWrapper( boolean async )
  * 
  * IT MUST BE SAID THAT IN FACT THIS OBJECT IS ONLY NECESSARY FOR THE MICROSOFT INTERNET EXPLORER 5.0 & 6.0 BROWSERS AS THEY ARE THE ONLY ONES
  * NOT IMPLEMENTING THE 'XMLHttpRequest' OBJECT. FROM MSIE 7.0, THIS OBJECT IS NOW CORRECTLY SUPPORTED - THOUGH IT CAN ALSO BE DISABLED.
  * CONSEQUENTLY, THE MAIN ISSUE COMES FROM THE FACT THAT FOR ASSURING A MSIE 5.0 & 6.0 COMPLIANCE, IT IS REQUIRED TO USE AN 'ACTIVE X' OBJECT
  * INSTEAD. BUT THE MAIN RULE OF 'ACTIVE X' OBJECTS IS THAT IT NOT POSSIBLE TO ADD ANY PROPERTY TO IT ... SO HERE COMES THE WRAPPER!
  * -------------------------------------------------------------------------------------------------------------------------------------------
  */
function xhrWrapper( async )
{
	this.mpid = xhrThread( ); // ... ( INITIALIZATION ) GET AN INSTANCE OF THE 'XML HTTP REQUEST' OBJECT.
	this.mode = ( async == false ) ? false : true; // ... SET THE CONNECTION MODE.
	
	this.language = 'php'; // ... SCRIPTING LANGUAGE OF THE SERVER.
	this.resource = './framework/';  // ... LOCATION OF THE SERVER RESOURCES.
	this._version  = '4.7'; // ... LIBRARY VERSION.
	
	this.send = xhrSender; // ... SEND A REQUEST TO THE DISTANT WEB SERVER!
	this.read = xhrReader; // ... ENABLE INCOMING DATA DETECTION!
	
	// ... this.trigger = xhrTrigger; ( DEPRECATED ON VERSION 4.2. )
}

/** -------------------------------------------------------------------------------------------------------------------------------------------
  * SEND A REQUEST FROM THE CLIENT WEB BROWSER TO THE WEB SERVER.
  * boolean xhrSender( string method, string url, string data, function callback )
  * 
  * THE 3rd ARGUMENT OF THE open METHOD OF THE 'XML HTTP REQUEST' OBJECT MAKES THE DISTINCTION BETWEEN AN ASYNCHRONOUS ( IF SET TO 'true' ) AND
  * A SYNCHRONOUS ( IF SET TO 'false' ) CONNECTION!
  * 
  * A TRANSACTION BETWEEN A CLIENT AND THE WEB SERVER WILL ALWAYS ACT THE SAME WAY, AS SPECIFIED BELOW:
  * 
  *    1. THE OBJECT WILL FIRST REQUIRE TO BE INSTANCIATE. STARTING FROM VERSION 4.0, IT IS NOW AUTOMATICALLY DONE ON CLASS LOADING.
  *    2. ONCE THE INSTANCE STORED, THE xhrSender METHOD WILL THEN BE ABLE TO REALISE THE FOLLOWING SUB-ACTIONS:
  *      2.1 PREPARE THE CONNECTION VIA THE open METHOD ( INITIALIZATION ).
  *      2.2 DEFINE VIA THE onreadystatechange PROPERTY THE ACTION NEEDED TO BE PROCESSED IN CASE OF ANY AKNOWLEDGEMENT. ( SEE IMPORTANT NOTE )
  *      2.3 EFFECTIVELY PUSH THE DATA TO THE WEB SERVER VIA THE send METHOD.
  * 
  * ADDITIONAL NOTE: THE onreadystatechange PROPERTY ONLY DEFINES A FUNCTION THAT WILL BE CALLED ON EACH CONNECTION STATE CHANGE. BUT IT CANNOT
  * PASS ANY KIND OF ARGUMENTS TO IT. SO THAT IT CAN BE NECESSARY TO PASS THROUGH AN ANONYMOUS FUNCTION THAT WILL IN TURN CALL THE DESIRED
  * FUNCTION WITH ALL THE ARGUMENTS REQUIRED OR NEEDED.
  * ( E.G. THE obj.onreadystatechange = myFunc; DECLARATION WOULD COME obj.onreadystatechange = function( ) { myFunc( param1, param2 ); }; )
  * 
  * IMPORTANT: IT HAS BEEN SAID THAT THE onreadystatechange PROPERTY WAS EVALUATED ON EACH SERVER ACKNOWLEGMENT. THERE IS IN FACT AN EXCEPTION:
  * IN FIREFOX ( 3.0+ ), THE MOZILLA FOUNDATION DROPPED ITS SUPPORT FOR SYNCHRONOUS CONNECTIONS ( AND SYNCHRONOUS ONLY! ) IN ORDER TO LET USERS
  * TO TERMINATE FROZEN REQUESTS WHENEVER THEY LIKE. THE DIRECT IMPACT OF THIS BEHAVIOUR IS THAT THE onreadystatechange PROPERTY WILL NOT BE
  * RUN AT ALL IF THE 3rd PARAMETER OF THE open METHOD IS SET TO 'false'. TO DETERMINE IF THE REQUEST HAS CORRECTLY ENDED, IT IS NECESSARY TO
  * CHECK THE status PROPERTY VALUE INSTEAD ( E.G. if ( obj.status == 200 ) alert( obj.responseText ); ).
  * -------------------------------------------------------------------------------------------------------------------------------------------
  */
function xhrSender( method, url, data, callback )
{
	if ( !this.mpid || typeof this.mpid != 'object' )
		return false; // ... EITHER THE METHOD HAS BEEN CALLED 'OUT OF SCOPE' OR THE 'XML HTTP REQUEST' OBJECT IS NOT SUPPORTED BY THE BROWSER!
	
	switch ( method )
	{
		case 'GET':
			url = this.resource + url + '.' + this.language + '?';
			
			if ( data != null )
			{
				url = url + 'method=GET';
				
				for ( var i = 0; i < data.length; i++ )
					url += '&' + data[i][0] + '=' + data[i][1];
				
				data = null;
			}
			
			this.mpid.open( 'GET', url, this.mode );
			this.mpid.setRequestHeader( 'If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT' ); // ... FIX: DISCARD MSIE INTERNAL CACHE USAGE!!!
			this.mpid.setRequestHeader( 'Cache-Control', 'no-cache' ); // ... FIX: AVOID MSIE REQUESTING FROM ITS INTERNAL CACHE!!!
			this.mpid.setRequestHeader( 'Content-Type' , 'text/plain; charset=UTF-8' );
			
			break;
		
		case 'POST':
			url = this.resource + url + '.' + this.language + '?';
			
			if ( data != null )
			{
				var tmp = 'method=POST';
				
				for ( var i = 0; i < data.elements.length; i++ )
					tmp += '&' + data.elements[i].name + '=' + data.elements[i].value;
				
				data = tmp;
			}
			
			this.mpid.open( 'POST', url, this.mode );
			this.mpid.setRequestHeader( 'Cache-Control', 'no-cache' ); // ... FIX: AVOID MSIE REQUESTING FROM ITS INTERNAL CACHE!!!
			this.mpid.setRequestHeader( 'Content-Type' , 'application/x-www-form-urlencoded; charset=UTF-8' );
			
			break;
		
		default:
			return false; // ... EITHER THE method ARGUMENT IS WRONG OR MISSING!
			break;
	}
	
	if ( this.mode != true ) // ... TO HANDLE MOZILLA FIREFOX onreadystatechange BUG ON SYNCHRONOUS MODE!
	{
		this.mpid.send( data );
		
		// ... STARTING FROM THAT POINT, THE SCRIPT SHOULD WAIT TILL A RESPONSE IS RECEIVED FROM THE SERVER!!!
		
		return this.read( );
	}
	else 
	{
		var _obj = this; this.mpid.onreadystatechange = function( ) { _obj.read( callback ); }; // ... SEE VERSION 4.2 NOTES.
		
		this.mpid.send( data );
		
		// ... this.trigger( 2 ); ( DEPRECATED ON VERSION 4.2. )
		
		// ... STARTING FROM THAT POINT, THE SCRIPT SHOULD TERMINATE IMMEDIATELY!!!
		
		return true;
	}
}

/** -------------------------------------------------------------------------------------------------------------------------------------------
  * READ AND PARSE ANY DATA RECEIVED FROM THE WEB SERVER BY THE CLIENT WEB BROWSER.
  * boolean xhrReader( function callback )
  * 
  * THIS METHOD CAN BE CALLED EACH TIME A STATE CHANGE OCCURS ON THE 'XML HTTP REQUEST' OBJECT. SO THAT IT SHOULD USUALLY BE CALLED FIVE TIMES
  * TO BE COMPLETED. THE readyState OJECT PROPERTY STATUS WILL ALWAYS START FROM STATE '0' AND SHOULD OBVIOUSLY REACH STATE '4' IN THE END.
  * 
  * IN OUR CASE, AS THIS PROPERTY IS DEFINED AFTER THE CONNECTION IS OPENED, THIS METHOD SHOULD ONLY RUN A MAXIMUM OF FOUR TIMES STARTING FROM
  * STATE '1'. HERE IS AN EXHAUSTIVE LIST OF THE VALUES THE readyState PROPERTY CAN TAKE:
  * 
  *    0. "Uninitialized" ... THE open METHOD HAS NOT YET BEEN CALLED.
  *    1. "Loading" ......... THE send METHOD HAS NOT YET BEEN CALLED.
  *    2. "Loaded"  ......... HEADERS HAVE RETURNED AND THE status PROPERTY IS AVAILABLE.
  *    3. "Interactive" ..... THE responseText PROPERTY IS BEING LOADED WITH DATA.
  *    4. "Complete" ........ ALL OPERATIONS HAVE FINISHED.
  * -------------------------------------------------------------------------------------------------------------------------------------------
  */
function xhrReader( callback )
{
	if ( !this.mpid || typeof this.mpid != 'object' )
		return false; // ... EITHER THE METHOD HAS BEEN CALLED 'OUT OF SCOPE' OR THE 'XML HTTP REQUEST' OBJECT IS NOT SUPPORTED BY THE BROWSER!
	
	switch ( this.mpid.readyState )
	{
		case 4:
			var response = null;
			
			if ( this.mpid.status == 200 )
				response = [ this.mpid.status, this.mpid.responseText ];
			else
				response = [ this.mpid.status, 'The server returned an error: \n' + this.mpid.status + '. ' + this.mpid.statusText + '.' ];
			
			if ( callback == null )
			{
				return response; // ... SHOULD BE SYNC. MODE AS NO CALLBACK IS REQUIRED.
			}
			else
			{
				callback( response, true ); // ... SHOULD BE ASYNC. MODE AS TRIGGERS ARE THE ONLY USABLE NOTIFIERS.
				return true;
			}
			
			break;
		
		default:
			return null; // ... THIS SHOULD PREVENT FROM OUTDATED DATA!
			break;
	}
}

/** -------------------------------------------------------------------------------------------------------------------------------------------
  * DEFINE A TRIGGER TO DETECT INCOMING DATA. ** DEPRECATED ON VERSION 4.2. **
  * number xhrTrigger( [number clock] )
  * 
  * THE TRIGGER DEFINES A ( RECURRENT ) BACKGROUND PROCESS RUN EVERY clock SECONDS. THE TRIGGER USES THE setInterval FUNCTION AND AUTOMATICALLY
  * CONVERTS THE clock ARGUMENT INTO 'MILLISECONDS'. THE clock ARGUMENT SHOULD REFLECT THE CLIENT BANDWITH.
  * PLEASE NOTE THAT THE setInterval FUNCTION RETURNS A UNIQUE ID. THAT SHOULD ALWAYS BE STORED IN ORDER TO KEEP THE ABILITY TO CANCEL THE
  * TRIGGER. OTHERWISE, IT COULD LEAD TO AN "INFINITE" EXECUTION OF THE TRIGGER! ( IN FACT, TILL THE USER QUITS OR REFRESHES ITS WEB BROWSER )
  * 
  * ADDITIONAL NOTE: THIS METHOD IS A MICROSOFT INTERNET EXPLORER FIX WHICH CONTRIBUTES TO SIMULATE THE __defineSetter__ JAVASCRIPT FUNCTION.
  * THIS METHOD IS IN FACT TIGHTLY-COUPLED WITH TIMER FUNCTIONS SUCH setTimeout OR setInterval THAT ENABLES BACKGROUND RUNNING PROCESSES SUCH
  * INCOMING DATA DETECTION. PLEASE NOTE THAT THE __defineSetter__ FUNCTION ONLY EXISTS IN MSIE STARTING FROM VERSION 8.0 ( WHILE ALL OTHERS
  * BROWSERS ALREADY SUPPORT THIS FEATURE ).
  * 
  * IMPORTANT: THIS METHOD IS DISABLED ON SYNCHRONOUS CONNECTIONS. THE MAIN REASON IS BECAUSE THERE IS NO NEED TO DETECT AN INCOMING MESSAGE AS
  * A SERVER THAT ACKNOWLEDGES A REQUEST USUALLY SEND BACK A RESPONSE AT THE SAME TIME WITH NO TRANSITIONAL STATE ( SEE THE xhrSender METHOD
  * NOTES, ESPECIALLY ABOUT THE MOZILLA FIREFOX BUG ).
  * -------------------------------------------------------------------------------------------------------------------------------------------
  * /
function xhrTrigger( clock )
{
	if ( !this.mpid || typeof this.mpid != 'object' )
		return false; // ... EITHER THE METHOD HAS BEEN CALLED 'OUT OF SCOPE' OR THE 'XML HTTP REQUEST' OBJECT IS NOT SUPPORTED BY THE BROWSER!
	
	if ( this.mode != true )
		return false; // ... THE CONNECTION MODE WAS SET TO SYNCHRONOUS! NO NEED FOR A TRIGGER!!!
	
	var timer = ( !clock || typeof clock != 'number' ) ? 3000 : clock * 1000;
	var __obj = this;
	
	return setInterval( function( ) { __obj.read.apply( __obj ); }, timer );
}
  */

