// Copyright (C) 2007-2010 Bristle Software, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 1, or (at your option)
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc.

/******************************************************************************
* com.bristle.jslib.Layout.js
*******************************************************************************
* Purpose:
*       This file contains routines that used to deal with issues of graphical
*       layout, computing sizes and bounding rectanlges, growing objects to 
*       enclose other objects, etc.
* Usage:
*       - The typical scenario for using this file from an HTML file is:
*         <script language='JavaScript' src='com.bristle.jslib.Layout.js'></script>
*         Call the various functions that reside here.
* Assumptions:
*       - The file "com.bristle.jslib.Util.js" has already been loaded.
* Effects:
*       - None.
* Anticipated Changes:
* Notes:
* Implementation Notes:
* Portability Issues:
* Revision History:
*   $Log$
******************************************************************************/

// Create the "namespace" to hold the functions in this file.
com.bristle.jslib.Layout = {};

/******************************************************************************
* Return the inner width of a window.
*
*@param objWindow   Window to measure.  Default: current window
*@return            Inner width of the window.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getInnerWindowWidth =
function(objWindow)
{
    if (com.bristle.jslib.Util.isMissingNullOrUndefined(objWindow))
    {
        objWindow = window;
    }
    if (objWindow.innerWidth)
    {
        return objWindow.innerWidth;
    }
    if (com.bristle.jslib.Util.blnIsIE6CSS)
    {
        return objWindow.document.body.parentElement.clientWidth;
    }
    if (   objWindow.document.body 
        && objWindow.document.body.clientWidth
       )
    {
        return objWindow.document.body.clientWidth;
    }
    //?? Test this.  When it's defined, is it always right?  Or can we only
    //?? do this when com.bristle.jslib.Util.blnIsIE6CSS()?
    if (   objWindow.document.body 
        && objWindow.document.body.parentElement 
        && objWindow.document.body.parentElement.clientWidth)
    {
        return objWindow.document.body.parentElement.clientWidth;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine inner window width with this browser.");
}

/******************************************************************************
* Return the inner height of a window.
*
*@param objWindow   Window to measure.  Default: Current window
*@return            Inner height of the window.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getInnerWindowHeight =
function(objWindow)
{
    if (com.bristle.jslib.Util.isMissingNullOrUndefined(objWindow))
    {
        objWindow = window;
    }
    if (objWindow.innerHeight)
    {
        return objWindow.innerHeight;
    }
    if (com.bristle.jslib.Util.blnIsIE6CSS)
    {
        return objWindow.document.body.parentElement.clientHeight;
    }
    if (   objWindow.document.body 
        && objWindow.document.body.clientHeight
       )
    {
        return objWindow.document.body.clientHeight;
    }
    //?? Test this.  When it's defined, is it always right?  Or can we only
    //?? do this when com.bristle.jslib.Util.blnIsIE6CSS()?
    if (   objWindow.document.body 
        && objWindow.document.body.parentElement 
        && objWindow.document.body.parentElement.clientHeight)
    {
        return objWindow.document.body.parentElement.clientHeight;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine inner window height with this browser.");
}

/******************************************************************************
* Rectangle object.
*******************************************************************************
* Usage:
*      var objRect = new com.bristle.jslib.Util.Rectangle
*                              (intLeft, intTop, intWidth, intHeight);
*      alert(  "  Left   = " + objRect.left
*            + "\nTop    = " + objRect.top
*            + "\nWidth  = " + objRect.width
*            + "\nHeight = " + objRect.height
*           );                
*      objRect.left = objRect.left + 100;
*      alert(  "  Left   = " + objRect.left
*            + "\nTop    = " + objRect.top
*            + "\nWidth  = " + objRect.width
*            + "\nHeight = " + objRect.height
*           );                
******************************************************************************/
com.bristle.jslib.Layout.Rectangle =
function(intLeft, intTop, intWidth, intHeight)
{
    this.left   = intLeft;
    this.top    = intTop;
    this.width  = intWidth;
    this.height = intHeight;
}

