////////////////////////////////////////////////

var HTML = {};

////////////////////////////////////////////////

HTML.BROWSER_TYPE_IE = 1;
HTML.BROWSER_TYPE_IPHONEPAD = 2;
HTML.BROWSER_TYPE_OTHER = 3;

HTML.GetBrowserType = function ()
{
    if(navigator.appName == 'Microsoft Internet Explorer')
    {
        return HTML.BROWSER_TYPE_IE;
    }
    else if (navigator.platform == 'iPhone' || navigator.platform == 'iPad')
    {
        return HTML.BROWSER_TYPE_IPHONEPAD;
    }
    return HTML.BROWSER_TYPE_OTHER;
}

////////////////////////////////////////////////

HTML.ClearFloats = function (oBlock)
{
    var divClearBlock = document.createElement('DIV');

    divClearBlock.style.clear = 'both';
    divClearBlock.style.margin = '0px';
    divClearBlock.style.padding = '0px';
    divClearBlock.style.border = '0px';
    divClearBlock.style.lineHeight = '0px';
    divClearBlock.style.fontSize = '0px';
    oBlock.appendChild(divClearBlock);
    return SW_OK;
};

HTML.BROWSER_TYPE_IE = 1;
HTML.BROWSER_TYPE_IPHONEPAD = 2;
HTML.BROWSER_TYPE_OTHER = 3;

HTML.GetBrowserType = function ()
{
    if(navigator.appName == 'Microsoft Internet Explorer')
    {
        return HTML.BROWSER_TYPE_IE;
    }
    else if (navigator.platform == 'iPhone' || navigator.platform == 'iPad')
    {
        return HTML.BROWSER_TYPE_IPHONEPAD;
    }
    return HTML.BROWSER_TYPE_OTHER;
}

HTML.RemoveChildren = function(oElement)
{
    if(oElement != null)
    {
        while(oElement.firstChild != null)
        {
            oElement.removeChild(oElement.firstChild);
        }
    }
    return SW_OK;
}

HTML.AddClassName = function(htmlElement, sClassName)
{
    htmlElement.className += ' ' + sClassName;
    return SW_OK;
}

HTML.RemoveClassName = function(htmlElement, sClassName)
{
    var sPattern = '\\b' + sClassName + '\\b';
    var oRegExp = new RegExp(sPattern, 'gi');
    var sClassName = htmlElement.className;
    sClassName = sClassName.replace(oRegExp, '');
    //trim
    sClassName = sClassName.replace(/^\s/, '');
    sClassName = sClassName.replace(/\s$/, '');
    htmlElement.className = sClassName;
    return SW_OK;
}

HTML.SetOpacity = function(htmlElement, dValue)     // 0<=dValue<=1
{
    htmlElement.style.opacity = dValue;
    if(dValue == 1)
    {
        htmlElement.style.filter = '';
    }
    else
    {
        htmlElement.style.filter = 'alpha(opacity=' + parseInt(dValue * 100) + ')';
    }
}

HTML.Fade = function(htmlElement, dStartOpacity, dEndOpacity, dDuration, oCallBack, nDirection)
{
    if(htmlElement.getAttribute('IsInLoop') != null)
    {
        return SW_OK_NO_ACTION;
    }
    else
    {
        htmlElement.setAttribute('IsInLoop', 1);
        htmlElement.setAttribute('CurrentOpacity', dStartOpacity);
        var nStepInterval = 50; // ms
        var nStepCount = Math.round(dDuration / nStepInterval);
        if(nStepCount == 0)
        {
            nStepCount++;
        }

        HTML.FadeLoop(htmlElement, dStartOpacity, dEndOpacity, (dEndOpacity - dStartOpacity) / nStepCount, nStepInterval, oCallBack, nDirection);
    }
    return SW_OK;
}

