/**
 * @fileoverview DHTML wait animation / progress indicator.
 *
 * @author Iain White iain.white@aotgroup.com.au
 * @version 2.0 
 */

if (window.JSLib) {
    JSLib.addVersion("Waiter.js", "Waiter / Progress Bar", "2.0");
}

/**
 * @todo
 * Pause / restart proressbar
 * Where to display time if message1 is not used?
 * Check onUnload show time, before returning from waitEnd
 * Double click
 * Click while form submitting
 * Use function to set XP bar opacity
 * Make waitAlign class configurable
 * Move getStyle to CSS and make take object as well as ID string
 * Move Position to document.js
 */

/**
 * Global_Waiter - Construct a new Global_Waiter object.
 * @class This is the basic Global_Waiter class.
 * Global settings for all waiters on page.
 * @constructor
 * @returns A new Global_Waiter
 */
function Global_Waiter()
{
    /**
     * {boolean} Set to true if page has been submited.
     */
    this.hasBeenSubmited = false;
    /**
     * {string} Text to display if page has already been submited.
     */
    this.resubmitMessage = null;
    /**
     * {boolean} If true animations do not stop [DO NOT USE IN PRODUCTION].
     */
    this.testMode = false;
    /**
     * {array} Collection of {@link Waiter} objects.
     */
    this.waiters = new Array();
}

/**
 * Global_Waiter.registerWaiter - Registers a new waiter object.
 * @param {Waiter} waiterObject Teh waiter object to register.
 * @extends Global_Waiter
 * @private
 */
Global_Waiter.prototype.registerWaiter = function(waiterObject)
{
    this.waiters[waiterObject.objName] = waiterObject;
}

/**
 * Global_Waiter.getWaiter - Retreave a waiter object.
 * @param {string} waiterName Name of waiter object
 * @returns {Waiter} A waiter object
 * @extends Global_Waiter
 * @private
 */
Global_Waiter.prototype.getWaiter = function(waiterName)
{
    return this.waiters[waiterName];
}

/**
 * Global_Waiter.setResubmitMessage - Set message to display on page re-submit.
 * @param {string} str Message to display
 * @extends Global_Waiter
 */
Global_Waiter.prototype.setResubmitMessage = function(str)
{
    this.resubmitMessage = str;
}

/**
 * Global_Waiter.setTestMode - Put ALL waiters into test mode.
 * @param {boolean} value If true animations will not end.
 * @extends Global_Waiter
 */
Global_Waiter.prototype.setTestMode = function(value)
{
    this.testMode = value;
}

/**
 * Global_Waiter.activateTimers - Handel the internal timmers.
 * @extends Global_Waiter
 * @private
 */
Global_Waiter.prototype.activateTimers = function()
{
    for (var i in this.waiters) {
        var obj = this.waiters[i];
        if (typeof(obj) == "object") {
            if (obj.startTime) {
                if (obj.showTimer == false) {
                    obj.showTimer = true;
                    obj.displayTimer();
                } else {
                    obj.showTimer = false;
                    obj.removeTimer();
                }
            }
        }
    }
}

/**
 * Waiter - Construct a new Waiter object.
 * @class This is the basic Waiter class.
 * An object that manipulates the DOM to show a 'wait...' dialog
 * @constructor
 * @returns A new Waiter object
 */
function Waiter(objName)
{
    /**
     * {string} Name of waiter object.
     * @private
     */
    this.objName = objName;
    /**
     * {string} Class name for waiter DIV.
     */
    this.waitClassName = "pleaseWait";
    /**
     * {object} DOM element to contain waiter.
     */
    this.containerElement = null;
    /**
     * {string} Vertical position of waiter withen container.
     * center, top, bottom
     */
    this.vPos = 'center';
    /**
     * {string} Horizontal position of waiter withen container.
     * center, left, right
     */
    this.hPos = 'center';
    /**
     * {boolean} Use the blanket.
     */
    this.useBlanket = true;
    /**
     * {string} Class name for blanket.
     */
    this.blanketClassName = "pleaseWaitBlanket";
    /**
     * {object} DOM element to blanket.
     */
    this.blanketElement = null;
    /**
     * {boolean} show progress bar.
     */
    this.useProgressBar = true;
    /**
     * {integer} Type of progress bar.
     * 0 = Default background expand, 1 = XP faiding boxes, 2 = CSS sprite,  3 = pulsing Div blocks
     */
    this.processBarType = 2;
    /**
     * {integer} Sub Type of progress bar.
     */
    this.processBarSubType = 0;
    /**
     * {string} Class name for progress bar.
     */
    this.progressBarClassName = "progressBar";
    /**
     * {string} ID for image e.g. div#waitImage. Null for no image
     */
    this.imageId = null;
    /**
     * {integer} Default width of waiter, overridden by CSS.
     */
    this.width = 400;
    /**
     * {integer} Default height of waiter, overridden by CSS.
     */
    this.height = 45;
    /**
     * {string} First wait message.
     */
    this.message = "Please wait...";
    /**
     * {string} Secondary message.
     */
    this.message2 = null;
    /**
     * {boolean} Bounce the progress bar back at end.
     */
    this.progressBarBounce = false;
    /**
     * {integer} Speed of progress bar.
     */
    this.progressBarSpeed = 100;
    /**
     * {object} DOM element for progress bar.
     * @private
     */
    this.progressBarEl = null;
    /**
     * {integer} Maximum width of progress bar.
     * @private
     */
    this.progressBarMaxWidth = -1;
    /**
     * {integer} Minimum width of progress bar.
     */
    this.progressBarMinWidth = 0;
    /**
     * {integer} Direction of progress bar movement.
     * @private
     */
    this.progressBarDir = 1;
    /**
     * {CCallWrapper} Timer for progress bar animation.
     * @private
     */
    this.progressBarcallwrapper = null;
    /**
     * {string} Class name for XP style progress bar.
     */
    this.xpBarClassName = "xpBar";
    /**
     * {string} Class name for XP style block.
     * Reused as CSS sprite class name
     */
    this.xpBlockClassName = "xpBlock"
    /**
     * {integer} Number of blocks on XP style progress bar. Reused as number of frames in CSS sprite animation
     */
    this.noBlocks = 12;
    /**
     * {integer} Width / height of blocks on XP style progress bar. Reused as the height of the images in CSS animation
     * 
     */
    this.blockSize = 16;
    /**
     * {boolean} Show real time, ellapsed time.
     * 
     */
    this.showTimer = false;
    /**
     * {Date} Start time of ellapsed timer.
     * @private
     */
    this.startTime = null;
    /**
     * {CCallWrapper} Timer for ellapsed timer.
     * @private
     */
    this.timercallwrapper = null;
    /**
     * {integer} Current height of pulse DIV.
     * @private
     */
    this.pulseHeight = 35;
    /**
     * {integer} Minimum height of pulse DIV.
     * @private
     */
    this.pulseMinHeight = 2;
    /**
     * {integer} Maximum height of pulse DIV.
     * @private
     */
    this.pulseMaxHeight = 35;
    /**
     * {integer} Start height of pulse DIV.
     * @private
     */
    this.pulseStartHeight = 0; 
    /**
     * {integer} Diference in height step for pulse DIV.
     * @private
     */
    this.pulseStep = 2;
    /**
     * {integer} Current DIV being animated.
     * @private
     */
    this.currentDiv = -1;
    /**
     * {integer} Number of pulse DIVs to display.
     * @private
     */
    this.pulseNumDivs = 22;
    /**
     * {array} Array of DIV elements to animate.
     * @private
     */
    this.divArray = new Array();

    AOT_WAITER.registerWaiter(this);
}

