

function AJAXQueueSystem() {
    /// <summary>Something in here</summary>
    /// <field name="Transactions" type="AJAXTransactionCollection">Transactions for this queue system</field>
    /// <field name="Channels" type="Collection">Communication channels for this queue system</field>
    /// <field name="Events" type="Events">Events</field>    

	Object.Inherits(this, WebControl);
	
	this.Transactions = new AJAXTransactionCollection(this);
				
	this.Channels = new Collection();
		this.Channels.Parent = this;
		this.Channels.DefaultCollectionObject = __AJAXQS_TransactionChannel;
		this.Channels.Add = function(uCount) { if (ISNAN(uCount)) uCount = 1; for (var i = 0; i < uCount; i++) { this.Items.splice(this.Items.length, 0, new __AJAXQS_TransactionChannel(this.Parent)); } }
		
	this.Channels.Add();	// Add a default single channel
		
		
	/* Properties */
	var oJSCtl = this;
 
	this.SetStatus = function(msg) { AJAXQS_SetStatus(this, msg); }
	
	
	/* Bound Events */
	this.Events.Add("TRANSACTION_READY", __AJAXQS_TransactionReady);
	this.Events.Add("TRANSFERCOMPLETE", __AJAXQS_TransactionComplete)
	this.Events.Add("TRANSFERFAILED", __AJAXQS_TransactionFailed);
	this.Events.Add("TRANSFERCANCELLED", __AJAXQS_TransactionCancelled);
	
	this.__ScreenBlocker = null;
	this.__StatusDialog = null;
	this.__ScreenDialogImgRef = null;
	this.__ScreenDialogTextRef = null;
}

Cast.As.AJAXQueueSystem = function(val) {
/// <summary>Returns a casted AJAXQueueSystem</summary>
/// <param name="val" type="AJAXQueueSystem">Value to cast</param>
/// <returns type="AJAXQueueSystem" />
    return val;
}


AJAXQueueSystem.prototype.toString = function() { return "[AJAX Queue System]" }

AJAXQueueSystem.prototype.Status                = "IDLE";
AJAXQueueSystem.prototype.StatusObject          = window;
AJAXQueueSystem.prototype.RemainingObject       = window;
AJAXQueueSystem.prototype.QueueNumber           = 0;
AJAXQueueSystem.prototype.DefaultTimeout        = 120;
AJAXQueueSystem.prototype.DefaultChannel        = null;
AJAXQueueSystem.prototype.AutoParseParams       = true;
AJAXQueueSystem.prototype.DefaultStatusImage    = "Controls/AJAX/loading-circle-brown.gif";
AJAXQueueSystem.prototype.DefaultStatusMessage  = '';
AJAXQueueSystem.prototype.DisplayRemainingInStatus = true;
AJAXQueueSystem.prototype.AllowErrorThrow       = false;


AJAXQueueSystem.prototype.__class = true;
AJAXQueueSystem.prototype.__Dispose = __AJAXQS_Dispose;

function __AJAXQS_Dispose() {
    for (var i = 0; i < this.Transactions.Items.length; i++)
	{
		this.Transactions.Items[i].__Dispose();
	}	
	
	this.AJAXRequest.__Dispose();
	
	this.Channels.Parent = null;
	for (var i = 0; i < this.Channels.Items.length; i++)
		this.Channel.Items.splice(this.Channel.Items.length, 1);
	
	this.Transactions.Parent = null;
	this.Transactions = null
	this.AJAXRequest = null;
	this.StatusObject = null;
	this.__ScreenBlocker = null;
	this.__StatusDialog = null;
	this.__ScreenDialogImgRef = null;
	this.__ScreenDialogTextRef = null;
}


AJAXTransactionCollection.prototype.Cancel = __ATC_Cancel;
AJAXTransactionCollection.prototype.Add = __ATC_Add;
AJAXTransactionCollection.prototype.Remaining = __ATC_Remaining;
AJAXTransactionCollection.prototype.Current = null;

function AJAXTransactionCollection(pParent) {
    /// <summary>Transaction collection</summary>
    /// <field name="Parent" type="AJAXQueueSystem">QueueSystem this collection is bound to</field>
    /// <field name="DefaultCollectionObject" type="AJAXTransaction" />
    /// <field name="Current" type="Array">Current transactions(s)</field>

    Object.Inherits(this, Collection);                          // Inherit Instantiated Collection properties
    
    delete this.Add;                                    // Remove inherited 'Add' function
    
    this.Parent = pParent;
	this.DefaultCollectionObject = AJAXTransaction;
	
	this.__Total = 0;
		
	this.Current = null;
}



function __ATC_Remaining() {
    /// <summary>Returns the count of the remmaining transactions queued</summary>
    /// <returns type="Number" />
	return (this.Current == null) ? 0 : (this.Items.length + (this.__Total - this.Current.TransactionID)) - 1;
}

