/* 
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
*/

//
// ElementUtils.js
//

function GetTagForElement(element) 
{
    if (! element)
        return "[null]";
    if (element == window)
        return "[" + (element==top?"top ":"") + "window" + (window.name?" "+window.name:"") + "]";
    if (element == document)
        return "[document]";
    if (!element.tagName && element.constructor)
        return "[" + element.constructor + "]";
    else
        return "<" + (element.tagName ? element.tagName : "?") + 
                (element.id ? " id=\"" + element.id + "\"" : "") +
                //" id=\"" + (element.id ? element.id : "?") + "\"" +
                ">"
}

/*
Function hide control if hide parameter is true
controlId - is an id for the control to hide
*/
function HideControl(controlId, hide)
{
	
    var control = document.getElementById(controlId);

    
    if(control != null)
    {
        if(hide)
        {
            control.style.visibility = "hidden";
            control.style.display="none";       
        }
        else
        {
            control.style.visibility = "visible";
            control.style.display="block";       
        }
    }
}

// Given an elementId, will show the element if it is hidden or hide the element if it is shown.
// If also given an imageId, it will adjust the image accordingly.
function ToggleShowHideElement(elementId, imageId, toShowImageSrc, toHideImageSrc, autoScrollEnabled)
{
    //alert("Entered ToggleShowHideElement(" + elementId + ", " + imageId + ", " + toShowImageSrc + ", " + toHideImageSrc + ")");
    
    autoScrollEnabled = new Boolean(autoScrollEnabled).valueOf();
    
    var area = document.getElementById(elementId);
    if (! area)
    {
        alert("ToggleShowHideElement():  Element with id " + elementId + " not found.");
        return;
    }
    
    var shouldShow = (area.style.display == "none");
    //alert("ToggleShowHideElement():  shouldShow="+shouldShow);
    if (shouldShow)
    {
        area.style.display = "";
        if (autoScrollEnabled) ScrollElementIntoView(elementId)
    }
    else
        area.style.display = "none";
    
    if (imageId && toShowImageSrc && toHideImageSrc) 
    {
        var image = document.getElementById(imageId);
        if (! image)
        {
            alert("ToggleShowHideElement():  Image with id " + imageId + " not found.");
            return;
        }
        
        //if (image.name) image = document.images[image.name];
        
        if (shouldShow)
        {
            //alert("ToggleShowHideElement() Updating image to " + toHideImageSrc);
            image.src = toHideImageSrc;
            image.alt = "hide";
        }
        else
        {
            //alert("ToggleShowHideElement() Updating image to " + toShowImageSrc);
            image.src = toShowImageSrc;
            image.alt = "show";
        }
        //alert("ToggleShowHideElement() Done Updating image");
    }
    
} // ToggleShowHideElement()

/*
Moves control at top and left coordinates
*/
function MoveControl(controlId, top, left)
{
    var e = document.getElementById(ctlId);
    if(e != null)
    {
        e.style.top = top;
        e.style.left = left;
    }
}

/*
    Moves control to the center of the screen
*/
function MoveToCenter(controlId, width)
{
    var control = document.getElementById(controlId);
    if(control != null)
    {
        var h = window.innerHeight?window.innerHeight:document.body.clientHeight;
        var w = window.innerWidth?window.innerWidth-20:document.body.clientWidth-0;
        var top = (h / 2) - (control.offsetHeight / 2);
        var left = (w / 2) - ((control.offsetWidth == 0 ? width :control.offsetWidth)  / 2);
        control.style.top = top;
        control.style.left = left;
    }
}
/*
Moves control identified by moveId next to control identified by targetId.
alignment can be "vertical" or "horizontal", "left", "right", "top", "bottom"
When alignment is "vertical" control will be placed on top or bottom side of target control
based on the fact if it fits in visual area.
When alignment is "horizontal" control will be placed on left or right side of target control
based on the fact if it fits in visual area.
When alignment is "left", "top", "right", "bottom", control will be placed on left, top, right, or bottom
side of target control.
*/
//debugger;
function MoveNextTo(moveId, targetId, alignment)
{
    
    // object to move from bottom of page
    var m = document.getElementById(moveId);
    // td call came from 
    var t = document.getElementById(targetId);
    if(t == null)
    {
        //if control not found, then try to find it by name
        var arr = document.getElementsByName(targetId);
        if(arr.length > 0)
           t = arr[0];
    }

    if(alignment == null) alignment = "bottom";
    
    if(m != null && t != null)
    {
        //move control to 0 coordinates to calculate its scroll offset
        m.style.top = 0;
        m.style.left = 0;
        //get control real position relative to the screen
        var cTop = GetElementTop(m);
        var cLeft = GetElementLeft(m);
        //Get real position of target control
        var top = GetElementTop(t);
        var left = GetElementLeft(t);
        //make sure that control we move has aboslute position and it is visible
        //m.style.position = "absolute";
        m.style.display="block";
        m.style.visibility="visible";
 
        var controlWidth = m.offsetWidth;
        var controlHeight = m.offsetHeight;
        
        var screenWidth = GetWindowWidth();
        var screenHeight = GetWindowHeight();
        //now lets calculate positions //TODO:
         
        switch(alignment)
        {
            case "vertical":
                break;
            case "horizontal":
                break;
            case "left":
                break;
            case "top":
                break;
            case "right":
                break;
            case "bottom":
            //GAB:  Adding a few pixels here as a hack to fix IE issue.
            //TODO:  figure out why IE is setting the popup in a different stacking context.
            // test for firefox 2+ if so add 21px to height
                if (Array.every)
                { 
                m.style.top = top - cTop + t.offsetHeight + 21;
                var bottom_left = left - cLeft;
               //if(bottom_left + controlWidth > screenWidth)
                //{
                    // bottom_left -= controlWidth;
              // }
               }
                 else 
                {
                 m.style.top = top - cTop + t.offsetHeight + 12;
                var bottom_left = left - cLeft;
                };
                 m.style.left = bottom_left + 1;
                break;
            default:
                m.style.top = top - cTop + t.offsetHeight + 2;
                m.style.left = left - cLeft + t.offsetWidth + 2;
                break;
        }
        
        /*
        m.style.position = "absolute";
        m.style.display="block";
        m.style.visibility="visible";

        var windowRight = GetWindowRight();
        
        var targetTop = GetElementTop(t);
        var targetBottom = GetElementBottom(t);
        var targetLeft = GetElementLeft(t);
        var targetRight = GetElementRight(t);
        
        switch(alignment)
        {
            case "vertical":
                break;
            case "horizontal":
                break;
            case "left":
                break;
            case "top":
                break;
            case "right":
                break;
            case "bottom":
            default:
                //AlertElementDimensions(m, "before", true);
            
                SetElementTop(m, targetBottom + 1);
                SetElementLeft(m, targetLeft + 1);

                //AlertElementDimensions(m, "after", true);
                
                var newRight = GetElementRight(m);
                if (newRight > windowRight) 
                {
                    SetElementLeft(m, GetElementLeft(m) - (newRight - windowRight));
                }
                break;
        }
        */
    }
} // MoveNextTo()

function SetElementOpacity(element, opacity)
{
    if (GetIsIE()) 
    {
        // Note - this will apply to the layout of the element.  Sizes matter.
        if (opacity == 100)
            element.style.filter = "";
        else
        {
            //if (!element.hasLayout) element.style.display = "inline-block";
            element.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + opacity + "), Style=0";
        }
    }
    else
        element.style.MozOpacity = (opacity / 100);
}


function GetElementOpacity(element)
{
    var currentOpacity;
    if (GetIsIE()) 
    {
        currentOpacity = 100;
        if (element.style.filter)
        {
            var result = element.style.filter.match(/progid:DXImageTransform.Microsoft.Alpha\(opacity=([\d\.]+)\)/);
            if (result != null)
                currentOpacity = parseInt(result[1]);
            //else alert("Non Alpha filter?: " + element.style.filter);
        }
    }
    else 
    {
        if (element.style.MozOpacity)
            currentOpacity = (element.style.MozOpacity * 100);
        else
            currentOpacity = 100;
    }
    return currentOpacity;
}




function FadeElement(element, desiredOpacity, totalDuration, maxSteps, stepDuration)
{
//    if (GetIsIE() && (!element.hasLayout)) 
//    {
//        alert("Element " + GetTagForElement(element) + " does not have layout. Fading won't work.");
//        return;
//    }
    
    if (! maxSteps) maxSteps = 50;
    desiredOpacity = parseInt(desiredOpacity);
    
    var currentOpacity = GetElementOpacity(element);
    
    var delta = desiredOpacity - currentOpacity;
    
    var steps = Math.abs(delta);    // Start with a step per opacity unit.  
    steps = Math.min(steps, maxSteps);
    if (steps == 0) steps = 1;
    
    var fadeSteps = (delta / steps);    // amount of fading per step
    
    var timerName = element.id + "_fadeTimer";
    var timerCounterName = element.id + "_fadeTimerCounter";
    window[timerCounterName] = 0;
    
    if (totalDuration) stepDuration = totalDuration / steps;
    stepDuration = Math.max(stepDuration, 1);
    
    /*
    var msg = "FadeElement()\n" +
          "element=|" + GetTagForElement(element) + "|\n" +
          "currentOpacity=|" + currentOpacity + "|\n" +
          "desiredOpacity=|" + desiredOpacity + "|\n" +
          "delta=|" + delta + "|\n" +
          "stepDuration=|" + stepDuration + "| steps=|" + steps + "| totalDuration=|" + totalDuration + "| \n" +
          "fadeSteps=|" + fadeSteps + "| (amount of fading per step)\n" +
          "timerName=|" + timerName + "|\n" +
          "";
    UpdateReportMonitor(element.id + "_fade", msg);
    //alert(msg);
    */
    
    var funcString = "var func = function() { _fadeElementTimerCallback(element, desiredOpacity, fadeSteps, timerName, timerCounterName); }";
    eval(funcString);
    StartTimer(timerName, stepDuration, func, true)
}

function _fadeElementTimerCallback(element, desiredOpacity, fadeSteps, timerName, timerCounterName)
{
    window[timerCounterName] = window[timerCounterName] + 1;
    if (window[timerCounterName] > 100)
    {
        alert("Endless loop detected.  Stopping.  timerCounter=" + window[timerCounterName]);
        StopTimer(timerName);
        //if (onCompletion) StartTimer("x", 0, onCompletion, false);
        return;
    }
    
    var currentOpacity = GetElementOpacity(element);
    
    // Are we where we need to be?
    if (currentOpacity == desiredOpacity)
    {
        //alert("Stopping : currentOpacity=|" + currentOpacity + "| desiredOpacity=|" + desiredOpacity + "|");    // onCompletion=|" + onCompletion + "|"
        StopTimer(timerName);
        //if (onCompletion) StartTimer("x", 0, onCompletion, false);
        return;
    }

    var newOpacity = currentOpacity + fadeSteps;
    if (currentOpacity != desiredOpacity)
    {
        // if we're within a step, then finish it.  This is in case of rounding error.
        if (Math.abs(desiredOpacity - currentOpacity) <= Math.max(Math.abs(fadeSteps),1)) newOpacity = desiredOpacity;
    }
    
    /*
    var msg = "Fade timerCounter=" + window[timerCounterName] + "\n" +
                "currentOpacity=|" + currentOpacity + "| fadeSteps=|" + fadeSteps + "| newOpacity=|" + newOpacity + "| desiredOpacity=|" + desiredOpacity + "|\n" +
                "";
    UpdateReportMonitor(timerName, msg);
    */
    
    /*
    if (confirm(msg +
                "\n" + 
                "\n" +
                "Click OK to stop the fade.  Escape or cancel to continue moving. \n" +
                ""))
    {
        StopTimer(timerName);
        //if (onCompletion) StartTimer("x", 0, onCompletion, false);
        return;
    }
    */    
    
    //alert("Fading to newOpacity=|" + newOpacity + "|");
    SetElementOpacity(element, newOpacity);

}



