/*-------------------------------------------------------------------- 
Scripts for creating and manipulating custom menus based on standard <ul> markup
Version: 3.0, 03.31.2009

By: Maggie Costello Wachs (maggie@filamentgroup.com) and Scott Jehl (scott@filamentgroup.com)
	http://www.filamentgroup.com
	* reference articles: http://www.filamentgroup.com/lab/jquery_ipod_style_drilldown_menu/
		
Copyright (c) 2009 Filament Group
Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
--------------------------------------------------------------------*/
var allUIMenus = [];
var mouseOverIsActive = true;

$.fn.fgmenu = function(options){
    var caller = this;
    var options = options;
    var m = new Menu(caller, options);	
    allUIMenus.push(m);
 
    $(this)
    .mouseover(function() {
        if(mouseOverIsActive == false)
            return false;
       
        if (m.menuOpen == false ) {
            m.showMenu();
        }
        else {
//            m.kill();
        };
        return false;
    });
};

function Menu(caller, options){
    var menu = this;
    var caller = $(caller);
    var container = $('<div class="fg-menu-container">'+options.content+'</div>'); // @note ui-widget ui-widget-content ui-corner-all
	
    this.getContainer = function() {
      return container;
    };
  
    this.menuOpen = false;
    this.menuExists = false;
	
    var options = jQuery.extend({
        content: null,
        width: 180, // width of menu container, must be set or passed in to calculate widths of child menus
        maxHeight: 180, // max height of menu (if a drilldown: height does not include breadcrumb)
        positionOpts: {
            posX: "left",
            posY: 'bottom',
            offsetX: 0,
            offsetY: 0,
            directionH: 'right',
            directionV: 'down', 
            detectH: true, // do horizontal collision detection  
            detectV: true, // do vertical collision detection
            linkToFront: false,
            detectVSub: true
        },
        showSpeed: 200, // show/hide speed in milliseconds
        callerOnState: 'ui-state-active', // class to change the appearance of the link/button when the menu is showing
        loadingState: 'ui-state-loading', // class added to the link/button while the menu is created
        linkHover: 'ui-state-hover', // class for menu option hover state
        linkHoverSecondary: 'li-hover', // alternate class, may be used for multi-level menus		
        // ----- multi-level menu defaults -----
        crossSpeed: 200, // cross-fade speed for multi-level menus
        crumbDefaultText: 'Choose an option:',
        backLink: true, // in the ipod-style menu: instead of breadcrumbs, show only a 'back' link
        backLinkText: 'Back',
        flyOut: false, // multi-level menus are ipod-style by default; this parameter overrides to make a flyout instead
        flyOutOnState: 'ui-state-default',
        nextMenuLink: 'ui-icon-triangle-1-e', // class to style the link (specifically, a span within the link) used in the multi-level menu to show the next level
        topLinkText: 'All',
        nextCrumbLink: 'ui-icon-carat-1-e',
        childLiTopPosition: -3
    }, options);
	
    var killAllMenus = function(){
        $.each(allUIMenus, function(i){
            if (allUIMenus[i].menuOpen) {
                allUIMenus[i].kill();
            };	
        });

        if( $('#main-navigation').find('.active') )
          $('#main-navigation').find('.active').removeClass('deactive');
    };

    this.kill = function(){
        caller
        .removeClass(options.loadingState)
        .removeClass('fg-menu-open')
        .removeClass(options.callerOnState);	
        container.find('li').removeClass(options.linkHoverSecondary).find('a').removeClass(options.linkHover);		
        if (options.flyOutOnState) {
            container.find('li a').removeClass(options.flyOutOnState);
        };	
        if (options.callerOnState) {
            caller.removeClass(options.callerOnState);
        };			
        if (container.is('.fg-menu-ipod')) {
            menu.resetDrilldownMenu();
        };
        if (container.is('.fg-menu-flyout')) {
            menu.resetFlyoutMenu();
        };	
        container.parent().hide();	
        menu.menuOpen = false;
        
        $(document).unbind('click', killAllMenus);
        $(document).unbind('keydown');        
        
        if( $('#main-navigation').find('.active') )
          $('#main-navigation').find('.active').removeClass('deactive');        
    };
	
    this.showLoading = function(){
        caller.addClass(options.loadingState);
    };

    this.showMenu = function(){
        killAllMenus();
        if( caller.hasClass('active') )
          caller.removeClass('deactive');
        else
          $('#main-navigation').find('.active').addClass('deactive');

        if (!menu.menuExists && caller.hasClass('fg-flyout-empty') == false) {
            menu.create()
        };
        caller
        .addClass('fg-menu-open')
        // .addClass(options.callerOnState);
        container.parent().show().click(function(){
            menu.kill();
            return false;
        })
        /* reset position if browser size was changed */
        .css({'left':caller.offset().left+5});
        /* show menu effect */
        container.hide().slideDown(options.showSpeed).find('.fg-menu:eq(0)');
        menu.menuOpen = true;
        caller.removeClass(options.loadingState);
        $(document).click(killAllMenus);
    };
	
    this.create = function(){	
        container.css({
            width: options.width
        }).appendTo('body').find('ul:first').not('.fg-menu-breadcrumb').addClass('fg-menu');
        // @note disable corners
        // container.find('ul, li a').addClass('ui-corner-all');
		
        // aria roles & attributes
        container.find('ul').attr('role', 'menu').eq(0).attr('aria-activedescendant','active-menuitem').attr('aria-labelledby', caller.attr('id'));
        container.find('li').attr('role', 'menuitem');
        container.find("li").hover(function(){
                  $(this).addClass("fg-li-hover");  
            },
            function()
            {
                $(this).removeClass("fg-li-hover");
            });
        container.find('li:has(ul)').attr('aria-haspopup', 'true').find('ul').attr('aria-expanded', 'false');
        container.find('a').attr('tabindex', '-1');
        container.find('a').hover(function(){
            $(this).addClass("fg-flyout-a-hover");
        },
        function(){
            $(this).removeClass("fg-flyout-a-hover");
        });
		
        // when there are multiple levels of hierarchy, create flyout or drilldown menu
        if (container.find('ul').size() > 1) {
            if (options.flyOut) { 
                menu.flyout(container, options); 
            }
            else {
                menu.drilldown(container, options); 
            }
        }
        else {
            container.find('a').click(function(){
                menu.chooseItem(this);
                return false;
            });
        };	
		
        if (options.linkHover) {
            var allLinks = container.find('.fg-menu li a');
            allLinks.hover(
                function(){
                    var menuitem = $(this);
                    $('.'+options.linkHover).removeClass(options.linkHover).blur().parent().removeAttr('id');
                    $(this).addClass(options.linkHover).focus().parent().attr('id','active-menuitem');
                },
                function(){
                    $(this).removeClass(options.linkHover).blur().parent().removeAttr('id');
                }
                );
        };
		
        if (options.linkHoverSecondary) {
            container.find('.fg-menu li').hover(
                function(){
                    $(this).siblings('li').removeClass(options.linkHoverSecondary);
                    if (options.flyOutOnState) {
                        $(this).siblings('li').find('a').removeClass(options.flyOutOnState);
                    }
                    $(this).addClass(options.linkHoverSecondary);
                },
                function(){
                    $(this).removeClass(options.linkHoverSecondary);
                }
                );
        };	

        menu.setPosition(container, caller, options);
        menu.menuExists = true;
    };
	
    this.chooseItem = function(item){
        menu.kill();
        // edit this for your own custom function/callback:
        $('#menuSelection').text($(item).text());	
        location.href = $(item).attr('href');
    };
};