function __ATC_Add(fBuilder, fHandler, bBlocking, sStatusMessage, oParams, bAsync, fFailHandler, fCancelHandler) {
    /// <summary>Adds a new transaction. Specifying paramaters will make the transaction auto-start [Ready()]</summary>
    /// <param name="fBuilder"          type="Function" mayBeNull="true">Function that will be called to build the transaction</param>
    /// <param name="fHandler"          type="Function" mayBeNull="true">Function that will be called to handle the transaction return</param>
    /// <param name="bBlocking"         type="Boolean" mayBeNull="true">Specifying true makes the user unable to interact with the page</param>
    /// <param name="sStatusMessage"    type="String" mayBeNull="true">Message to show when blocking</param>
    /// <param name="oParams"           type="Object" mayBeNull="true">List of parameters to attach to the transaction</param>
    /// <param name="bAsync"            type="Boolean" mayBeNull="true">Set to false if you want to run a synchronouse operation, default is Async</param>
    /// <param name="fFailHandler"      type="Function" mayBeNull="true">Function that will be called when the transaction fails</param>
    /// <param name="fCancelHandler"    type="Function" mayBeNull="true">Function that will be called when the transaction is cancelled</param>
    /// <returns type="AJAXTransaction" />
	var o = new AJAXTransaction(this.Parent.QueueNumber++, this.Parent);
	this.__Total++;
	    
	if (fBuilder != undefined && fHandler != undefined) {
        o.RequestBuilder = fBuilder;
        o.ResponseHandler = fHandler;
        
  
        if (oParams != undefined && oParams != null) {
            if (typeof(oParams) != 'object') {
                var e = new Error();
                e.message = 'Parameters should be passed in using the following syntax.\n "Transactions.Add([Function_Builder], [Function_Handler], {"VariableParam" : VariableParamValue, "StringParam" : "String value"}, [Function_FailHandler], [Function_CancelHandler]);"';            
                e.description = e.message;
                throw e;
            }
                       
            for (var n in oParams) {
                if (!oParams.hasOwnProperty(n)) continue;                    
                o.Params.Add(n, oParams[n]);
            }
        }
        
        if (bBlocking == true || bBlocking == false) o.Blocking = bBlocking;
        if (sStatusMessage != undefined && sStatusMessage != null) {
            o.StatusMessage = sStatusMessage;
        }
        
        if (fFailHandler != undefined) o.FailHandler = fFailHandler;        
        if (fCancelHandler != undefined) o.CancelHandler = fCancelHandler;
        
        if (bAsync == true || bAsync == false) {       
            o.Async = bAsync;
        }
        
        o.Ready();
    }
    
	return o;
}

function __ATC_Cancel() {
    /// <summary>Cancels all currently running and queued transations.</summary>
    var Channels = this.Parent.Channels.Items;
			
	for (var c = 0; c < Channels.length; c++)
	{
		var Channel = Channels[c];
							
		if (Channel.AJAXRequest.Status == "PROCESSING")
			Channel.AJAXRequest.Cancel();
			
		for (var i = 0; i < Channel.Queue.length; i++) {
			if (Channel.Queue[i].Status == "PROCESSING" || Channel.Queue[i].Status == "WAITING") {
				Channel.Queue[i].Status = "CANCELLED";
				Channel.Queue[i].Events.RaiseEvent("CANCELLED");
			}
		}		
		
		if (Channel.__TimeoutHandle != null) {
			clearTimeout(Channel.__TimeoutHandle);
			Channel.__TimeoutHandle = null;
		}
	}
}



AJAXTransaction.prototype.toString  = function() { return "[AJAX Transaction]" }
AJAXTransaction.prototype.Events    = null;
AJAXTransaction.prototype.Channel   = null;
AJAXTransaction.prototype.QueueChannel = null;
AJAXTransaction.prototype.Status    = "INITIALISING";
AJAXTransaction.prototype.TransactionID = -1;
AJAXTransaction.prototype.Parent    = null;
AJAXTransaction.prototype.StatusMessage = "";
AJAXTransaction.prototype.CompletedStatusMessage = "";
AJAXTransaction.prototype.Timeout   = -1;
AJAXTransaction.prototype.Async = null;
AJAXTransaction.prototype.Blocking = false;
AJAXTransaction.prototype.CallBackOnly = false;
AJAXTransaction.prototype.StatusDialogImage = '';
AJAXTransaction.prototype.RequestBuilder = null;
AJAXTransaction.prototype.ResponseHandler = null;
AJAXTransaction.prototype.FailHandler = null;
AJAXTransaction.prototype.CancelHandler = null;
AJAXTransaction.prototype.Params = null;
AJAXTransaction.prototype.TransactionReady = null;
AJAXTransaction.prototype.EncodeResponse = false;

