/* Build namespace */
if (NMR == undefined) var NMR = {};
if (NMR.Common == undefined) NMR.Common = {};
if (NMR.Common.Web == undefined) NMR.Common.Web = {};
if (NMR.Common.Web.GenericControls == undefined) NMR.Common.Web.GenericControls = {};
if (NMR.Common.Web.GenericControls.GenericContextMenu == undefined) NMR.Common.Web.GenericControls.GenericContextMenu = {};

/* Dummy comment format
<summary>
    <example>
        <code>
        </code>
    </example>
</summary>
<param name="myParam"></param>
<returns></returns>
<remarks></remarks>
*/

/*
<summary>
Constructor function, passes arguments to constructor in prototype
</summary>
*/
NMR.Common.Web.GenericControls.GenericContextMenu.ContextMenu = function()
{
    // Call base constructor
    myBase(this, NMR.Common.Web.GenericControls.GenericBase.prototype.create, arguments);
    
    // Call constructor
    this.create.apply(this, arguments);
}

NMR.Common.Web.GenericControls.GenericContextMenu.ContextMenu.prototype = 
{
    zIndexValue: 10000,                 // Starting Z-index for menu items in the <controlid_menuDiv/> stacking context
    newMenuItems: new Object(),         // Object 'array' containing all child menu items (instances of class: menuItem). 

    lastMousePosX: -1,                  // Mouse pointers last position on the X axis of the browser window
    lastMousePosY: -1,                  // Mouse pointers last position on the Y axis of the browser window
    
    lastElementMouseMove: null,         // The element the mouse pointer was over when the mousemove event last fired. 
    lastMenuItem: null, 
    
    orientationArray: new Array(),
    imagePreloadArray: new Array(),     // Preload rollover images.
    rootMenuId: null,
    
    timerId: null,
    
    
    /*
    <summary>
    Constructor. Called when the document is loaded, and is performs calls to various functions that 
    mechanise the context menu system.
    </summary>
    */
    create: function()
    {
        this.rootMenuId = arguments[4];
        this.clientObjectId = this.clientObjectId + "_menuDiv";
        this.attachEventHandlers(false);
    },
    
    /*
    <summary>
    Performs a simple image swap when an image elements 'onmouseover' or 'onmouseout' events
    are raised.
    </summary>
    <remarks>
    Images are preloaded in the ContextToolbar.ascx.vb::Page_Load event via the creation of 
    an array called: imagePreloadArray(). 
    </remarks>
    */
    swapImage: function(imageElement, newImagePath)
    {
        imageElement.src = newImagePath;
    },
        
    /*
    <summary>
    Attaches/detatches callback (<see cref="mousePosCallback" />) event listeners to capture the mouse pointers position with respect
    to the document element. 
    </summary>
    <param name="detach">
    If true, the method will attempt to unhook any event listeners it previously attached. If false 
    the method will attempt to attach event the relevant event listeners.
    </param>
    */
    attachEventHandlers: function(detach)
    {
        myBase(this, NMR.Common.Web.GenericControls.GenericBase.prototype.attachEventHandlers, arguments);
        var inst = this;
        if(!detach)
        {
            if(document.addEventListener)
            {
                // for Firefox
                document.addEventListener("mousemove",function(e){inst.mousePosCallback(e);}, false);
            }
            else if(document.attachEvent) 
            {
                // for IE
                document.attachEvent("onmousemove",function(){inst.mousePosCallback(event);});
            }
            
            // Attach broadcast event(s)
            var globalEvent = NMR.Common.Web.GenericControls.GlobalEvents.getCustomEvent("onAllMenusHidden");
            if(!globalEvent)
            {
                globalEvent = new NMR.Common.CustomEvent("onAllMenusHidden");
                NMR.Common.Web.GenericControls.GlobalEvents.addCustomEvent(globalEvent);
            }
            globalEvent = NMR.Common.Web.GenericControls.GlobalEvents.getCustomEvent("onShowMenu");
            if(!globalEvent)
            {
                globalEvent = new NMR.Common.CustomEvent("onShowMenu");
                NMR.Common.Web.GenericControls.GlobalEvents.addCustomEvent(globalEvent);
            }
            globalEvent = NMR.Common.Web.GenericControls.GlobalEvents.getCustomEvent("onMenuItemClicked");
            if(!globalEvent)
            {
                globalEvent = new NMR.Common.CustomEvent("onMenuItemClicked");
                NMR.Common.Web.GenericControls.GlobalEvents.addCustomEvent(globalEvent);
            }
        }
        else
        {
            if(document.removeEventListener)
            {
                // for Firefox
                document.removeEventListener("mousemove",function(e){inst.mousePosCallback(e);}, false);
            }
            else if(document.detachEvent)
            {
                // for IE
                document.detachEvent("onmousemove",function(){inst.mousePosCallback(event);});
            }
        }
    },
    
    /*
    <summary>
    Callback for the event listener. 
    </summary>
    <param name="e">The event object containing the mouse pointers current location with respect to the browser window.</param>
    */
    mousePosCallback: function(e)
    {
        if(e == null) { e = window.event; }

        if(e)
        {
            if(e.pageX || e.pageY)
            {
                // for Firefox
                this.lastMousePosX=e.pageX; 
                this.lastMousePosY=e.pageY;
            }
            else
            {
                // for IE
                if(document.documentElement.scrollTop)
                {
                    this.lastMousePosX=e.clientX+document.documentElement.scrollLeft;
                    this.lastMousePosY=e.clientY+document.documentElement.scrollTop;
                }
                else
                {
                    this.lastMousePosX=e.clientX+document.body.scrollLeft;
                    this.lastMousePosY=e.clientY+document.body.scrollTop;
                }
            }
            
            // Retrieve and store the current element the mouse pointer is over
            this.lastElementMouseMove = this.getSourceTargetElementsFromEvent(e)[0];
        }
    },        
    
    /*
    <summary>
    Determines if a menu should be displayed for a given <li /> element.
        <example>
            The following example demonstrates the use of the function when wired to an <li /> element: 
            <code>
                <li id="control1_root_item12_menu1_item8" onmouseout="hideMenu(this, event, 'ContextToolbar1_menuDiv');" onmouseover="showMenu(this, 'control1_root_item12_menu1_item8_menu1_item1');">
            </code>
        </example>
    </summary>
    <param name="el">Contains the object raising the showMenu function.</param> 
    <param name="childMenuId">Contains the id of the child menu <ul /> which the element (el) wishes to display.</param>
    <returns> Returns null on success or failure.</returns>
    */ 
    showMenu: function(el, childMenuId)
    {
        var me = this;
        NMR.Common.Web.GenericControls.GlobalEvents.raiseCustomEvent("onShowMenu", me);

        var parentPos = this.findPos(el);
        var childMenu = document.getElementById(childMenuId);
        var iframe = document.getElementById("menuIframe");
        var div = document.getElementById(this.clientObjectId);
        var liobj = document.getElementsByTagName("li");
        
        if (iframe)
             {
             if (el.innerText == "Home")
                {
                    iframe.style.display ="none";
                }
             }
               
        if(childMenu)
        {
            var offsetY=0, offsetX=0;        // How many pixels to offset menu items - they *must* touch or overlap otherwise
                                             // events in elements below will fire, hiding the entire menu.

            // Build a predicate class to interrogate the orientation array. This ensures that child menus
            // are correctly positioned relative to their parent menu. For example: If the parent menu has
            // been rendered horizontally, then any child menu needs to appear below any parent menu element
            // rather then beside it (otherwise adjacent parent elements would be obscured). Likewise, if the
            // parent menu has been rendered vertically (ala standard context menus), then the child menu would
            // need to be offset horizontally, making it appear beside the parent menu element. 
            var deriveOrientation = {
                matchElement: function(arrEl){
                    var mnuEl = document.getElementById(arrEl.split("#")[0]);
                    if(mnuEl) 
                    {
                        if(mnuEl.id == el.parentNode.id)
                        {
                            // Horizontal alignment
                            if((arrEl.split("#")[1]) == "0")
                            {
                                offsetX = 0;
                                offsetY = el.offsetHeight;
                            }
                            // Vertical alignment
                            else if((arrEl.split("#")[1]) == "1")
                            {
                                offsetX = el.offsetWidth;
                                offsetY = 0; 
                            }
                        }
                    }
                }
            };

            // Perform child menu orientation relative to the parent menu. 
            // NB: orientationArray() is created and populated in the 
            // GenericContextMenu::ContextMenu::RegisterJavaScript() method.
            this.orientationArray.forEach(deriveOrientation.matchElement, deriveOrientation);
            
            // *** IMPORTANT ***
            // The offsets determine the overlap of child menu's relative to their parent. This
            // is required so that events are not raised at the boundry of a menu element where
            // a child menu is being displayed.  
            offsetX += -4;     
            offsetY += -4; //##was -4
       
            // Configure the position of the child menu
            this.zIndexValue = this.zIndexValue + 1;                // Ensure the child menu appears higher in the stacking context then it's parent.
            childMenu.style.position = "absolute";                  // Position the menu *against the first BLOCK LEVEL element*. This will not be the parent <li /> but it's parent <ul />. 
            childMenu.style.zIndex = this.zIndexValue;                   
            childMenu.style.left = (parentPos[0] + offsetX) + "px"; // Positions the child menu along the X-Axis (Horizontally).
            childMenu.style.top = (parentPos[1] + offsetY - 2)  + "px"; // Positions the child menu along the Y-Axis (Vertically).
            childMenu.style.display = "";                           // Force the child menu to be displayed.
 
            var mnuItm = new NMR.Common.Web.GenericControls.GenericContextMenu.MenuItem();
            mnuItm.menuItemElementId = el.id;
            mnuItm.childMenuElementId = childMenuId;
            this.newMenuItems[el.id] = mnuItm;
            
            if (iframe)
             {
                {
                    iframe.style.display ="none";
                }
             }
            
            //iframe added to fix dropdown overlapping issue in ie6- DS 22 Sep 08
            if (iframe)
            {
            if (el.innerText == "My Reports")
            {
                var div = document.getElementById(this.clientObjectId);
                iframe.style.position ="absolute"; 
                iframe.style.zIndex = childMenu.style.zIndex - 1;                 
                iframe.style.left = childMenu.style.left;
                iframe.style.top = childMenu.style.top;
                iframe.style.width = ((isNaN(parseInt(liobj(0).style.width))?0:parseInt(liobj(0).style.width)) + 6) + "px";
                iframe.style.height = ((el.offsetHeight + 6)*(childMenu.children.length)) + "px";
                iframe.style.display ="block";
            }
            if (el.innerText == "My Account")
            {
                var div = document.getElementById(this.clientObjectId);
                iframe.style.position ="absolute"; 
                iframe.style.zIndex = childMenu.style.zIndex - 1;                 
                iframe.style.left = childMenu.style.left;
                iframe.style.top = childMenu.style.top;
                iframe.style.width = ((isNaN(parseInt(liobj(7).style.width))?0:parseInt(liobj(7).style.width)) + 6) + "px";
                iframe.style.height = ((el.offsetHeight)*(childMenu.children.length)) + "px";
                iframe.style.display ="block";
            }
            }   
        }
        else if(childMenuId == null)
        {
            // Special case for end elements (child elements with no child menus). 
            var mnuItm = new NMR.Common.Web.GenericControls.GenericContextMenu.MenuItem();
            mnuItm.menuItemElementId = el.id;
            mnuItm.childMenuElementId = null;
            this.newMenuItems[el.id] = mnuItm;
        }
        
        
    },

    /*
    <summary>
    Determines if the calling element (el) has any child menus displayed that need hiding, and if the 
    calling element (el) also needs hiding. 
        <example>
            The following example demonstrates the use of the function when wired to an <li /> element: 
            <code>
                <li id="control1_root_item12_menu1_item8" onmouseout="hideMenu(this, event, 'ContextToolbar1_menuDiv');" onmouseover="showMenu(this, 'control1_root_item12_menu1_item8_menu1_item1');">
            </code>            
        </example>
    </summary>    
    <param name="el">Contains the element raising the event.</param>
    <param name="e">Contains the event with the mouse pointers current position (used for boundry determination).</param>
    <returns>Returns null on success or failure.</returns>
    <remarks>
    If this function is called as a result of an 'onmouseout' event, where the target element does not 
    belong to the parent <ul /> (E.g. <html /> element), then a call will be made to hideAllMenus(). 
    </remarks>
    */ 
    hideMenu: function(el, e)
    {
        // If the mouse pointer is within the boundry element (e.g. it's over a nested
        // <a /> or <img /> element) do not attempt to hide any of the menus. 
        //document.getElementById("debugel").innerHTML += " -> doing boundry check...!";
        
        if(this.inBoundryElement(el, e))
        {   
            return;
        }
        
        var srcTargEls = this.getSourceTargetElementsFromEvent(e);
        var srcEl = srcTargEls[0];
        var targEl = srcTargEls[1];
        var kid;
        var nde;                    // for each iterator object
        var me = this;
        this.lastElementMouseMove = targEl;
        
        var testAgainstCurrentMousePosition = function(me, srcEl){
            if(me.newMenuItems[srcEl.id] && (me.lastElementMouseMove != null))
            {
                // It is possible lastElementMouseMove will be one of the nested elements
                // inside a menu item <li />. If lastElementMouseMove is *not* a menu item
                // or menu, attempt to resolve it to a menu item before continuing. 
                if(me.newMenuItems[me.lastElementMouseMove.id] == null)
                {
                    var obj = me.lastElementMouseMove;
                    while(obj != null)
                    {
                        if(me.newMenuItems[obj.id] != null){ break;}
                        obj = obj.parentNode;
                    }
                    // Found a match! Re-assign me.lastElementMouseMove to the parent 
                    // menu item.
                    if(obj != null){me.lastElementMouseMove = obj;}
                }
            
                // First work out if the mouse pointer is currently still over the source
                // menu item or one of the other menu item's in the same menu. NB: srcEl.parentNode is 
                // synonymous with me.newMenuItems[srcEl.id].getMyMenuElement().
                if(me.lastElementMouseMove != srcEl &&                      // Same menu - same menu item
                   me.lastElementMouseMove != srcEl.parentNode &&           // Same menu - unknown menu item (happens when the underlying menu <ul /> raises the event)
                   me.lastElementMouseMove.parentNode != srcEl.parentNode)  // Same menu - different menu item
                {
                    // The lastElement the mouse moved over was *not* the source menu item or
                    // another menu item in the same menu. Determine if the lastElementMouseMove was
                    // the parent menu item of the menu that contains the srcEl menu item.
                    if(me.newMenuItems[me.lastElementMouseMove.id])
                    {
                        // lastElementMouseMove *is* a menu item - but is it the parent of srcEl?
                        if(me.newMenuItems[me.lastElementMouseMove.id].getChildMenuElement() != srcEl && 
                           me.newMenuItems[me.lastElementMouseMove.id].getChildMenuElement() != srcEl.parentNode)
                        {
                             
                            // lastElementMouseMove is *not* a related menu item, hide srcEl's menu.
                            me.newMenuItems[srcEl.id].getMyMenuElement().style.display = "none";
                        }
                    }
                    else
                    {
                        
                        // Hide all the menus...
                        me.hideAllMenus();
                    }
                }
                // If the target of the mouse pointer has still not been identified (E.g. the mouse pointer moves over
                // another DOM document embedded in an <iframe /> element) - hide all open menus.
                else
                {
                    
                    // Hide all the menus...
                    me.hideAllMenus();  
                }
            }
        };
        
        
        //document.getElementById("debugel").innerHTML += " -> entering menu...!";
        if(srcEl == null || targEl == null){ this.hideAllMenus(); return;}
        //document.getElementById("debugel").innerHTML += " -> beginning processing!";

        // The purpose of comparing the original element that raised the request to
        // hide the menu with the last element recorded as the mouse pointer being over,
        // ensures that if the users' cursor moved, but moved off and then back onto the 
        // srcEl menu item, the menu is not closed. 
        if(srcEl.parentNode == targEl){ 
            //document.getElementById("debugel").innerHTML += "<b> -> raising source parent is target!</b>";

            //document.getElementById("debugBeforeX").innerHTML = this.lastMousePosX
            //document.getElementById("debugBeforeY").innerHTML = this.lastMousePosY
            setTimeout(function(){testAgainstCurrentMousePosition(me, srcEl);},300);
            return;
        }

        // Bubble nested elements. If a nested element (inside the <li> menu item
        // that handles the 'onmouseover'/'onmouseout' events) fires an 
        // 'onmouseover'/'onmouseout', reassign the sourcing element (srcEl) to that 
        // of the <li /> handling element. This ensures the menu item element (<li />) 
        // is used to base all menu hide/show decisions. 
        if(srcEl != el) 
        { 
            // It is possible lastElementMouseMove will be one of the nested elements
            // inside a menu item <li />. If lastElementMouseMove is *not* a menu item
            // or menu, attempt to resolve it to a menu item before continuing. 
            if(this.newMenuItems[srcEl.id] == null)
            {
                var obj = srcEl;
                while(obj != null)
                {
                    if(this.newMenuItems[obj.id] != null){ break;}
                    obj = obj.parentNode;
                }
                // Found a match! Re-assign me.lastElementMouseMove to the parent 
                // menu item.
                if(obj != null){srcEl = obj;this.lastMenuItem = srcEl;}
            }
            
        }
        // Attempts to resolve the srcEl(ement) to last <li /> item that was raised. This ensures
        // that any mouseout/mouseover events raised by a parent <ul /> are rerouted to appear to originate
        // from the <li /> element the mouse cursor last traversed. 
        else if(srcEl == el && !document.all)
        {
            // The targEl *is* an <li /> already 
            if (this.newMenuItems[targEl.id])
            {
                srcEl = targEl;
                this.lastMenuItem = srcEl;
            }
            // The targEl *is not* an <li /> - assign it to the last known menuItem <li />
            else if(this.lastMenuItem)
            {
                srcEl = this.lastMenuItem;
            }
        }

        if(this.newMenuItems[srcEl.id])
        {
            // The mouse is moving over the parent element.
            // Hide any subordinate children of the srcEl(ement - NB: should be an <li /> element).
            kid = this.newMenuItems[srcEl.id].getChildMenuElement();
            
            if(kid)
            {
                // If the target element *is not* a child of the menu we want to hide, 
                // continue to hide the menu.
                if(this.newMenuItems[targEl.id])
                {
                    if(targEl.parentNode.id != kid.id) 
                    {
                        kid.style.display="none";
                    }
                }
                // Pointer is over a non-menu item. If the mouse pointer is over a non menu item
                // that is not related to the source element (e.g. not an <li /> element on a child menu
                // linked to the srcEl(ement), then continue to hide the menu.
                else 
                {
                    if(targEl.parentNode == null)
                    {
                        kid.style.display="none";
                    }
                    else if(targEl.parentNode.id != kid.id)
                    {
                        kid.style.display="none";
                    }
                }
                var iframe = document.getElementById("menuIframe"); 
                if(iframe){iframe.style.display ="none";}
            }
            else if(kid == null && this.newMenuItems[targEl.id] == null)
            {
                // Determine if the target element is a child of the underlying menu. E.g. another
                // menu item. If *not*, close all the menus.
                if(!this.iterateNodesForChild(srcEl.parentNode, targEl))
                { 
                    // The target element isn't one in the menu - Hide ME! *unless*
                    // the parentNode is the root menu (E.g. id of 'controlName_root'). 
                    if(!this.elementBelongsToRootMenu(srcEl.parentNode.id))
                    {
                        srcEl.parentNode.style.display="none";
                    }
                    //document.getElementById("debugel").innerHTML += "<b>-> hiding everything!</b>";
                    //debugger;
                    this.hideAllMenus();                 
                }    
            }
            else if(kid == null && targEl.parentNode != null)
            {
                if(this.newMenuItems[targEl.id] != null)
                {
                    // Determine if the mousepointer has moved over a menuItem that belongs to the same
                    // menu that initially raised srcEl's menu. If srcEl *was* raised from one of targEl's 
                    // menuItem elements, but *not* the current targEl (E.g. the mouse pointer is not 
                    // moving back over the menuItem that raised it, but another menu Item belonging to the
                    // same menu); hide srcEl's menu.
                    var mnuThatRaisedSrcEl;
                    var mnuItmThatRaisedSrcEl;
                    
                    for(var i in this.newMenuItems)
                    {
                        nde = this.newMenuItems[i];
                        if(nde.childMenuElementId == srcEl.parentNode.id)
                        {
                            mnuThatRaisedSrcEl = nde.getMyMenuElement();
                            mnuItmThatRaisedSrcEl = document.getElementById(nde.menuItemElementId);
                            break;                            
                        }
                    }
                
                    // The targElement *is* a menuitem. Determine if the srcEl was raised from one of 
                    // the other menuItems belonging to targEl's underlying menu.
                    var targElMenu = this.newMenuItems[targEl.id].getMyMenuElement();
                    if(targElMenu && mnuThatRaisedSrcEl && mnuItmThatRaisedSrcEl)
                    {
                        if(targElMenu.id == mnuThatRaisedSrcEl.id)
                        {
                            if(targEl.id != mnuItmThatRaisedSrcEl.id)
                            {
                                // srcEl *was* raised by an element on targEl's underlying menu, but *not* by
                                // targEl. Therefore, hide the srcEl's underlying menu - provided it is not a root menu.
                                if(!this.elementBelongsToRootMenu(srcEl.parentNode.id))
                                {
                                    srcEl.parentNode.style.display = "none";
                                }
                            }
                        }
                    }
                }
            }
            
            // Occasionally, if a menu <ul /> has caught a boundry event, a child menu of another
            // menu item in the same menu will remain open, even though the user has navigated away from 
            // the menu item. Test if any menus are open that do not belong to the menu item currently
            // selected, and if so - hide them. 
            for(var i in this.newMenuItems)
            {
                nde = this.newMenuItems[i];
                if(nde.getMyMenuElement() == this.newMenuItems[srcEl.id].getMyMenuElement() && 
                   nde != this.newMenuItems[srcEl.id])
                {
                    if(nde.getChildMenuElement() != null)
                    {
                        if(nde.getChildMenuElement().style.display != "none")
                        {
                            nde.getChildMenuElement().style.display = "none";
                        }
                    }
                }
            } 
        }
        
        // Decide if the raising menu (srcEl) needs to hide itself...
        if(kid)
        {
            if(kid.style.display=="none")
            {
                // The child has been hidden. Determine if the srcElement also needs hiding...
                if(!this.iterateNodesForChild(srcEl.parentNode, targEl))
                {
                    // The parent becomes the child (mainly for readability)
                    kid = srcEl;
            
                    if(kid)
                    {
                        // If the target element *is not* a child of the menu we want to hide, 
                        // continue to hide the menu.
                        if(this.newMenuItems[targEl.id])
                        {
                            if(targEl.parentNode.id != kid.id) 
                            {
                                kid.parentNode.style.display="none";
                            }
                        }
                        else
                        {
                            this.hideAllMenus();
                        }                   
                    }
                }
            }
        }
    },    
    
    /*
    <summary>
    Determines if the supplied element ID belongs to an element attached to a root menu.
    </summary>
    <param name="elId">Id of the element which needs to be determined if it belongs to a root menu.</param>
    <returns>True if the element does belong to a root menu, false otherwise.</returns>
    */
    elementBelongsToRootMenu: function(elId)
    {
        var ctlName = elId.split("_");
        var x;
        for(x=0;x<ctlName.length;x++)
        {
            if(ctlName[x] == "ROOT") 
            {
                return true;
            }
        }
        return false;
    },    
    
    /*
    <summary>
    Iterates through all non-root menus (<ul /> elements) and hides them.
    </summary>
    <returns>Returns null on success or failure.</returns>
    */
    hideAllMenus: function()
    {
       var iframe = document.getElementById("menuIframe"); 
        var childMenusToHide = document.getElementById(this.clientObjectId).getElementsByTagName("ul");
        for(var z=0;z<childMenusToHide.length;z++)
        {
            // Encountered a root menu - skip hiding. 
            if(this.elementBelongsToRootMenu(childMenusToHide[z].id)) { continue; }
            // Hide the child menu.
            childMenusToHide[z].style.display="none";
        }
        if(iframe){iframe.style.display ="none";}
        // Fire global event
        var me = this;
        NMR.Common.Web.GenericControls.GlobalEvents.raiseCustomEvent("onAllMenusHidden", me);
    },
    
    /*
    <summary>
    Iterates the child elements of a parent element, to determine if the element:
    childEl exists as a dependent of the parent element.   
    </summary>
    <param name="parentEl">The element whose node tree is to be iterated to determine if childEl existswithin it.</param>
    <param name="childEl">The element to look for in the parent elements node tree.</param>
    <returns>Returns true if the childEl(ement) is located within the parents tree, false otherwise.</returns>
    <remarks>
    Any page implemented hard returns that are not html (E.g. when formatting 
    the page for readability) are interpreted as textNodes (nodeName: #text) in 
    FireFox, incase there is any confusion as to why the number of nodes in a 
    extract of HTML can differ on two browsers. 
    </remarks>    
    */
    iterateNodesForChild: function(parentEl, childEl)
    {
        if(parentEl.childNodes.length > 0)
        {
            for(var x=0;x<parentEl.childNodes.length;x++)
            {
                if(this.iterateNodesForChild(parentEl.childNodes[x], childEl) == true)
                {
                    return true;
                }
                if(childEl == parentEl.childNodes[x])
                {
                    return true;
                }
            }
        }
        return false;
    },    
        
    /*
    <summary>
    Determines if the mouse pointer lies within the boundry element (typically <li />) and is 
    not postioned over an element nested inside the boundry element (e.g. an <a /> or <img /> element).
    </summary>
    <param name="boundryElement">The element to be used as the bounding box for boundry determination.</param>
    <param name="myEvent">Event containing the mouse pointers current position. </param>
    <returns>Returns true if the mouse pointer is within the boundry element false otherwise.</returns>
    */ 
    inBoundryElement: function(boundryElement, myEvent)
    {
        var mousePosX,mousePosY;                // Mouse pointers current position
        var inHorizontalAxis = false;           // Mouse pointer lies across the boundry elements X axis (default false)
        var inVerticalAxis = false;             // Mouse pointer lies across the boundry elements Y axis (default false)
        
        if(myEvent.pageX || myEvent.pageY)
        {
            // for Firefox
            mousePosX=myEvent.pageX; 
            mousePosY=myEvent.pageY;
        }
        else
        {
            // for IE
            if(document.documentElement.scrollTop)
            {
                mousePosX=myEvent.clientX+document.documentElement.scrollLeft;
                mousePosY=myEvent.clientY+document.documentElement.scrollTop;
            }
            else
            {
                mousePosX=myEvent.clientX+document.body.scrollLeft;
                mousePosY=myEvent.clientY+document.body.scrollTop;
            }
        }

        // Derive the top left corner of the element on the page. 
        pos = this.findPos(boundryElement);
        
        if(this.lastMousePosX == -1 || this.lastMousePosY == -1)
        {
            this.lastMousePosX = mousePosX;
            this.lastMousePosY = mousePosY;
        }
            
        // Moving from RIGHT to LEFT <--.
        if (mousePosX <= this.lastMousePosX)
        {
            if(
               (mousePosX <= (pos[0] + boundryElement.offsetWidth) + 2) &&         // Gets the far end of the bounding box (horizontal)
               (mousePosX > (pos[0] + 2))                                          // Gets the start of the bounding box   (horizontal)
              )                                     
              {    
                inHorizontalAxis = true;
              }             
        }
        // Moving from LEFT to RIGHT -->.
        else if(mousePosX >= this.lastMousePosX)
        {
            if(
               (mousePosX <= (pos[0] + boundryElement.offsetWidth) - 2) &&         // Gets the far end of the bounding box (horizontal)
               (mousePosX >= (pos[0] - 2))                                         // Gets the start of the bounding box   (horizontal)
              )                                     
              {    
                inHorizontalAxis = true;
              }  
        } 

        // Moving from BOTTOM to TOP (Up).
        if (mousePosY <= this.lastMousePosY)
        {
             if(
                (mousePosY <= (pos[1] + boundryElement.offsetHeight) + 2) &&      //Gets the far end of the bounding box (vertical)
                (mousePosY > pos[1] + 2)                                          //Gets the start of the bounding box   (vertical)
              )
            {
                inVerticalAxis = true;
            }               
        
        }
        // Moving from TOP to BOTTOM (Down).
        else if(mousePosY >= this.lastMousePosY)
        {
             if(
                (mousePosY <= (pos[1] + boundryElement.offsetHeight) - 2) &&      //Gets the far end of the bounding box (vertical)
                (mousePosY >= pos[1] - 2)                                         //Gets the start of the bounding box   (vertical)
              )
            {
                inVerticalAxis = true;
            }  
        } 

        if(inHorizontalAxis && inVerticalAxis)
        {
            return true;
        }
        else
        {
            return false;
        }
    },

    /*
    <summary>
    Gives an elements' position in pixels with respect to the root element (e.g. <html />).    
    </summary>
    <param name="obj">The object that needs locating in x and y pixel co-ordinates on the browser window.</param>
    <returns>A 1D array of 2 elements containing the offsets of obj in top and left with respect to the root element. </returns>    
    */ 
    findPos: function(obj) 
    {
        var curleft = curtop = 0;
        if (obj.offsetParent) 
        {
	        curleft = obj.offsetLeft;
	        curtop = obj.offsetTop;
	        while (obj = obj.offsetParent) 
	        {
		        curleft += obj.offsetLeft;
		        curtop += obj.offsetTop;
	        }
        }
        return [curleft,curtop];
    },

    /*
    <summary>
    Retrieves the source and target elements for any mouseover or mousemove event across both
    Firefox and Internet Explorer. 
    </summary> 
    <param name="e">The event object containing the necessary information.</param>
    <returns>A 1D array of 2 elements containing the source and target elements.</returns>    
    */
    getSourceTargetElementsFromEvent: function(e) 
    {
        var sourceEl, targetEl;

        if(e.currentTarget || e.relatedTarget)
        {
            // For Firefox
            if(e.type.toLowerCase() == "mouseout")
            {
                targetEl = e.relatedTarget;
                sourceEl = e.currentTarget;
            }
            else if(e.type.toLowerCase() == "mouseover")
            {
                targetEl = e.currentTarget;
                sourceEl = e.relatedTarget;
            }
            else if(e.type.toLowerCase() == "mousemove")
            {
                // If this is a mousemove event, no target will be available - only
                // the element the mouse pointer is currently positioned over.
                sourceEl = e.originalTarget;
                targetEl = null;
            }
        }
        else
        {
            // For IE
            if(e.type.toLowerCase() == "mouseout"  || 
               e.type.toLowerCase() == "mouseover")
            {
                targetEl = e.toElement;
                sourceEl = e.fromElement; 
            }
            else if(e.type.toLowerCase() == "mousemove")
            {
                // If this is a mousemove event, no target will be available - only
                // the element the mouse pointer is currently positioned over.
                sourceEl = e.srcElement;
                targetEl = null;
            }
        }
        return [sourceEl, targetEl];
    },
    
    /*
    <summary>
    Postback event wrapper. Allows the Context Menu to store eventTracking
    information in the hidden field: ControlId_eventTracking, prior to calling to postback event from
    a users menu item click.
    </summary>
    <param name="menuItem">The menu item that was clicked</param>
    <param name="targetURL">
    Optional parameter. Contains the target URL to redirect the client to (without performing
    a postback). Parameter only contains a value if the navigation xml fragment set the 
    'isClientSideLink' attribute to true on the <menu /> or <menuitem /> element(s).
    <param>
    */
    menuItemClicked: function(menuItem, targetURL)
    {
        var me = this;
        NMR.Common.Web.GenericControls.GlobalEvents.raiseCustomEvent("onMenuItemClicked", me);
            
        if((menuItem.length > 0) && (targetURL == undefined))
        {
            var evtTrk = document.getElementById(this.clientObjectId + "_eventTracking");
            evtTrk.value = "ItemClicked";
            this.initialisePostback(menuItem);
        }
        else if(targetURL != undefined)
        {
            (document.all)?window.navigate(targetURL):window.location.href=targetURL;
        }
    }
    
};