Menu.prototype.flyout = function(container, options) {
    var menu = this;
	
    this.resetFlyoutMenu = function(){
        var allLists = container.find('ul ul');
        allLists.removeClass('ui-widget-content').hide();	
    };
	
    container.addClass('fg-menu-flyout').find('li:has(ul)').each(function(){
        var linkWidth = container.width();
        var showTimer, hideTimer;
        var allSubLists = $(this).find('ul');
        var containerMarginRight = $(this).css("marginRight").replace(/px/, '');
    
    /**
     * @note sub menu positions
     */
        
        var ieSubstract = $('html').hasClass('msie') && $('html').hasClass('msie8') ? 1 : 0;
        allSubLists.css({
            left: linkWidth-containerMarginRight-ieSubstract,
            top: options.childLiTopPosition, 
            width: linkWidth
        }).hide();

        $(this).find('a:eq(0)').addClass('fg-menu-indicator').html('<span>' + $(this).find('a:eq(0)').text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>').hover(
            function(){
                clearTimeout(hideTimer);
                var subList = $(this).next();
                var parentLI = $(this).parent();
                if (!fitVertical(subList, $(this).offset().top)) {
                    if(options.positionOpts.detectVSub)
                        subList.css({
                            top: 'auto', 
                            bottom: 0
                        });
                };
                if (!fitHorizontal(subList, $(this).offset().left + 100)) {
                    subList.css({
                        left: 'auto', 
                        right: linkWidth, 
                        'z-index': 999
                    });
                };
                showTimer = setTimeout(function(){
                    parentLI.addClass("fg-flyout-li-hover");
                    subList
                    // .addClass('ui-widget-content')
                    .show(options.showSpeed)
                    .attr('aria-expanded', 'true');	
                }, 300);	
            },
            function(){
                clearTimeout(showTimer);
                var subList = $(this).next();
                var parentLI = $(this).parent();
                hideTimer = setTimeout(function(){
                    parentLI.removeClass("fg-flyout-li-hover");
                                    
                    subList
                    // .removeClass('ui-widget-content')
                    .hide(options.showSpeed)
                    .attr('aria-expanded', 'false');
                }, 400);	
            }
            );

        $(this).find('ul a').hover(
            function(){
                clearTimeout(hideTimer);
                if ($(this).parents('ul').prev().is('a.fg-menu-indicator')) {
                    $(this).parents('ul').prev().addClass(options.flyOutOnState);
                }
            },
            function(){
                hideTimer = setTimeout(function(){
                    allSubLists.hide(options.showSpeed);
                    container.find(options.flyOutOnState).removeClass(options.flyOutOnState);
                }, 500);	
            }
            );	
    });
	
    container.find('a').click(function(){
        menu.chooseItem(this);
        return false;
    });
};

