/************************************************************************************
												AJAX QUEUE
	
	This is a pretty simple class that encapsulates the "seminal" AJAX functionality
	into an object. It also gets around the "single-threaded" nature of your standard
	HTTP request by "queuing" requests, so they happen one after the other.
	
	Javascript is a pretty pathetic language when it comes to handling multiple concurrent
	tasks. It doesn't. Javascript can only do one thing at a time. HTTP also only allows
	one request to one frame/window at a time, so most of the complexity of this class is
	around making sure that we don't cram too much stuff into the pipe.
	
	This supports both POST and GET. When using either one, simply pass a URL in standard
	form, with parameters assigned by equals (=), and separated by ampersands (&). If the
	selected method is a POST, the calling function will strip the parameters from the
	URL and pass them in separately.
	
	The way you use this is to create a Javascript function that is a "callback." This is
	a function that will be called when the XML HTTP request task is complete. The function
	is very simple. It is simply a function with one (string) parameter, and one parameter
	that can be whatever you need. You can also set up a "partial" callback, so that you get
	a call before the request has completed.
	
	You create an instance of this class, then call it with a pointer to your function. The
	browser sends the request to the server, which churns away for a while, then calls your
	browser back by calling the function you sent it. The parameter will be a string provided
	by the server. You will probably need to write special code on the server to support these
	calls. The string can be anything. I usually use HTML for things like <select> <options>,
	but it can also be text or <img> tags to be inserted into <div> tags. It can also be
	Javascript, to be put through an eval() statement.
	
	This class allows you to pass an additional parameter to the callback, which can be used
	to propagate a context or to "key" the callback.
	
	Another interesting thing: In order to get around the frequent problem of "double-posting,"
	which occurs when AJAX is slow or broken. It has TWO callbacks. One callback is made when
	the HTTP Request reaches Stage 3 (interactive). By this time, the server has the command,
	and is acting on it. I use it to disable controls, and prevent secondary posts. It is not
	perfect, but seems to work well in the example here.
	
	Version: 1.1.1
*/

// This is the global that comprises the SINGLETON pattern.

var	g_ajax_obj = new c_ajax_object;	// This will be the gobal AJAX object. There can only be one...

/******************************************************************
	This class handles "queueing" objects in a pseudo-asynchronous fashion.
	Ajax can't actually handle multiple streams, so we "queue" the requests
	to execute one after another.
*/
function c_ajax_object() {
	if ( g_ajax_obj ) {
		return g_ajax_obj;
		}
	else {
		return this;
		}
};

/******************************************************************
	Constructs a new HTTP Request object. IE and the rest of the
	world have different ideas about what constitutes an HTTP
	Request class, so we deal with that here.
	
	We use the conditional Jscript stuff that IE supports to create
	an *.XMLHTTP object, or the standard Mozilla/Netscape XMLHttpRequest object.
	
	We use this as a test. If this object can't create the HTTP request object
	(either XMLHttpRequest or *.XMLHTTP), then the browser can't handle AJAX.
*/

c_ajax_object.prototype.GetNewRequestObject = function() {
	/*
		All this whacky stuff is for Internet Exploder.
		This came from http://swik.net/
	*/
	/*@cc_on @*/
	
	/*@if (@_jscript_version >= 5)
		try {
			this._dm_xmlhttprequestobject = new ActiveXObject("Msxml2.XMLHTTP");
			}
		catch (e) {
			try {
				this._dm_xmlhttprequestobject = new ActiveXObject("Microsoft.XMLHTTP");
				}
			catch (e2) {
				this._dm_xmlhttprequestobject = false;
				}
			}
	@end @*/
	
	if (!this._dm_xmlhttprequestobject && typeof XMLHttpRequest != 'undefined') {
		this._dm_xmlhttprequestobject = new XMLHttpRequest();
		}
};

// Initialize up the prototype fields.