function MoveElement(element, desiredLeft, desiredTop, totalDuration, maxSteps, stepDuration, onCompletion)
{
    var reportEnabled = false;
    if (! maxSteps) maxSteps = 50;
    
    desiredLeft = parseFloat(desiredLeft);
    desiredTop = parseFloat(desiredTop);
    
    var currentLeft = GetElementLeft(element);
    var currentTop = GetElementTop(element);
    
    var deltaLeft = desiredLeft - currentLeft;
    var deltaTop = desiredTop - currentTop;
    
    var steps = Math.max(Math.abs(deltaLeft), Math.abs(deltaTop));
    steps = Math.min(steps, maxSteps);
    if (steps == 0) steps=1;
    
    var leftSteps = (deltaLeft / steps);
    var topSteps = (deltaTop / steps);
    
    var timerName = element.id + "_moveTimer";
    var timerCounterName = element.id + "_moveTimerCounter";
    window[timerCounterName] = 0;
    
    if (totalDuration) stepDuration = totalDuration / steps;
    stepDuration = Math.max(stepDuration, 1);

    if (reportEnabled)
    {
        var msg = "MoveElement()\n" +
              "element=|" + GetTagForElement(element) + "|\n" +
              "currentLeft=|" + currentLeft + "| currentTop=|" + currentTop + "|\n" +
              "desiredLeft=|" + desiredLeft + "| desiredTop=|" + desiredTop + "|\n" +
              "deltaLeft=|" + deltaLeft + "| deltaTop=|" + deltaTop + "|\n" +
              "stepDuration=|" + stepDuration + "| totalDuration=|" + totalDuration + "| steps=|" + steps + "|\n" +
              "leftSteps=|" + leftSteps + "| topSteps=|" + topSteps + "| (pixels per step)\n" +
              "timerName=|" + timerName + "|\n" +
              "onCompletion=|" + onCompletion + "| \n" +
              //GetElementDimensionsMsg(element) + 
              "";
        UpdateReportMonitor(element.id + "_move", msg);
        //alert(msg);
    }
    
    var funcString = "var func = function() { _moveElementTimerCallback(element, desiredLeft, desiredTop, leftSteps, topSteps, timerName, timerCounterName, reportEnabled); }";
    eval(funcString);
    StartTimer(timerName, stepDuration, func, true, onCompletion);
}

function _moveElementTimerCallback(element, desiredLeft, desiredTop, leftSteps, topSteps, timerName, timerCounterName, reportEnabled)
{
    reportEnabled = new Boolean(reportEnabled).valueOf();
    
    var counter = window[timerCounterName] + 1;
    window[timerCounterName] = counter;
    if (counter > 100)
    {
        alert("Endless loop detected.  Stopping.  timerCounter=" + counter);
        StopTimer(timerName);
        //if (onCompletion) StartTimer("x", 0, onCompletion, false);
        return;
    }
    
    var lastLeft = element["_moveLastLeft"];
    var lastTop = element["_moveLastTop"];
    
    var currentLeft = GetElementLeft(element);
    var currentTop = GetElementTop(element);

    element["_moveLastLeft"] = currentLeft;
    element["_moveLastTop"] = currentTop;
    
    // Are we where we need to be?
    if ((currentLeft == desiredLeft) && (currentTop == desiredTop))
    {
        //alert("Stopping - onCompletion=|" + onCompletion + "|");
        if (reportEnabled) UpdateReportMonitorStyle(timerName, "backgroundColor", "#ddd");
        StopTimer(timerName);
        //if (onCompletion) StartTimer("x", 0, onCompletion, false);
        return;
    }

    var newLeft = currentLeft + leftSteps;
    if (currentLeft != desiredLeft)
    {
        // if we're close enough (within a step), then finish it.  This is in case of rounding error.
        var remainingDistance = Math.abs(desiredLeft - currentLeft);
        var closeEnough = Math.max(Math.abs(leftSteps), 1);
        if (remainingDistance <= closeEnough) newLeft = desiredLeft;
        // If we're not moving (rounding errors?) then finish it.
        if ((counter > 10) && (lastLeft == currentLeft)) newLeft = desiredLeft;
    }
    
    var newTop = currentTop + topSteps;
    if (currentTop != desiredTop)
    {
        // if we're close enough (within a step), then finish it.  This is in case of rounding error.
        var remainingDistance = Math.abs(desiredTop - currentTop);
        var closeEnough = Math.max(Math.abs(topSteps), 1);
        if (remainingDistance <= closeEnough) newTop = desiredTop;
        // If we're not moving (rounding errors?) then finish it.
        if ((counter > 10) && (lastTop == currentTop)) newTop = desiredTop;
    }

    if (reportEnabled)
    {
        UpdateReportMonitorStyle(timerName, "backgroundColor", "#faa");
        var msg = "timerCounter=" + counter + "\n" +
                  "currentLeft=|" + currentLeft + "| leftSteps=|" + leftSteps + "| newLeft=|" + newLeft + "| desiredLeft=|" + desiredLeft + "|\n" +
                  "currentTop=|" + currentTop + "| topSteps=|" + topSteps + "| newTop=|" + newTop + "| desiredTop=|" + desiredTop + "|\n" +
                  "(lastLeft=|" + lastLeft + "| lastTop=|" + lastTop + "|)\n" +
                  "";
        UpdateReportMonitor(timerName, msg);
    }
    
    /*
    if (confirm(msg +
                "\n" + 
                "\n" +
                "Click OK to stop the move.  Escape or cancel to continue moving. \n" +
                ""))
    {
        StopTimer(timerName);
        //if (onCompletion) StartTimer("x", 0, onCompletion, false);
        return;
    }
    */    

    if (currentLeft != desiredLeft)
    {
        //alert("Moving to newLeft=|" + newLeft + "|");
        SetElementLeft(element, newLeft);
    }

    if (currentTop != desiredTop)
    {
        //alert("Moving to newTop=|" + newTop + "|");
        SetElementTop(element, newTop);
    }
}



function GetParentElement(element)
{
    //alert("Getting parent for element " + GetTagForElement(element));
    if (element == element.documentElement) return null;
    
    var parent = element.parentNode;
    while (parent && (parent.nodeType != 1))
    {
        //alert("parent = " + GetTagForElement(parent));
        parent = parent.parentNode;
    }
    
    return parent;
}


function SetElementLeftAndTop(element, left, top, options)
{
    SetElementLeft(element, left, options);
    SetElementTop(element, top, options);
}


/*
    The number of pixels from the top of the document.
    If wrtElement is not provided, this will return the number of pixels from the top of the document.
    If wrtElement is provide, this will return the number of pixels from the top of the wrtElement.
    
    options (a Hash object - see shared.js):
        wrtElement - an element
        adjustForScrolling - boolean
        verbose - boolean
    
    Example:
        GetElementTop(element, CreateHashFromKeyValues([['adjustForScrolling',true],['verbose',false]]))
*/
//debugger;
function GetElementTop(element, options)
{
    var origElement = element;
    var origOptions = (options ? options.Clone() : null);
    var origDisplay = origElement.style.display;
    
    var wrtElement = null;
    var adjustForScrolling = false;
    var verbose = false;
    if (options)
    {
        if (options.ContainsKey("wrtElement")) { wrtElement = options.Get("wrtElement"); options.Remove("wrtElement"); }
        if (options.ContainsKey("adjustForScrolling")) { adjustForScrolling = new Boolean(options.Get("adjustForScrolling")).valueOf(); options.Remove("adjustForScrolling"); }
        if (options.ContainsKey("verbose")) { verbose = new Boolean(options.Get("verbose")).valueOf(); options.Remove("verbose"); }
        if (options.Count() > 0) alert("Unsupported options: " + options.KeysString());
    }
    
    if (verbose) 
    {
        LogReportMonitor("ElementUtils", "--------------------------------------");
        LogReportMonitor("ElementUtils", "Entered GetElementTop() for " + GetTagForElement(origElement) + "\n" +
          "\t wrtElement=" + GetTagForElement(wrtElement) + "\n" +
          "\t adjustForScrolling=|" + adjustForScrolling + "|\n" +
          "\n" +
          "parentElement = " + GetTagForElement(GetParentElement(origElement)) + "\n" +
          "offsetParent = " + GetTagForElement(origElement.offsetParent) + "\n" +
          "display = |" + origElement.style.display + "|\n" +
          "\n"
          );
    }
    
    if (element == wrtElement) return 0;
    
    if (origDisplay == "none") 
        origElement.style["display"] = "block";
    
    var y = 0;
    var scrollingAdjustment = 0;

    if(element)
    {
        if (element.getBoundingClientRect)
        {
            // IE
            y = element.getBoundingClientRect().top;    // distance from the top of the window.
            if (verbose) 
                LogReportMonitor("ElementUtils", "getBoundingClientRect().top of " + GetTagForElement(element) + " : y = |" + y + "|");
            
            // If there was any scrolling, it could have reduced the Y (moved it up).
            // Computed how much the element has been scrolled, and adjust for it.
            // Scrolling adjustments will always be either 0 or a postive value.
            var bodyScroll = 0;
            var ieScrollingAdjustment = 0;
            while (element.parentElement)
            {
                if (element.parentElement.tagName.toLowerCase() == "body")
                    bodyScroll = element.parentElement.scrollTop;
                    
                ieScrollingAdjustment += element.parentElement.scrollTop;
                
                if (verbose) 
                    LogReportMonitor("ElementUtils", GetTagForElement(origElement) + "\n" +
                           "Added y scrolling for: " + GetTagForElement(element.parentElement) + "\n" +
                           "\t ieScrollingAdjustment=|" + ieScrollingAdjustment + "|\n" +
                           "\n"
                           );              
                
                element = element.parentElement;
            }
            if (verbose) LogReportMonitor("ElementUtils", "ieScrollingAdjustment = |" + ieScrollingAdjustment + "| bodyScroll=|" + bodyScroll + "|");
            
            y += ieScrollingAdjustment;
                        
            scrollingAdjustment = ieScrollingAdjustment - bodyScroll;
        }
        else
        {
            y += parseFloat(element.offsetTop);

            var offsetParent = element.offsetParent;
            while (offsetParent)
            {
                y += offsetParent.offsetTop;
                
//                if (verbose) 
//                    LogReportMonitor("ElementUtils", GetTagForElement(origElement) + "\n" +
//                           "Added y for: " + GetTagForElement(offsetParent) + "\n" +
//                           "\t y=|" + y + "|\n" +
//                           "\n"
//                           );              
                
                offsetParent = offsetParent.offsetParent;
            }

            if (adjustForScrolling)
            {
                scrollingAdjustment = 0;

                var parent = GetParentElement(element);
                while (parent)
                {
                    if (parent.tagName.toLowerCase() != "body")
                        scrollingAdjustment += parent.scrollTop;
                        
                    if (verbose) 
                        LogReportMonitor("ElementUtils", GetTagForElement(origElement) + "\n" +
                               "Added y scrolling (" + parent.scrollTop + ") for: " + GetTagForElement(parent) + "\n" +
                               "\t scrollingAdjustment=|" + scrollingAdjustment + "|\n" +
                               "\n"
                               );              
                    
                    parent = GetParentElement(parent);
                }
            }
            
        }
    }
    
    if (verbose) 
        LogReportMonitor("ElementUtils", "GetElementTop(" + GetTagForElement(origElement) + ")  \n" +
            "Options:\n" +
            "\t wrtElement=" + GetTagForElement(wrtElement) + "\n\t adjustForScrolling=" + adjustForScrolling + 
            "\n" +
            "y before scroll adjustment = |" + y + "|\n" +
            "scroll adjustment = |" + scrollingAdjustment + "|\n" +
            "\n" +
            ".");

    if (adjustForScrolling)
    {
        y -= scrollingAdjustment;
        if (verbose) LogReportMonitor("ElementUtils", "After scrollingAdjustment: y = |" + y + "|");
    }

    if (wrtElement)
    {
        var optionsHash = origOptions;
        optionsHash.Remove("wrtElement");
        if (verbose) alert("Determining wrtElement Y (" + optionsHash.Get("verbose") + ")");
        var wrtElementY = GetElementTop(wrtElement, optionsHash);
//        if (verbose) LogReportMonitor("ElementUtils", "wrtElementY=|" + wrtElementY + "|");
        y -= wrtElementY;

//        if (verbose) 
//            LogReportMonitor("ElementUtils", "After wrtElement adjustment: \n" +
//                "wrtElement=" + GetTagForElement(wrtElement) + "\n" + 
//                "y = |" + y + "|\n" +
//                "wrtElementY = |" + wrtElementY + "|\n" +
//                "\n" +
//                ".");

    }
    
    if (origDisplay == "none") 
        origElement.style["display"] = "none";
    
    return y;
    alert(y);
} // GetElementTop()