function AJAXTransaction(ID, oJSParent) {
    /// <summary>The Transaction object is used by the QueueSystem to interact with the server</summary>
    /// <param name="ID" type="Number">Unique ID of the transaction</param>
    /// <param name="oJSParent" type="AJAXQueueSystem" >Bound QueueSystem</param>
    /// <field name="Events" type="Events">Events for the transaction</field>
    /// <field name="Channel" type="__AJAXQS_TransactionChannel">Channel the transaction is bound to (if any)</field>
    /// <field name="QueueChannel" type="Number">Channel index the transaction is bound to (if any)</field>
    /// <field name="Status" type="String">Current status</field>
    /// <field name="TransactionID" type="Number">Same as ID</field>
    /// <field name="Parent" type="AJAXQueueSystem">Bound QueueSystem</field>
    /// <field name="StatusMessage" type="String">Message to show while blocking</field>
    /// <field name="CompletedStatusMessage" type="String">Message to show on window status bar when completed</field>
    /// <field name="Timeout" type="Number">Amount of time to wait before timing out.  Best not touched.</field>
    /// <field name="Async" type="Boolean">Whether to run in synchronous mode or not</field>
    /// <field name="Blocking" type="Boolean">Whether to block (show the status as a blocking status dialog)</field>
    /// <field name="CallBackOnly" type="Boolean">If set to True, the transaction will not try to run the handler.  This can be used to determine when a set of transactions running synchronously have been completed</field>
    /// <field name="StatusDialogImage" type="String">Image to show on the status dialog</field>
    /// <field name="RequestBuilder" type="Function">Function to be called when the transaction is being built</field>
    /// <field name="ResponseHandler" type="Function">Called when the transaction has completed</field>
    /// <field name="FailHandler" type="Function">Called when the transaction has failed</field>
    /// <field name="CancelHandler" type="Function">Called when the transaction is cancelled</field>
    /// <field name="Params" type="ICTWebParameterCollection">Collection of custom parameters to pass through.  All items are garbage collected to stop cyclic references</field>
    /// <field name="TransactionReady" type="Boolean">Used for determining whether the transation is ready.  Do NOT manually change this field.  To set a transaction as ready, use the Ready() function</field>
    /// <field name="EncodeResponse" type="Boolean">Used for determining whether the transation response should be hex encoded.  Use this if you are sending XML fragments.  It does cause a large decoding overhead however</field>

    this.Events             = new Events();                 // Custom Events Object

	this.Channel	        = null;						/* Transaction Request Sub-Channel to use */
	this.QueueChannel	    = null;
	this.Status             = "INITIALISING";				/* Represents the current status of the transaction */
	this.TransactionID      = ID;					/* Transaction Unique Identifier */
	this.Parent             = oJSParent;					/* Bound Queue System Object */
	this.StatusMessage      = oJSParent.DefaultStatusMessage; /* Message Displayed on the windows status screen while processing */
	this.CompletedStatusMessage = "";			/* Message Displayed after the transaction has completed */
	this.Timeout            = oJSParent.DefaultTimeout;	/* Default Timeout */
	this.EncodeResponse     = false;
    this.Async              = null;                 // Whether or not to run the transaction in async mode

	this.Blocking           = false;
	this.CallBackOnly	    = false;				/* If set to TRUE, the transaction will not try to run the handler */
	this.StatusDialogImage  = oJSParent.DefaultStatusImage;
	this.RequestBuilder     = null;					/* Pointer to the function that will build the request, function interface must support: function([AJAXQueueSystem] oJSCtl, [AJAXTransaction] oTransaction)		*/			
	this.ResponseHandler    = null;				/* Pointer to the function that will handle the response, function interface must support: function([AJAXTransaction] oTransaction, [AJAXRequest] oAJAXRequest) */
												/*		NOTE- [AJAXQueueSystem] AJAXRequest.Parent will be referend to the bound Ajax queue system */
	this.FailHandler        = null;
	this.CancelHandler      = null;
	
	this.Params = new Collection();
		this.Params.DefaultCollectionObject = CTWebParameter;
		/* Add() Overload */
		this.Params.Add = function(Name, Value) {
			var o = new CTWebParameter();
			o.Name = this.Add.arguments[0];
			o.Value = this.Add.arguments[1];
			
			this.Items.splice(this.Items.length, 0, o);
			return o;
		}
	
	/* Triggers AJAX Queue System to send this transaction - If its already processing, this transaction will be added to the queue*/	
	this.TransactionReady = false;
}

Cast.As.AJAXTransaction = function(val) {
/// <summary>Returns a casted AJAXTransaction</summary>
/// <param name="val" type="AJAXTransaction">Value to cast</param>
/// <returns type="AJAXTransaction" />
    return val;
}

AJAXTransaction.prototype.Ready		= __AJAXQSTRANSACTION_Ready;
AJAXTransaction.prototype.__Dispose	= __AJAXQSTRANSACTION_Dispose;
AJAXTransaction.prototype.Cancel    = __AJAXQSTRANSACTION_Cancel;


function __AJAXQSTRANSACTION_Cancel(bDontCallChannel) 
{    
    if (this.Status != "CANCELLED") 
    {
        if (bDontCallChannel == true) {
            // Do nothing
        } else if (this.QueueChannel.Status == "PROCESSING") {
            this.QueueChannel.Cancel();        
        }
    } 
    
    this.Status = "CANCELLED";
    this.Events.RaiseEvent("CANCELLED", null);
}

