//
// Javascript popup menu
//
// Created on: <10-Mar-2004 16:35:43 gl>
//
// Copyright (C) 1999-2004 eZ systems as. All rights reserved.
//


/*! \file javascriptmenu.js
  A JavaScript-based popup menu designed for eZ publish navigation.
*/

/*
  Handles multilevel popup menus. Writes menu content after the document is loaded, so browsers without
  javascript does not see the menu content.
  Requieres DOM support, which should be available in 5th generation browsers.
  Author: Gunnstein Lye <gl@ez.no>
  Additional code from http://www.quirksmode.org is used with permission.

  Allows sorting by index if you uncomment the prioritySort() call in init().

  Links:
  http://www.quirksmode.org/js/findpos.html
  http://www.howtocreate.co.uk/tutorials/index.php?tut=0&part=28

  TODO: menuArray could perhaps fetch submenu HTML from templates.
  TODO: <body onClick="hideAllMenus()"> does not work well in all browsers (opera).

  Tested and working browsers:

  Netscape 7.1
  User agent: mozilla/5.0 ... gecko/20030624 netscape/7.1

  Mozilla 1.5
  User agent: mozilla/5.0 ... gecko/20031007

  Mozilla Firefox 0.8
  User agent: mozilla/5.0 ... gecko/20040207 firefox/0.8

  Internet Explorer 6.0
  User agent: mozilla/4.0 ... msie 6.0

  Opera 7.23
  User agent: mozilla/4.0 ... msie 6.0 ... opera 7.23


  Works, but has a flicker problem:

  Konqueror 3.1.1
  User agent: mozilla/5.0 ... konqueror/3.1

  Modified 26.08.2004 by Magne Zachrisen at eZ Systems (mz@ez.no) to allow timed closing of menus. Edit the timeOut variable
  to set menu closing timeout.
*/


var menuArray = new Array();      // Contains the result from the content tree fetch
var cancelMouse = false;	  // True if timed closing of menu is cancelled by a mouseover event
var timeOut = 1000;

// Inserts menuArray into the document.
function init()
{
     prioritySort();  // Uncomment this to enable priority sorting
    var urlAliasForDepth = new Array();
    for ( var i in menuArray )
    {
        urlAliasForDepth[menuArray[i]['depth']] = menuArray[i]['url_alias'];

        var subMenuElement = document.getElementById( menuArray[i]['url_alias'] );
        if ( subMenuElement )
        {
            continue;
        }

        var parentElement = document.getElementById( urlAliasForDepth[Number( menuArray[i]['depth'] ) - 1] );
        var next = Number( i ) + 1;
        // If the item has children
        if ( next < menuArray.length &&
             menuArray[i]['depth'] < menuArray[next]['depth'] )
        {
            if ( menuArray[i]['child_placement'] != undefined &&
                 menuArray[i]['child_placement'] == 'left' )
            {
                parentElement.innerHTML += '<div onMouseOut="mouseOutMenu()" class="popuplink" onMouseOver="showMenu( this, &#39;' +
                                           menuArray[i]['url_alias'] +
                                           '&#39;, &#39;left&#39; )">\n' +
                                           '  <a href=' + menuArray[i]['url_alias_ezurl'] +
                                           ' target=' + menuArray[i]['target'] + '>' +
                                           menuArray[i]['name'] + ' ' + leftMenuArrow +
                                           '</a>\n' +
                                           '</div>\n';
            }
            else if ( menuArray[i]['child_placement'] == 'auto' )  // Defaults to right, but left if outside of screen
            {
                parentElement.innerHTML += '<div onMouseOut="mouseOutMenu()" class="popuplink" onMouseOver="showMenu( this, &#39;' +
                                           menuArray[i]['url_alias'] +
                                           '&#39;, &#39;auto&#39; )">\n' +
                                           '  <a href=' + menuArray[i]['url_alias_ezurl'] +
                                           ' target=' + menuArray[i]['target'] + '>' +
                                           menuArray[i]['name'] + ' ' + rightMenuArrow +
                                           '</a>\n' +
                                           '</div>\n';
            }
            else  // Default is right
            {
                parentElement.innerHTML += '<div onMouseOut="mouseOutMenu()" class="popuplink" onMouseOver="showMenu( this, &#39;' +
                                           menuArray[i]['url_alias'] +
                                           '&#39;, &#39;right&#39; )">\n' +
                                           '  <a href=' + menuArray[i]['url_alias_ezurl'] +
                                           ' target=' + menuArray[i]['target'] + '>' +
                                           menuArray[i]['name'] + ' ' + rightMenuArrow +
                                           '</a>\n' +
                                           '</div>\n';
            }
            parentElement.parentNode.innerHTML += '<div onMouseOut="mouseOutMenu()" class="popupmenu" id="' +
                                                  menuArray[i]['url_alias'] +
                                                  '"></div>\n';
        }
        else // If the item has no children
        {
            parentElement.innerHTML += '<div onMouseOut="mouseOutMenu()" class="popuplink" onMouseOver="showMenu( this, &#39;' +
                                       urlAliasForDepth[Number( menuArray[i]['depth'] ) - 1] +
                                       '&#39;, &#39;right&#39; )">\n' +
                                       '  <a href=' + menuArray[i]['url_alias_ezurl'] +
                                       ' target=' + menuArray[i]['target'] + '>' +
                                       menuArray[i]['name'] +
                                       '</a>\n' +
                                       '</div>\n';
        }
    }
}