/******************************************************************************
* Grow specified com.bristle.jslib.Layout.Rectangle to enclose the HTML 
* objects in specified array.
*
*?? Note:  Works fine for height, but how to determine the needed width?
*??        Many elements grow to fit the current window width, 
*??        regardless of the width they need:  DIV, CENTER, FORM, TABLE,
*??        TBODY, THEAD, TFOOT, TR, TH, TD, etc.  Can't just recur on
*??        them, using the contents of their children instead because 
*??        the children may be #text nodes which have no width.
*??        Also, if you ignore the container elements, the positions
*??        and sizes of the children are irrelevant because they are 
*??        relative to the position of the parent.  For example, can't
*??        compute the enclosing rectangle for all buttons if the 
*??        buttons are laid out in a table.  Each one claims to be at 
*??        position (1,1) -- the top left corner of its table cell.
*
*@param arrObjects  Array of objects to enclose.
*@param objRect     The rectangle.  It is updated in place.  Grown from 
*                   initial value to enclose the objects.  Not shrunk.
*@return            objRect
*@throws            None.
******************************************************************************/
com.bristle.jslib.Layout.growRectangleToEncloseHTMLObjects =
function(arrObjects, objRect)
{
    for (var i = 0; i < arrObjects.length; i++)
    {
        var obj = arrObjects[i];
//?? The following doesn't work.  See Note above.
//??            if (   obj.tagName == "DIV"
//??                || obj.tagName == "CENTER"
//??                || obj.tagName == "FORM"
//??                || obj.tagName == "TABLE"
//??                || obj.tagName == "TBODY"
//??                || obj.tagName == "THEAD"
//??                || obj.tagName == "TFOOT"
//??                || obj.tagName == "TR"
//??                || obj.tagName == "TH"
//??                || obj.tagName == "TD"
//??               )
//??            {
//??                // Recur, enclosing children of a container element, 
//??                // instead of using the size of the container itself.  
//??                // This is because such containers always report the 
//??                // same width as the entire window form, not the width 
//??                // used actually used by their contents.
//??                com.bristle.jslib.Layout.growRectangleToEncloseHTMLObjects
//??                                            (obj.childNodes, objRect);
//??            }
//??            else
//??            {
//??com.bristle.jslib.MsgBox.reportError(obj.tagName + " " + obj.id + ": " 
//??+ obj.offsetLeft + ", " 
//??+ obj.offsetTop + ", " 
//??+ obj.offsetWidth + ", " 
//??+ obj.offsetHeight);
        if (!isNaN(obj.offsetLeft))
        {
            objRect.left   = Math.min(objRect.left, obj.offsetLeft);
        }
        if (!isNaN(obj.offsetTop))
        {
            objRect.top    = Math.min(objRect.top,  obj.offsetTop);
        }
        if (!isNaN(obj.offsetWidth))
        {
            objRect.width  = Math.max(objRect.width, 
                                      obj.offsetLeft + obj.offsetWidth);
        }
        if (!isNaN(obj.offsetHeight))
        {
            objRect.height = Math.max(objRect.height, 
                                      obj.offsetTop + obj.offsetHeight);
        }
//??com.bristle.jslib.MsgBox.reportError("Rectangle = "
//??+ objRect.left + ", " 
//??+ objRect.top + ", " 
//??+ objRect.width + ", " 
//??+ objRect.height);
//??            }
    }
    return objRect;
}

/******************************************************************************
* Grow specified com.bristle.jslib.Layout.Rectangle to enclose the contents 
* of the specified window.
*
*?? Note:  Works fine for height, but not width.  See Note at 
*??        com.bristle.jslib.Layout.growRectangleToEncloseHTMLObjects.
*
*@param objWindow   Window to base rectangle size on.  Default: Current window
*@param objRect     The rectangle.  It is updated in place.  Grown from 
*                   initial value to enclose the objects.  Not shrunk.
*@return            objRect
*@throws            None.
******************************************************************************/
com.bristle.jslib.Layout.growRectangleToEncloseWindowContents =
function(objWindow, objRect)
{
    if (com.bristle.jslib.Util.isMissingNullOrUndefined(objWindow))
    {
        objWindow = window;
    }
    return com.bristle.jslib.Layout.growRectangleToEncloseHTMLObjects
                    (objWindow.document.body.childNodes, objRect);
}