HTML.FadeLoop = function(htmlElement, dStartOpacity, dEndOpacity, dStep, dTimeout, oCallBack, nDirection)
{
    var dCurrentOpacity = parseFloat(htmlElement.getAttribute('CurrentOpacity'));
    if(dCurrentOpacity <= dEndOpacity - dStep)
    {
        dCurrentOpacity += dStep;
        htmlElement.setAttribute('CurrentOpacity', dCurrentOpacity);
        setTimeout(function() { HTML.FadeLoop(htmlElement, dStartOpacity, dEndOpacity, dStep, dTimeout, oCallBack, nDirection); }, dTimeout);
    }
    else
    {
        dCurrentOpacity = dEndOpacity;
        htmlElement.removeAttribute('CurrentOpacity');
        htmlElement.removeAttribute('IsInLoop');
        oCallBack.FadeComplete(nDirection);
    }
    HTML.SetOpacity(htmlElement, dCurrentOpacity);
    return SW_OK;
}

////////////////////////////////////////////////

HTML.GetPosition = function (oElement)
{
    var nXPos = 0;
    var nYPos = 0;
    if(oElement.offsetParent)
    {
        nXPos = oElement.offsetLeft;
        nYPos = oElement.offsetTop;
        while(oElement = oElement.offsetParent)
        {
            nXPos += oElement.offsetLeft;
            nYPos += oElement.offsetTop;
        }
    }
    return [nXPos, nYPos];
}

////////////////////////////////////////////////

HTML.GetScrollOffset = function (oElement)
{
    //returns the total contribution of scroll offsets from all the way up the document tree
    var nScrollLeft = 0;
    var nScrollTop = 0;
    if(oElement.parentNode)
    {
        nScrollLeft += oElement.scrollLeft;
        nScrollTop += oElement.scrollTop;
        while(oElement = oElement.parentNode)
        {
            if((oElement.scrollLeft != null) &&
		       (oElement.scrollTop != null))
            {
                nScrollLeft += oElement.scrollLeft;
                nScrollTop += oElement.scrollTop;
            }
        }
    }
    return [nScrollLeft, nScrollTop];
}

////////////////////////////////////////////////

HTML.GetBoundingClientRect = function (htmlElement)
{
    //returns rectangle wrt client coordinates
    var nLeft = null;
    var nTop = null;
    var nRight = null;
    var nBottom = null;
    if(htmlElement.getBoundingClientRect)
    {
        var rect = htmlElement.getBoundingClientRect();
        nLeft = (parseInt(rect.left) || 0);
        nTop = (parseInt(rect.top) || 0);
        nRight = (parseInt(rect.right) || 0);
        nBottom = (parseInt(rect.bottom) || 0);
    }
    else
    {
        var arrPos = HTML.GetPosition(htmlElement);
        var arrScrollOffset = HTML.GetScrollOffset(htmlElement);
        var nScrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
        var nScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
        nLeft = arrPos[0] - arrScrollOffset[0] - nScrollLeft;
        nTop = arrPos[1] - arrScrollOffset[1] - nScrollTop;
        nRight = nLeft + htmlElement.offsetWidth;
        nBottom = nTop + htmlElement.offsetHeight;
    }
    return new CRect(nLeft, nTop, nRight, nBottom);
}

HTML.CreateMouseEvent = function (e)
{
    var nPageX, nPageY;
    if(e.pageX)
    {
        nPageX = e.pageX;
        nPageY = e.pageY;
    }
    else if(e.clientX)
    {
        nPageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
        nPageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }

    var nOffsetX, nOffsetY;
    if(e.offsetX)
    {
        nOffsetX = e.offsetX;
        nOffsetY = e.offsetY;
    }
    else if(e.layerX)
    {
        nOffsetX = e.layerX;
        nOffsetY = e.layerY;
    }

    var htmlTarget = e.target || e.srcElement;
    if(htmlTarget.nodeType == 3)
    {
        htmlTarget = htmlTarget.parentNode;
    }

    var oMouseEventDetails = {
        PageX: nPageX,
        PageY: nPageY,
        OffsetX: nOffsetX,
        OffsetY: nOffsetY,
        Target: htmlTarget
    };

    return oMouseEventDetails;
}

////////////////////////////////////////////////

function CRect(nLeft, nTop, nRight, nBottom)
{
    this.m_nLeft = nLeft - 0;
    this.m_nTop = nTop - 0;
    this.m_nRight = nRight - 0;
    this.m_nBottom = nBottom - 0;
}

// bitwise corner IDs
CRect.LEFT = 1;
CRect.RIGHT = 2;
CRect.TOP = 4;
CRect.BOTTOM = 8;
CRect.TOP_LEFT = 5;
CRect.TOP_RIGHT = 6;
CRect.BOTTOM_LEFT = 9;
CRect.BOTTOM_RIGHT = 10;