// Hides all menus.
function hideAllMenus()
{
    for ( var i in menuArray )
    {
        var subMenuElement = document.getElementById( menuArray[i]['url_alias'] );
        if ( subMenuElement )
        {
            subMenuElement.style.display = 'none';
        }
    }
}

// Hide all menus on the same or lower levels than the one we are opening.
// Menus on higher levels should stay open.
function hideOtherMenus( dontHideID )
{
    var hideMeByDepth = new Array();
    // Reverse loop, children before parents
    for ( var i = menuArray.length - 1; i >= 0; i-- )
    {
        var subMenuElement = document.getElementById( menuArray[i]['url_alias'] );
        if ( subMenuElement )
        {
            // If my ID is dontHideID, then I should not be hidden.
            if ( menuArray[i]['url_alias'] == dontHideID )
            {
                hideMeByDepth[Number( menuArray[i]['depth'] ) - 1] = false;  // Don't hide my parent
            }
            // If one of my children should not be hidden, then I should not be hidden.
            else if ( hideMeByDepth[menuArray[i]['depth']] == false )
            {
                hideMeByDepth[Number( menuArray[i]['depth'] ) - 1] = false;  // Don't hide my parent
            }
            // Otherwise, hide me
            else
            {
                hideMeByDepth[menuArray[i]['depth']] = true;
                subMenuElement.style.display = 'none';
            }
        }
    }
}

// Time out. Close menu block
function mouseReallyOut()
{
	if (!cancelMouse)
	{ 
	  hideAllMenus();
	}
}

// Start timed closing of menu block
function mouseOutMenu()
{
	cancelMouse = false;
	setTimeout('mouseReallyOut()', timeOut);
}

// Shows and positions the menu given by subMenuID. Hides other menus first, as necessary.
function showMenu( posElement, subMenuID, position )
{
    hideOtherMenus( subMenuID );

    var subMenuElement = document.getElementById( subMenuID );
    if ( !subMenuElement || subMenuElement.childNodes.length == 0 )  // Don't show empty menu
    {
        return;
    }
    // Show popup menu
    subMenuElement.style.display = 'block';
    cancelMouse = true;		 // Cancel timed closing of any windows

    // Avoid repositioning. This fixes a problem with menu items that have no submenu,
    // and therefore call showMenu() with their own id.
    // TODO: We may have to reposition menus on window resize or scroll.
    if ( subMenuElement.isPositioned )
    {
        return;
    }

    // Position popup menu correctly
    // NB: The adjustment values depend on the stylesheet
    var userAgent = navigator.userAgent.toLowerCase();
    if ( position == 'auto' )  // Defaults to right, but left if outside of screen
    {
        position = 'right';

        var xOffset = -4;

        windowWidth = 0;
        if ( window.innerWidth > 0 )
            windowWidth = window.innerWidth;
        else if ( document.body.clientWidth > 0 )
            windowWidth = document.body.clientWidth;

        elementWidth = 0;
        if ( subMenuElement.clip && subMenuElement.clip.width > 0 )
            elementWidth = subMenuElement.clip.width;
        else if ( subMenuElement.style.pixelWidth > 0 )
            elementWidth = subMenuElement.style.pixelWidth;
        else if ( subMenuElement.offsetWidth > 0 )
            elementWidth = subMenuElement.offsetWidth;

//        if ( userAgent.indexOf( 'msie' ) < 0 )
//            alert( windowWidth + ' - ' + ( findPosX( posElement ) + posElement.offsetWidth ) + ' - ' + elementWidth );

        if ( ( windowWidth > 0 && elementWidth > 0 ) &&
             ( findPosX( posElement ) + posElement.offsetWidth + xOffset + elementWidth > windowWidth ) )
            position = 'left';
    }

    if ( position == 'below' )
    {
        var yOffset = 5;
        var xOffset = -5;
        if ( userAgent.indexOf( 'msie' ) >= 0 ||
             userAgent.indexOf( 'opera' ) >= 0 ||
             userAgent.indexOf( 'konqueror' ) >= 0 )
        {
            yOffset = 1;
            xOffset = -9;
        }

        subMenuElement.style.top = findPosY( posElement ) + posElement.offsetHeight + yOffset + 'px';
        subMenuElement.style.left = findPosX( posElement ) + xOffset + 'px';
    }
    else if ( position == 'right' )
    {
        var yOffset = 0;
        var xOffset = 30;
        if ( userAgent.indexOf( 'konqueror' ) >= 0 )
        {
            yOffset = -98;
            xOffset = -1;
        }

        subMenuElement.style.top = findPosY( posElement ) + yOffset + 'px';
        subMenuElement.style.left = findPosX( posElement ) + posElement.offsetWidth + xOffset + 'px';
    }
    else if ( position == 'left' )
    {
        var yOffset = -5;
        var xOffset = -4;
        if ( userAgent.indexOf( 'konqueror' ) >= 0 )
        {
            yOffset = -102;
            xOffset = -13;
        }

        subMenuElement.style.top = findPosY( posElement ) + yOffset + 'px';
        subMenuElement.style.left = findPosX( posElement ) - subMenuElement.offsetWidth + xOffset + 'px';
    }
    subMenuElement.isPositioned = true;
}