/******************************************************************************
* Resize the window to fit its contents.
*
*@param objWindow   Window to resize.  Default: Current window
*@return            None.
*@throws            None.
******************************************************************************/
com.bristle.jslib.Layout.resizeWindowToFitContents =
function(objWindow)
{
    if (com.bristle.jslib.Util.isMissingNullOrUndefined(objWindow))
    {
        objWindow = window;
    }

    // Note:  Don't use the built-in window.sizeToContent, even for browsers
    //        where it it exists, because it sets the width to the full screen 
    //        width if the page contains any tables with width=100%.
    //        At least with Firefox 2.0.0.13 and Netscape 9.0.0.6.
    // if (objWindow.sizeToContent)
    // {
    //     // Make the window extra wide at first, to encourage it to wrap 
    //     // less when fitting to wrappable content.
    //     objWindow.resizeBy(2000, 0)
    // 
    //     objWindow.sizeToContent();
    //     return;
    // }

    // Start with the min suggested window width.
    // Note: Height is irrelevant.  It gets re-calculated later.
    // Note: Since so many things grow to fit the entire window width, it is 
    //       not possible to compute the min width based on the current widths 
    //       and positions of the contents.  Tables with width of "100%", for 
    //       example, prevent the window from ever getting any narrower 
    //       because they appear to need all of its current width.  With such 
    //       tables, the final width computed below is never less than this 
    //       min suggested width.  However, it may be wider, if any elements
    //       are unable to fit into this width.  Don't arbitrarily choose too
    //       small a width value however, because that forces things to wrap 
    //       more than might be desirable.  Things that can wrap to fit in 
    //       this suggested width will do so, and will not cause the final 
    //       width to increase beyond the suggested width.
    //?? Make this suggested width a param?  Only for this case where the 
    //?? native window.sizeToContent() is not supported.
    window.resizeTo(500,300);

    // Bounding rectangle for the window contents.
    // Note:  Don't make an empty window too tiny.  Non-zero values 
    //        below are chosen purely for cosmetic reasons.
    var objRect = new com.bristle.jslib.Layout.Rectangle(0, 0, 50, 50);

    // Determine size required to hold all contents.
    objRect = com.bristle.jslib.Layout.growRectangleToEncloseWindowContents
                                            (objWindow, objRect);

    // Extra space for cosmetic reasons.  Too tight a fit looks cramped.
    // Note: This also prevents IE 6.0.2800.1106 from missing by a little 
    //       and showing an enabled vertical scrollbar.  15 is too little.
    //       16 is better and 20 seems safer than that.
    var intExtraSpace = 20; 
    objRect.width += intExtraSpace; 
    objRect.height += intExtraSpace; 

    // Restrict to the size of the physical screen.
    objRect.width  = Math.min(objRect.width,  screen.availWidth);
    objRect.height = Math.min(objRect.height, screen.availHeight);

    // Resize the window accordingly.
    var intCurrentWidth = Math.min
                (com.bristle.jslib.Layout.getInnerWindowWidth(objWindow), 
                 screen.availWidth);
    var intCurrentHeight = Math.min
                (com.bristle.jslib.Layout.getInnerWindowHeight(objWindow), 
                 screen.availHeight);
    var intDeltaWidth  = objRect.width  - intCurrentWidth;
    var intDeltaHeight = objRect.height - intCurrentHeight;
    
    objWindow.resizeBy(intDeltaWidth, intDeltaHeight);

    // Move the window to keep it as visible as it was, since growing it can
    // push the right and bottom edges off the screen.
    //?? How?  Better yet, how to force it to be entirely visible?
    //?? How to determine the size of the window, it order to decide
    //?? that it is hanging off the screen?
    //??var intDeltaX = Math.min(0, intDeltaWidth);
    //??var intDeltaY = Math.min(0, intDeltaHeight);
    //??objWindow.moveBy(-intDeltaX, -intDeltaY);
}

