

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);
}