function __AJAXQSTRANSACTION_Dispose() {
	this.Parent = null;
	this.RequestBuilder  = null;
	this.ResponseHandler = null;
	this.Params = null;
}


function __AJAXQSTRANSACTION_Ready() {
	 if (typeof(this.RequestBuilder) != "function") {
		var e = new Error();
		e.description = "No valid request builder has been specified for this transaction";
		throw e;
	 }
	 
	 if (this.CallBackOnly != true && typeof(this.ResponseHandler) != "function") {
		var e = new Error();
		e.description = "No valid response handler has been specified for this transaction";
		throw e;
	 }
	 
	 this.TransactionReady = true; 
	 this.Status = "WAITING"; 
	 this.Events.RaiseEvent("READY");
	 this.Parent.Events.RaiseEvent("TRANSACTION_READY", this)	 
}


__AJAXQS_TransactionChannel.prototype.toString = function() { return "[Transaction Channel]" }
function __AJAXQS_TransactionChannel(oJSParent) {
	this.Parent = oJSParent;
	this.Queue	= new Array();
	
	var self = this;
	this.__TimoutCallBack	= function() { __AJAXQS_Timeout(oJSParent, self); }
	this.__TimeoutHandle	= null;
	
	this.Private		    = false;			// If set to true this channel will only allow transactions if explicitly set on the transaction itself
	
	this.Status			    = "IDLE";
	
	this.CurrentTransaction = null;
		
	this.AJAXRequest	    = new AJAXRequestControl();
		this.AJAXRequest.Parent = this;
		this.AJAXRequest.Events = oJSParent.Events;
		
	this.ChannelNumber = function() { for (var c = 0; c < this.Parent.Channels.Items.length; c++)  { if (this.Parent.Channels.Items[c] == this) return c } return -1; }
}
__AJAXQS_TransactionChannel.prototype.Cancel = __AJAXQS_TransactionChannel_Cancel;

function __AJAXQS_TransactionChannel_Cancel() {    
    this.AJAXRequest.Cancel();
    
    if (this.Status != "IDLE") {
        this.Status = "IDLE";
        
        if (this.CurrentTransaction.Status == "PROCESSING")
            this.CurrentTransaction.Cancel(true);
    }
    
    //__AJAXQS_CheckTransactions(this.Parent, this);
}


function __AJAXQS_Timeout(oJSCtl, oChannel) {
	///<summary>
	///	<input name="oJSCtl"	type="object/AJAXQueueSystem"	required="yes"	default=""	description="Reference to the QueueSystem firing this event">
	///	<description>
	///		This function is fired when an AJAX Transaction has timed out.  This function works as an Event Delegate for internal use only.  It is used
	///		used to abort and clean up the internal AJAXXfer control.  The user is also alerted to the timeout.
	///	</description>
	///</summary>


	oChannel.__TimeoutHandle = null;

	if (oChannel.CurrentTransaction != null) {
		oChannel.CurrentTransaction.Status = "FAILED";
	}

	try {	
	    oChannel.AJAXRequest.Cancel();
	} catch (err) {
		// Failed to abort ?
	}
	
	oChannel.Status = "IDLE";

	Msgbox("Your transaction has timed out.  You may no longer be connected to the internet, or the server has stopped responding.", vbCritical + vbOKOnly, "Timeout Experienced");

	__AJAXQS_CheckTransactions(oJSCtl, oChannel);
}


function __AJAXQS_TransactionReady(oAJAXTransaction) {
	///<summary>
	///	<input name="oAJAXTransaction"	type="object/AJAXTransaction"	required="yes"	default=""	description="Transaction Object">
	///	<description>
	///		This function is fired whenever an ajax transactions .Ready() is called.  It is used to jump-start the queueing system
	///		if it has shut down because of inactivity.
	///	</description>
	///</summary>
	
	var iChannel = 0;
	var Channels = oAJAXTransaction.Parent.Channels;
		
	// Assign the Transaction to a data sub-channel
	if (oAJAXTransaction.Channel == null || isNaN(oAJAXTransaction.Channel))
	{
		// Check to see if a default channel exists
		if (oAJAXTransaction.Parent.DefaultChannel != null)
		{
			// Assign to default channel
			iChannel = oAJAXTransaction.Parent.DefaultChannel;
		} else {
			// Assign to lowest transaction count	
			var iMin = 999;
			
			for (var c = 0; c < Channels.Items.length; c++)
			{
				if (Channels.Items[c].Private) continue;
				
				if (Channels.Items[c].Queue.length < iMin) {
					iChannel = c; 
					iMin = Channels.Items[c].Queue.length; 						
				}
			}
		}
		
	} else {
		if (oAJAXTransaction.Channel > Channels.Items.length) {
			var err = new Error();
			err.description = "Invalid channel index passed to transaction.  The channel requested does not exist/is out of bounds."
			err.message = err.description;
			throw err;
		}
	}
	
	var oChannel = Channels.Items[iChannel];
	
	oAJAXTransaction.QueueChannel = oChannel;
	
	oChannel.Queue.splice(oChannel.Queue.length, 0, oAJAXTransaction);	
	
	__AJAXQS_CheckTransactions(oAJAXTransaction.Parent, oChannel);
}