c_ajax_object.prototype._dm_xmlhttprequestobject=null;	// This is the HTTP Request Object for this instance.
c_ajax_object.prototype._dm_callback_function=null;		// The function to be called upon completion of a request.
c_ajax_object.prototype._dm_param=null;						// An additional parameter to be passed to the function
c_ajax_object.prototype._dm_partialcallback_function=null;	// A function to be called for the interactive phase
c_ajax_object.prototype._dm_param2=null;						// An additional parameter to be passed to that function
c_ajax_object.prototype._dm_queue=new Array();				// This is the queue
c_ajax_object.prototype._dm_committed=false;					// This is set to true when the HTTPRequest reaches Stage 3.
c_ajax_object.prototype._dm_pre_queue_in_url=null;			// These are all used for the "pre-queue."
c_ajax_object.prototype._dm_pre_queue_in_callback=null;
c_ajax_object.prototype._dm_pre_queue_in_method=null;
c_ajax_object.prototype._dm_pre_queue_in_param=null;
c_ajax_object.prototype._dm_pre_queue_in_pcallback=null;
c_ajax_object.prototype._dm_pre_queue_in_param2=null;

/******************************************************************
	Basic Ajax Call for GET method
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
*/

c_ajax_object.prototype.CallXMLHTTPObjectGET = function ( in_url, in_callback ) {
	return this.CallXMLHTTPObject ( in_url, in_callback, "GET", null, null );
}

/******************************************************************
	Basic Ajax Call for GET method (with additional parameter)
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_param:		A parameter (any type) that is passed into the callback
							This parameter is used to pass things such as a field ID,
							etc. to the callback, and can be used to propagate a
							context. Callbacks tend to be free of context, so this
							helps to get around that problem.
*/

c_ajax_object.prototype.CallXMLHTTPObjectGETParam = function ( in_url, in_callback, in_param ) {
	return this.CallXMLHTTPObject ( in_url, in_callback, "GET", in_param, null );
}

/******************************************************************
	Basic Ajax Call for GET method (with additional parameter and partial callback)
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_param:		A parameter (any type) that is passed into the callback
							This parameter is used to pass things such as a field ID,
							etc. to the callback, and can be used to propagate a
							context. Callbacks tend to be free of context, so this
							helps to get around that problem.
		in_pcallback:	This specifies a "partial callback" function that is called
							when the request reaches Phase 3 (interactive).
		in_param2:		A second parameter for the partial callback
*/

c_ajax_object.prototype.CallXMLHTTPObjectGETParamPartial = function ( in_url, in_callback, in_param, in_pcallback, in_param2 ) {
	return this.CallXMLHTTPObject ( in_url, in_callback, "GET", in_param, in_pcallback, in_param2 );
}

/******************************************************************
	Basic Ajax Call for POST method
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
*/

c_ajax_object.prototype.CallXMLHTTPObjectPOST = function ( in_url, in_callback ) {
	return this.CallXMLHTTPObject ( in_url, in_callback, "POST", null );
}

/******************************************************************
	Basic Ajax Call for POST method (with additional parameter)
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_param:		A parameter (any type) that is passed into the callback
							This parameter is used to pass things such as a field ID,
							etc. to the callback, and can be used to propagate a
							context. Callbacks tend to be free of context, so this
							helps to get around that problem.
*/

c_ajax_object.prototype.CallXMLHTTPObjectPOSTParam = function ( in_url, in_callback, in_param ) {
	return this.CallXMLHTTPObject ( in_url, in_callback, "POST", in_param );
}

/******************************************************************
	Basic Ajax Call for POST method (with additional parameter and partial callback)
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_param:		A parameter (any type) that is passed into the callback
							This parameter is used to pass things such as a field ID,
							etc. to the callback, and can be used to propagate a
							context. Callbacks tend to be free of context, so this
							helps to get around that problem.
		in_pcallback:	This specifies a "partial callback" function that is called
							when the request reaches Phase 3 (interactive).
		in_param2:		A second parameter for the partial callback
*/

