function Queue() {
    var ajaxRequestInProgress = false;

    // the list of elements, initialised to the empty array
    var queue = [];

    // the amount of space at the front of the queue, initialised to zero
    var queueSpace = 0;

    // Returns the size of this Queue. The size of a Queue is equal to the number
    // of elements that have been enqueued minus the number of elements that have
    // been dequeued.
    this.getSize = function () {
        // return the number of elements in the queue
        return queue.length - queueSpace;
    }

    // Returns true if this Queue is empty, and false otherwise. A Queue is empty
    // if the number of elements that have been enqueued equals the number of
    // elements that have been dequeued.    
    this.isEmpty = function () {
        return (queue.length == 0);
    }

    // Enqueues the specified element in this Queue. The parameter is:
    // element - the element to enqueue    
    this.enqueue = function (element) {
        queue.push(element);
    }

    this.peek = function () {
        // initialise the element to return to be undefined
        var element = undefined;
        // check whether the queue is empty
        if (queue.length) {
            // fetch the oldest element in the queue
            element = queue[queueSpace];
        }
        // return the removed element
        return element;
    }

    // Dequeues an element from this Queue. The oldest element in this Queue is
    // removed and returned. If this Queue is empty then undefined is returned.
    this.dequeue = function () {
        // initialise the element to return to be undefined
        var element = undefined;
        // check whether the queue is empty
        if (queue.length) {
            // fetch the oldest element in the queue
            element = queue[queueSpace];
            // update the amount of space and check whether a shift should occur
            if (++queueSpace * 2 >= queue.length) {
                // set the queue equal to the non-empty portion of the queue
                queue = queue.slice(queueSpace);
                // reset the amount of space at the front of the queue
                queueSpace = 0;
            }
        }
        this.ajaxRequestInProgress = false;
        this.runNextItem();
        // return the removed element
        return element;
    }

    this.runNextItem = function () {
        if (!this.ajaxRequestInProgress) {
            if (!this.isEmpty()) {
                this.ajaxRequestInProgress = true;
                var nextItem = this.peek();
                nextItem.run();
            }
        }
    }
}

function CallbackElement(eventTarget, eventArgument, eventCallback, context, errorCallback) {
    this.eventTarget = eventTarget;
    this.eventArgument = eventArgument;
    this.eventCallback = eventCallback;
    this.context = context;
    this.errorCallback = errorCallback;

    this.run = function () {
        WebForm_DoCallback(this.eventTarget, this.eventArgument, AjaxQueueCallbackWrapper, this.context, AjaxQueueErrorCallbackWrapper, true);
    }
}

// The Queue
var callbackQueue = new Queue();

// Queue CallBack Wrapper
function AjaxQueueCallbackWrapper(args, context) {    
    var currentItem = callbackQueue.dequeue();
    currentItem.eventCallback(args, context);
}

// Queue Error CallBack Wrapper
function AjaxQueueErrorCallbackWrapper(msg, context) {    
    var currentItem = callbackQueue.dequeue();
    if (typeof currentItem.errorCallback == 'function') {
        currentItem.errorCallback(msg, context);
    }
}

function AjaxQueueWebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback) {
    callbackQueue.enqueue(new CallbackElement(eventTarget, eventArgument, eventCallback, context, errorCallback));
    callbackQueue.runNextItem();
}