// Sets the top style of the element so that it is y pixels from the top of the document.
function SetElementTop(element, y, preventRetry) 
{
    //alert("Entered SetElementTop to " + y + " for " + GetTagForElement(element));
    var origDisplay = element.style.display;

    if (origDisplay == "none") 
        element.style["display"] = "block";

    y = parseFloat(y);
    
    var yNow = GetElementTop(element, CreateHashFromKeyValues([['adjustForScrolling',false],['verbose',false]]));
    var topStyleNow = GetEffectiveTopStyle(element);
    var delta = y - yNow ;
    var newY = topStyleNow + delta;
    
    /*
    alert("SetElementTop() for " + GetTagForElement(element) + " to " + y + "\n" + 
          "Before setting....\n" + 
          "preventRetry=" + preventRetry + ", topStyleNow=" + topStyleNow + "\n" +
          "GetElementTop() (now) = " + yNow + ", Want to be at " + y + ", delta = " + delta + "\n" +
          ((delta != 0) ? "Setting topStyle for element " + GetTagForElement(element) + " to " + newY : "No position change.") + "\n" + 
          //"DOMPath: " + GetElementDOMPathAsMessage(element) + "\n" +
          "PositionContexts: " + GetElementPositioningContextsAsMessage(element) + "\n" +
          "");
    */
    
    if (delta != 0)
    {
        element.style.top = newY + "px";
        
        if (!preventRetry)
            if (GetElementTop(element) != y)
                SetElementTop(element, y, true)
    }
    
    if (origDisplay == "none") 
        element.style["display"] = "none";
}

function GetEffectiveTopStyle(element)
{
    var topStyle = parseFloat(element.style.top);
    if (isNaN(topStyle)) 
    {
        var parent = element.offsetParent;
        while (isNaN(topStyle)) 
        {
            if (! parent) break;
            if (parent.tagName.toLowerCase() == "body") break;
            topStyle = parseFloat(parent.style.top);
            parent = parent.offsetParent;
        }
        if (isNaN(topStyle)) topStyle = 0;
    }
    return topStyle;
}

function GetEffectiveLeftStyle(element)
{
    var leftStyle = parseFloat(element.style.left);
    if (isNaN(leftStyle)) 
    {
        var parent = element.offsetParent;
        while (isNaN(leftStyle)) 
        {
            if (! parent) break;
            if (parent.tagName.toLowerCase() == "body") break;
            leftStyle = parseFloat(parent.style.left);
            parent = parent.offsetParent;
        }
        if (isNaN(leftStyle)) leftStyle = 0;
    }
    return leftStyle;
}

/*
    Returns the height of the element.
*/
function GetElementHeight(element) 
{
    var origDisplay = element.style.display;

    if (origDisplay == "none") 
        element.style["display"] = "block";

    var height = element.offsetHeight;
    
    if (origDisplay == "none") 
        element.style["display"] = "none";
    
    return height;
}

/*
    Returns the bottom of the element (w.r.t. the document).
*/
function GetElementBottom(element, options) 
{
    return GetElementTop(element, options) + GetElementHeight(element);
}

/*
    Returns the bottom of the element (w.r.t. the window).
*/
function GetElementWindowBottom(element)
{
    return GetElementWindowY(element) + GetElementHeight(element);
}

// Useful for debugging.
function AlertElementDimensions(element, msg, includeBodyDimensions)
{
    if (msg) msg += "\n";
    else msg = "";
    
    msg += GetTagForElement(element) + "\n";
            
    alert(msg + 
            GetElementDimensionsMsg(element) +
            "\n" +
            "\n" +
            (includeBodyDimensions?
                "<BODY>\n" +
                GetElementDimensionsMsg(document.body) +
                "\n" +
                "\n"
                :
                "") +
            "Notes:\n" +
            "  scroll: How tall/wide an element is (some may be scrolled off)\n" +
            "  client: How much of the element is visible.  This could be larger than the element in which case its how much room there is to show the element.\n" +
            "  offset: How much of the element is visible + scrollbar width\n" +
            "\n" +
            "\n" +
            "");
}

function GetElementDimensionsMsg(element) 
{
    return "" +
           " Left=" + GetElementLeft(element) + ", Top=" + GetElementTop(element) + "\n" +
           "    (accounting for scroll) Left=" + GetElementLeft(element, CreateHashFromKeyValues([['adjustForScrolling',true],['verbose',false]])) + ", TopY=" + GetElementTop(element, CreateHashFromKeyValues([['adjustForScrolling',true],['verbose',false]])) + "\n" +
           "    (wrt window) Left=" + GetElementLeftWRTWindow(element) + ", Top=" + GetElementTopWRTWindow(element) + "\n" +
           "  scrollTop=" + element.scrollTop + ", clientTop=" + element.clientTop + ", offsetTop=" + element.offsetTop + ", style.top=" + element.style.top + ",  GetElementTop()=" + GetElementTop(element) + "\n" +
           "  scrollLeft=" + element.scrollLeft + ", clientLeft=" + element.clientLeft + ", offsetLeft=" + element.offsetLeft + ", style.left=" + element.style.left + "\n" + 
           "  scrollHeight=" + element.scrollHeight + ", clientHeight=" + element.clientHeight + ", offsetHeight=" + element.offsetHeight + ", style.height=" + element.style.height + " (" + (element.scrollHeight<=element.clientHeight?"no ":"")  + "vertical scroll)\n" +
           "  scrollWidth=" + element.scrollWidth + ", clientWidth=" + element.clientWidth + ", offsetWidth=" + element.offsetWidth + ", style.width=" + element.style.width + " (" + (element.scrollWidth<=element.clientWidth?"no ":"")  + "horizontal scroll)\n" +
           "  (scrollbottom=" + (element.scrollTop + element.scrollHeight) + ", clientbottom=" + (element.clientTop + element.clientHeight) + ", offsetbottom=" + (element.offsetTop+element.offsetHeight) + ")\n" +
           "  (scrollRight=" + (element.scrollLeft + element.scrollWidth) + ", clientRight=" + (element.clientLeft + element.clientWidth) + ", offsetRight=" + (element.offsetLeft+element.offsetWidth) + ")\n" +
           "  border-left-width=" + element.style.borderLeftWidth + "  border-right-width=" + element.style.borderRightWidth + "\n" + 
           "  border-top-width=" + element.style.borderTopWidth + "  border-bottom-width=" + element.style.borderBottomWidth + "\n" + 
           "  z-index=" + element.style.zIndex + "\n" +
           "\n" +
           "  WindowTop=" + GetWindowTop() + ", GetWindowLeft=" + GetWindowLeft() + "\n" +
           "  parentNode = " + GetTagForElement(element.parentNode) + "\n" +
           "  offsetParent = " + GetTagForElement(element.offsetParent) + "\n" +
           "";
}

function AlertElementDOMPath(element, msg)
{
    var elements = GetElementDOMPath(element);
    
    if (! msg) msg = "";
    else msg += "\n";
    
    msg += GetElementDOMPathAsMessage(element);
    alert(msg +
            "\n" +
            "");
}

function GetElementDOMPathAsMessage(element)
{
    var elements = GetElementDOMPath(element);
    
    var msg = "There are " + elements.length + " elements in the path \n";
    var indent = "";
    for (var i=0; i<elements.length; i++)
    {
        var element = elements[i];
        var offset = element.offsetTop;
        indent += " ";
        msg += indent + GetTagForElement(element) + " offsetTop=" + offset + " " +
               "Y=" + GetElementTop(element) + ", X=" + GetElementLeft(element) + ", scrollTop=" + element.scrollTop + ", scrollLeft=" + element.scrollLeft + "\n";
    } 
    
    return msg;  
}

function GetElementDOMPath(element) 
{
    var elements = new Array();
    elements.unshift(element);
    
    while (element.parentNode)
    {
        element = element.parentNode;
        if (element.nodeType == 1)
            elements.unshift(element);
    }
        
    return elements;
}



function AlertElementPositioningContexts(element, msg)
{
    if (! msg) msg = "";
    else msg += "\n";
    
    msg += GetElementPositioningContextsAsMessage(element);
    alert(msg +
            "\n" +
            "");
}

function GetElementPositioningContextsAsMessage(element) 
{
    var contexts = GetElementPositioningContexts(element);
    
    var msg = "There are " + contexts.length + " levels of positioning context \n";
    var indent = "";
    for (var i=0; i<contexts.length; i++)
    {
        var element = contexts[i]; 
        var offset = element.offsetTop;
        if (offset > 0) indent += "    ";
        msg += indent + GetTagForElement(element) + " offsetTop=" + offset + " " +
               "Y=" + GetElementTop(element) + ", X=" + GetElementLeft(element) + ", scrollTop=" + element.scrollTop + ", scrollLeft=" + element.scrollLeft + ", overflow=" + element.style.overflow + "\n";
    }   
    
    return msg;
}

function GetElementPositioningContexts(element) 
{
    var origDisplay = element.style.display;
    if (origDisplay == "none") element.style["display"] = "block";

    var contexts = new Array();
    contexts.unshift(element);
    
    var context = element;
    while (context.offsetParent)
    {
        context = context.offsetParent;
        contexts.unshift(context);
    }
        
    if (origDisplay == "none") element.style["display"] = "none";

    return contexts;
}

function GetOffsetParent(element)
{
    var origDisplay = element.style.display;
    if (origDisplay == "none") element.style["display"] = "block";

    var offsetParent = element.offsetParent;

    if (origDisplay == "none") element.style["display"] = "none";

    return offsetParent;
}

function AlertElementStackingContexts(element, msg)
{
    if (! msg) msg = "";
    else msg += "\n";
    
    msg += GetElementStackingContextsAsMessage(element);
    alert(msg +
            "\n" +
            "");
}

function GetElementStackingContextsAsMessage(element)
{
    var contexts = GetElementStackingContexts(element);
    
    var msg = "There are " + contexts.length + " levels of stacking contexts \n";
    var indent = "";
    for (var i=0; i<contexts.length; i++)
    {
        var element = contexts[i]; 
        indent += " ";
        msg += "z-index=" + (element.style.zIndex ? element.style.zIndex : 0) + indent + GetTagForElement(element) + "\n";
    }   
    
    return msg;
}


function GetElementStackingContexts(element) 
{
    var elements = new Array();
    
    if (element.style.zIndex)
        elements.unshift(element);
    
    while (element.parentNode)
    {
        element = element.parentNode;
        if (element.nodeType == 1)
            if (element.style.zIndex)
                elements.unshift(element);
    }
    
    elements.unshift(document.body);  // root
        
    return elements;
}


function AlertStackingContexts(msg)
{
    if (! msg) msg = "";
    else msg += "\n";
    
    msg += GetStackingContextsAsMessage();
    alert(msg +
            "\n" +
            "");
}

function GetStackingContextsAsMessage()
{
    var contexts = GetStackingContexts();
    //alert("contexts=|" + contexts + "|");
    
    var msg = "There are " + contexts.length + " stacking contexts in the doc.\n";
    var indent = "";
    for (var i=0; i<contexts.length; i++)
    {
        var element = contexts[i];
        var stackingContextMsgForElement = GetElementStackingContextsAsMessage(element);
        indent += " ";
        msg += indent + GetTagForElement(element) + stackingContextMsgForElement + "\n";
    } 
    
    return msg;  
}