/******************************************************************************
* Return the width of the document in pixels.
*
*@return Width of the document.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getDocumentWidth =
function()
{
    if (   typeof(document.documentElement)             != "undefined" 
        && typeof(document.documentElement.scrollWidth) != "undefined"
        &&        document.documentElement.scrollWidth  != 0)
    {
        return    document.documentElement.scrollWidth;
    }
    else if (   typeof(document.body)             != "undefined" 
             && typeof(document.body.scrollWidth) != "undefined")
    {
        return         document.body.scrollWidth;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine document width with this browser.");
}

/******************************************************************************
* Return the height of the document in pixels.
*
*@return Height of the document.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getDocumentHeight =
function()
{
    if (   typeof(document.documentElement)              != "undefined" 
        && typeof(document.documentElement.scrollHeight) != "undefined"
        &&        document.documentElement.scrollHeight  != 0)
    {
        return    document.documentElement.scrollHeight;
    }
    else if (   typeof(document.body)              != "undefined" 
             && typeof(document.body.scrollHeight) != "undefined")
    {
        return         document.body.scrollHeight;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine document height with this browser.");
}

/******************************************************************************
* Return the left coordinate of the window on the screen in pixels.
*
*@return Left coordinate of the window.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getWindowLeft =
function()
{
    if (typeof(window.screenLeft) != "undefined")
    {
        return window.screenLeft;
    }
    else if (typeof(window.screenX) != "undefined")
    {
        return window.screenX;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine window left coordinate with this browser.");
}

/******************************************************************************
* Return the top coordinate of the window on the screen in pixels.
*
*@return Top coordinate of the window.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getWindowTop =
function()
{
    if (typeof(window.screenTop) != "undefined")
    {
        return window.screenTop;
    }
    else if (typeof(window.screenY) != "undefined")
    {
        return window.screenY;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine window top coordinate with this browser.");
}

/******************************************************************************
* Return the left coordinate of the viewport in the document in pixels, taking 
* scrolling into account.
*
*@return Left coordinate of the viewport.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getViewportLeft =
function()
{
    if (typeof(window.pageXOffset) != "undefined")
    {
        return window.pageXOffset;
    }
    else if (   typeof (document.documentElement)            != "undefined" 
             && typeof (document.documentElement.scrollLeft) != "undefined"
             &&         document.documentElement.scrollLeft  != 0)
    {
        return          document.documentElement.scrollLeft;
    }
    else if (   typeof (document.body)            != "undefined" 
             && typeof (document.body.scrollLeft) != "undefined")
    {
        return          document.body.scrollLeft;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine viewport left coordinate with this browser.");
}

/******************************************************************************
* Return the top coordinate of the viewport in the document in pixels, taking 
* scrolling into account.
*
*@return Top coordinate of the viewport.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getViewportTop =
function()
{
    if (typeof(window.pageYOffset) != "undefined")
    {
        return window.pageYOffset;
    }
    else if (   typeof(document.documentElement)           != "undefined" 
             && typeof(document.documentElement.scrollTop) != "undefined"
             &&        document.documentElement.scrollTop  != 0)
    {
        return         document.documentElement.scrollTop;
    }
    else if (   typeof(document.body)           != "undefined" 
             && typeof(document.body.scrollTop) != "undefined")
    {
        return         document.body.scrollTop;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine viewport top coordinate with this browser.");
}

/******************************************************************************
* Return the width of the viewport in pixels.
*
*@return Width of the viewport.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getViewportWidth =
function()
{
    if (typeof(window.innerWidth) != "undefined")
    {
        return window.innerWidth;
    }
    else if (   typeof(document.documentElement)             != "undefined" 
             && typeof(document.documentElement.clientWidth) != "undefined"
             &&        document.documentElement.clientWidth  != 0)
    {
        return         document.documentElement.clientWidth;
    }
    else if (   typeof(document.body)             != "undefined" 
             && typeof(document.body.clientWidth) != "undefined")
    {
        return         document.body.clientWidth;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine viewport width with this browser.");
}

/******************************************************************************
* Return the height of the viewport in pixels.
*
*@return Height of the viewport.
*@throws com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER
******************************************************************************/
com.bristle.jslib.Layout.getViewportHeight =
function()
{
    if (typeof(window.innerHeight) != "undefined")
    {
        return window.innerHeight;
    }
    else if (   typeof(document.documentElement)              != "undefined" 
             && typeof(document.documentElement.clientHeight) != "undefined"
             &&        document.documentElement.clientHeight  != 0)
    {
        return         document.documentElement.clientHeight;
    }
    else if (   typeof(document.body)              != "undefined" 
             && typeof(document.body.clientHeight) != "undefined")
    {
        return         document.body.clientHeight;
    }
    throw new com.bristle.jslib.Exception.Exception
            (com.bristle.jslib.Exception.intEXC_UNSUPPORTED_BROWSER,
             "Cannot determine viewport height with this browser.");
}