/**
 * Waiter.doSubmitWithWait - If required add apropreate DOM elements
 * @returns {boolean} True if form sumbited
 * @requires CSS
 * @requires DOM
 * @requires Position
 * @requires Sniffer
 * @extends Waiter
 * @private
 */
Waiter.prototype.doSubmitWithWait = function()
{
    var waiterObject = AOT_WAITER.getWaiter(this.waiterName);
    var curResult = true;
    // Do not submit again
    if (AOT_WAITER.hasBeenSubmited) {
        if (AOT_WAITER.resubmitMessage) {
            alert(AOT_WAITER.resubmitMessage);
        }
        return false;
    }
    
    // Do onSubmit
    if (typeof this.oOnSubmit == "function") {
        curResult = this.oOnSubmit();
    }
    
    if (curResult) {
        if (this.nodeName != "A") {
            AOT_WAITER.hasBeenSubmited = true;
        }
        if (!sniffer.is_ie6) {
            document.body.style.cursor = "wait";
        }
        waiterObject.startTime = new Date();
        document.addOnKeyUpHandler(waiterObject.checkKey);
 
        var pageSize = SCREEN.getPageBigestSize();
        w = pageSize[0];
        h = pageSize[1];
        if (waiterObject.useBlanket) {
            var blanketEl = document.createElement('div'); 
            blanketEl.id = waiterObject.objName + '_waitBlanket';
            blanketEl.className = waiterObject.blanketClassName;
            blanketEl.style.position = 'absolute';
            blanketEl.style.visibility = 'visible';
            blanketEl.style.zIndex = '9998';

            // Cover full page (even when scrolled) can be overridden by CSS
            blanketEl.style.top = '0px';
            blanketEl.style.left = '0px';

            document.body.appendChild(blanketEl);

            // Is the blanket size set in the CSS?
            var blanketWidth = CSS.getCuomputedStyleOrValue(blanketEl.id, 'width', 0);
            var blanketHeight = CSS.getCuomputedStyleOrValue(blanketEl.id, 'height', 0);
            if (blanketWidth > 0 || blanketHeight > 0) {
                if (blanketWidth > 0) {
                    blanketEl.style.width = DOM.addPX(blanketWidth);
                }
                if (blanketHeight > 0) {
                    blanketEl.style.height = DOM.addPX(blanketHeight);
                }
                // Align on page
                blanketEl.style.top = DOM.addPX((h / 2) - (blanketHeight / 2));
                blanketEl.style.left = DOM.addPX((w / 2) - (blanketWidth / 2));
            } else {
                if (waiterObject.blanketElement) {
                    if (window.Position) {
                        var pos = Position.get(waiterObject.blanketElement);
                        blanketEl.style.width = DOM.addPX(pos.width);
                        blanketEl.style.height = DOM.addPX(pos.height);
                        Position.set(blanketEl, pos);
                    }
                } else {
                    blanketEl.style.width = DOM.addPX(w);
                    blanketEl.style.height = DOM.addPX(h);
                }
            }

            // IFRAME shim
            if (sniffer.is_ie5_5up) {
                var iframe = document.createElement('iframe');
                if (document.location.protocol == "https:") {
                    var blankFile = globPathPrefix + '/globals/blank.html';
                    iframe.setAttribute('src', blankFile);
                } else {
                    iframe.setAttribute('src', 'JavaScript:void(0);');
                }
                iframe.id = waiterObject.objName + '_waitBlanket_iframe';
                iframe.setAttribute('frameborder', '0');
                iframe.setAttribute('scrolling', 'no');
                iframe.style.position = 'absolute';
                iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
                iframe.style.zIndex = blanketEl.style.zIndex - 1;
                iframe.style.left = blanketEl.style.left;
                iframe.style.top = blanketEl.style.top;
                iframe.style.width = blanketEl.style.width;
                iframe.style.height = blanketEl.style.height;
                if (blanketEl.nextSibling) {
                    blanketEl.parentNode.insertBefore(iframe, layer.nextSibling);
                } else {
                    blanketEl.parentNode.appendChild(iframe);
                }
            }
        }

        var waitContainerEl = document.createElement('div'); 
        waitContainerEl.className = waiterObject.waitClassName;
        waitContainerEl.id = waiterObject.objName + '_pleaseWait';
        waitContainerEl.style.position = 'absolute';
        waitContainerEl.style.top = "0px";
        waitContainerEl.style.left = "-1000px";
        waitContainerEl.style.visibility = 'hidden';
        waitContainerEl.style.zIndex = '9999';
        waitContainerEl.style.opacity = 1.0;
 


        if (waiterObject.imageId) {
            var waitImgAlignEl = document.createElement('div'); 
            waitImgAlignEl.className = 'waitImgAlign';

            var waitImgEl = document.createElement('div'); 
            waitImgEl.id = waiterObject.imageId;
            waitImgEl.className = 'imageReplace';
            waitImgEl.title = 'Wait';
            var text = document.createTextNode("Wait Image.");
            waitImgEl.appendChild(text);

            waitImgAlignEl.appendChild(waitImgEl);
            waitContainerEl.appendChild(waitImgAlignEl);
        }

         if (waiterObject.useProgressBar) {
         
            // Normal background image animation
            if (waiterObject.processBarType == 0) {
                var waitEl = document.createElement('div');
                waitEl.id = waiterObject.objName + '_progress_bar';
                waitEl.style.position = 'relative';
                waitEl.className = waiterObject.progressBarClassName;
                waitContainerEl.appendChild(waitEl);  
            }
            
            // XP style
            if (waiterObject.processBarType == 1) {
                var XPbarEl = document.createElement('div'); 
                XPbarEl.id = waiterObject.objName + '_xpBar';
                XPbarEl.className = waiterObject.xpBarClassName;
                XPbarEl.style.position = 'relative';
                XPbarEl.style.visibility = 'visible';
                XPbarEl.style.overflow = 'hidden';
                XPbarEl.style.fontSize = "1px";
                XPbarEl.style.zIndex = '9998';

                waitContainerEl.appendChild(XPbarEl);

                var XPblocksEl = document.createElement('span');
                XPblocksEl.id = waiterObject.objName + '_xpBlocks';
                XPblocksEl.style.position = 'absolute';
                XPblocksEl.style.fontSize = "1px";
                XPblocksEl.style.left = -(waiterObject.blockSize * 2 + 1) + "px";
    
                XPbarEl.appendChild(XPblocksEl);
                
                for (var i = 0; i < waiterObject.noBlocks; i++) {
                    var XPblockEl = document.createElement('span');
                    XPblockEl.className = waiterObject.xpBlockClassName;
                    XPblockEl.style.position = 'absolute';
                    XPblockEl.style.fontSize = "1px";
                    XPblockEl.style.left =  -(waiterObject.blockSize * i + i) + "px";
                    XPblockEl.style.width = waiterObject.blockSize + "px";
                    XPblockEl.style.height = waiterObject.blockSize + "px";
                    // Set opacity
                    XPblockEl.style.opacity = ((100 - i * (100 / waiterObject.noBlocks)) / 100); 
                    XPblockEl.style.filter = 'alpha(opacity=' + (100 - i * (100 / waiterObject.noBlocks)) + ')';
                    XPblocksEl.appendChild(XPblockEl);
                }
            }
            
            // CSS Sprite image animation
            if (waiterObject.processBarType == 2) {
                var spriteContainerEl = document.createElement('div');
                spriteContainerEl.style.position = 'relative';
                spriteContainerEl.textAlign = 'center'; 
                
                var spriteEl = document.createElement('div');
                spriteEl.id = waiterObject.xpBarClassName;
                spriteEl.className = 'imageReplace';
                
                spriteContainerEl.appendChild(spriteEl);  
                waitContainerEl.appendChild(spriteContainerEl); 
            }
        
            // Pulsating Div bars
            if (waiterObject.processBarType == 3) {
                if (waiterObject.processBarSubType == 2 || waiterObject.processBarSubType == 5 || waiterObject.processBarSubType == 9) {
                    waiterObject.pulseStartHeight = waiterObject.pulseMinHeight;
                } else {
                    waiterObject.pulseStartHeight = waiterObject.pulseMaxHeight;
                }
                waiterObject.pulseHeight = waiterObject.pulseStartHeight;
	            for (var i = 0; i < waiterObject.pulseNumDivs; i++) {
		            waiterObject.divArray[i] = waitContainerEl.appendChild(document.createElement("div"));
                    waiterObject.divArray[i].className = "pulse";
                    waiterObject.divArray[i].style.height = waiterObject.pulseHeight + "px";
                    // Flat
                    if (waiterObject.processBarSubType == 0) {
		                waiterObject.divArray[i].style.marginTop = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
                    } 
		            // Inward arc top
                    if (waiterObject.processBarSubType == 1) { 
		                waiterObject.divArray[i].style.marginTop = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
		                if (i <= (waiterObject.pulseNumDivs - 3) / 2) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight  - waiterObject.pulseStep;
		                } else if (i > (waiterObject.pulseNumDivs / 2)) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
		                }
                    }
                    // Up slope
		            if (waiterObject.processBarSubType == 2) {
		                waiterObject.divArray[i].style.marginTop = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
                        if (waiterObject.pulseHeight < waiterObject.pulseMaxHeight - waiterObject.pulseStep) {
                            waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
                        }
                        
                    }
                    // Down slope
		            if (waiterObject.processBarSubType == 3) {
		                waiterObject.divArray[i].style.marginTop = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
			            if (waiterObject.pulseHeight > waiterObject.pulseMinHeight + waiterObject.pulseStep) {
                            waiterObject.pulseHeight = waiterObject.pulseHeight - waiterObject.pulseStep;
                        }
                    }
		            // Arc top and bottom
                    if (waiterObject.processBarSubType == 4) { 
		                waiterObject.divArray[i].style.marginTop = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
		                waiterObject.divArray[i].style.marginBottom = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
		                if (i <= (waiterObject.pulseNumDivs - 3) / 2) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight - waiterObject.pulseStep;
		                } else if (i > (waiterObject.pulseNumDivs / 2)) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
		                }
                    }
                    // Up slope reversed
		            if (waiterObject.processBarSubType == 5) {
		                waiterObject.divArray[i].style.marginBottom = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
			            if (waiterObject.pulseHeight < waiterObject.pulseMaxHeight - waiterObject.pulseStep) {
                            waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
                        }
                    }
                    // Down slope reversed
		            if (waiterObject.processBarSubType == 6) {
		                waiterObject.divArray[i].style.marginBottom = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
			            if (waiterObject.pulseHeight > waiterObject.pulseMinHeight + waiterObject.pulseStep) {
                            waiterObject.pulseHeight = waiterObject.pulseHeight - waiterObject.pulseStep;
                        }
                    }
		            // Outward arc top
                    if (waiterObject.processBarSubType == 7) { 
		                waiterObject.divArray[i].style.marginTop = waiterObject.pulseMaxHeight - waiterObject.pulseHeight + "px";
		                if (i <= (waiterObject.pulseNumDivs - 3) / 2) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
		                } else if (i > (waiterObject.pulseNumDivs / 2)) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight - waiterObject.pulseStep;
		                }
                    }
		            // Outward arc top and bottom
                    if (waiterObject.processBarSubType == 8) { 
		                waiterObject.divArray[i].style.marginTop = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
		                waiterObject.divArray[i].style.marginBottom = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
		                if (i <= (waiterObject.pulseNumDivs - 3) / 2) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
		                } else if (i > (waiterObject.pulseNumDivs / 2)) {
			                waiterObject.pulseHeight = waiterObject.pulseHeight - waiterObject.pulseStep;
		                }
                    }
                    // Up slope from centre
		            if (waiterObject.processBarSubType == 9) {
		                waiterObject.divArray[i].style.marginTop = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
                        waiterObject.divArray[i].style.marginBottom = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
			            if (waiterObject.pulseHeight < waiterObject.pulseMaxHeight - waiterObject.pulseStep) {
                            waiterObject.pulseHeight = waiterObject.pulseHeight + waiterObject.pulseStep;
                        }
                    }
                    // Down slope from centre
		            if (waiterObject.processBarSubType == 10) {
                        waiterObject.divArray[i].style.marginTop = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
                        waiterObject.divArray[i].style.marginBottom = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
			            if (waiterObject.pulseHeight > waiterObject.pulseMinHeight + waiterObject.pulseStep) {
                            waiterObject.pulseHeight = waiterObject.pulseHeight - waiterObject.pulseStep;
                        }
                    }
		            // Centre enlage
                    if (waiterObject.processBarSubType == 11) {
                        waiterObject.pulseHeight = waiterObject.pulseMinHeight;
		                waiterObject.divArray[i].style.marginTop = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
		                waiterObject.divArray[i].style.marginBottom = (waiterObject.pulseMaxHeight - waiterObject.pulseHeight) / 2 + "px";
                    }
	            }
            }
        }



        if (waiterObject.message) {
            var waitP1El = document.createElement('div');
            waitP1El.id = waiterObject.objName + '_P';
            waitP1El.className = 'waitMessage';
            waitP1El.innerHTML = waiterObject.message;
            waitContainerEl.appendChild(waitP1El);
        }



        if (waiterObject.message2) {
            var waitP2El = document.createElement('div');
            waitP2El.id = waiterObject.objName + '_P2';
            waitP2El.className = 'waitMessage2';
            waitP2El.innerHTML = waiterObject.message2;
            waitContainerEl.appendChild(waitP2El);
        }

        document.body.appendChild(waitContainerEl);

        // Can we get the width / hight set in the CSS
        waiterObject.width = CSS.getCuomputedStyleOrValue(waiterObject.objName + '_pleaseWait', 'width', waiterObject.width)
        waitContainerEl.style.width = DOM.addPX(waiterObject.width );
        waiterObject.height = CSS.getCuomputedStyleOrValue(waiterObject.objName + '_pleaseWait', 'height', waiterObject.height)
        waitContainerEl.style.height = DOM.addPX(waiterObject.height);
        if (window.Position && waiterObject.containerElement) {
            var containerPos = Position.get(waiterObject.containerElement);
            var waitContainerPos = Position.get(waitContainerEl);
            if (waiterObject.hPos == 'center') {
                waitContainerEl.style.left = containerPos.left + ((containerPos.width / 2) - (waitContainerPos.width / 2)) + "px";
            }
            if (waiterObject.hPos == 'left') {
                waitContainerEl.style.left = containerPos.left + "px";
            }
            if (waiterObject.hPos == 'right') {
                waitContainerEl.style.left = ((containerPos.left + containerPos.width) - waitContainerPos.width) + "px";
            }
            if (waiterObject.vPos == 'center') {
                waitContainerEl.style.top = containerPos.top + ((containerPos.height / 2) - (waitContainerPos.height / 2)) + "px";
            }
            if (waiterObject.vPos == 'top') {
                waitContainerEl.style.top = containerPos.top + "px";
            }
            if (waiterObject.vPos == 'bottom') {
                waitContainerEl.style.top = ((containerPos.top + containerPos.height) - waitContainerPos.height) + "px";
            }
        } else {
            var fullPageSize = getPagePosScroll(true);
            var scrollOffset = getPagePosScroll(false);
            var midX = (fullPageSize[0] - scrollOffset[0]) / 2;
            var midY = (fullPageSize[1] - scrollOffset[1]) / 2;
            waitContainerEl.style.left = (midX - (waiterObject.width / 2)) + scrollOffset[0] + "px";
            waitContainerEl.style.top = (midY - (waiterObject.height / 2)) + scrollOffset[1] + "px";
        }
        
        if (!this.useBlanket) {
            // IFRAME shim
            if (sniffer.is_ie5_5up) {
                var iframe = document.createElement('iframe');
                if (document.location.protocol == "https:") {
                    var blankFile = globPathPrefix + '/globals/blank.html';
                    iframe.setAttribute('src', blankFile);
                } else {
                    iframe.setAttribute('src', 'JavaScript:void(0);');
                }
                iframe.id = waiterObject.objName + '_wait_iframe';
                iframe.setAttribute('frameborder', '0');
                iframe.setAttribute('scrolling', 'no');
                iframe.style.position = 'absolute';
                iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
                iframe.style.zIndex = waitContainerEl.style.zIndex - 1;
                iframe.style.left = waitContainerEl.style.left;
                iframe.style.top = waitContainerEl.style.top;
                iframe.style.width = waitContainerEl.style.width;
                iframe.style.height = waitContainerEl.style.height;
                if (waitContainerEl.nextSibling) {
                    waitContainerEl.parentNode.insertBefore(iframe, layer.nextSibling);
                } else {
                    waitContainerEl.parentNode.appendChild(iframe);
                }
            }
        }
        
        if (waiterObject.useProgressBar) {
            switch(waiterObject.processBarType) {
                case 0:
                    var progressBarWidth = CSS.getCuomputedStyleOrValue(waiterObject.objName + '_progress_bar', 'width', waitContainerEl.style.width);
                    waitEl.style.width = DOM.addPX(parseInt(progressBarWidth));
                    break;
                case 1:
                    var progressBarWidth = CSS.getCuomputedStyleOrValue(waiterObject.objName + '_xpBar', 'width', waitContainerEl.style.width);
                    break;
                case 2:
                    var progressBarWidth = CSS.getCuomputedStyleOrValue(waiterObject.xpBarClassName, 'width', waitContainerEl.style.width);
                    break;
            }

            // Keep the progressbar within the container.
            var containerWidth = parseInt(getStyle(waiterObject.objName + '_pleaseWait' ,'width'));
            if (progressBarWidth > containerWidth) {
                var allPadding = 0
                allPadding += CSS.getCuomputedStyleOrValue(waiterObject.objName + '_pleaseWait', 'paddingLeft', 0);
                allPadding += CSS.getCuomputedStyleOrValue(waiterObject.objName + '_pleaseWait', 'paddingRight', 0);
                allPadding += CSS.getCuomputedStyleOrValue(waiterObject.objName + '_pleaseWait', 'borderLeft', 0);
                allPadding += CSS.getCuomputedStyleOrValue(waiterObject.objName + '_pleaseWait', 'borderRight', 0);
                allPadding += 4; // Magic number  
                if (waiterObject.processBarType == 0) {
                    waitEl.style.width = DOM.addPX(containerWidth - allPadding);
                }
                if (waiterObject.processBarType == 1) {
                    XPbarEl.style.width = DOM.addPX(containerWidth - allPadding);
                }
            }
            
            // Do the animation
            switch (waiterObject.processBarType) {
                case 0:
                    waiterObject.startProgressBar();
                    break;
                case 1:
                    waiterObject.startXPProgressBar();
                    break;
                case 2:
                    waiterObject.startSprite();
                    break;
                case 3:
                    waiterObject.startBlockDivs();
                    break;
            }
        }
    
        // Show waiter
        waitContainerEl.style.visibility = 'visible';

        // Do onClick
        if (this.nodeName == "A") {
            if (typeof this.oOnClick == "function") {
                curResult = this.oOnClick();
            }
        }

        // Test only
        if (AOT_WAITER.testMode && this.nodeName != "A") {
            alert("You are in test mode!\n\nWait will not end, press refreash.");
            curResult = false;
        }
    }
    
    return curResult;
}