function __AJAXQS_CheckTransactions(oJSCtl, uChannel) {
	///<summary>
	///	<input name="oJSCtl"	type="object/AJAXQueueSystem"	required="yes"	default=""	description="Queue System object">
	///	<input name="uChannel"	type="object/numeric"			required="yes"	default=""	description="Index of, or Actual Data SubChannel to check">
	///	<description>
	///		This function is used to check the current status of the QueueSystem.  If it is idle, it scans its list for the 
	///		next transaction inline to be fired.  If none are found, it performs a cleanup and shuts the system down to save
	///		resources.
	///
	///		uChannel *MUST* be passed in.  This makes the Check Transactions fire on a particular data channel.
	///	</description>
	///</summary>

	if (typeof(uChannel) == 'object') {
		// nothing, we already have the channel
	} else {
		// retrieve the channel from the QueueSystem.
		uChannel = oJSCtl.Channels.Items[uChannel];
	}

	var oChannel = uChannel;			// In context data channel
	
	if (oChannel.Status != "IDLE") return;
	
	if (oJSCtl.__ScreenBlocker != null) {
		var bHide = true;
		
		for (var c = 0; c < oJSCtl.Channels.Items.length; c++)
			if (oJSCtl.Channels.Items[c].CurrentTransaction != null && oJSCtl.Channels.Items[c].Status == "PROCESSING")  { 
				bHide = false; 
				break;
			}
		
		if (bHide) {
			oJSCtl.__ScreenBlocker.style.visibility = "hidden";
			oJSCtl.__StatusDialog.style.visibility = "hidden";
		}
	}

	var oAJAXTransaction = null;
	oChannel.CurrentTransaction = null;
	
	for (var i = 0; i < oChannel.Queue.length; i++) {
		if (oChannel.Queue[i].Status != "WAITING") continue;
		
		oAJAXTransaction = oChannel.Queue[i];
		break;
	}	
	
	
	if (oAJAXTransaction == null) {	
		oChannel.Status = "IDLE";		
		// Nothings being done, Clean Up time!
		
		var bFinished = false;
		var oQueue = oChannel.Queue;
		
		while (!bFinished)
		{		
			var iFound = -1;
			
			for (var i = 0; i < oQueue.length; i++)
			{
				var sStatus = oQueue[i].Status;
				
				if (sStatus != "WAITING" && sStatus != "PROCESSING") {				
					iFound = i;
					try {
						oQueue[i].__Dispose();
						oQueue.splice(i, 1);
					} catch (err) {					
					}
					break;
				}
			}

			if (iFound == -1) bFinished = true;
		}

		oChannel.CurrentTransaction = null;
	
		__AJAXQS_CheckChannels(oJSCtl);
	
		oJSCtl.Events.RaiseEvent("STATUS_CHANGE_IDLE", oChannel);
		return;
	}
	
	oChannel.CurrentTransaction = oAJAXTransaction;
	
	oChannel.Status = "PROCESSING";
	oJSCtl.SetStatus(oAJAXTransaction.StatusMessage);
	oJSCtl.Events.RaiseEvent("STATUS_CHANGE_PROCESSING", oJSCtl);
	
	oChannel.AJAXRequest.Clear();								/* Clear old Request */
	oChannel.AJAXRequest.Async.Clear();

    if (!oJSCtl.AllowErrorThrow) 
    {
	    try 
	    {
    	    __AJAXQS_BuildAndSendRequest(oChannel, oAJAXTransaction, oJSCtl);    		
	    } 
	    catch (err) 
	    {
		    oChannel.Status = "IDLE";
		    oAJAXTransaction.Status = "FAILED";
    		
		 /*
		    if (oJSCtl.AllowErrorThrow) 
		    {
		        err.message = err.description;
			    throw err;
		    } 
		    else 
		    {
		 */
			    var sFunctionCode = oAJAXTransaction.RequestBuilder.toString();
			    sFunctionCode = sFunctionCode.substr(sFunctionCode.indexOf(" "), sFunctionCode.indexOf("(") - sFunctionCode.indexOf(" "));

			    alert("An error occurred on a transaction builder [" + sFunctionCode + "], the system returned the following error: \n\n" + err.description + "\n\nPlease contact VitalSoftware Technical Support for more information");
		 //   }
	    } 
	    finally 
	    {
		    __AJAXQS_CheckTransactions(oChannel, oAJAXTransaction, oJSCtl);
	    }
	}
	else
	{
	    __AJAXQS_BuildAndSendRequest(oChannel, oAJAXTransaction, oJSCtl);
	    __AJAXQS_CheckTransactions(oChannel, oAJAXTransaction, oJSCtl);
	}
	
}	