function GetStackingContexts(element, contexts)
{
    if (! contexts) contexts = new Array();
    if (! element)
    {
        element = document.documentElement;
        contexts.unshift(element)
    }
    else
    {
        if (element.style.zIndex)
            contexts.unshift(element);
    }
    
    for (var i=0; i<element.childNodes.length; i++)
    {
        var child = element.childNodes[i];
        if (child.nodeType == 1) 
        {
            //if (confirm("child=|" + child + "|")) return;
            GetStackingContexts(child, contexts);
        }
    }

    return contexts;
}


/*
function AlertElementStackingContexts(element, msg)
{
    var contexts = GetStackingContexts(element);
    
    if (! msg) msg = "";
    msg += "\n There are " + contexts.length + " levels of stacking contexts \n";
    var indent = "";
    for (var i=0; i<contexts.length; i++)
    {
        var zIndex = contexts[i].style.zIndex;
        indent += "    ";
        msg += indent + GetTagForElement(contexts[i]) + " zIndex=" + zIndex + "\n";
    }   
    alert(msg +
            "\n" +
            "");
}

function GetStackingContexts(element)
{
    var contexts = new Array();
    
    alert("element=" + GetTagForElement(element) + " zIndex=|" + element.style.zIndex + "|");
    if (element.style.zIndex)
        contexts.unshift(element);
    
    var context = element;
    while (context.parentElement)
    {
        context = context.parentElement;
        //alert("context=" + GetTagForElement(context) + " zIndex=|" + context.style.zIndex + "|");
        if (context.style.zIndex)
            contexts.unshift(context);
    }
        
    return contexts;
}
*/


// Returns the top of the window wrt the document.
// Note - if the body is not vertically scrolled, this will be 0.
function GetWindowTop()
{
    return parseFloat(document.body.scrollTop);
}

function GetMinTop() { return GetWindowTop(); }

// Returns the bottom of the window wrt the document.
function GetWindowBottom() 
{
    return GetWindowTop() + parseFloat(document.body.clientHeight);
}

function GetMaxBottom() { return GetWindowBottom() }

// Returns the left side of the window wrt the document.
// Note - if the body is not horizontally scrolled, this will be 0.
function GetWindowLeft()
{
    return parseFloat(document.body.scrollLeft);
}

// Returns the right side of the window wrt the document.
// Note - if the body is not horizontally scrolled, this will be 0.
function GetWindowRight()
{
    return GetWindowLeft() + parseFloat(document.body.clientWidth);
}

/*
    The number of pixels from the left of the document.
    If wrtElement is not provided, this will return the number of pixels from the left of the document.
    If wrtElement is provide, this will return the number of pixels from the left of the left-side of wrtElement.
    If adjustForScrolling is true, it will take in account any scrolling.
    
    options (a Hash object - see shared.js):    
        wrtElement - an element
        adjustForScrolling - boolean
        verbose - boolean
    
    Examples:
    
        GetElementLeft(element, CreateHashFromKeyValues([['verbose',true]]))
   
        GetElementLeft(element, 
                        CreateHashFromKeyValues(
                                                [
                                                 ['adjustForScrolling',true],
                                                 ['verbose',false]
                                                ]
                                               )
                      )
                         
        GetElementLeft(element, 
                       CreateHashFromKeyValues(
                                               [
                                                ['adjustForScrolling',true],
                                                ['verbose',false],
                                                ['wrtElement', testElem]
                                               ]
                                              )
                      )
    
*/
function GetElementLeft(element, options)
{
    var origElement = element;
    var origOptions = (options ? options.Clone() : null);
    var origDisplay = origElement.style.display;
        
    var wrtElement = null;
    var adjustForScrolling = false;
    var verbose = false;
    if (options)
    {
        if (options.ContainsKey("wrtElement")) { wrtElement = options.Get("wrtElement"); options.Remove("wrtElement"); }
        if (options.ContainsKey("adjustForScrolling")) { adjustForScrolling = new Boolean(options.Get("adjustForScrolling")).valueOf(); options.Remove("adjustForScrolling"); }
        if (options.ContainsKey("verbose")) { verbose = new Boolean(options.Get("verbose")).valueOf(); options.Remove("verbose"); }
        if (options.Count() > 0) alert("Unsupported options: " + options.KeysString());
    }
    
    if (verbose) 
        alert("Entered GetElementLeft() for " + GetTagForElement(origElement) + "\n" +
          "\t wrtElement=" + GetTagForElement(wrtElement) + "\n" +
          "\t adjustForScrolling=|" + adjustForScrolling + "|\n" +
          "\n" +
          "parentElement = " + GetTagForElement(GetParentElement(origElement)) + "\n" +
          "offsetParent = " + GetTagForElement(origElement.offsetParent) + "\n" +
          "display = |" + origElement.style.display + "|\n" +
          "\n"
          );
    
    if (element == wrtElement) return 0;
    
    if (origDisplay == "none") 
        origElement.style["display"] = "block";
    
    var x = 0.0;
    var scrollingAdjustment = 0.0;

    if(element)
    {
        if (element.getBoundingClientRect)
        {
            // IE
            x = element.getBoundingClientRect().left;    // distance from the left of the window.
            if (verbose) 
                alert("getBoundingClientRect().left of " + GetTagForElement(element) + " : x = |" + x + "|");
            
            // If there was any scrolling, it could have reduced the X (moved it to the left).
            // Computed how much the element has been scrolled, and adjust for it.
            // Scrolling adjustments will always be either 0 or a postive value.
            var bodyScroll = 0;
            var ieScrollingAdjustment = 0.0;
            while (element.parentElement)
            {
                if (element.parentElement.tagName.toLowerCase() == "body")
                    bodyScroll = element.parentElement.scrollLeft;
                    
                ieScrollingAdjustment += parseFloat(element.parentElement.scrollLeft);
                
                if (verbose) 
                    alert(GetTagForElement(origElement) + "\n" +
                           "Added x scrolling for: " + GetTagForElement(element.parentElement) + "\n" +
                           "\t ieScrollingAdjustment=|" + ieScrollingAdjustment + "|\n" +
                           "\n"
                           );              
        
                element = element.parentElement;
            }
            if (verbose) alert("ieScrollingAdjustment = |" + ieScrollingAdjustment + "| bodyScroll=|" + bodyScroll + "|");
            
            x += ieScrollingAdjustment;
                        
            scrollingAdjustment = ieScrollingAdjustment - bodyScroll;
        }
        else
        {
            x += parseFloat(element.offsetLeft);

            // I think that any element that has scrolling will become a positioning context, so I think we're fine using offsetParent instead of parentElement.
            var offsetParent = element.offsetParent;
            while (offsetParent)
            {
                x += parseFloat(offsetParent.offsetLeft);
                
                if (verbose) 
                    alert(GetTagForElement(origElement) + "\n" +
                           "Added x for: " + GetTagForElement(offsetParent) + "\n" +
                           "\t x=|" + x + "|\n" +
                           "\n"
                           );              
                
                offsetParent = offsetParent.offsetParent;
            }

            if (adjustForScrolling) 
            {
                scrollingAdjustment = 0;

                var parent = GetParentElement(element);
                while (parent)
                {
                    if (parent.tagName.toLowerCase() != "body")
                        scrollingAdjustment += parent.scrollLeft;
                        
                    if (verbose) 
                        alert(GetTagForElement(origElement) + "\n" +
                               "Added x scrolling for: " + GetTagForElement(parent) + "\n" +
                               "\t scrollingAdjustment=|" + scrollingAdjustment + "|\n" +
                               "\n"
                               );              
                    
                    parent = GetParentElement(parent);
                }
            }

        }
    }
    
    if (verbose) 
        alert("GetElementLeft(" + GetTagForElement(origElement) + ")  \n" +
            "Options:\n" +
            "\t wrtElement=" + GetTagForElement(wrtElement) + "\n\t adjustForScrolling=" + adjustForScrolling + 
            "\n" +
            "x before scroll adjustment = |" + x + "|\n" +
            "scroll adjustment = |" + scrollingAdjustment + "|\n" +
            "\n" +
            ".");

    if (adjustForScrolling)
    {
        x -= scrollingAdjustment;
        if (verbose) alert("After scrollingAdjustment: x = |" + x + "|");
    }

    if (wrtElement)
    {
        var optionsHash = origOptions;
        optionsHash.Remove("wrtElement");
        if (verbose) alert("Determining wrtElement X (" + optionsHash.Get("verbose") + ")");
        var wrtElementX = GetElementLeft(wrtElement, optionsHash);
        if (verbose) alert("wrtElementX=|" + wrtElementX + "|");
        x -= wrtElementX;

        if (verbose) 
            alert("After wrtElement adjustment: \n" +
                "wrtElement=" + GetTagForElement(wrtElement) + "\n" + 
                "x= |" + x + "|\n" +
                "wrtElementX = |" + wrtElementX + "|\n" +
                "\n" +
                ".");

    }

    if (origDisplay == "none") 
        origElement.style["display"] = "none";
        
    return x;
} // GetElementLeft()


// Sets the left style of the element so that it is x pixels from the left side of the document.
function SetElementLeft(element, desiredLeft, preventRetry) 
{
    //alert("Entered SetElementLeft to " + x + " for " + GetTagForElement(element));
    
    var origDisplay = element.style.display;

    if (origDisplay == "none") 
        element.style["display"] = "block";

    desiredLeft = parseFloat(desiredLeft);
    
    var leftNow = GetElementLeft(element);
    var leftStyleNow = parseFloat(element.style.left); //GetEffectiveLeftStyle(element);
    if (isNaN(leftStyleNow)) leftStyleNow = 0;
    var delta = leftNow - desiredLeft;
    var newLeftStyle = leftStyleNow - delta;
    
    /*
    var msg = "SetElementLeft() " + (preventRetry? "RETRY ":"")+ "for " + GetTagForElement(element) + " to " + desiredLeft + "\n" + 
          "Before setting....\n" + 
          "leftNow = " + leftNow + ", delta = " + delta + ", desiredLeft = " + desiredLeft + "\n" +
          "leftStyleNow=" + leftStyleNow + ", newLeftStyle=" + newLeftStyle + "\n" + 
          ((delta != 0) ? "Setting style.left for element " + GetTagForElement(element) + " to " + newLeftStyle : "No position change.") + "\n" + 
          //"DOMPath: " + GetElementDOMPathAsMessage(element) + "\n" +
          "PositionContexts: " + GetElementPositioningContextsAsMessage(element) + "\n" +
          "";
    UpdateReportMonitor("SetElementLeft", msg);
    // alert(msg);
    */
    
    if (delta != 0)
    {
        element.style.left = newLeftStyle + "px";
        
        if (!preventRetry)
            if (GetElementLeft(element) != desiredLeft)
                SetElementLeft(element, desiredLeft, true)
    }
    
    if (origDisplay == "none") 
        element.style["display"] = "none";
}


function GetElementWidth(element)
{
    var origDisplay = element.style.display;

    if (origDisplay == "none") 
        element.style["display"] = "block";

    var width = element.offsetWidth;

    if (origDisplay == "none") 
        element.style["display"] = "none";
    
    return width;
}


/*
Returns element right coordinate
*/
function GetElementRight(element)
{
    var r = GetElementLeft(element) + GetElementWidth(element);
    return r;
}



/*
    The number of pixels from the left of the window.
*/
function GetElementLeftWRTWindow(element)
{
    var x = 0;
    var scrollLeft = 0;
    
    if (element)
    {
        if (element.getBoundingClientRect)
        {
            x = element.getBoundingClientRect().left;
        }
        else
        {
            x += parseFloat(element.offsetLeft);
            scrollLeft += element.scrollLeft;
            
            while (element.offsetParent)
            {
                x += element.offsetParent.offsetLeft;
                scrollLeft += element.offsetParent.scrollLeft;
                
                element = element.offsetParent;
            }
        }
    }
    
    // 'x' is now how far from the left of the window.
    // Adjust it now incase of scrolling.
    x -= scrollLeft;
    
    return x;
    
} // GetElementLeftWRTWindow()


