//Parameter keys.
var keys = {
    MenuID : "menuID",                  //Html ID of the menu
    LeftOffset : "leftOffset",          //Left pixel offset for sub items.
    TopOffset : "topOffset",            //Top pixel offset for sub items.
    ShowDelay : "showDelay",            //Time between hovering over menu and displaying sub menu.
    HideDelay : "hideDelay",            //Time between hovering off of a menu and it disappearing.
    MinWidth : "minWidth",              //Minimum width of sub items. (0 = none)
    MaxWidth : "maxWidth",              //Maximum width of sub items. (0 = none)
    FadeSpeed : "fadeSpeed",            //Fade animation speed. (0 = disabled)
    IsVertical: "isVertical",           //Menu is vertical (first level sub menu will appear beside the main menu instead of below it).
    ActiveCssClass : "activeCssClass",  //CSS class to apply when a item is hovered over.
    ParentCssClass : "parentCssClass",  //CSS class to apply when an item has children.
    FirstCssClass : "firstCssClass",    //CSS class to apply to the first item in a menu group.
    LastCssClass : "lastCssClass"     //CSS class to apply to the last item in a menu group.
}

//Status enumeration.
var eMenuStatus = { shown: 0, showing: 1, hiding: 2, hidden: 3 };
    
/*===================================================
Name:       BWMenu

Purpose:    Creates a drop down menu for the
            Bancworks software package.
===================================================*/
function CreateMenu(params) {
    new dropDownMenu(params);
}

function dropDownMenu(params) {
    //Make sure a menu Id is passed in.
    if (!params[keys.MenuID])
        throw "Error: No Menu ID is specified.";
        
    //Check for IE6.
    this._isIE6 = (navigator.userAgent.indexOf(' MSIE 6') > -1);
        
    //Get parameters.
    this._id = params[keys.MenuID];
    this._rootMenu = $("#" + params[keys.MenuID]);
    this._leftOffset = (params[keys.LeftOffset] != null ? params[keys.LeftOffset] : -10);
    this._topOffset = (params[keys.TopOffset] != null ? params[keys.TopOffset] : -15);
    this._showDelay = (params[keys.ShowDelay] != null ? params[keys.ShowDelay] : 200);
    this._hideDelay = (params[keys.HideDelay] != null ? params[keys.HideDelay] : 400);
    this._minWidth = (params[keys.MinWidth] != null ? params[keys.MinWidth] : 0);
    this._maxWidth = (params[keys.MaxWidth] != null ? params[keys.MaxWidth] : 0);
    this._isVertical = (params[keys.IsVertical] != null ? params[keys.IsVertical] : false);
    this._activeCssClass = (params[keys.ActiveCssClass] != null ? params[keys.ActiveCssClass] : "MenuItemActive");
    this._parentCssClass = (params[keys.ParentCssClass] != null ? params[keys.ParentCssClass] : "MenuItemParent");
    this._defaultZIndex = 2000;
    this._zIndex = this._defaultZIndex;
    this._firstCssClass = (params[keys.FirstCssClass] != null ? params[keys.FirstCssClass] : "FirstMenuItem");
    this._lastCssClass = (params[keys.LastCssClass] != null ? params[keys.LastCssClass] : "LastMenuItem");
    
    //Fading has issues in IE6, so only enable it by default if it's not IE6.
    this._fadeSpeed = (params[keys.FadeSpeed] != null ? params[keys.FadeSpeed] : (this._isIE6 ? 0 : 400));
    
    //Resize menus.
    this.resizeMenus();
    this.resizeSubMenus();
    
    //Now that everything is set to the correct width, unhide the menu.
    this._rootMenu.css("visibility", "visible");
    
    return true;
}