// Finds the X position of an element.
// From http://www.quirksmode.org/js/findpos.html
// Copied with permission
function findPosX( obj )
{
    var curleft = 0;
    if ( obj.offsetParent )
    {
        while ( obj.offsetParent )
        {
            curleft += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    }
    else if ( obj.x )
    {
        curleft += obj.x;
    }
    return curleft;
}

// Finds the Y position of an element.
// From http://www.quirksmode.org/js/findpos.html
// Copied with permission
function findPosY( obj )
{
    var curtop = 0;
    if ( obj.offsetParent )
    {
        while ( obj.offsetParent )
        {
            curtop += obj.offsetTop;
            obj = obj.offsetParent;
        }
    }
    else if ( obj.y )
    {
        curtop += obj.y;
    }
    return curtop;
}

// Sorts menuArray by priority (without messing with depth)
function prioritySort()
{
    var tempMenuArray = menuArray;
    menuArray = new Array();
    var depthArray = new Array();

    // Split array into arrays by depth
    var firstDepth;
    for ( var i in tempMenuArray )
    {
        if ( firstDepth == undefined )
        {
            firstDepth = tempMenuArray[i]['depth'];
        }
        if ( depthArray[tempMenuArray[i]['depth']] == undefined )
        {
            depthArray[tempMenuArray[i]['depth']] = new Array();
        }
        depthArray[tempMenuArray[i]['depth']].push( tempMenuArray[i] );
    }

    // Sort depth arrays by priority
    for ( var j in depthArray )
    {
        depthArray[j].sort( comparePriority );
    }

    // Insert top level elements
    for ( var k in depthArray[firstDepth] )
    {
        menuArray.push( depthArray[firstDepth][k] );
    }

    // Insert children
    for ( var l = firstDepth + 1; depthArray[l] != undefined; l++ )
    {
        for ( var m in depthArray[l] )
        {
            var parentNodeID = depthArray[l][m]['parent_node_id'];
            var indexForParentNodeID = new Array();
            if ( indexForParentNodeID[parentNodeID] == undefined )
            {
                indexForParentNodeID[parentNodeID] = findIndexAfterParent( depthArray[l][m] );
            }

            menuArray.splice( indexForParentNodeID[parentNodeID], 0, depthArray[l][m] );

            indexForParentNodeID[parentNodeID] += 1;
        }
    }
}

// Returns the index at which to insert children
function findIndexAfterParent( parent )
{
    for ( var i in menuArray )
    {
        if ( menuArray[i]['node_id'] == parent['parent_node_id'] )
        {
            return Number(i) + 1;
        }
    }
    return -1;
}

// Compare two items by priority
function comparePriority( a, b )
{
    if ( Number(a['priority']) < Number(b['priority']) )
    {
        return 1;
    }
    if ( Number(a['priority']) > Number(b['priority']) )
    {
        return -1;
    }
    return 0;
}