/*
    The number of pixels from the top of the visible window.
*/
function GetElementTopWRTWindow(element)
{
    var y = 0;
    var scrollTop = 0;
    
    if(element)
    {
        if (element.getBoundingClientRect)
        {
            y = element.getBoundingClientRect().top;
        }
        else
        {
            y += parseFloat(element.offsetTop);
            scrollTop += element.scrollTop;
            
            while (element.offsetParent)
            {
                y += element.offsetParent.offsetTop;
                scrollTop += element.offsetParent.scrollTop;

                element = element.offsetParent;
            }
        }
    }
    else 
    {
        // return the Y of the window
        y = parseFloat(document.body.scrollTop);
    }
    
    // 'y' is now how far from the top of the window.
    // Adjust it now incase of scrolling.
    y -= scrollTop;
    
    return y;
    
} // GetElementTopWRTWindow()





/*
    Resizes all divs, which have bottomoffset or rightoffset attributes set
    Function should be called onload and onresize for body tag, as well as can be
    executed when repating screen to force all divs to take right size
*/
function ResizeDivs()
{
    return;
    
    var h = window.innerHeight?window.innerHeight:document.body.clientHeight;
    var w = window.innerWidth?window.innerWidth-20:document.body.clientWidth-0;

    var divs = document.getElementsByTagName('div');
    
    for(i=0; i < divs.length; i++)
    {
        var e = divs[i];
        if(e!= null && e.tagName == 'DIV')
        {
            var bottomoffset = e.getAttribute("bottomoffset");
            var rightoffset = e.getAttribute("rightoffset");
            if(bottomoffset != null)
            {
                //var p = e.offsetParent;
                var topOffset = getDivTop(e);
                var divH = h - topOffset - bottomoffset;
                if(divH > 0)
                    e.style.height = divH;
            }
            
            /*
            if(rightoffset != null)
            {
                var leftOffset = getDivLeft(e);
                var divW = w - leftOffset - rightoffset -0;
                if(divW > 0)
                    e.style.width = divW;
            }
            */
            
        }
    }
}

function getDivTop(e) { return GetDivTop(e); }
function GetDivTop(e)
{
    var top = e.offsetTop;
    var p = e.offsetParent;
    while(p != null)
    {
        top += p.offsetTop;
        p = p.offsetParent;
    }
    return top;
}

function getDivLeft(e) { return GetDivLeft(e); }
function GetDivLeft(e)
{
    var left = e.offsetLeft;
    var p = e.offsetParent;
    while(p != null)
    {
        left += p.offsetLeft;
        p = p.offsetParent;
    }
    return left;
}


function ScrollIntoView(controlId, num)
{
    var d = document.getElementById(controlId);
    if(d != null)
    {
        var row = d.getElementsByTagName("table")[0].rows[num];
        if(row != null)
        {
            d.scrollTop = row.offsetTop;
            //row.style.fontWeight = 800;
        }
    }
}

function SetFocusTo(controlId)
{
    var str = "SetFocusToDelayed('" + controlId + "');";
    window.setTimeout(str, 350);
}



function SetFocusToDelayed(controlId)
{
    try
    {
        var c = document.getElementById(controlId);
        if(c != null && c.focus)
        {
            c.focus();
        }
    }
    catch(e)
    {
        // do nothing when error while setting focus. May be control is invisibile or disabled
    }
}

// Find the first descendent element of parent with the given tagName and id.
function GetInnerElement(parent, tagName, id)
{
    var elements = parent.getElementsByTagName(tagName);
    var element = null;
    for(i=0;i<elements.length;i++)
    {
        if(elements[i].id == id)
        {
            element = elements[i];
            break;
        }
    }
    
    return element;
}

function GetInnerElementByAttribute(parent, attributeName, attributeValue)
{
    var children = parent.childNodes;
    for(i=0;i<children.length;i++)
    {
        var matches = false;
        eval("matches = children[i]." + attributeName + " == attributeValue");
        if (matches)
            return children[i];
    }
    for(i=0;i<children.length;i++)
    {
        var elem = GetInnerElementByAttribute(children[i], attributeName, attributeValue);
        if (elem) return elem;
    }
    
    return null;
}

function GetInnerElementById(element, id)
{
    //alert("Checking children for " + GetTagForElement(element) + " for id=" + id);
    
    if (element.id && (element.id == id))
    {
        //alert("Found " + GetTagForElement(element));
        return element;
    }
    
    for (var i=0; i<element.childNodes.length; i++)
    {
        var child = element.childNodes[i];
        //alert("Checking child (" + GetTagForElement(child) + ") for " + GetTagForElement(element) + " for id=" + id);
        if (child.id && (child.id == id))
        {
            //alert("Found " + GetTagForElement(child));
            return child;
        }
    }
    
    for (var i=0; i<element.childNodes.length; i++)
    {
        var child = element.childNodes[i];
        var found = GetInnerElementById(child, id);
        if (found) return found;
    }
    
    return null;
}


function GetFirstScrolledAncesterElement(elem) 
{
	/*
	var msg = "Entered ScrollElementOntoScreen(<" + elem.tagName + " id=\"" + elem.id + "\">) " +
		"scrollTop=|" + elem.scrollTop + "| " +
		"scrollHeight=|" + elem.scrollHeight + "| " +
		"hasLayout=|" + elem.hasLayout + "| " +
		"height=|" + elem.style.height + "| " +
		"overflow=|" + elem.style.overflow + "| " +
		"";
	if (elem.offsetParent)
		msg += "\n offsetParent=|<" + elem.offsetParent.tagName + " id=\"" + elem.offsetParent.id + "\">| "
	alert(msg);
	*/
	
	if (elem.style.height)
		return elem;
	
	if (elem.offsetParent)
		return GetFirstScrolledAncesterElement(elem.offsetParent);		// <--	go up the tree of elements.
	
	return elem;
	
} // GetFirstScrolledAncesterElement()

function ScrollElementIntoView(elemId) 
{
	//alert("Entered ScrollElementIntoView(" + elemId + ")");
	
	var elem = document.getElementById(elemId);
	if (! elem)
	{
		alert("Element '" + elemId + "' not found");
		return;
	}
	
	if (! elem.document)
		return;		// IE only.
	
	
	//elem.scrollIntoView(); return;		// Cheap and dirty way.  Always scrolls, even when not necc (can cause jitter).
	
	//
	// Get info about the containing (scrollable) element.
	//
	
	var scrollElem = GetFirstScrolledAncesterElement(elem);
	var scrollElemTop = GetElementTopWRTWindow(scrollElem);
	var scrollElemVisibleHeight = scrollElem.style.height;
	if (scrollElemVisibleHeight)
		scrollElemVisibleHeight = Number(scrollElemVisibleHeight.replace(/px/,""));

	//alert("scrollElem = <" + scrollElem.tagName + " id=\"" + scrollElem.id + "\"> scrollElemTop=|" + scrollElemTop + "|  scrollElemVisibleHeight=|" + scrollElemVisibleHeight + "|  scrollElem.scrollHeight=|" + scrollElem.scrollHeight + "|");

	//
	// Get info about the element.
	//
	
	var elemTop = GetElementTopWRTWindow(elem) - scrollElemTop;		// top w.r.t. the scrollable container.
	var elemHeight = elem.offsetHeight;
	var elemBottom = elemTop + elemHeight;							// bottom w.r.t. the scrollable container.

	//alert("elemTop=|" + elemTop + "| elemHeight=|" + elemHeight + "| elemBottom=|" + elemBottom + "| scrollTop=|" + elem.scrollTop + "|.");

	//
	// Determine how much to scroll, and scroll it if we need to.	
	//
	
	var scrollBy = elemBottom - scrollElemVisibleHeight;
	if (scrollBy > 0)
	{
		//alert("Scrolling <" + scrollElem.tagName + " id=\"" + scrollElem.id + "\"> by " + scrollBy + ".");
		scrollElem.scrollTop = scrollBy;
	}
	
	
} // ScrollElementIntoView()





function control_hover(ctl)
{
	var attr = ctl.getAttribute("hoverclass")
	if(attr != null && attr.length > 0)
	{
		ctl.MouseOutEventHandler = control_mouseout;
		XBrowserAddHandler(ctl, "mouseout", "MouseOutEventHandler");
		var org = ctl.getAttribute("orgclass");
		if(org == null || org.length == 0)
			ctl.setAttribute("orgclass", ctl.className);
		ctl.className = attr;
	}
}

function control_mouseout(evt)
{
	var button = null;
	if(evt.currentTarget)
		button = evt.currentTarget;
	else
		button = evt.srcElement;
	button.className = button.getAttribute("orgclass");
}

function control_focus(ctl)
{
	var attr = ctl.getAttribute("focusclass")
	if(attr != null && attr.length > 0)
	{
		ctl.BlurEventHandler = control_blur;
		XBrowserAddHandler(ctl, "blur", "BlurEventHandler");
		var org = ctl.getAttribute("orgclass");
		if(org == null || org.length == 0)
			ctl.setAttribute("orgclass", ctl.className);
		ctl.className = attr;
	}
}

function control_blur(evt)
{
	var button = null;
	if(evt.currentTarget)
		button = evt.currentTarget;
	else
		button = evt.srcElement;
	button.className = button.getAttribute("orgclass");
}


//HEX functions
function hex2dec(hex)
{
	return(parseInt(hex,16));
}

var _hexDigit=new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F");

function dec2hex(dec)
{
	return(_hexDigit[dec>>4]+_hexDigit[dec&15]);
}


function GetCurrentBackgroundColor(element) 
{
	var style = '';
	if(element.currentStyle) // for IE 
	{
		style = element.currentStyle.backgroundColor;
		while(style == 'transparent')
		{
			element = element.offsetParent;
			style = element.currentStyle.backgroundColor;
		}
		return style.substring(1,7);
	}
	else // for NS
	{
		style = '';
		styleRGB = document.defaultView.getComputedStyle(element, '').getPropertyValue("background-color");
		comma = styleRGB.indexOf(',');
		style += dec2hex(styleRGB.substring(4, comma));
		commaPrevious = comma;
		comma = styleRGB.indexOf(',', commaPrevious+1);
		style += dec2hex(styleRGB.substring(commaPrevious+2, comma));
		style += dec2hex(styleRGB.substring(comma+2, styleRGB.lastIndexOf(')')));
		return style;
	}
}






// BEGIN - In-Progress functions for adjusting the height/width of elements

function SetElementFullWidthWRTContainer(targetElementId, viaHandler, verbose) 
{
    //if (verbose) alert("Entered SetElementFullWidthWRTContainer()");
    
    var targetElem = document.getElementById(targetElementId);
    if (! targetElem)
        alert("SetElementFullWidthWRTContainer(): Couldn't find targetElem |" + targetElementId + "|");
    var wrtElem = targetElem.offsetParent; //;parentNode
    if (verbose) 
        if ( confirm("Entered SetElementFullWidthWRTContainer() " + GetTagForElement(wrtElem) + " => " + GetTagForElement(targetElem)) )
            verbose=true;
    //AlertElementDimensions(wrtElem, "SetElementFullWidthWRTContainer(): WRTElem for |" + targetElementId + "|", true);
    
    // Determine the width
    var width = wrtElem.clientWidth;
    //if (isNaN(width)) width = wrtElem.scrollWidth;
    if (isNaN(width)) AlertElementDimensions(wrtElem, "SetElementFullWidthWRTContainer(): NaN problems...");
    var extra = GetBeyondWidth(targetElem);
    var desiredWidth = (width - extra);
    var widthPerc = 100;
    if (targetElem["widthPerc"])
        widthPerc = targetElem["widthPerc"]
    width = ((desiredWidth/width)*widthPerc) + "%";
    
    // Determine the height
    //var height = wrtElem.clientHeight;
    var extraHeight = GetBeyondHeight(targetElem);
    //if (extraHeight > 0) height -= extraHeight;
    var desiredHeight = wrtElem.clientHeight - extraHeight;
    var heightPerc = 100;
    if (targetElem["heightPerc"])
        heightPerc = targetElem["heightPerc"]    
    var height = Math.round((heightPerc/100) * desiredHeight) + "px";

    // Set the width and height
    if (verbose) AlertElementDimensions(targetElem, "SetElementFullWidthWRTContainer(): wrt=" + GetTagForElement(wrtElem) + "\nChanging width to " + width + " [" + wrtElem.clientWidth + "] (approx " + desiredWidth + ")\n" + "Changing height to " + height + " [" + wrtElem.clientHeight + "] \n widthPerc=|" + widthPerc + "|  heightPerc=|" + heightPerc + "|", true);
    targetElem.style.width = width;    
    targetElem.style.height = height;
    if (verbose) AlertElementDimensions(targetElem, "SetElementFullWidthWRTContainer() targetElem after");
    
    var myEvent = document.createEvent("HTMLEvents");
    myEvent.initEvent("TNSMIresize", false, true);
    //if (confirm("dispatching... ok = stop")) return;
    targetElem.dispatchEvent(myEvent);
    //alert("... done dispatching");
    
    if (! viaHandler)
    {
        RegisterHandler(window, 
                        "resize", new Function("SetElementFullWidthWRTContainer('" + targetElementId + "', true);"), false);
                        
        RegisterHandler(wrtElem, // fire me when wrtelem changes, 
                        "TNSMIresize", new Function("SetElementFullWidthWRTContainer('" + targetElementId + "', true);"), false);
    }   
    //if (verbose) alert("Leaving SetElementFullWidthWRTContainer()");
}