/**
 * Waiter.endWait - Removes the waiter from the DOM.
 * @requires DOM
 * @extends Waiter
 * @private
 */
Waiter.prototype.endWait = function()
{
    if (this.showTimer) {
        this.showTimer = false;
        alert("Stopped to show timer.");
    }
    document.body.style.cursor = "default";
    document.removeOnKeyUpHandler(this.checkKey);
    if (this.timercallwrapper) {
        this.timercallwrapper.cancel();
    }
    this.startTime = null;
    if (this.progressBarcallwrapper) {
        this.stopProgressBar();
    }
    var waitContainerObj = document.getElementById(this.objName + '_pleaseWait');
    if (waitContainerObj) {
        // Remove children
        DOM.removeChildrenFromNode(waitContainerObj);
        // Remove container node.
        DOM.removeNode(waitContainerObj);
    }

    if (this.useBlanket) {
        // Remove blanket
        var blanketObj = document.getElementById(this.objName + '_waitBlanket');
        if (blanketObj) {
            DOM.removeNode(blanketObj);
        }
        // Remove IFRAME
        var blanketIFrameObj = document.getElementById(this.objName + '_waitBlanket_iframe');
        if (blanketIFrameObj) {
            DOM.removeNode(blanketIFrameObj);
        }
    } else {
        var waitIFrameObj = document.getElementById(this.objName + '_wait_iframe');
        if (waitIFrameObj) {
            DOM.removeNode(waitIFrameObj);
        }
    }
}