c_ajax_object.prototype.CallXMLHTTPObjectPOSTParamPartial = function ( in_url, in_callback, in_param, in_pcallback, in_param2 ) {
	return this.CallXMLHTTPObject ( in_url, in_callback, "POST", in_param, in_pcallback, in_param2 );
}

/******************************************************************
	Prime a call to the queue
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_method:		The HTTP method to use (default is GET).
		in_param:		A parameter (any type) that is passed into the callback
		in_pcallback:	Partial callback
		in_param2:		A second parameter for the partial callback
*/

c_ajax_object.prototype.CallXMLHTTPObject = function ( in_url, in_callback, in_method, in_param, in_pcallback, in_param2 ) {
	// Set up the "pre queue."
	g_ajax_obj._dm_pre_queue_in_url=in_url;
	g_ajax_obj._dm_pre_queue_in_callback=in_callback;
	g_ajax_obj._dm_pre_queue_in_method=in_method;
	g_ajax_obj._dm_pre_queue_in_param=in_param;
	g_ajax_obj._dm_pre_queue_in_pcallback=in_pcallback;
	g_ajax_obj._dm_pre_queue_in_param2=in_param2;

	this.Enqueue();
	return true;
};

/******************************************************************
	Add a call to the queue
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_method:		The HTTP method to use (default is GET).
		in_param:		A parameter (any type) that is passed into the callback
		in_pcallback:	Partial callback
		in_param2:		A second parameter for the partial callback
*/

c_ajax_object.prototype.Enqueue = function ( in_url, in_callback, in_method, in_param, in_pcallback, in_param2 ) {
	// Set up the main queue from the prequeue.
	this._dm_queue[this._dm_queue.length] = new Array ( this._dm_pre_queue_in_url, this._dm_pre_queue_in_callback, this._dm_pre_queue_in_method, this._dm_pre_queue_in_param, this._dm_pre_queue_in_pcallback, this._dm_pre_queue_in_param2 );
	
	// As you were...
	this._dm_pre_queue_in_url=null;
	this._dm_pre_queue_in_callback=null;
	this._dm_pre_queue_in_method=null;
	this._dm_pre_queue_in_param=null;
	this._dm_pre_queue_in_pcallback=null;
	this._dm_pre_queue_in_param2=null;
		
	// If there are no other commands in progress, we start the daisy-chain.
	if ( !this._dm_xmlhttprequestobject ) {
		this.Dequeue();
		}
};

/******************************************************************
	Dequeue and execute
*/

c_ajax_object.prototype.Dequeue = function ( ) {
	var command = null;
	var ret=false;
	
	if ( this._dm_queue.length ) {
		command = this._dm_queue[0];
		
		var url = command[0];
		this._dm_callback_function = command[1];	// The basic callback
		var method = command[2];
		this._dm_param = command[3];	// If there is a parameter, we get it here.
		this._dm_partialcallback_function = command[4];	// If there is a partial callback, we get it here.
		this._dm_param2 = command[5];	// If there is a second parameter, we get it here.
		
		for ( var counter = 1; counter < this._dm_queue.length; counter++ ) {
			this._dm_queue[counter - 1] = this._dm_queue[counter];
			}
		
		this._dm_queue.length = counter - 1;
		}
	
	if ( url && method ) {
		ret = this._CallXMLHTTPObject ( url, method );
		}
	
	return ret;
};

/******************************************************************
	Basic low-level Ajax Call
	
	Params:
		in_url: 			The URL to call
		in_callback:	A function to be called upon completion
		in_method:		The HTTP method to use (default is GET).
*/