function __AJAXQS_BuildAndSendRequest(oChannel, oAJAXTransaction, oJSCtl)
{
    oAJAXTransaction.Events.RaiseEvent("PREBUILD");
    // Check if the async property has been set
    if (oAJAXTransaction.Async != null && (oAJAXTransaction.Async == true || oAJAXTransaction.Async == false )) oChannel.AJAXRequest.Async.Enabled = oAJAXTransaction.Async;
    if (oChannel.AJAXRequest.Async.Enabled) oChannel.AJAXRequest.Async.Timeout = oAJAXTransaction.Timeout;
    
	var buildRet = oAJAXTransaction.RequestBuilder(oJSCtl, oAJAXTransaction, oChannel.AJAXRequest);		/* Build Request	 */
    oAJAXTransaction.Events.RaiseEvent("POSTBUILD", buildRet);
          
   
	if (buildRet == false || oAJAXTransaction.CallBackOnly == true) {
	    // Cancel Request.
	    oChannel.Parent.Events.RaiseEvent("TRANSFERCANCELLED", oChannel.AJAXRequest);
	} else {
	    oChannel.AJAXRequest.EncodeResponse = oAJAXTransaction.EncodeResponse;
		oChannel.AJAXRequest.SendRequest();						/* Send Request		 */
	
		oAJAXTransaction.Status = "PROCESSING";
        
        if (oAJAXTransaction.Timeout > 0) {
            // Set timeout
		    oChannel.__TimeoutHandle = setTimeout(oChannel.__TimoutCallBack, (oAJAXTransaction.Timeout * 1000));
	    }
	}
}


function __AJAXQS_CheckChannels(oJSCtl) {
	///<summary>
	///	<input name="oJSCtl"	type="object/AJAXQueueSystem"	required="yes"	default=""	description="Queue System Object">
	///	<description>
	///		Checks to see if all the channels are idle, performs internal clean up if they are
	///	</description>
	///</summary>
	
	var oChannels = oJSCtl.Channels.Items;
	
	for (var c = 0; c < oChannels.length; c++)
	{
		if (oChannels[c].Status != "IDLE") {
			return;			// Not idle, exit early
		}
	}	
		
	// All idle, perform clean up
	var oTrns = oJSCtl.Transactions.Items;
	for (var t = 0; t < oTrns.length; t++)
		oTrns[t].__Dispose();

	// remove all transactions
	oTrns.splice(0, oTrns.length);
	
	// Raise QS idle event
	oJSCtl.Events.RaiseEvent("QUEUESYSTEM_IDLE", oJSCtl);
}

function AJAXXFER_InstantiateBlocker(oJSCtl) {
	///<summary>
	///	<input name="oJSCtl"	type="object/AJAXQueueSystem"	required="yes"	default=""	description="Queue System Object">
	///	<description>
	///		Creates a new screen blocker object.
	///	</description>
	///</summary>

	/* Blocker Constructor */
	var o = document.createElement("div");
		o.style.top = 0;
		o.style.left = 0;
		o.style.width = "100%";
		o.style.height = "100%";
		o.style.position = "absolute";
		o.style.backgroundColor = "#FFFFFF";
		o.style.display = "block";
		o.style.visibility = "hidden";
		o.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=60)";
		o.style.zIndex = 9999;
		oJSCtl.__ScreenBlocker = document.body.appendChild(o);
		
	/* Blocker Dialog Constructor */
		o = document.createElement("div");
		o.style.position = "absolute";
		o.style.display = "block";
		o.style.visibility = "hidden";
		o.style.top = 0;
		o.style.left = 0;
		o.style.width = 300;
		
		o.style.zIndex = 10000;
		oJSCtl.__StatusDialog = document.body.appendChild(o);
		
		var aOut = new Array(0);
		var idx = 0;
		
		
		aOut[idx++] = '	<table style="padding-top:0px; width: 100%; height: 100%; table-layout: fixed; filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=\'#FF3a7085\',EndColorStr=\'#ff0e2d3d\');" cellpadding=4 cellspacing=0>';
		aOut[idx++] = '		<tr>';
		aOut[idx++] = '			<td>';
		aOut[idx++] = '				<div valign=middle style="padding: 0px; width: 100%; height: 100%;">';
		aOut[idx++] = '				</div>';
		aOut[idx++] = '			</td>';
		aOut[idx++] = '		</tr>';
		aOut[idx++] = '	</table>';

		var s = aOut.join("");		
		o.innerHTML = s;
		
		oJSCtl.__ScreenDialogContainer = o.children[0].children[0].children[0].children[0].children[0];
		
		//oJSCtl.__ScreenDialogImgRef = o.children[0].children[0].children[0].children[0].children[0].children[0].children[0].children[0].children[0].children[0];
		//oJSCtl.__ScreenDialogTextRef = o.children[0].children[0].children[0].children[0].children[0].children[0].children[0].children[0].children[1].children[0];
}