/**
 * Waiter.startProgressBar - Start the default progress bar anomation.
 * @extends Waiter
 * @private
 */
Waiter.prototype.startProgressBar = function()
{
    this.progressBarEl = document.getElementById(this.objName + "_progress_bar");
    this.progressBarMaxWidth = parseInt(this.progressBarEl.style.width);
    if (this.progressBarBounce) {
        this.progressBarEl.style.width =  this.progressBarMinWidth + "px";
    }
    this.progressBar();
}

/**
 * Waiter.stopProgressBar - Halts a progress bar.
 * @extends Waiter
 * @private
 */
Waiter.prototype.stopProgressBar = function()
{
    this.progressBarcallwrapper.cancel();
}

/**
 * Waiter.progressBar - Default progress bar animation.
 * @requires CCallWrapper
 * @requires DOM
 * @extends Waiter
 * @private
 */
Waiter.prototype.progressBar = function()
{
    var intWidth = parseInt(this.progressBarEl.style.width) + this.progressBarDir;
    
    if (this.progressBarBounce) {
        if (intWidth > this.progressBarMaxWidth || intWidth < this.progressBarMinWidth) {
            this.progressBarDir = -1 * this.progressBarDir;
        } else {
            this.progressBarEl.style.width = DOM.addPX(intWidth);
        }
    } else {
        if (intWidth <= this.progressBarMaxWidth) {
            this.progressBarEl.style.width = DOM.addPX(intWidth);
        } else {
            this.progressBarEl.style.width = DOM.addPX(this.progressBarMinWidth);
        }
    }
    
    this.progressBarcallwrapper = new CCallWrapper(this, this.progressBarSpeed, 'progressBar');
    CCallWrapper.asyncExecute(this.progressBarcallwrapper);
}