dropDownMenu.prototype.resizeMenus = function() {
    //Get the width of the container and the menu ul.
    var nContainerWidth = this._rootMenu.parent().innerWidth();
    var nMenuWidth = this._rootMenu.innerWidth();

    //If the menu ul is wider than its container, resize it to fit.
    if (this._rootMenu.outerWidth() > nContainerWidth) nMenuWidth = nMenuWidth - (this._rootMenu.outerWidth() - nContainerWidth);
    this._rootMenu.css("width", nMenuWidth);

    //Get the anchors and get the first anchor for width/height purposes.
    var anchors = $("> li > a", this._rootMenu);
    var firstAnchor = anchors.filter(":first");
    var lastAnchor = anchors.filter(":last");

    //Add a first and last class for different end styles.
    firstAnchor.addClass(this._firstCssClass)
    lastAnchor.addClass(this._lastCssClass)

    if (!this._isVertical) {
        //Get the number of menu items
        var nChildCount = anchors.length;

        //Calculate the total width of all the menu items.
        var nItemsWidth = 0;
        for (var i = 0; i < anchors.length; i++) {
            var curAnchor = $(anchors[i]);

            //IE6 has trouble with an "auto" width so set it to 0 because it is just so awesome it
            // uses 0 as the "real" auto.  In other words, 'auto' doesn't work like it should, but 0
            // works like 'auto' should.  Go figure.  Just another example of IE6 being awesome.
            if (curAnchor.css("width") == "auto" && this._isIE6)
                curAnchor.css("width", 0);

            //Add to the total item width.    
            nItemsWidth += curAnchor.outerWidth();
        }

        //If the width of the items is less than the width of the menu, expand them to fill the space.
        if (nItemsWidth != nMenuWidth) {
            //Get the difference between the items width and menu width, then calculate the additional 
            //width needed for each item and any gap that would be left over if each child has the same width.
            var nDiff = nMenuWidth - nItemsWidth;
            var nAddWidth = Math.floor(nDiff / nChildCount);
            var nMod = nDiff % nChildCount;

            //Expand the width of each menu item.
            for (var i = 0; i < anchors.length; i++) {
                var curAnchor = $(anchors[i]);
                curAnchor.width(curAnchor.width() + nAddWidth);
            };

            //Check to see if the first/last items need to add a little bit extra to fill in the gap.
            if (nMod != 0) {
                //Divide the remainder by 2 and put the ceiling on the front and the floor on the back.
                firstAnchor.css("width", firstAnchor.width() + Math.ceil(nMod / 2));
                lastAnchor.css("width", lastAnchor.width() + Math.floor(nMod / 2));
            }
        }

        //Set the height of the menu to the height of the menu items if it's a horizontal menu.
        this._rootMenu.css("height", anchors.outerHeight());
    }
}

dropDownMenu.prototype.resizeSubMenus = function() {
    //Get all the ul of the menu.
    var submenus = $(" ul", this._rootMenu);

    //Loop through all the ul and set them up.
    for (var i = 0; i < submenus.length; i++) {
        this.initSubMenu(submenus[i]);
    }
}

dropDownMenu.prototype.initSubMenu = function(ul) {
    var ul = $(ul);
    var menu = this;
    ul.parent().hover(function() { menu.setMenuShown(this) }, function() { menu.setMenuHidden(this) });

    //Get the length of the longest menu item and set all the menu items width to that.
    var nLongest = this._minWidth;
    var anchors = $("> li > a", ul);
    if (anchors.length > 0) {
        $(anchors[0]).addClass(this._firstCssClass);
        $(anchors[anchors.length -1]).addClass(this._lastCssClass);
    }

    if (!this._isVertical) {
        for (var i = 0; i < anchors.length; i++) {
            var curAnchor = $(anchors[i]);

            //IE6 fix to get the correct width if set to auto.
            if (curAnchor.css("width") == "auto" && this._isIE6)
                curAnchor.css("width", 0);

            //Get the longest anchor.
            if (curAnchor.outerWidth() > nLongest)
                nLongest = curAnchor.outerWidth();
        }

        if (this._maxWidth && nLongest > this._maxWidth) nLongest = this._maxWidth;
        for (var i = 0; i < anchors.length; i++) { $(anchors[i]).css("width", nLongest); }
    }
    else if (this._isIE6) {
        //Set the width of each anchor to the width of the ul if it's IE6 and vertical.
        if (ul.css("width") == "auto") ul.css("width", 0);
        var x = ul.width();
        for (var i = 0; i < anchors.length; i++) {
            var curAnchor = $(anchors[i]);
            curAnchor.css("width", x);
        }
    }


    //Fix for IE6 to make the menu show over drop down uls.
    if (this._isIE6) {
        var iframe = $(document.createElement('iframe'));
        iframe.css("filter", "mask()");
        iframe.css("width", ul.outerWidth());
        iframe.css("height", ul.outerHeight());
        iframe.css("position", "absolute");
        iframe.css("z-index", -1);
        iframe.css("margin-left", -ul.outerWidth());
        iframe.attr("src", "javascript:false");
        ul.append(iframe);
    }

    //Set each menu to hidden and set the uls parent to a parent css.
    ul.get(0).status = eMenuStatus.hidden;
    ul.siblings("a").addClass(this._parentCssClass);
}