/* Menu.prototype.setPosition parameters (defaults noted with *):
	referrer = the link (or other element) used to show the overlaid object 
	settings = can override the defaults:
		- posX/Y: where the top left corner of the object should be positioned in relation to its referrer.
				X: left*, center, right
				Y: top, center, bottom*
		- offsetX/Y: the number of pixels to be offset from the x or y position.  Can be a positive or negative number.
		- directionH/V: where the entire menu should appear in relation to its referrer.
				Horizontal: left*, right
				Vertical: up, down*
		- detectH/V: detect the viewport horizontally / vertically
		- linkToFront: copy the menu link and place it on top of the menu (visual effect to make it look like it overlaps the object) */

Menu.prototype.setPosition = function(widget, caller, options) { 
    var el = widget;
    var referrer = caller;
        
    var dims = {
        refX: referrer.offset().left,
        refY: referrer.offset().top, // @note -15px fuer ausbildungsreif menu
        refW: referrer.getTotalWidth(),
        refH: referrer.getTotalHeight()
    };	
    var options = options;
    var xVal, yVal;
	
    var helper = $('<div class="positionHelper"></div>');
    var ieTopAdd = $('html').hasClass('msie') && $('html').hasClass('msie8') ? 32 : 0;
    helper.css({
        position: 'absolute', 
        left: dims.refX+5,
        top: dims.refY+ieTopAdd, 
        width: dims.refW, 
        height: dims.refH
    });
    helper.click(function()
    {
        mouseOverIsActive = false;
        window.location = caller.attr('href') !== undefined ? caller.attr('href') : caller.find('a').attr('href');
        return false;
    }); 
        
    //	helper.css({ position: 'absolute', left: dims.refX+5, top: dims.refY, width: 0, height: 0 });
    el.wrap(helper);
	
    // get X pos
    switch(options.positionOpts.posX) {
        case 'left':
            xVal = 0; 
            break;				
        case 'center':
            xVal = dims.refW / 2;
            break;				
        case 'right':
            xVal = dims.refW;
            break;
        default:
            xVal = options.positionOpts.posX;
            break;
    };
	
    // get Y pos
    switch(options.positionOpts.posY) {
        case 'top':
            yVal = 0;
            break;				
        case 'center':
            yVal = dims.refH / 2;
            break;				
        case 'bottom':
            yVal = dims.refH;
            break;
    };
	
    // add the offsets (zero by default)
    xVal += options.positionOpts.offsetX;
    yVal += options.positionOpts.offsetY;
	
    // position the object vertically
    if (options.positionOpts.directionV == 'up') {
        el.css({
            top: 'auto', 
            bottom: yVal
        });
        if (options.positionOpts.detectV && !fitVertical(el)) {
            el.css({
                bottom: 'auto', 
                top: yVal
            });
        }
    } 
    else {
        el.css({
            bottom: 'auto', 
            top: yVal
        });
        if (options.positionOpts.detectV && !fitVertical(el)) {
            el.css({
                top: 'auto', 
                bottom: yVal
            });
        }
    };
	
    // and horizontally
    if (options.positionOpts.directionH == 'left') {
        el.css({
            left: 'auto', 
            right: xVal
        });
        if (options.positionOpts.detectH && !fitHorizontal(el)) {
            el.css({
                right: 'auto', 
                left: xVal
            });
        }
    } 
    else {
        el.css({
            right: 'auto', 
            left: xVal
        });
        if (options.positionOpts.detectH && !fitHorizontal(el)) {
            el.css({
                left: 'auto', 
                right: xVal
            });
        }
    };

    // if specified, clone the referring element and position it so that it appears on top of the menu
    if (options.positionOpts.linkToFront) {
        referrer.clone().addClass('linkClone').css({
            position: 'absolute', 
            top: 0, 
            right: 'auto', 
            bottom: 'auto', 
            left: 0, 
            width: referrer.width(), 
            height: referrer.height()
        }).insertAfter(el);
    };
};