/******************************************************************************
* Return the left coordinate of the object in pixels relative to its parent.
*
*@return Left coordinate of the object
******************************************************************************/
com.bristle.jslib.Layout.getLeft =
function(obj)
{
    return obj.offsetLeft;
}

/******************************************************************************
* Return the top coordinate of the object in pixels relative to its parent.
*
*@return Top coordinate of the object
******************************************************************************/
com.bristle.jslib.Layout.getTop =
function(obj)
{
    return obj.offsetTop;
}

/******************************************************************************
* Return the left coordinate of the object in pixels relative to the entire 
* document.
*
*@return Left coordinate of the object
******************************************************************************/
com.bristle.jslib.Layout.getAbsoluteLeft =
function(obj)
{
    var intLeft = 0;

    // Sum the offsets of the offset ancestors.
    for(var o = obj; o; o = o.offsetParent) 
    {
        intLeft += o.offsetLeft;
    }

    // Subtract the scroll offsets of the enclosing ancestors.
    // Note: Stop at the document body.  Don't want to subtract scrolling of 
    //       entire document.  That would convert document coordinates back to 
    //       window coordinates.
    for(o = obj.parentNode; o && o != document.body; o = o.parentNode)
    {
        if (o.scrollLeft)
        {
            intLeft -= o.scrollLeft;
        }
    }

    return intLeft;
}

/******************************************************************************
* Return the top coordinate of the object in pixels relative to the entire 
* document.
*
*@return Top coordinate of the object
******************************************************************************/
com.bristle.jslib.Layout.getAbsoluteTop =
function(obj)
{
    var intTop = 0;

    // Sum the offsets of the offset ancestors.
    for(var o = obj; o; o = o.offsetParent) 
    {
        intTop += o.offsetTop;
    }

    // Subtract the scroll offsets of the enclosing ancestors.
    // Note: Stop at the document body.  Don't want to subtract scrolling of 
    //       entire document.  That would convert document coordinates back to 
    //       window coordinates.
    for(o = obj.parentNode; o && o != document.body; o = o.parentNode)
    {
        if (o.scrollTop)
        {
            intTop -= o.scrollTop;
        }
    }

    return intTop;
}

/******************************************************************************
* Return the inner width (without margins, padding, and border) of the object
* in pixels.
*
*@return Inner width of the object
******************************************************************************/
com.bristle.jslib.Layout.getInnerWidth = 
function(obj)
{
    var objStyle = com.bristle.jslib.Util.getStyle(obj);

    // Note:  parseInt() stops at first non-numeric char, so it strips off 
    //        the units, which are assumed to be px.
    //        ?? How to avoid assuming units are px?
    return Math.max(0, com.bristle.jslib.Layout.getOuterWidth(obj)
                       - parseInt(objStyle.paddingLeft,      10)
                       - parseInt(objStyle.paddingRight,     10)
                       - parseInt(objStyle.borderLeftWidth,  10)
                       - parseInt(objStyle.borderRightWidth, 10)
                       // Note:  Margins already left out
                    );
}

/******************************************************************************
* Return the inner height (without margins, padding, and border) of the object
* in pixels.
*
*@return Inner height of the object
******************************************************************************/
com.bristle.jslib.Layout.getInnerHeight = 
function(obj)
{
    var objStyle = com.bristle.jslib.Util.getStyle(obj);
    // Note:  parseInt() stops at first non-numeric char, so it strips off 
    //        the units, which are assumed to be px.
    //        ?? How to avoid assuming units are px?
    return Math.max(0, com.bristle.jslib.Layout.getOuterHeight(obj)
                       - parseInt(objStyle.paddingTop,        10)
                       - parseInt(objStyle.paddingBottom,     10)
                       - parseInt(objStyle.borderTopWidth,    10)
                       - parseInt(objStyle.borderBottomWidth, 10)
                       // Note:  Margins already left out
                    );
}