/**
 * Waiter.startXPProgressBar - Start XP style progress bar animation.
 * @requires CSS
 * @extends Waiter
 * @private
 */
Waiter.prototype.startXPProgressBar = function()
{
    this.progressBarEl = document.getElementById(this.objName + "_xpBlocks");
    this.progressBarMaxWidth = CSS.getCuomputedStyleOrValue(this.objName + '_xpBar', 'width', this.width);
    
    this.xpProgressBar();
}            

/**
 * Waiter.xpProgressBar - Do XP style progress bar animation.
 * @requires CCallWrapper
 * @extends Waiter
 * @private
 */        
Waiter.prototype.xpProgressBar = function()
{
    if (parseInt(this.progressBarEl.style.left) + this.blockSize + 1 - (this.noBlocks * this.blockSize + this.noBlocks) > this.progressBarMaxWidth) {
        this.progressBarEl.style.left = -(this.blockSize * 2 + 1) + 'px';
    } else {
        this.progressBarEl.style.left = (parseInt(this.progressBarEl.style.left) + this.blockSize + 1) + 'px';
    }
    this.progressBarcallwrapper = new CCallWrapper(this, this.progressBarSpeed, 'xpProgressBar');
    CCallWrapper.asyncExecute(this.progressBarcallwrapper);
}