function PinFrameSize(frameIndex, frameId, viaHandler)
{
    alert("Entered PinFrameSize():  frameIndex=|" + frameIndex + "|  frameId=|" + frameId + "|  viaHandler=|" + viaHandler + "|");
    
    var frame = window.frames[frameIndex];
    var frameElem = document.getElementById(frameId);
    //alert("frame=|" + frame + "| frameElem=|" + frameElem + "|");
    var wrtElem = frameElem;
    var targetElem = frame.document.getElementById("_inner");

    // Determine the width    
    var width = wrtElem.clientWidth;
    if (isNaN(width)) width = wrtElem.scrollWidth;
    //if (isNaN(width)) AlertElementDimensions(wrtElem, "NaN problems...");
    //var extra = frameElem.offsetWidth - frameElem.clientWidth;
    var extra = GetBeyondWidth(targetElem);
    var desiredWidth = (width - extra);
    width = ((desiredWidth/width)*100) + "%";
    
    // Determine the height
    var height = wrtElem.clientHeight;
    //var extraHeight = frameElem.offsetHeight - frameElem.clientHeight;
    var extraHeight = GetBeyondHeight(targetElem);
    if (extraHeight > 0) height -= extraHeight;
    height = height + "px";
    //height = "500px";
    //height = (((height - extra)/height)*100) + "%";

    // Set the width and height
    //AlertElementDimensions("PinFrameSize(): " + wrtElem);
    if (! viaHandler) 
        AlertElementDimensions(targetElem, "PinFrameSize(): Changing width to " + width + " (approx " + desiredWidth + ")\n" + "Changing height to " + height + " [" + wrtElem.clientHeight + "] (extraHeight=" + extraHeight + ") wrt=" + GetTagForElement(wrtElem) + "\n", true);
    targetElem.style.width = width;
    targetElem.style.height = height;
    AlertElementDimensions(targetElem, "PinFrameSize() after");
    
    var myEvent = document.createEvent("HTMLEvents");
    myEvent.initEvent("TNSMIresize", false, true);
    if (confirm("dispatching... ok = stop")) return;
    targetElem.dispatchEvent(myEvent);
    alert("... done dispatching");

    if (!viaHandler)
    {
        RegisterHandler(window, "resize", new Function("PinFrameSize(" + frameIndex + ", '" + frameId + "', true);"), false);
    }
    //AlertElementDimensions(frame.document.body);
}

function LetMeKnowWhenHeChanges(targetElementId, wrtElementId)
{
    var wrtElem = document.getElementById(wrtElementId);
    
    RegisterHandler(wrtElem, // fire me when wrtelem changes, 
                    "TNSMIresize", new Function("SetElementFullWidthWRTContainer('" + targetElementId + "', true);"), false);

}

function RememberProportionalSize(targetElementId)
{
    alert("Entered RememberProportionalSize() for targetElementId=|" + targetElementId + "|");
    
    var targetElem = document.getElementById(targetElementId);
    if (! targetElem) 
    {
        alert("RememberProportionalSize(): Couldn't find targetElem |" + targetElementId + "|");
        return;
    }
    var wrtElem = targetElem.offsetParent;
    
    var targetWidth = targetElem.clientWidth;
    var targetHeight = targetElem.clientHeight;
    
    var wrtWidth = wrtElem.clientWidth;
    var wrtHeight = wrtElem.clientHeight - GetSiblingHeights(targetElementId);

    /*    
    alert("Starting with h=" + targetHeight + "|");
    for (var i=0; i<wrtElem.childNodes.length; i++)
    {
        var child = wrtElem.childNodes[i];
        if ((child.id != targetElem.id) && (! isNaN(child.clientHeight)))
        {
            alert("child[" + i + "]=" + GetTagForElement(child) + " h=" + child.clientHeight + "\n");
            targetHeight -= child.clientHeight;
        }
    }
    // Prop assumes everything is in proprotion, however, toolbar is fixed height!!!!
    */
    
    var widthPerc = (targetWidth / wrtWidth) * 100;
    var heightPerc = (targetHeight / wrtHeight) * 100;
    
    alert("RememberProportionalSize(): \n" + 
          "target=" + GetTagForElement(targetElem) + " w=" + targetWidth + ", h=" + targetHeight + "\n" +
          "wrt=" + GetTagForElement(wrtElem) + " w=" + wrtWidth + ", h=" + wrtHeight + "\n" +
          "widthPerc=|" + widthPerc + "|  heightPerc=|" + heightPerc + "|");
    
    //targetElem["widthPerc"] = widthPerc;
    targetElem["heightPerc"] = heightPerc;
}

function GetSiblingHeights(targetElementId)
{
    alert("Entered GetSiblingHeights() for targetElementId=|" + targetElementId + "|");
    
    var targetElem = document.getElementById(targetElementId);
    if (! targetElem) 
    {
        alert("GetSiblingHeights(): Couldn't find targetElem |" + targetElementId + "|");
        return;
    }
    
    var parentElem = targetElem.offsetParent;
    
    var siblingHeight = 0;
    for (var i=0; i<parentElem.childNodes.length; i++)
    {
        var child = parentElem.childNodes[i];
        if ((child.id != targetElem.id) && (! isNaN(child.clientHeight)))
        {
            alert("child[" + i + "]=" + GetTagForElement(child) + " h=" + child.clientHeight + "\n");
            siblingHeight += child.scrollHeight;
        }
    }

    return siblingHeight;    
}


function GetBeyondWidth(element) 
{
    var width = 0;
    
    var borderLeftWidth = parseInt(element.style.borderLeftWidth);
    if (isNaN(borderLeftWidth)) borderLeftWidth = 0;
    
    var borderRightWidth = parseInt(element.style.borderRightWidth);
    if (isNaN(borderRightWidth)) borderRightWidth = 0;
    
    var borderWidth = (borderLeftWidth + borderRightWidth);
    
    var scrollerWidth = 0;
    if (GetIsIE()) scrollerWidth = element.offsetWidth - element.clientWidth;
    
    return (borderWidth + scrollerWidth);
}

function GetBeyondHeight(element) 
{
    var width = 0;
    
    var borderTopWidth = parseInt(element.style.borderTopWidth);
    if (isNaN(borderTopWidth)) borderTopWidth = 0;
    
    var borderBottomWidth = parseInt(element.style.borderBottomWidth);
    if (isNaN(borderBottomWidth)) borderBottomWidth = 0;
    
    return (borderTopWidth + borderBottomWidth);
}


//
// This function uses the "shim" method to cover active content for IE, so that standard selects won't show through the element.
// 'delayed' is used internally to delay the hide on the body element.  It indicates that it have already be delayed.  
//
function HideActiveContentForElement(element, delayed) 
{

    if (GetIsIE()) {
    
        var frameId = element.id + "_shim";
        var tempElem = document.createElement("temp");
        tempElem.innerHTML = '<iframe id="' + frameId + '" src="about:blank" scroll="no" frameborder="0"  style="position: absolute; display: none;"></iframe>';

        var parent = element.offsetParent;
        //AlertElementDimensions(parent, "parent of the shimmed element");

        if ((parent.tagName.toLowerCase() == "body") && (! delayed))
        {
            var funcString = "var func = function(event) { HideActiveContentForElement(element, true); }";
            eval(funcString);
            StartTimer(element.id + "_HideActiveContentForElement", 50, func, false);
        }
        else
        {
            var shim = parent.appendChild(tempElem.firstChild);
            if (shim)
            {
                //Unremark this line if you need your menus to be transparent for some reason
                shim.style.filter="progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)";

                shim.style.width = element.offsetWidth + "px";
                shim.style.height = element.offsetHeight + "px";
                shim.style.left = element.offsetLeft + "px";
                shim.style.top = element.offsetTop + "px";
                //shim.style.border = "1px solid green";
                shim.style.zIndex = Math.max(0, element.style.zIndex - 1); // Put it just behind the element.  Sometimes (event though the element has a css class w/ a high z-index, zIndex will be zero.  If the shim is placed at -1, it will be too low to cover other controls.
                shim.style.display = "block";
                
                //AlertElementDimensions(shim, "added the shim");
            }
            else
            {
                alert("elementUtils HideActiveContentForElement(): Problems creating shim.");
            }
        }
    }
    
} // HideActiveContentForElement()

// Removes the shim iframe that is used to hide active content in IE.
// See HideActiveContent().
function UnHideActiveContentForElement(element) 
{
    if (GetIsIE()) {
        var shimId = element.id + "_shim";
        var shim = document.getElementById(shimId);
        if (shim)
            shim.parentElement.removeChild(shim);
        //alert("Removed shim");
    }
} // UnHideActiveContentForElement()

function UpdateShimPosition(element) 
{
    if (GetIsIE()) {
        var shimId = element.id + "_shim";
        var shim = document.getElementById(shimId);
        if (shim)
        {
            shim.style.left = element.offsetLeft + "px";
            shim.style.top = element.offsetTop + "px";
        }  
    }      
}

function UpdateShim(element) 
{
    if (GetIsIE()) {
        var shimId = element.id + "_shim";
        var shim = document.getElementById(shimId);
        if (shim)
        {
            shim.style.left = element.offsetLeft + "px";
            shim.style.top = element.offsetTop + "px";
            shim.style.width = element.offsetWidth + "px";
            shim.style.height = element.offsetHeight + "px";
        }  
    }      
}

function GetShimElement(element)
{
    var shimId = element.id + "_shim";
    var shim = document.getElementById(shimId);
    return shim;
}


/*
function SetFrameFullWidth(percentage)
{
    var parentWindow = this.window.parent;
    
    var frame = null;
    for (var i=0; i<parentWindow.frames.length; i++) 
    {
        if (parentWindow.frames[i] == this.window) 
        {
            frame = parentWindow.frames[i];
            break;
        }
    }
    
    if (! frame)
        alert("Couldn't locate window as a frame within the parent window");
    
    alert("frame=|" + frame + "| (" + frame.id + ")");
    AlertElementDimensions(frame, "myframe");    
       
    var frameId = frame.id;
    var frameElem = window.parent.document.getElementById(frameId);
    alert("frameElem=|" + frameElem + "|");
}
*/

// END - In-Progress functions for adjusting the height/width of elements



// ++++++++++++++++++++++++ Event Handler functions +++++++++++++++++++++++++


// ++ BEGIN: RegisteredEvent ++
var _registeredEventHandlers = new Object();
var _registeredEventHandlerCounter = 0

function RegisteredEvent(target, eventType, handler, method, capture)
{
    this.target = target;
    this.eventType = eventType;
    this.handler = handler;
    this.method = method;
    this.capture = capture;
}
RegisteredEvent.prototype.SetHandlerFunction = function(func)
{
    this.handlerFunction = func;
}
RegisteredEvent.prototype.GetHandlerFunction = function()
{
    return this.handlerFunction;
}
// ++ END: RegisteredEvent ++