/* Utilities to sort and find viewport dimensions */

function sortBigToSmall(a, b) {
    return b - a;
};

jQuery.fn.getTotalWidth = function(){
    return $(this).width() + parseInt($(this).css('paddingRight')) + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('borderRightWidth')) + parseInt($(this).css('borderLeftWidth'));
};

jQuery.fn.getTotalHeight = function(){
    return $(this).height() + parseInt($(this).css('paddingTop')) + parseInt($(this).css('paddingBottom')) + parseInt($(this).css('borderTopWidth')) + parseInt($(this).css('borderBottomWidth'));
};

function getScrollTop(){
    return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
};

function getScrollLeft(){
    return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
};

function getWindowHeight(){
    var de = document.documentElement;
    return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
};

function getWindowWidth(){
    var de = document.documentElement;
    return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
};

/* Utilities to test whether an element will fit in the viewport
	Parameters:
	el = element to position, required
	leftOffset / topOffset = optional parameter if the offset cannot be calculated (i.e., if the object is in the DOM but is set to display: 'none') */
	
function fitHorizontal(el, leftOffset){
    var leftVal = parseInt(leftOffset) || $(el).offset().left;
    return (leftVal + $(el).width() <= getWindowWidth() + getScrollLeft() && leftVal - getScrollLeft() >= 0);
};

function fitVertical(el, topOffset){
    var topVal = parseInt(topOffset) || $(el).offset().top;
    return (topVal + $(el).height() <= getWindowHeight() + getScrollTop() && topVal - getScrollTop() >= 0);
};

/*-------------------------------------------------------------------- 
 * javascript method: "pxToEm"
 * by:
   Scott Jehl (scott@filamentgroup.com) 
   Maggie Wachs (maggie@filamentgroup.com)
   http://www.filamentgroup.com
 *
 * Copyright (c) 2008 Filament Group
 * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
 *
 * Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.  
 * Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
 * Demo: http://www.filamentgroup.com/examples/pxToEm/	 	
 *							
 * Options:  	 								
 		scope: string or jQuery selector for font-size scoping
 		reverse: Boolean, true reverses the conversion to em-px
 * Dependencies: jQuery library						  
 * Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
 *
 * Version: 2.0, 08.01.2008 
 * Changelog:
 *		08.02.2007 initial Version 1.0
 *		08.01.2008 - fixed font-size calculation for IE
--------------------------------------------------------------------*/

Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){
    //set defaults
    settings = jQuery.extend({
        scope: 'body',
        reverse: false
    }, settings);
	
    var pxVal = (this == '') ? 0 : parseFloat(this);
    var scopeVal;
    var getWindowWidth = function(){
        var de = document.documentElement;
        return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
    };	
	
    /* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size. 
		For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size. 	
		When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size) 
		to get an accurate em value. */
				
    if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
        var calcFontSize = function(){		
            return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
        };
        scopeVal = calcFontSize();
    }
    else {
        scopeVal = parseFloat(jQuery(settings.scope).css("font-size"));
    };
			
    var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
    return result;
};