/******************************************************************************
* Return the outer width (with padding and border, but not margins) of the 
* object in pixels.
*
*@return Outer width of the object
******************************************************************************/
com.bristle.jslib.Layout.getOuterWidth =
function(obj)
{
    return obj.offsetWidth;
}

/******************************************************************************
* Return the outer height (with padding and border, but not margins) of the 
* object in pixels.
*
*@return Outer height of the object
******************************************************************************/
com.bristle.jslib.Layout.getOuterHeight =
function(obj)
{
    var intHeight = obj.offsetHeight;

    //?? Mac Safari 4.0, Netscape 9.0, Chrome 1.0, and perhaps others return 
    //?? a zero offsetHeight for HTML elements sometimes.  For example, when 
    //?? populated by setting innerHTML.  Why?
    //?? This attempt to check the CSS height doesn't help.  It returns 0
    //?? in the same case.  Why?
    //?? Even if this did work, is the CSS height the same as the offsetHeight?
    //?? Or does it differ by thickness of margins, padding, border, etc?
    if (intHeight == 0)
    {
        var strCSSHeight = com.bristle.jslib.Util.getStyle(obj).height;
        if (com.bristle.jslib.Util.endsWith(strCSSHeight, "px"))
        {
            var intLengthWithoutPX = strCSSHeight.length - "px".length;
            intHeight = strCSSHeight.substr(0, intLengthWithoutPX);
        }
    }
    return intHeight;
}

/******************************************************************************
* Simulate CSS position "fixed", which Microsoft IE6, IE8 and probably others
* don't support, by using CSS position "absolute", and adjusting the position
* by calling this routine when necessary, especially in the onscroll event.
*
*@param obj     The object to remain in a "fixed" position.
*@param intLeft The "fixed" left position
*@param intTop  The "fixed" top position
******************************************************************************/
com.bristle.jslib.Layout.simulateCSSPositionFixed =
function(obj, intLeft, intTop)
{
    if (navigator.appName.indexOf("Microsoft") >= 0)
    {
        obj.style.position = "absolute";
        obj.style.left = intLeft + com.bristle.jslib.Layout.getViewportLeft();
        obj.style.top  = intTop  + com.bristle.jslib.Layout.getViewportTop();
    }
    else
    {
        obj.style.position = "fixed";
        obj.style.left = intLeft;
        obj.style.top  = intTop;
    }
    
}


/******************************************************************************
* Test the functions in this file.
*
*@param  obj Object to use for testing position, size, etc.
******************************************************************************/
com.bristle.jslib.Layout.runTests =
function(obj)
{
    alert(" DocumentWidth = " + com.bristle.jslib.Layout.getDocumentWidth()
      + "\n DocumentHeight = " + com.bristle.jslib.Layout.getDocumentHeight()
      + "\n WindowLeft = " + com.bristle.jslib.Layout.getWindowLeft()
      + "\n WindowTop = " + com.bristle.jslib.Layout.getWindowTop()
      + "\n ViewportLeft = " + com.bristle.jslib.Layout.getViewportLeft()
      + "\n ViewportTop = " + com.bristle.jslib.Layout.getViewportTop()
      + "\n ViewportWidth = " + com.bristle.jslib.Layout.getViewportWidth()
      + "\n ViewportHeight = " + com.bristle.jslib.Layout.getViewportHeight()
      + "\n Object Left = " + com.bristle.jslib.Layout.getLeft(obj)
      + "\n Object Top = " + com.bristle.jslib.Layout.getTop(obj)
      + "\n Object AbsoluteLeft = " + com.bristle.jslib.Layout.getAbsoluteLeft(obj)
      + "\n Object AbsoluteTop = " + com.bristle.jslib.Layout.getAbsoluteTop(obj)
      + "\n Object InnerWidth = " + com.bristle.jslib.Layout.getInnerWidth(obj)
      + "\n Object InnerHeight = " + com.bristle.jslib.Layout.getInnerHeight(obj)
      + "\n Object OuterWidth = " + com.bristle.jslib.Layout.getOuterWidth(obj)
      + "\n Object OuterHeight = " + com.bristle.jslib.Layout.getOuterHeight(obj)
      );
}