/**
 * Waiter.startSprite - Start CSS sprite animation.
 * @extends Waiter
 * @private
 */
Waiter.prototype.startSprite = function()
{
    this.progressBarEl = document.getElementById(this.xpBarClassName);
    this.progressBarMaxWidth = 0; // Offset
    
    this.sprite();
} 

/**
 * Waiter.sprite - CSS sprite based animation.
 * @requires CCallWrapper
 * @extends Waiter
 * @private
 */
Waiter.prototype.sprite = function()
{
    var offset = this.progressBarMaxWidth;
    if (offset < ((this.noBlocks - 1) * this.blockSize)) {
        offset += this.blockSize;
    } else {
        offset = 0;
    }
    this.progressBarMaxWidth = offset;
    this.progressBarEl.style.backgroundPosition = '0px ' + -offset + 'px';
    this.progressBarcallwrapper = new CCallWrapper(this, this.progressBarSpeed, 'sprite');
    CCallWrapper.asyncExecute(this.progressBarcallwrapper);
}

/**
 * Waiter.prototype.startBlockDivs - Start Div block based amination.
 * @extends Waiter
 * @private
 */
Waiter.prototype.startBlockDivs = function()
{
    for (var i = 0; i < this.pulseNumDivs; i++) {
        if (this.processBarSubType != 11) {
            this.divArray[i].style.visibility = 'hidden';
        }
    }    
    this.blockDivs();
}

/**
 * Waiter.prototype.blockDivs - Div block based amination.
 * @extends Waiter
 * @requires CCallWrapper
 * @private
 */
Waiter.prototype.blockDivs = function()
{
    this.currentDiv++;
    if (this.currentDiv >= this.pulseNumDivs) {
        this.currentDiv = 0;
        for (var i = 0; i < this.pulseNumDivs; i++) {
            if (this.processBarSubType != 11) {
                this.divArray[i].style.visibility = 'hidden';
            } else {
	            this.divArray[i].style.height = this.pulseHeight;
    	        this.divArray[i].style.marginTop = (this.pulseMaxHeight - this.pulseHeight) / 2 + "px";
		        this.divArray[i].style.marginBottom = (this.pulseMaxHeight - this.pulseHeight) / 2 + "px";
            }
        }
    }
    if (this.processBarSubType != 11) {
        this.divArray[this.currentDiv].style.visibility = 'visible';
    } else {
        this.divArray[this.currentDiv].style.height = this.pulseMaxHeight;
        this.divArray[this.currentDiv].style.marginTop = 0;
        this.divArray[this.currentDiv].style.bottomTop = 0;
        if (this.currentDiv > 0) {
	        this.divArray[this.currentDiv - 1].style.height = this.pulseHeight;
    	    this.divArray[this.currentDiv - 1].style.marginTop = (this.pulseMaxHeight - this.pulseHeight) / 2 + "px";
		    this.divArray[this.currentDiv - 1].style.marginBottom = (this.pulseMaxHeight - this.pulseHeight) / 2 + "px";
        }
    }
    this.progressBarcallwrapper = new CCallWrapper(this, this.progressBarSpeed, 'blockDivs');
    CCallWrapper.asyncExecute(this.progressBarcallwrapper);
}

/**
 * Waiter.displayTimer - Show a real time timer during wait.
 * @requires CCallWrapper
 * @extends Waiter
 * @private
 */