dropDownMenu.prototype.setMenuShown = function(li) {
    var ul = $("> ul", li);
    var menu = this;
    
    //Show Menu
    switch (ul.get(0).status) {
        case eMenuStatus.hidden:
            //Start showing timer.
            this.clearTimer(ul);
            this.setTimer(ul, function() { menu.showMenu(ul) }, this._showDelay);
            break;
        case eMenuStatus.hiding:
            //Stop hiding and start showing.
            this.clearTimer(ul);
            ul.stop();
            this.showMenu(ul);
            break;
        case eMenuStatus.shown:
            //Shown, so just stop any timers.
            this.clearTimer(ul);
            break;
        case eMenuStatus.showing:
            this.clearTimer(ul);
            //Showing, do nothing.
            break;
    }
}

dropDownMenu.prototype.showMenu = function(ul) {
    //Show the menu.
    ul.get(0).status = eMenuStatus.showing;
    ul.css("left", "auto");
    ul.css("z-index", this._zIndex++);
    var menu = this;

    //Watch out for rollover.
    if (this._zIndex < 0) this._zIndex = this._defaultZIndex;

    //If it isn't the first level of the menu, adjust the position so it doesn't show over the parent.
    if (ul.parent().parent().get(0).id != this._id || this._isVertical) {
        //Set the margins.
        ul.css("margin-left", ul.parent().outerWidth() + this._leftOffset);
        ul.css("margin-top", this._topOffset);

        //Get the absolute position so we know whether to display the menu to the left or right of the current menu.
        var win = $(window);
        var offset = { left: ul.get(0).offsetLeft, top: ul.get(0).offsetTop };
        ul.parents(" ul", this._rootMenu).each(function() { offset.left += this.offsetLeft; offset.top += this.offsetTop; });

        //If it goes beyond the window, display to the left of the current menu.
        if ((offset.left + ul.outerWidth()) > win.width())
            ul.css("margin-left", -ul.outerWidth() - this._leftOffset);
    }

    //Fade Effect.
    if (this._fadeSpeed > 0) {
        if (ul.css("opacity") == 1) ul.css("opacity", 0);
        ul.animate({ opacity: 1 }, this._fadeSpeed, null, function() {
            //Remove opacity style to fix a bug with ie.
            ul.css("opacity", null);
            ul.siblings("a:first").addClass(menu._activeCssClass);
            this.status = eMenuStatus.shown;
        });
    }
    else
        ul.get(0).status = eMenuStatus.shown;
}

dropDownMenu.prototype.setMenuHidden = function(li) {
    var ul = $("> ul", li);
    var menu = this;
    
    //Hide Menu.
    switch (ul.get(0).status) {
        case eMenuStatus.shown:
            //Start hiding timer.
            this.clearTimer(ul);
            this.setTimer(ul, function() { menu.hideMenu(ul); }, this._hideDelay);
            break;
        case eMenuStatus.showing:
            //Stop showing and start hiding.
            this.clearTimer(ul)
            ul.stop();
            this.hideMenu(ul);
            break;
        case eMenuStatus.hidden:
            //Already hidden, so just stop any timers.
            this.clearTimer(ul);
            break;
        case eMenuStatus.hiding:
            this.clearTimer(ul);
            //Hiding, do nothing.
            break;
    }
}

dropDownMenu.prototype.hideMenu = function(ul) {
    //Remove the active css class.
    ul.siblings("a:first").removeClass(this._activeCssClass);

    //Hide the menu.
    if (this._fadeSpeed > 0) {
        //Fade effect.
        ul.get(0).status = eMenuStatus.hiding;
        ul.animate({ opacity: 0 }, this._fadeSpeed, null, function() {
            ul.css("left", "-999em");
            this.status = eMenuStatus.hidden;
        });
    }
    else {
        //No fade effect.
        ul.css("left", "-999em");
        ul.get(0).status = eMenuStatus.hidden;
    }
}

dropDownMenu.prototype.clearTimer = function(ul) {
    //Check to see if the timer exists and stop it.
    if (ul.get(0).timer) {
        clearTimeout(ul.get(0).timer);
        ul.get(0).timer = null;
    }
}

dropDownMenu.prototype.setTimer = function(ul, code, delay) {
    //Start a timer.
    ul.get(0).timer = setTimeout(code, delay);
}
