/**
 * Multi-level menu.
 * Version: 1.0
 *
 * The use of this script is strictly forbidden without prior written
 * consent. You must obtain permission before copying, modifying, 
 * redistributing, deriving or selling any part of this program.
 *
 * Copyright (c) 2008 HYPERZOID
 * All Rights Reserved
 */

// The menu object.
var menu = null;


/**
 * Initializes this program.
 */
function initMenu(direction) {
    menu = new Menu(direction);
}


/**
 * The constructor.
 */
function Menu(direction) {
    // The direction ("left" or "right") in which menu boxes expand.
    this.direction = direction;

    // The menu container element.
    this.menu = document.getElementById('menu');

    // Menu offsets relative to page corner.
    this.menuX = this.getX(this.menu);
    this.menuY = this.getY(this.menu);

    // A list of timers for menu box collapse delay, indexed by the box's DOM ID.
    this.timers = new Array();

    // A list of all menu items.
    this.items = new Array();
    this.getItemIds(this.menu);
}


/**
 * Collapses a list of menu boxes after a certain delay.
 * @param boxes The list of menu boxes to close.
 */
Menu.prototype.collapse = function(boxes) {
    for (var i = 0; i < boxes.length; i++) {
        this.timers[boxes[i]] = setTimeout("document.getElementById('" + boxes[i] + "').style.visibility = 'hidden';", 150);
    }
}


/**
 * Expands a menu box.
 * @param boxes  A list DOM ID's of nested menu boxes to be expanded.
 * @param itemId The DOM ID of the menu item causing this expansion.
 * @param level  The depth of the menu item itemId.
 */
Menu.prototype.expand = function(boxes, itemId, level) {
    // Cancel collapse timers for all menu boxes in the list to keep them open.
    for (var i = 0; i < boxes.length; i++) {
        clearTimeout(this.timers[boxes[i]]);
    }

    var box  = document.getElementById(boxes[boxes.length - 1]);
    var item = document.getElementById(itemId);

    // The x coordinate of the expanded menu box.
    var x = 0;
    if (this.direction == 'left') { // Menu expands to the left of item.
        x = this.getX(item) - this.menuX - box.offsetWidth - 3;
        if (level > 1) {
            x = x - parseInt(box.style.paddingLeft)
        }
    } else {
        x = this.getX(item) - this.menuX + item.offsetWidth + 3;
        if (level > 1) {
            x = x + parseInt(box.style.paddingLeft)
        }
    }
    
    // The y coordinate of the expanded menu box.
    var y = this.getY(item) - this.menuY;
    if (level > 1 && navigator.userAgent.indexOf('Gecko') == -1) { // IE.
        y = y - parseInt(box.style.paddingTop);
    } else if (level > 1) { // Gecko.
        y = y - (parseInt(box.style.paddingTop) + 2);
    }

    box.style.left       = x + 'px';
    box.style.top        = y + 'px';
    box.style.visibility = 'visible';
}


/**
 * Recursively get menu item DOM ID's.
 */
Menu.prototype.getItemIds = function(node) {
    var children = node.childNodes;
    for (var i = 0; i < children.length; i++) {
        this.getItemIds(children[i]);
    }
    if (node.nodeName.toLowerCase() == 'div') {
        if (node.id == 'menu') {
            return;
        }
        this.items[this.items.length] = node.id;
        this.timers[node.id] = null;
        document.getElementById(node.id).style.padding = '1px 0px 0px 0px';
    }
}


/**
 * Gets the absolute x coordinate of an element.
 * @param node An element.
 */
Menu.prototype.getX = function(node) {
    var x = 0;
    while (node != null) {
        x += node.offsetLeft;
        node = node.offsetParent;
    }
    return parseInt(x);
}

/**
 * Gets the absolute y coordinate of an element.
 * @param node An element.
 */
Menu.prototype.getY = function(node) {
    var y = 0;
    while (node != null) {
        y += node.offsetTop;
        node = node.offsetParent;
    }
    return parseInt(y);
}
