//****************************************************************************
// Copyright (c) 2005, Coveo Solutions Inc.
//****************************************************************************

//****************************************************************************
// Shortcut for document.getElementById(...).
//****************************************************************************
function G(p_Id)
{
    return document.getElementById(p_Id);
}

//****************************************************************************
// Returns whether the current browser is Internet Explorer.
//****************************************************************************
function CNL_IsIE()
{
    return navigator.userAgent.indexOf('MSIE') != -1;
}

//****************************************************************************
// Returns whether the browser is in standard mode.
//****************************************************************************
function CNL_IsStandard()
{
    // I tested those with IE, FireFox and Opera.
    return document.compatMode != 'BackCompat' && document.compatMode != 'QuirksMode';
}

//****************************************************************************
// Adds an event handler to an object.
// p_Target  - The object tag fires the event.
// p_Event   - The name of the event.
// p_Handler - The event handler to register.
//****************************************************************************
function CNL_WireEvent(p_Target, p_Event, p_Handler)
{
    if (CNL_IsIE()) {
        p_Target.attachEvent(p_Event, p_Handler);
    } else {
        // addEventLister takes an event type that is usually the name of the
        // event with the 'on' at the start removed.
        if (p_Event.substring(0, 2) != 'on') {
            throw "Unknown event type: " + p_Event;
        }
        var name = p_Event.substring(2, p_Event.length);
        p_Target.addEventListener(name, p_Handler, false);
    }
}

//****************************************************************************
// Stops the propagation of an event.
// p_Event - The event whose propagation to stop.
//****************************************************************************
function CNL_StopPropagation(p_Event)
{
    if (CNL_IsIE()) {
        p_Event.cancelBubble = true;
    } else {
        p_Event.stopPropagation();
    }
}

//****************************************************************************
// Cancels an event (prevents it from bubbling up, and disable the default action).
// p_Event - The event to cancel.
//****************************************************************************
function CNL_CancelEvent(p_Event)
{
    if (CNL_IsIE()) {
        p_Event.cancelBubble = true;
        p_Event.returnValue = false;
    } else {
        p_Event.stopPropagation();
        p_Event.preventDefault();
    }
}

//****************************************************************************
// Returns a new  XMLHttpRequest object.
//****************************************************************************
function CNL_CreateXmlHttpRequest()
{
    var req;
    if (CNL_IsIE()) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
    } else {
        req = new XMLHttpRequest();
    }

    return req;
}