function AJAXQS_SetStatus(oJSCtl, sMessage) {
	///<summary>
	///	<input name="oJSCtl"	type="object/AJAXQueueSystem"	required="true"	default=""	description="Queue System object">
	/// <input name="sMessage"	type="String"	required="true"	default=""	description="Sets the current status of a queue system status display object">
	///
	/// 	<output type="" default="" description="">
	///
	///	<description>
	///	</description>
	///</summary>

	var oDialog		= oJSCtl.__StatusDialog;
	var oBlocker	= oJSCtl.__ScreenBlocker
	
	var bBlocking	= false;
	var Channels	= oJSCtl.Channels.Items;
	var aMessages	= new Array(0);
	var aImages		= new Array(0);
	var n			= -1;
	
	for (var c = 0; c < Channels.length; c++)
	{
		if (Channels[c].CurrentTransaction != null && Channels[c].CurrentTransaction.Blocking) { 
			bBlocking = true; 
			aMessages[++n]	= Channels[c].CurrentTransaction.StatusMessage; 
			aImages[n]		= Channels[c].CurrentTransaction.StatusDialogImage;	
		} 
	}
	
	if (!bBlocking && aMessages.length == 0) {
		return;
	}
	
	if (bBlocking) {
		if (oDialog == null) {
			AJAXXFER_InstantiateBlocker(oJSCtl);  
			oDialog = oJSCtl.__StatusDialog;
			oBlocker = oJSCtl.__ScreenBlocker	
		}
		
			
		var t = (parseInt(document.body.clientHeight, 10) - 50) / 2;
		var l = (parseInt(document.body.clientWidth, 10) - 300) / 2;

		oBlocker.style.visibility = "visible";

		oDialog.style.left = l;
		oDialog.style.top = t;
		oDialog.style.visibility = "visible";
		
		var aOut = new Array(0);
		var idx  = 0;
		
		aOut[idx++] = '<table cellpadding=5 cellspacing=0 style="padding-top:0px; width:100%;">';
		for (var i = 0; i < aMessages.length; i++)
		{
			aImages[i] = (aImages[i] && aImages[i].length == 0) ? '0009-doc_search.gif' : aImages[i];
		
		    if(aMessages[i] == "" || aMessages[i] == null) aMessages[i] = "Loading, please wait...";
		
			aOut[idx++] =	'<tr>';
			/*aOut[idx++] =		'<td style="display: block;">';
			aOut[idx++] =			'<img src="/CTWebImages/' + aImages[i] + '" style="width: 32px; height: 32px;">';
			aOut[idx++] =		'</td>';			*/					
			aOut[idx++] =		'<td style="color:white; border-bottom: 1px solid white; width: 100%; vertical-align: middle; text-align:center;">';
			aOut[idx++] =			'<div style="color:white; font: 8pt Tahoma; font-weight: bold; padding-left: 5px; vertical-align: middle;">' + aMessages[i] + '</div>';
			aOut[idx++] =		'</td>';
			aOut[idx++] =	'</tr>';
			aOut[idx++] =   '<tr>';
			aOut[idx++] =       '<td style="text-align:center;"><marquee direction="right" scrolldelay="1"><img src="/CTWebImages/progbar2.gif" /></marquee></td>';
			aOut[idx++] =   '</tr>';
			aOut[idx++] =	'';
		}
		aOut[idx++] = '</table>';
		
		var sOut = aOut.join("");
		oJSCtl.__ScreenDialogContainer.innerHTML = sOut;

	} else {

		if (oJSCtl.StatusObject == oJSCtl.RemainingObject) {	
			if (sMessage != "") sMessage += " - (" + oJSCtl.Transactions.Remaining() + ") Transactions remaining";
		} else if (oJSCtl.RemainingObject != null) {
			var RemMessage = "(" + oJSCtl.Transactions.Remaining() + ") Transactions remaining";
			
			if (oJSCtl.RemainingObject != window) {
				oJSCtl.RemainingObject.innerHTML = RemMessage;
			} else {
				window.status = RemMessage;
			}
		}

		if (oJSCtl.StatusObject != null) {
			switch (oJSCtl.StatusObject)
			{
				case window:
					window.status = sMessage;				/* Set status message */
					break;
					
				default:
					if (oJSCtl.StatusObject.tagName){
						oJSCtl.StatusObject.innerHTML = sMessage;
					} else {
						window.status = "Unsupported Status Object!";
					}
			}
		}
	}

}