Waiter.prototype.displayTimer = function()
{
    var tDate = new Date();
    var tDiff = tDate.getTime() - this.startTime.getTime();
    var tSecs = (tDiff / 1000) | 0;
    var tMins = (tSecs / 60) | 0;
    var tHrs = (tMins / 60) | 0 ;
    tMins = tMins - tHrs * 60;
    tSecs = tSecs - tMins * 60 - tHrs * 60 * 60;
    if (tHrs < 10)  {
        tHrs  = '0' + tHrs;
    }
    if (tMins < 10) {
        tMins = '0' + tMins;
    }
    if (tSecs < 10) {
        tSecs = '0' + tSecs;
    }
    document.getElementById(this.objName + '_P').innerHTML = this.message + ' [' + tHrs + ':' + tMins + ':' + tSecs + ']';    
    this.timercallwrapper = new CCallWrapper(this, 1000, 'displayTimer');
    CCallWrapper.asyncExecute(this.timercallwrapper);
}

/**
 * Waiter.removeTimer - Stop the timer and remove from page.
 * @extends Waiter
 * @private
 */
Waiter.prototype.removeTimer = function()
{
    this.timercallwrapper.cancel();
    document.getElementById(this.objName + '_P').innerHTML = this.message; 
}

/**
 * Waiter.checkKey - Check for Ctrl-Shift-T.
 * @param {event} e Browser event
 * @extends Waiter
 * @private
 */
Waiter.prototype.checkKey = function(e)
{
    var evt = e ? e : window.event;
    var shiftPressed = evt.shiftKey;
    var altPressed = evt.altKey;
    var ctrlPressed = evt.ctrlKey;
    
    if (typeof(evt) == 'string') {
        if (evt == "1") {
            shiftPressed = false;
            altPressed = false;
            ctrlPressed = true;
        }
    }
    var code;

    if (evt.keyCode) {
        code = evt.keyCode;
    } else if (evt.which) {
        code = evt.which;
    }
    if (shiftPressed && ctrlPressed && !altPressed) {
        if (code == 20) {
            AOT_WAITER.activateTimers();
        }
    }
}

/**
 * Waiter.attachToForm - Attach a waiter to a form.
 * @param {string} frmId form ID
 * @param {string} frmId form NAME (only used if frmId is null)
 * @extends Waiter
 */
Waiter.prototype.attachToForm = function(frmId, frmName)
{
    if (!document.getElementById) {
        return;
    }
    if (frmId) {
        var frm = document.getElementById(frmId);
    } else {
        var frm = document.frams[frmName];
    }
    frm.oOnSubmit = frm.onsubmit;
    frm.waiterName = this.objName;
    frm.onsubmit = this.doSubmitWithWait;

    // Dummp DIV to force imge to load
    var waitEl0 = document.createElement('div'); 
    waitEl0.className = this.progressBarClassName;
    waitEl0.id = this.objName + '_temp_progressbar';
    waitEl0.style.position = 'absolute';
    waitEl0.style.top = "0px";
    waitEl0.style.left = "-1000px";
    waitEl0.style.visibility = 'hidden';
        
    document.body.appendChild(waitEl0);
    //document.body.removeChild(waitEl0);
/*  
    this is an attempt to establish a preloadable element

    var spriteContainerEl0 = document.createElement('div');
    var spriteEl0 = document.createElement('div');
    spriteEl0.id = this.xpBarClassName;
    spriteEl0.className = 'imageReplace';
    spriteContainerEl0.style.position = 'absolute';
    spriteContainerEl0.style.top = "0px";
    spriteContainerEl0.style.left = "-1000px";
    spriteContainerEl0.style.visibility = 'hidden';
    spriteContainerEl0.appendChild(spriteEl0);  
    document.body.appendChild(spriteContainerEl0); 

*/
}

/**
 * Waiter.attachToPage - Attach a waiter to ALL forms on a page. 
 * [Append _noWait to ID of form to exclude]
 * @extends Waiter
 */
Waiter.prototype.attachToPage = function()
{
    if (!document.getElementById) {
        return;
    }
    var formEls = document.getElementsByTagName('form');
    for (var i = 0; i < formEls.length; i++) {
      var mask = new RegExp("_noWait$", "i");
      var id = formEls[i].id;
      if (!id.match(mask)) {
          formEls[i].oOnSubmit = formEls[i].onsubmit;
          formEls[i].waiterName = this.objName;
          formEls[i].onsubmit = this.doSubmitWithWait;
      }
    }
}

/**
 * Waiter.attachToLink - Attach a waiter to a link (for Ajax calls).
 * @param {string} linkId Link ID
 * @extends Waiter
 */
Waiter.prototype.attachToLink = function(linkId)
{
    if (!document.getElementById) {
        return;
    }
    var linkEl = document.getElementById(linkId);

    linkEl.oOnClick = linkEl.onclick;
    linkEl.waiterName = this.objName;
    linkEl.onclick = this.doSubmitWithWait;
}

/**
 * Waiter.setWaitClassName - Set class name for waiter DIV.
 * @param {string} className Name of class 
 * @extends Waiter
 */
Waiter.prototype.setWaitClassName = function(className)
{
    this.waitClassName = className;
}

/**
 * Waiter.setUseBlanket - Set if to use the blanket DIV.
 * @param {boolean} value True to use blanket 
 * @extends Waiter
 */
Waiter.prototype.setUseBlanket = function(value)
{
    this.useBlanket = value;
}

/**
 * Waiter.setProgressBarClassName - Set clas sname for the progress bar DIV.
 * @param {string} className Name of class 
 * @extends Waiter
 */
Waiter.prototype.setProgressBarClassName = function(className)
{
    this.progressBarClassName = className;
}

/**
 * Waiter.setBlanketClassName - Set clas sname for the blanket DIV.
 * @param {string} className Name of class 
 * @extends Waiter
 */
Waiter.prototype.setBlanketClassName = function(className)
{
    this.blanketClassName = className;
}

/**
 * Waiter.setImageId - Set the CSS ID for the image.
 * @param {string} id CSS ID
 * @extends Waiter
 */