//****************************************************************************
// Performs a SelectNodes in a cross-browser compatible way.
// p_Node  - The node on which to perform the operation.
// p_XPath - The xpath expression to use.
// Returns the matching nodes.
//****************************************************************************
function CNL_SelectNodes(p_Node, p_XPath)
{
    var nodes;
    if (CNL_IsIE()) {
        nodes = p_Node.selectNodes(p_XPath);
    } else {
        var resolver = p_Node.createNSResolver(p_Node);
        var results = p_Node.evaluate(p_XPath, p_Node, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

        nodes = new Array();
        for (i = 0; i < results.snapshotLength; ++i) {
            nodes.push(results.snapshotItem(i));
        }
    }

    return nodes;
}

//****************************************************************************
// Performs a SelectSingleNode in a cross-browser compatible way.
// p_Node  - The node on which to perform the operation.
// p_XPath - The xpath expression to use.
// Returns the first matching node, if any, and null otherwise.
//****************************************************************************
function CNL_SelectSingleNode(p_Node, p_XPath)
{
    var node;
    if (CNL_IsIE()) {
        node = p_Node.selectSingleNode(p_XPath);
    } else {
        var nodes = CNL_SelectNodes(p_Node, p_XPath);
        node = nodes.length != 0 ? nodes[0] : null;
    }

    return node;
}

//****************************************************************************
// Returns the text content of an xml node.
//****************************************************************************
function CNL_GetTextContent(p_Node)
{
    return CNL_IsIE() ? p_Node.text : p_Node.textContent;
}

//****************************************************************************
// Converts an RGB value to a color string.
// p_Red   - The Red component.
// p_Green - The Green component.
// p_Blue  - The Blue component.
// Returns The color as a string (in the #xxxxxx format).
//****************************************************************************
function CNL_RGBToString(p_Red, p_Green, p_Blue)
{
    var r = p_Red.toString(16);
    if (r.length == 1) {
        r = '0' + r;
    }
    var g = p_Green.toString(16);
    if (g.length == 1) {
        g = '0' + g;
    }
    var b = p_Blue.toString(16);
    if (b.length == 1) {
        b = '0' + b;
    }

    return ('#' + r + g + b).toUpperCase();
}

//****************************************************************************
// Sets the absolute position of an object, relative to the document.
// p_Object - The object whose position should be set.
// p_X      - The X offset from the left of the document.
// p_Y      - The Y offset from the top of the document.
//****************************************************************************
function CNL_SetPosition(p_Object, p_X, p_Y)
{
    p_Object.style.left = p_X + 'px';
    p_Object.style.top = p_Y + 'px';
}

//****************************************************************************
// Retrieves the absolute position of an HTML object (in pixels) from the top of the document.
// p_Object - The object whose position to retrieve.
// Returns an object that contains the information.
//****************************************************************************
function CNL_GetPosition(p_Object)
{
    var pos = new Object();
    pos.m_Left = 0;
    pos.m_Top = 0;

    var obj = p_Object;
    while (obj != null) {
        pos.m_Left += obj.offsetLeft - obj.scrollLeft;
        pos.m_Top += obj.offsetTop - obj.scrollTop;
        obj = obj.offsetParent;
    }

    return pos;
}

//****************************************************************************
// Retrieves the size of an HTML object (in pixels).
// p_Object - The object whose size to retrieve.
// Returns An object that contains the information.
//****************************************************************************
function CNL_GetSize(p_Object)
{
    var size = new Object();
    size.m_Width = p_Object.offsetWidth;
    size.m_Height = p_Object.offsetHeight;
  
    return size;
}

//****************************************************************************
// Sets the size of an object.
// p_Object - The object whose size should be set.
// p_Width  - The width to set.
// p_Height - The height to set.
//****************************************************************************
function CNL_SetSize(p_Object, p_Width, p_Height)
{
    p_Object.style.width = p_Width + 'px';
    p_Object.style.height = p_Height + 'px';
}

//****************************************************************************
// Retrieves the bounding rectangle of an HTML object (in pixels).
// p_Object - The object whose bounding rectangle to retrieve.
// Returns An object that contains the information.
//****************************************************************************
function CNL_GetBoundingRectangle(p_Object)
{
    var pos = CNL_GetPosition(p_Object);
    var size = CNL_GetSize(p_Object);
    var rect = new Object();
    rect.m_Left = pos.m_Left;
    rect.m_Top = pos.m_Top;
    rect.m_Right = pos.m_Left + size.m_Width;
    rect.m_Bottom = pos.m_Top + size.m_Height;
  
    return rect;
}

//****************************************************************************
// Retrieves the bounding of the region that is visible in the window (in pixels).
// Returns An object that contains the information.
//****************************************************************************
function CNL_GetVisibleRectangle()
{
    var rect = new Object();
    if (CNL_IsStandard()) {
        rect.m_Left = document.documentElement.scrollLeft;
        rect.m_Top = document.documentElement.scrollTop;
        rect.m_Right = rect.m_Left + document.documentElement.clientWidth;
        rect.m_Bottom = rect.m_Top + document.documentElement.clientHeight;
    } else {
        rect.m_Left = document.body.scrollLeft;
        rect.m_Top = document.body.scrollTop;
        rect.m_Right = rect.m_Left + document.body.clientWidth;
        rect.m_Bottom = rect.m_Top + document.body.clientHeight;
    }
  
    return rect;
}

//****************************************************************************
// Checks if a point is within a rectangle.
// p_X - The X coordinate.
// p_Y - The Y coordinate.
// p_Rect - The rectangle.
// Returns Whether the point is within the rectangle.
//****************************************************************************
function CNL_IsWithin(p_X, p_Y, p_Rect)
{
    return p_X >= p_Rect.m_Left && p_X < p_Rect.m_Right &&
           p_Y >= p_Rect.m_Top && p_Y < p_Rect.m_Bottom; 
}

//****************************************************************************
// Checks if a rectangle is within another rectangle.
// p_Inside  - The rectangle that should be inside.
// p_Outside - The rectangle that should contain p_Inside.
// Returns Whether the first rectangle is within the second one.
//****************************************************************************
function CNL_IsRectangleWithin(p_Inside, p_Outside)
{
    return CNL_IsWithin(p_Inside.m_Left, p_Inside.m_Top, p_Outside) &&
           CNL_IsWithin(p_Inside.m_Right, p_Inside.m_Top, p_Outside) &&
           CNL_IsWithin(p_Inside.m_Left, p_Inside.m_Bottom, p_Outside) &&
           CNL_IsWithin(p_Inside.m_Right, p_Inside.m_Bottom, p_Outside);
}

//****************************************************************************
// Checks if two rectangles overlap.
// p_Rect1 - The first rectangle.
// p_Rect2 - The second rectangle.
// Returns Whether the two rectangles overlap.
//****************************************************************************
function CNL_IsOverlap(p_Rect1, p_Rect2)
{
    // Look for an horizontal overlap
    var horiz = (p_Rect1.m_Left >= p_Rect2.m_Left &&
                 p_Rect1.m_Left < p_Rect2.m_Right) ||
                (p_Rect1.m_Right > p_Rect2.m_Left &&
                 p_Rect1.m_Right < p_Rect2.m_Right) ||
                (p_Rect2.m_Left >= p_Rect1.m_Left &&
                 p_Rect2.m_Left < p_Rect1.m_Right) ||
                (p_Rect2.m_Right > p_Rect1.m_Left &&
                 p_Rect2.m_Right < p_Rect1.m_Right);

    // Look for a vertical overlap
    var vert = (p_Rect1.m_Top >= p_Rect2.m_Top &&
                p_Rect1.m_Top < p_Rect2.m_Bottom) ||
               (p_Rect1.m_Bottom > p_Rect2.m_Top &&
                p_Rect1.m_Bottom < p_Rect2.m_Bottom) ||
               (p_Rect2.m_Top >= p_Rect1.m_Top &&
                p_Rect2.m_Top < p_Rect1.m_Bottom) ||
               (p_Rect2.m_Bottom > p_Rect1.m_Top &&
                p_Rect2.m_Bottom < p_Rect1.m_Bottom);

    return horiz && vert;
}

//****************************************************************************
// Positions an object relative to another.
// p_Object - The object to position.
// p_Reference - The object to position relatively to.
// p_Position - Where to place the object from the other.
//****************************************************************************
function CNL_PositionObject(p_Object, p_Reference, p_Position)
{
    // Determine the default major and minor positions
    var position1;
    var position2;
    if (p_Position == 'LeftBelow') {
        position1 = 'Left';
        position2 = 'Below';
    } else if (p_Position == 'LeftAbove') {
        position1 = 'Left';
        position2 = 'Above';
    } else if (p_Position == 'AboveLeft') {
        position1 = 'Above';
        position2 = 'Left';
    } else if (p_Position == 'AboveRight') {
        position1 = 'Above';
        position2 = 'Right';
    } else if (p_Position == 'RightBelow') {
        position1 = 'Right';
        position2 = 'Below';
    } else if (p_Position == 'RightAbove') {
        position1 = 'Right';
        position2 = 'Above';
    } else if (p_Position == 'BelowLeft') {
        position1 = 'Below';
        position2 = 'Left';
    } else if (p_Position == 'BelowRight') {
        position1 = 'Below';
        position2 = 'Right';
    } else {
        throw 'Invalid value for p_Position';
    }
    
    var left;
    var top;
    var done = false;
    var attempts = 0;
    var osize = CNL_GetSize(p_Object);
    var rrect = CNL_GetBoundingRectangle(p_Reference);
    var vrect = CNL_GetVisibleRectangle();

    // Try 3 times to position the object. Globally speaking, when first iteration
    // fails, the invert position on all bad levels will be tried. If this position
    // is bad too, we want to allow another iteration to revert positions to the
    // initial ones (which are better when everything is bad).
    while (!done && attempts < 3) {
        // Position the object using the two indicators
        if (position1 == 'Left') {
            left = rrect.m_Left - osize.m_Width;
        } else if (position1 == 'Right') {
            left = rrect.m_Right - 1;
        } else if (position1 == 'Above') {
            top = rrect.m_Top - osize.m_Height;
        } else if (position1 == 'Below') {
            top = rrect.m_Bottom - 1;
        }
        if (position2 == 'Left') {
            left = rrect.m_Left;
        } else if (position2 == 'Right') {
            left = rrect.m_Right - osize.m_Width;
        } else if (position2 == 'Above') {
            top = rrect.m_Bottom - osize.m_Height;
        } else if (position2 == 'Below') {
            top = rrect.m_Top;
        }

        // Invert major and/or minor positions when needed
        done = true;
        var right = left + osize.m_Width;
        var bottom = top + osize.m_Height;
        if (left < vrect.m_Left || right >= vrect.m_Right) {
            if (position1 == 'Left') {
                position1 = 'Right';
            } else if (position1 == 'Right') {
                position1 = 'Left'
            } else if (position2 == 'Left') {
                position2 = 'Right'
            } else if (position2 == 'Right') {
                position2 = 'Left'
            }
            done = false;
        }
        if (top < vrect.m_Top || bottom >= vrect.m_Bottom) {
            if (position1 == 'Above') {
                position1 = 'Below';
            } else if (position1 == 'Below') {
                position1 = 'Above'
            } else if (position2 == 'Above') {
                position2 = 'Below'
            } else if (position2 == 'Below') {
                position2 = 'Above'
            }
            done = false;
        }
        
        ++attempts;
    }

    CNL_SetPosition(p_Object, left, top);
}

//****************************************************************************
// Fits the width of an element to the client width of it's parent.
// p_Element - The element whose width to set.
// Works only under IE!
//****************************************************************************
function CNL_FitToParentWidth(p_Element)
{
    var borderWidth = parseInt(p_Element.currentStyle.borderLeftWidth) + parseInt(p_Element.currentStyle.borderRightWidth);
    var paddingWidth = parseInt(p_Element.currentStyle.paddingLeft) + parseInt(p_Element.currentStyle.paddingRight);
    var available = p_Element.parentElement.scrollWidth - parseInt(p_Element.parentElement.currentStyle.paddingLeft) - parseInt(p_Element.parentElement.currentStyle.paddingRight);
    p_Element.style.pixelWidth = available - borderWidth - paddingWidth;
}

//****************************************************************************
// Resizes an iframe to it's content.
// p_IFrame - The iframe to resize.
//****************************************************************************
function CNL_ResizeIFrame(p_IFrame)
{
    var width = p_IFrame.contentWindow.document.documentElement.scrollWidth;
    var height = p_IFrame.contentWindow.document.documentElement.scrollHeight;
    CNL_SetSize(p_IFrame, width, height);
}

//****************************************************************************
// Sets the opacity of an object.
// p_Object  - The object whose opacity should be set.
// p_Opacity - The opacity to set (from 0 to 1).
//****************************************************************************
function CNL_SetOpacity(p_Object, p_Opacity)
{
    if (p_Opacity != 1) {
        if (CNL_IsIE()) {
            p_Object.style.filter = 'alpha(opacity=' + p_Opacity * 100 + ')'; 
        } else {
            p_Object.style.opacity = p_Opacity;
        }
    } else {
        if (CNL_IsIE()) {
            p_Object.style.filter = ''; 
        } else {
            p_Object.style.opacity = '';
        }
    }
}

//****************************************************************************
// Retrieves the position of a mouse click relative to an object.
// p_Event     - The event object.
// p_Reference - The reference element to use to size the iframe.
//****************************************************************************
function CNL_GetMouseClickPosition(p_Event, p_Reference)
{
    var rpos = CNL_GetPosition(p_Reference);

    // First compute the click offset from the page
    var px, py;
    if (CNL_IsIE()) {
        // IE provides the offset from the clicked object.
        var tpos = CNL_GetPosition(p_Event.srcElement);
        px = tpos.m_Left + p_Event.offsetX;
        py = tpos.m_Top + p_Event.offsetY;
    } else {
        // FireFox provides the offset from the page
        px = p_Event.pageX;
        py = p_Event.pageY;
    }

    // Then compute the offset from the reference
    var rpos = CNL_GetPosition(p_Reference);
    var pos = new Object();
    pos.m_Left = px - rpos.m_Left;
    pos.m_Top = py - rpos.m_Top;

    return pos;
}