// TO DO - look at Event.DOMEvents array (Prototype JS Framework?)?
var StandardEventTypes = new Array("abort", "blur", "change", "click", "dblclick", "error", "focus", "load",
                                         "mousedown", "mousemove", "mouseout", "mouseover", "mouseup",
                                         "reset", "resize", "scroll", "select", "submit", "unload",
                                         "DOMActivate", "DOMAttrModified", "DOMCharacterDataModified",
                                         "DOMFocusIn", "DOMFocusOut", 
                                         "DOMNodeInserted", "DOMNodeInsertedIntoDocument",
                                         "DOMNodeRemoved", "DOMNodeRemovedFromDocument",
                                         "DOMSubtreeModified"
                                         );
function IsStandardEventType(eventType)
{
    for (var i=0; i<StandardEventTypes.length; i++)
        if (StandardEventTypes[i] == eventType) return true;
    return false;
}

function EventTypeToEventModule(eventType)
{
    switch (eventType)
    {
        case "abort":
        case "blur":
        case "change":
        case "error":
        case "focus":
        case "load":
        case "reset":
        case "resize":
        case "scroll":
        case "select":
        case "submit":
        case "unload":
            return "HTMLEvents";
        case "click":
        case "dblclick":
        case "mousedown":
        case "mousemove":
        case "mouseout":
        case "mouseover":
        case "mouseup":
            return "MouseEvents";
        case "DOMActivate":
        case "DOMFocusIn":
        case "DOMFocusOut":
            return "UIEvents";
        case "DOMAttrModified":
        case "DOMCharacterDataModified":
        case "DOMNodeInserted":
        case "DOMNodeInsertedIntoDocument":
        case "DOMNodeRemoved":
        case "DOMNodeRemovedFromDocument":
        case "DOMSubtreeModified":
            return "MutationEvents";
        default:
            throw new Exception("Unsupported eventType: '" + eventType + "'");
    }
}


// Begin Event ----------------------------------------------------

// TO DO - look into the DOM's Event class, and also at 
//         the Prototype JavaScript framework at http://prototypejs.org/.

//
// Example - to create your own generic event:
//
//          var myEvent = new TNSMIEvent("Event");
//          myEvent.InitEvent("TNSMI_ProgressBoxRefresh", false, true);
//

function CreateTNSMIEventFromEvent(ev)
{
    var eventType = ev.eventType;
    if (!eventType) eventType = "Event";
    
    var tnsmiEvent = new TNSMIEvent(eventType);
    
    if (tnsmiEvent.eventType == "Event")
    {
        tnsmiEvent.InitEvent(ev.type, ev.bubbles, ev.cancelable);
    }
    else if (tnsmiEvent.eventType == "MouseEvent")
    {
        tnsmiEvent.InitMouseEvent(ev.type, ev.bubbles, ev.cancelable,
                                  ev.view, ev.detail,
                                  0, 0, ev.clientX, ev.clientY,
                                  ev.ctrlkey, ev.altkey, ev.shiftkey, ev.metakey,
                                  ev.button, ev.relatedTarget);
    }
    
    for (var prop in ev)
    {
        //alert("tnsmiEvent.event." + prop + " = ev." + prop);
        try { eval("tnsmiEvent.event." + prop + " = ev." + prop); }
        catch (ex) { /*alert("error setting " + prop);*/ }
    }
    
    return tnsmiEvent;
}

function TNSMIEvent(eventType) 
{
    this.eventType = eventType;
    
    if (document.createEvent)
    {
        this.event = document.createEvent(eventType);
    }
    else if (document.createEventObject)
    {
        this.event = document.createEventObject();
    }
    else
        alert("Event() Unable to create event object");
        
    this.event.eventType = this.eventType;
}

TNSMIEvent.prototype.constructor = "TNSMIEvent";

TNSMIEvent.prototype.InitEvent = function(typeArg, canBubbleArg, cancelableArg)
{
    if (this.eventType != "Event")
    {
        alert("Invalid eventType " + this.eventType + ".  Use InitEvent() only with Event.");
        return;
    }
    
    if (this.event.initEvent)
        this.event.initEvent(typeArg, canBubbleArg, cancelableArg);
    else
    {
        this.event.type = typeArg;          // need this for the fireEvent call.
    }
}

TNSMIEvent.prototype.InitMouseEvent = function(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, 
                                          screenXArg, screenYArg, clientXArg, clientYArg, 
                                          ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
                                          buttonArg, relatedTargetArg)
{
    if (this.eventType != "MouseEvent")
    {
        alert("Invalid eventType " + this.eventType + ".  Use InitMouseEvent() only with MouseEvent.");
        return;
    }

    this.type = typeArg;
    
    if (this.event.initMouseEvent)
        this.event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, 
                                  screenXArg, screenYArg, clientXArg, clientYArg, 
                                  ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
                                  buttonArg, relatedTargetArg);
    else
    {
        this.event.type = typeArg;          // need this for the fireEvent call.
        this.event.detail = detailArg;
        this.event.screenX = screenXArg;
        this.event.screenY = screenYArg;
        this.event.clientX = clientXArg;
        this.event.clientY = clientYArg;
        this.event.ctrlKey = ctrlKeyArg;
        this.event.altKey = altKeyArg;
        this.event.shiftKey = shiftKeyArg;
        this.event.metaKey = metaKeyArg;
        this.event.button = buttonArg
        this.event.relatedTarget = relatedTargetArg;
    }
}

// Note - I think we could also implement an InitHtmlEvent() method at some point too.

TNSMIEvent.prototype.Dispatch = function(target) 
{
    /*
    LogReportMonitor("TNSMIEvent", 
                     "Entered Event.Dispatch():  target=" + GetTagForElement(target) + " --> " + this.constructor + ":" + this.eventType + "." + (this.event ? this.event.type + " " : "?") +
                     "");
    */
    
    var eventToSend = this.event;
    
    if (!target)
    {
        alert("Event.Dispatch(): Missing target");
        return;
    }
    
    if (target.dispatchEvent)
    {
        target.dispatchEvent(eventToSend);
    }
    else
    {
        if (IsStandardEventType(this.event.type))
        {
            var sEvent = "on" + this.event.type;
            //AlertElementDimensions(target, "firing event sEvent=|" + sEvent + "|  event=|" + this.event + "|");
            target.fireEvent(sEvent, eventToSend);
        }
        else
        {
            if (target.EventListeners && target.EventListeners[this.event.type])
            {
                for (var i=0; i<target.EventListeners[this.event.type].Keys.length; i++)
                {
                    var key = target.EventListeners[this.event.type].Keys[i];
                    var func = target.EventListeners[this.event.type].Get(key);
                    //LogReportMonitor("TNSMIEvent", "Dispatch(): this.event.type=|" + this.event.type + "|  key=|" + key + "| func=|" + func + "| (" + typeof(func) + ") eventToSend=|" + eventToSend + "|");
                    if (typeof(func) == "function")
                        func.call(window, eventToSend);
                    else
                        func.Call(eventToSend);
                }
            }
            //else alert("Event.Dispatch()  No custom handlers for " + this.event.type + " in " + target + ".  (No body cares...)");
        }
    }
}


function CreateEventBroker(id) 
{
    var formElem = document.getElementsByTagName("form")[0];
    if (! formElem) alert("No formElem");
    
    var brokerId = id + "_EventBroker";
    var broker = document.getElementById(brokerId);
    if (broker) 
    {
        return broker;
    }
    
    var tempElem;
    tempElem = document.createElement("temp");
    tempElem.innerHTML = "<div id=\""+brokerId+"\" style=\"width: auto; height: auto; display: none\"></div>";
    broker = formElem.insertBefore(tempElem.firstChild, formElem.firstChild);
    //AlertElementDimensions(document.getElementById('_draggerReport'), "Added DraggerReport");

    return broker;
}

function RemoveEventBroker(id)
{
    //LogReportMonitor("TNSMIEvent", "Removing event for id=" + id);
    
    var formElem = document.getElementsByTagName("form")[0];
    if (! formElem) alert("No formElem");
    
    var brokerId = id + "_EventBroker";
    var broker = document.getElementById(brokerId);
    if (broker)
    {
        // Unregister event listeners?
        //ShowEventListeners(broker);
        broker.EventListeners = null;
        broker.parentNode.removeChild(broker);
    }
}

// End Event ----------------------------------------------------


// eventHandlerId (optional)
function RegisterEventHandler(target, eventType, handler, capture, eventHandlerId) 
{
    if (target.EventElement) 
        target = target.EventElement;        
    else if (target.GetEventElement)
        target = target.GetEventElement();

    //LogReportMonitor("TNSMIEvent", "Entered RegisterEventHandler() : \ntarget=" + target + " " + GetTagForElement(target) + "\neventType=" + eventType + "\nhandler=" + handler + " \ncapture=" + capture);
    
    if (! eventHandlerId) 
        eventHandlerId = ++_registeredEventHandlerCounter;
    else 
    {
        // Make sure there are no old events around.
        if (_registeredEventHandlers[eventHandlerId]) UnregisterEventHandler(eventHandlerId);
    }
    
    if (target.addEventListener) 
        target.addEventListener(eventType, handler, capture);
    else if (target.attachEvent) 
        if (IsStandardEventType(eventType))
            target.attachEvent("on" + eventType, handler);
        else
        {
            if (! target.EventListeners) target.EventListeners = new Object;
            if (! target.EventListeners[eventType]) target.EventListeners[eventType] = new Hash();
            target.EventListeners[eventType].Add(eventHandlerId, handler);
            //alert("Registered handler for " + eventType + " from " + target);
        }
    else
    {
        alert("RegisterEventHandler(): Unsupported browser for registering handlers or target does not provide an EventElement.  target=" + GetTagForElement(target));
        return;
    }
    
    _registeredEventHandlers[eventHandlerId] = new RegisteredEvent(target, eventType, handler, null, capture);
    return eventHandlerId;
    
}


function UnregisterEventHandler(eventHandlerId)
{
    var eventHandler = _registeredEventHandlers[eventHandlerId];
    
    if (! eventHandler)
    {
        alert("UnregisterEventHandler():  No event handlers registered with id=|" + eventHandlerId + "|");
        return;
    }
    
    //LogReportMonitor("TNSMIEvent", "Entered UnregisterEventHandler(): eventHandlerId=|" + eventHandlerId + "|");
    
    if (eventHandler.target.removeEventListener) 
        eventHandler.target.removeEventListener(eventHandler.eventType, (eventHandler.GetHandlerFunction()?eventHandler.GetHandlerFunction():eventHandler.handler), eventHandler.capture);
    else if (eventHandler.target.detachEvent)
    { 
        if (IsStandardEventType(eventHandler.eventType))
            eventHandler.target.detachEvent("on" + eventHandler.eventType, (eventHandler.GetHandlerFunction()?eventHandler.GetHandlerFunction():eventHandler.handler));
        else
        {
            if (eventHandler.target.EventListeners && 
                eventHandler.target.EventListeners[eventHandler.eventType] && 
                eventHandler.target.EventListeners[eventHandler.eventType][eventHandlerId]
               )
                eventHandler.target.EventListeners[eventHandler.eventType].Remove(eventHandlerId);
        }
    }
    else
        alert("Unsupported browser for unregistering handlers");
    
    _registeredEventHandlers[eventHandlerId] = null;
}


function IsEventRegistered(eventHandlerId)
{
    var eventHandler = _registeredEventHandlers[eventHandlerId];
    if (eventHandler)
        return true;
    else
        return false;
}