function __AJAXQS_TransactionFailed(oAJAXRequest) 
{
	///<summary>
	///	<input name="oAJAXRequest"	type="object/AJAXRequest"	required="true"	default=""	description="AJAXRequest object">
	///	<description>
	///		Event delegated function for when a transaction fails at the lowest level (server) and an error is thrown by the
	///		by the AJAXRequest object itself.
	///	</description>
	///</summary>
	
	var oJSCtl = oAJAXRequest.Parent.Parent;	
	var oChannel = oAJAXRequest.Parent;
	var oCurTransaction = oChannel.CurrentTransaction;
	oCurTransaction.Status = "FAILED";
	oCurTransaction.Events.RaiseEvent("FAILED");
	
	clearTimeout(oChannel.__TimeoutHandle);
	oChannel.__TimeoutHandle = null;
		
	oChannel.Status = "IDLE";

    if (oCurTransaction.FailHandler != undefined && oCurTransaction.FailHandler != null) 
    {
        oCurTransaction.FailHandler(oCurTransaction, oAJAXRequest);
    }
    else
    {
        oAJAXRequest.ReportError();
    }
    
	__AJAXQS_CheckTransactions(oJSCtl, oChannel);
}

function __AJAXQS_TransactionCancelled(oAJAXRequest)
{
    var oJSCtl = oAJAXRequest.Parent.Parent;
    var oChannel = oAJAXRequest.Parent;
    var oCurTransaction = oChannel.CurrentTransaction;

    oCurTransaction.Cancel(); 
    oJSCtl.SetStatus('Transaction Cancelled');

    /* Clean up memory in the transaction */
    oCurTransaction.__Dispose();
    __AJAXQS_CheckTransactions(oJSCtl, oChannel);
}

function __AJAXQS_TransactionComplete(oAJAXRequest) {
	///<summary>
	///	<input name="oAJAXRequest"	type="object/AJAXRequest"	required="true"	default=""	description="AJAXRequest object">
	///	<description>
	///		Event delegated function for when a transaction succeeds at the lowest level (server). 
	///	</description>
	///</summary>

	var oJSCtl = oAJAXRequest.Parent.Parent;
	var oChannel = oAJAXRequest.Parent;
	var oCurTransaction = oChannel.CurrentTransaction;	
	
	clearTimeout(oChannel.__TimeoutHandle);
	oChannel.__TimeoutHandle = null;
	
	if (oCurTransaction.Status != "PROCESSING") 
	{
		oJSCtl.Events.RaiseEvent("TRANSACTION_UNHANDLED", oAJAXRequest, oChannel);
		// Transaction has since been removed / cancelled (or something went horribly wrong...)
		oJSCtl.SetStatus("Transaction went unhandled");
	} else {
		oJSCtl.Events.RaiseEvent("TRANSACTION_COMPLETE", oCurTransaction, oChannel);
		

	    if (oJSCtl.AllowErrorThrow == false)
	    {
	        try 
	        {
    			__AJAXQS_ProcessTransactionComplete(oAJAXRequest, oCurTransaction);        			
	        } 
	        catch (err) 
	        {
		        var fName = oCurTransaction.ResponseHandler.toString();
		        var ErrorDetails = '';
    			
		        for (var n in err)
		        {
			        if (typeof(err[n]) == "function") continue;
    				
			        ErrorDetails += n + ': ' + err[n] + '\n' 
		        }
    						        	
		        var sFunctionCode = oCurTransaction.ResponseHandler.toString();

		        sFunctionCode = sFunctionCode.substr(sFunctionCode.indexOf(" "), sFunctionCode.indexOf("(") - sFunctionCode.indexOf(" "));
		        alert("An error ocurred trying to run the response handler [" + sFunctionCode + "]:  \n\n" + ErrorDetails);			        
            }	
            finally 
            {
                oCurTransaction.Status = "COMPLETE";
	            oCurTransaction.Events.RaiseEvent("COMPLETE", oCurTransaction, oAJAXRequest);
	            oJSCtl.SetStatus(oCurTransaction.CompletedStatusMessage);
    		
	            /* Clean up memory in the transaction */
	            oCurTransaction.__Dispose();
            }	        
	    }
	    else
	    {    		
	        __AJAXQS_ProcessTransactionComplete(oAJAXRequest, oCurTransaction);
	        oCurTransaction.Status = "COMPLETE";
	        oCurTransaction.Events.RaiseEvent("COMPLETE", oCurTransaction, oAJAXRequest);
	        oJSCtl.SetStatus(oCurTransaction.CompletedStatusMessage);
		
	        /* Clean up memory in the transaction */
	        oCurTransaction.__Dispose();
	    }	
							
	}
	
	
	oChannel.Status = "IDLE";
	
	__AJAXQS_CheckTransactions(oJSCtl, oChannel);
}


function __AJAXQS_ProcessTransactionComplete(oAJAXRequest, oCurTransaction)
{
    if (typeof(oCurTransaction.ResponseHandler) != "function") 
	{
		throw new Error("No valid response handler has been assigned to this transaction.");
	}
	
	oCurTransaction.Events.RaiseEvent("PRERESPONSE", oCurTransaction, oAJAXRequest);
	oCurTransaction.ResponseHandler(oCurTransaction, oAJAXRequest);
	oCurTransaction.Events.RaiseEvent("POSTRESPONSE", oCurTransaction, oAJAXRequest);
}