Waiter.prototype.setImageId = function(id)
{
    this.imageId = id;
}

/**
 * Waiter.setWidth - Set width of waiter DIV.
 * This is a default, CSS value overides if it can.
 * @param {integer} w Width in px
 * @extends Waiter
 */
Waiter.prototype.setWidth = function(w)
{
    this.width = w;
}

/**
 * Waiter.setHeight - Set height of waiter DIV.
 * This is a default, CSS value overides if it can.
 * @param {integer} h Height in px
 * @extends Waiter
 */
Waiter.prototype.setHeight = function(h)
{
    this.height = h;
}

/**
 * Waiter.setUseProgressBar - Display a progress bar / animation.
 * @param {boolean} value True to have progress bar
 * @extends Waiter
 */
Waiter.prototype.setUseProgressBar = function(value)
{
    this.useProgressBar = value;
}

/**
 * Waiter.setMessage - Message to display at top of waiter DIV.
 * @param {string} str Initial message
 * @extends Waiter
 */
Waiter.prototype.setMessage = function(str)
{
    this.message = str;
}

/**
 * Waiter.setMessage2 - Message to display after progress bar / animation.
 * @param {string} str Secondary message
 * @extends Waiter
 */
Waiter.prototype.setMessage2 = function(str)
{
    this.message2 = str;
}

/**
 * Waiter.setProgressBarBounce - Reverse the animation at end.
 * @param {boolean} value True to bounce progress bar
 * @extends Waiter
 */
Waiter.prototype.setProgressBarBounce = function(value)
{
    this.progressBarBounce = value;
}

/**
 * Waiter.setProgressBarSpeed - Set speed of progress bar animation.
 * @param {integer} value Speed
 * @extends Waiter
 */
Waiter.prototype.setProgressBarSpeed = function(value)
{
    this.progressBarSpeed = value;
}

/**
 * Waiter.setContainerElement - Make the waiter be contained withen a DOM element.
 * @param {object} el DOM element
 * @extends Waiter
 */
Waiter.prototype.setContainerElement = function(el)
{
    this.containerElement = el;
}

/**
 * Waiter.setVPos - Set vertical position of waiter withen container element.
 * @param {string} pos Position: center, top, bottom
 * @returns {boolean} False on invaled pos
 * @extends Waiter
 */
Waiter.prototype.setVPos = function(pos)
{
    if (pos == "centre" || pos == "middle") {
        pos = "center";
    }
    if (pos != "center" && pos != "top" && pos != "bottom") {
        alert("VPos not valid!");
        return false;
    }
    this.vPos = pos;
}

/**
 * Waiter.setHPos - Set horizontal position of waiter withen container element.
 * @param {string} pos Position: center, left, right
 * @returns {boolean} False on invaled pos
 * @extends Waiter
 */
Waiter.prototype.setHPos = function(pos)
{
    if (pos == "centre" || pos == "middle") {
        pos = "center";
    }
    if (pos != "center" && pos != "left" && pos != "right") {
        alert("HPos not valid!");
        return false;
    }
    this.hPos = pos;
}

/**
 * Waiter.setBlanketElement - Make the blanket cover a DOM element.
 * @param {object} el DOM element
 * @extends Waiter
 */
Waiter.prototype.setBlanketElement = function(el)
{
    this.blanketElement = el;
}

/**
 * Waiter.setProgressBarMinWidth - Set minimum width of progress bar, used by bouncing image.
 * @param {integer} width The minimum width. normaly the same as the width of the image to bounce.
 * @extends Waiter
 */
Waiter.prototype.setProgressBarMinWidth = function(width)
{
    this.progressBarMinWidth = width;
}

/**
 * Waiter.setProcessBarType - Sets type of brogress bar.
 * @param {integer} value 0 = Default background expand, 1 = XP faiding boxes, 2 = CSS sprite, 3 = pulsing Div blocks
 * @extends Waiter
 */
Waiter.prototype.setProcessBarType = function(value)
{
    this.processBarType = value;
}

/**
 * Waiter.setProcessBarSubType - Sets sub type of brogress bar.
 * @param {integer} value 0 = ????
 * @extends Waiter
 */
Waiter.prototype.setProcessBarSubType = function(value)
{
    this.processBarSubType = value;
}

/**
 * Waiter.setXPBar - Set-up XP style progress bar.
 * @param {string} barClass Class name for progress bar container
 * @param {string} blockClass Class name for block
 * @param {integer} noBlocks Number of blocks to draw
 * @param {integer} blockSize Width / height of square block
 * @extends Waiter
 */
Waiter.prototype.setXPBar = function(barClass, blockClass, noBlocks, blockSize)
{
    this.xpBarClassName = barClass;
    this.xpBlockClassName = blockClass;
    this.noBlocks = noBlocks;
    this.blockSize = blockSize;
}

/**
 * Waiter.prototype.setSprite - Set-up CSS sprite animation.
 * @param {string} imageClass Class name for image
 * @param {integer} noFrames Number of frames in animation
 * @param {integer} h Hight of each image (Images are stacked verticaly)
 * @extends Waiter
 */
Waiter.prototype.setSprite = function(imageClass, noFrames, h)
{
    this.xpBarClassName = imageClass;
    this.noBlocks = noFrames;
    this.blockSize = h;
}

/**
 * Waiter.prototype.Configure - JSON driven configurator.
 * @param {JSON} args JSON parameters
 * @requires ParseJSON
 * @extends Waiter
 */
Waiter.prototype.Configure = function(args)
{
//    args = args.parseJSON();
    args = eval("(" + args + ")");

    for (var i in args) {
        if (typeof(this[i]) != "undefined") {
            this[i] = args[i];
        }
    }
    
}

/**
 * Create a global instance of the waiter
 */
AOT_WAITER = new Global_Waiter();

if (window.JSLib) {
    JSLib.setLoaded("Waiter.js");
}