/*
<summary>
Constructor function, passes arguments to constructor in prototype
</summary>
*/
NMR.Common.Web.GenericControls.GenericContextMenu.MenuItem = function()
{
    // Call constructor
    this.create.apply(this, arguments);
}

/*
<summary>
Class for providing information on each menu item that displays a child menu. 
Provides accessors for retreiving related elements based on string ID's provided.
</summary>
*/ 
NMR.Common.Web.GenericControls.GenericContextMenu.MenuItem.prototype = 
{
    menuItemElementId: "",            // Id of the element in the source menu <li>
    childMenuElementId: "",           // Id of any child menu element <ul />
   
    /* 
    <summary>
    Constructor.
    </summary>
    */
    create: function(){},
   
    /*
    <summary>
    Provides the parent <ul /> menu element for any menu item (<li />) element.  
    </summary>
    <returns>
    The menu element (<ul />) for the class instances menuItemElementId (<li />). 
    If no parent can be located - E.g. because the class property menuItemElement has
    not been populated - null is returned.
    </returns>
    */
    getMyMenuElement: function()
    {
        var docEl = document.getElementById(this.menuItemElementId);
        if(docEl)
        {
            return docEl.parentNode;
        }
        else
        {
            return null;
        }
    },
    
    /*
    <summary>
    Returns the child menu (<ul />) element for any menu item (<li />) that is responsible 
    for displaying a child menu. 
    </summary>
    <returns>
    Returns the child menu (<ul />) or null if no child menu exists under then menu item or the
    child menu cannot be located.
    </returns>
    */
    getChildMenuElement: function()
    {
        var docEl = document.getElementById(this.childMenuElementId);
        if(docEl)
        {
            return docEl;
        }
        else
        {
            return null;
        }
    }
};