CRect.prototype.SetPositions = function (nLeft, nTop, nRight, nBottom)
{
    this.m_nLeft = nLeft;
    this.m_nTop = nTop;
    this.m_nRight = nRight;
    this.m_nBottom = nBottom;
    return SW_OK;
}

CRect.prototype.MoveTo = function (pos, nCorner)
{
    switch(nCorner)
    {
        case CRect.TOP_LEFT:
            this.Offset(-this.m_nLeft, -this.m_nTop);
            break;
        case CRect.TOP_RIGHT:
            this.Offset(-this.m_nRight, -this.m_nTop);
            break;
        case CRect.BOTTOM_LEFT:
            this.Offset(-this.m_nLeft, -this.m_nBottom);
            break;
        case CRect.BOTTOM_RIGHT:
            this.Offset(-this.m_nRight, -this.m_nBottom);
            break;
        default:
            return SW_FAIL;
    }

    this.Offset(pos.GetLeft(), pos.GetTop());
    if(window.ActiveXObject)
    {
        this.Offset(-2, -3);
    }

    return SW_OK;
}

// Moves this object so it is next to rectTarget, but entirely within rectLimit.
// nPadding is the gap between the edges of this and the target.
// Preference is to put this object to the right of the target with the tops aligned.
CRect.prototype.DockTo = function (rectTarget, rectLimit, nPadding)
{
    var nX = 0;
    var nY = 0;
    var nCorner = 0;

    if((rectTarget.GetRight() + this.GetWidth() + nPadding) < rectLimit.GetRight())
    {
        nX = rectTarget.GetRight() + nPadding;
        nCorner |= CRect.LEFT;
    }
    else
    {
        nX = rectTarget.GetLeft() - nPadding;
        nCorner |= CRect.RIGHT;
    }

    if((rectTarget.GetTop() + this.GetHeight()) < rectLimit.GetBottom())
    {
        nY = rectTarget.GetTop();
        nCorner |= CRect.TOP;

    }
    else
    {
        nY = rectTarget.GetBottom();
        if(rectLimit.GetBottom() < (nY + this.GetHeight()))
        {
            nY = rectLimit.GetBottom() - nPadding;
        }
        nCorner |= CRect.BOTTOM;
    }

    // TODO - add y padding if popup overlaps target horizontally

    return this.MoveTo(new CPos(nX, nY), nCorner);
}

CRect.prototype.GetLeft = function ()
{
    return this.m_nLeft;
}

CRect.prototype.GetTop = function ()
{
    return this.m_nTop;
}

CRect.prototype.GetRight = function ()
{
    return this.m_nRight;
}

CRect.prototype.GetBottom = function ()
{
    return this.m_nBottom;
}

CRect.prototype.GetWidth = function ()
{
    return this.m_nRight - this.m_nLeft;
}

CRect.prototype.GetHeight = function ()
{
    return this.m_nBottom - this.m_nTop;
}

CRect.prototype.GetContainsCoordinates = function (nX, nY)
{
    return ((nX > this.m_nLeft) &&
            (nX < this.m_nRight) &&
            (nY > this.m_nTop) &&
            (nY < this.m_nBottom));
}

CRect.prototype.GetContainsMouseEvent = function (oMouseEvent)
{
    var nX = oMouseEvent.Attributes['PageX'];
    var nY = oMouseEvent.Attributes['PageY'];
    return ((nX > this.m_nLeft) &&
            (nX < this.m_nRight) &&
            (nY > this.m_nTop) &&
            (nY < this.m_nBottom));
}

CRect.prototype.Offset = function (nDX, nDY)
{
    this.m_nLeft += nDX;
    this.m_nRight += nDX;
    this.m_nTop += nDY;
    this.m_nBottom += nDY;
    return SW_OK;
}

CRect.prototype.OffsetPosition = function (pos)
{
    return this.Offset(pos.GetLeft(), pos.GetTop());
}

CRect.prototype.OffsetPositionNegative = function (pos)
{
    return this.Offset(-pos.GetLeft(), -pos.GetTop());
}

////////////////////////////////////////////////