c_ajax_object.prototype._CallXMLHTTPObject = function ( in_url, in_method ) {
		
	try {
		var sVars = null;
		
		// Split the URL up, if this is a POST.
		if ( in_method == "POST" ) {
			var rmatch = /^([^\?]*)\?(.*)$/.exec ( in_url );
			in_url = rmatch[1];
			sVars = unescape ( rmatch[2] );
			}
		
		while ( this._dm_xmlhttprequestobject ) {};	// We can't be putting in a request while there is still one out there.
		this._dm_committed = false;
		this.GetNewRequestObject();
		this._dm_xmlhttprequestobject.open(in_method, in_url, true);
		
		if ( in_method == "POST" ) {
		  this._dm_xmlhttprequestobject.setRequestHeader("Method", "POST "+in_url+" HTTP/1.1");
		  this._dm_xmlhttprequestobject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			}
		
		this._dm_xmlhttprequestobject.onreadystatechange = Handle_HTTP_Response;
	
		this._dm_xmlhttprequestobject.send(sVars);
		
		return true;
		}
	catch ( z ) { }
	
	return false;
};

/******************************************************************
	This is the callback router.
	
	Note the partial callback we make at Stage 3.
	
	This could easily be rigged to call back at every stage, but that's
	not very useful. Stages 3 and 4 are the ones we need to watch.
	
	If you really want to be anal, then do the partial callback at Step 2.
	Step 3 is more certain.
*/

function Handle_HTTP_Response () {
	if ( g_ajax_obj && g_ajax_obj._dm_xmlhttprequestobject ) {
		if ( g_ajax_obj._dm_xmlhttprequestobject.readyState == 0 ) {	// Uninitialized (sent, but no information yet)
			}
		else {
			if ( g_ajax_obj._dm_xmlhttprequestobject.readyState == 1 ) {	// Loading (probably received)
				}
			else {
				if ( g_ajax_obj._dm_xmlhttprequestobject.readyState == 2 ) {	// Loaded (received for sure, but no further data)
					// At this point, the server has the request, and is executing it (probably).
					}
				else {
					if ( g_ajax_obj._dm_xmlhttprequestobject.readyState == 3 ) {	// Interactive
						// At this point, the server has the request, and is executing it. A partial response MAY be available
						// in the g_ajax_obj._dm_xmlhttprequestobject.responseText and g_ajax_obj._dm_xmlhttprequestobject.responseBody
						// fields.
						// We have the option of sending a "Partial Callback" function, which we can use to do things like
						// disable a button to prevent additional requests.
						g_ajax_obj._dm_committed = true;
						if ( g_ajax_obj._dm_partialcallback_function ) {
							g_ajax_obj._dm_partialcallback_function ( g_ajax_obj._dm_xmlhttprequestobject.responseText, g_ajax_obj._dm_param2 ? g_ajax_obj._dm_param2 : g_ajax_obj._dm_param );
							}
						}
					else {
						if ( g_ajax_obj._dm_xmlhttprequestobject.readyState == 4 ) {	// We're done. Back to you.
							// We send both parameters, just in case they both apply (for example, the partial disables a field,
							// so the complete one re-enables it).
							g_ajax_obj._dm_callback_function ( g_ajax_obj._dm_xmlhttprequestobject.responseText, g_ajax_obj._dm_param, g_ajax_obj._dm_param2 );
							g_ajax_obj._dm_xmlhttprequestobject = null;	// Kill the request object. we're done.

							g_ajax_obj._dm_committed = false;
							g_ajax_obj.Dequeue();
							}
						}
					}
				}
			}
	}
};

/******************************************************************
	Returns true if the browser will support Ajax
	
	Very simple. We just create a request object. If it succeeds, we're in like Flint.
*/

function SupportsAjax ( in_embedded ) {
	var test_obj = new c_ajax_object;
	
	test_obj.GetNewRequestObject();
	
	if ( !in_embedded && test_obj._dm_xmlhttprequestobject ) {
		test_obj._dm_xmlhttprequestobject = null;
		test_obj = null;
		return true;
		}
	
	test_obj = null;
	
	return false;
};