function ShowEventListeners(target, eventType)
{
    if (target.EventListeners)
    {
        if (eventType) 
        {
            if (target.EventListeners[eventType])
            {
                for (var i=0; i<target.EventListeners[eventType].Keys.length; i++)
                {
                    var key = target.EventListeners[eventType].Keys[i];
                    var func = target.EventListeners[eventType].Get(key);
                    if (func && func.constructor && func.constructor == "FunctionCall")
                        func = (func.target.constructor?func.target.constructor:func.target)+"." + func.methodName+"()";
                    LogReportMonitor("TNSMIEvent", "ShowEventListeners():  eventType=|" + eventType + "|  " +
                                                   //"key=|" + key + "| " +
                                                   "func=|" + func + "|  " +
                                                   "");
                }
            }
            else
                LogReportMonitor("TNSMIEvent", "No event listener for eventType=|" + eventType + "| on target=|" + target + "|");
        }
        else
        {
            var c = 0;
            for (var t in target.EventListeners)
            {
                ShowEventListeners(target, t);
                c++;
            }
            if (c == 0) LogReportMonitor("TNSMIEvent", "No event listeners on target=|" + target + "|");
        }
    }
    else
        LogReportMonitor("TNSMIEvent", "No event listeners on target=|" + target + "|");

}


//
// target is an element or other DOM object that supports the firing of events.
// eventType is a string that is either a standard event type, or a custom event type.
// handlerInstance is the instance of the object to invoke the method on.
// methodName is the name of the method of the handler instance that is invoked.
// capture is a boolean that is applicable only to FireFox (standard)
// eventHandleId is an int that represents this registration.  It is useful to manage registrations.
//

function RegisterObjectMethodEventHandler(target, eventType, handlerInstance, methodName, capture, eventHandlerId) 
{
    if (target.EventElement) 
        target = target.EventElement;        
    else if (target.GetEventElement)
        target = target.GetEventElement();

    //LogReportMonitor("TNSMIEvent", "Entered RegisterObjectMethodEventHandler(target=" + GetTagForElement(target) + ", eventType=" + eventType + ", handler=" + handlerInstance.constructor + "." + methodName + "(), capture=" + capture);

    if (! eventHandlerId) 
        eventHandlerId = ++_registeredEventHandlerCounter;
    else 
    {
        // Make sure there are not old events around.
        if (_registeredEventHandlers[eventHandlerId]) UnregisterEventHandler(eventHandlerId);
    }
    
    var funcString = "var func = function(event) { handlerInstance." + methodName + "(event); }";
    eval(funcString);
    
    if (target.addEventListener) 
        target.addEventListener(eventType, func, capture);
    else if (target.attachEvent) 
    {
        if (IsStandardEventType(eventType))
            target.attachEvent("on" + eventType, func);
        else
        {
            if (! target.EventListeners) target.EventListeners = new Object;
            if (! target.EventListeners[eventType]) target.EventListeners[eventType] = new Hash();
            target.EventListeners[eventType].Add(eventHandlerId, new FunctionCall(handlerInstance, methodName));
            //alert("Registered handler for " + eventType + " from " + target);
        }
    }
    else
        alert("TNSMIEvent.RegisterObjectMethodEventHandler(): Unsupported browser for registering handlers or target does not provide an EventElement.  target=" + GetTagForElement(target));
    
    _registeredEventHandlers[eventHandlerId] = new RegisteredEvent(target, eventType, handlerInstance, methodName, capture);
    _registeredEventHandlers[eventHandlerId].SetHandlerFunction(func);
    return eventHandlerId;
}

// --------------
function FunctionCall(target, methodName) 
{
    this.target = target;
    this.methodName = methodName;
}

FunctionCall.prototype.constructor = "FunctionCall";

FunctionCall.prototype.Call = function(ev)
{
    var stringToEval = "this.target." + this.methodName + "(ev)";
    //alert("FunctionCall.Call(): stringToEval=|" + stringToEval + "|");
    eval(stringToEval);
}
// --------------

// For debugging...
function GetEventDataMsg(anEvent)
{
    var s;
    for (var prop in anEvent)
    {
        try { eval("s += prop + \" = \" + anEvent[prop] + \"\\n\""); }
        catch (ex) 
        { 
            //alert("error setting " + prop); 
        }
    }
    
    return s;
    /*
    var eventType = "";
    //if (anEvent["type"]) eventType = anEvent["type"];
    if (anEvent.type != null) eventType = anEvent.type;
    
    var msg = "event=|" + anEvent + "|  typeof=|" + typeof(anEvent) + "|\n" +
              "type=|" + eventType + "|\n" +
              "";
    return msg;
    */
}

// Begin Event Info ----------------------------------------------------

function EventInfo(event)
{
    if (!event) event = window.event;
    
    // Event
    this.type = event.type;
    this.target = event.target;
    this.currentTarget = event.currentTarget;
    this.eventPhase = event.eventPhase;
    this.timeStamp = event.timeStamp;
    this.bubbles = event.bubbles;
    this.cancelable = event.cancelable;
    
    // UIEvent
    this.view = event.view;
    this.detail = event.detail;
    
    // MouseEvent
    this.button = event.button;
    this.altkey = event.altkey;
    this.ctrlkey = event.ctrlkey;
    this.metakey = event.metakey;
    this.shiftkey = event.shiftkey;
    this.clientX = event.clientX;
    this.clientY = event.clientY;
    this.screenX = event.screenX;
    this.screenY = event.screenY;
    this.relatedTarget = event.relatedTarget;
    
    // IE
    this.srcElement = event.srcElement;
    this.offsetX = event.offsetX;
    this.offsetY = event.offsetY;
    this.keyCode = event.keyCode;
    this.fromElement = event.fromElement;
    this.toElement = event.toElement;
    this.cancelBubble = event.cancelBubble;
    this.returnValue = event.returnValue;
    
}

EventInfo.prototype.constructor = "EventInfo";

// End Event Info ----------------------------------------------------



// TODO: Look into XBrowserAddHandler() from shared.js.

// eventHandlerId (optional)
function RegisterHandler(target, eventType, handler, capture) 
{
    //alert("Entered RegisterHandler() : \ntarget=" + target + " " + GetTagForElement(target) + "\neventType=" + eventType + " \n\nhandler=" + handler + " \n\ncapture=" + capture);
    
    if (target.addEventListener) 
        target.addEventListener(eventType, handler, capture);
    else if (target.attachEvent) 
        target.attachEvent("on" + eventType, handler);
    else
        alert("Unsupported browser for registering handlers");
}

function UnregisterHandler(target, eventType, handler, capture) 
{
    //alert("Entered UnregisterHandler(" + target + ", " + eventType + ", " + handler + ", " + capture);
    
    if (target.removeEventListener) 
        target.removeEventListener(eventType, handler, capture);
    else if (target.detachEvent) 
        target.detachEvent("on" + eventType, handler);
    else
        alert("Unsupported browser for unregistering handlers");
}


// ------------------------ Event Handler functions -------------------------


    function TNSMIResizeEventHandler(e)
    {
        var element = null;
        if(e == null)
            e = window.event;
            
        if(e.originalTarget)
            element = e.originalTarget;
        else
            element = e.srcElement;
            
        var childId = element.getAttribute("handlerId");
        var child = document.getElementById(childId);
        ResizeHandlerElement(child, element);
    }

    function ResizeHandlerElement(handlerElement, parent)
    {
        var w = 0;
        var h = 0;
        if(GetIsIE())
        {
            w = parent.clientWidth;
            h = parent.clientHeight;
        }
        else
        {
            w = parent.offsetWidth;
            h = parent.offsetHeight;
        }
        handlerElement.style.width = w + "px";
        handlerElement.style.height = h + "px";
        var tnsmiResizeEvent = handlerElement.getAttribute("TNSMIresize");
        if(tnsmiResizeEvent != null)
            eval(tnsmiResizeEvent);
    }
    
    function ResizeAll_RESIZEHANDLERs()
    {
        try
        {
            var items = new Array();
            if(!GetIsIE())
                items = document.getElementsByName("RESIZEHANDLER");
            else
            {
                var divs = document.getElementsByTagName("div");
                for(var i=0; i < divs.length; i++)
                {
                    if(divs[i].name == "RESIZEHANDLER")
                        items.push(divs[i]);
                }
            }
            for(var i=0; i < items.length; i++)
            {
                var p = items[i].parentNode;
                var size = p.offsetWidth + " " + p.offsetHeight;
                var lastSize = p.getAttribute("lastsize");
                if(lastSize == null) lastSize = "";
                if(lastSize != size)
                {
                    p.setAttribute("lastsize", size);
                    ResizeHandlerElement(items[i], p);
                }
            }
        }
        catch(ex)
        {
            //alert(ex.message);
            // this method should be errorless
        }        
    }

    function FFResizeMonitor()
    {
        ResizeAll_RESIZEHANDLERs();
        window.setTimeout("FFResizeMonitor()", 1000);
    }
    
    function InstallTNSMIResizeEventHandlers(eventName)
    {
        var items = new Array();
        if(!GetIsIE())
            items = document.getElementsByName("RESIZEHANDLER");
        else
        {
            var divs = document.getElementsByTagName("div");
            for(var i=0; i < divs.length; i++)
            {
                if(divs[i].name == "RESIZEHANDLER")
                    items.push(divs[i]);
            }
        }
        for(var i=0; i < items.length; i++)
        {
            var div = items[i];
            var p = div.parentNode;
            p.vAlign = "top";
            var install = (p.getAttribute("handlerId") == null); // event handler already installed
            if(p.getAttribute("handlerId") != div.id)  // if child was changed then set new id
            {
                p.setAttribute("handlerId", div.id);
            }
            if(install)
            {
                ResizeHandlerElement(div, p);
                RegisterEventHandler(p, eventName, TNSMIResizeEventHandler, true);
            }
        }
    }
    
    function InitializeRESIZEHANDLER()
    {
        //if(!GetIsIE())
        //{
        //    window.setTimeout("InstallTNSMIResizeEventHandlers('TNSMIresize')", 200);
        //}
        //else
        //{
        //    window.setTimeout("InstallTNSMIResizeEventHandlers('resize')", 200);
        //}
    }    

    //if(!GetIsIE())
    //{
        window.setTimeout("FFResizeMonitor()", 1000);
    //}
    
    
    
    
var _FadeBackColor_TimerId = 0;
var _FadeBackColor_Element = null;
var _FadeBackColor_Color = null;

function FadeBackColor(controlId, colorHex, colorHex2, delay) 
{
	_FadeBackColor_Element = document.getElementById(controlId);
	if(_FadeBackColor_Element == null) return;
	if(colorHex == null) colorHex = "FF0000";
	if(delay == null) delay = 1000;
	
	var style = GetCurrentBackgroundColor(_FadeBackColor_Element);
		
	_FadeBackColor_Element.setAttribute("normalStyle", style);
	

	_FadeBackColor_Element.style.backgroundColor = "#" + colorHex;

    _FadeBackColor_Color = colorHex2;
    
	if(_FadeBackColor_TimerId != 0)
	{
		window.clearTimeout(_FadeBackColor_TimerId);
		_FadeBackColor_TimerId = 0;
	}
	_FadeBackColor_TimerId = window.setTimeout('StartFadeBackColor()', delay);
}

function StartFadeBackColor()
{
	for(i = 1; i <= 10; i++) 
		window.setTimeout('FadeBackColorStyle(' + i * 10 + ')', i * 50);
	
	_FadeBackColor_TimerId = 0;
}

function FadeBackColorStyle(percent) 
{	
	var currentStyle = _FadeBackColor_Color;// 'c60c30';

	if(_FadeBackColor_Element == null) return;
	
	var normalStyle = _FadeBackColor_Element.getAttribute("normalStyle");
	var r1 = hex2dec(currentStyle.slice(0,2));
	var g1 = hex2dec(currentStyle.slice(2,4));
	var b1 = hex2dec(currentStyle.slice(4,6));
	
	var r2 = hex2dec(normalStyle.slice(0,2));
	var g2 = hex2dec(normalStyle.slice(2,4));
	var b2 = hex2dec(normalStyle.slice(4,6));

	var pc = percent / 100;

	var r= Math.floor(r1+(pc*(r2-r1)) + .5);
	var g= Math.floor(g1+(pc*(g2-g1)) + .5);
	var b= Math.floor(b1+(pc*(b2-b1)) + .5);

	_FadeBackColor_Element.style.backgroundColor = "#" + dec2hex(r) + dec2hex(g) + dec2hex(b);
}
