/*-------------------------------------------------------------------- 
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 allT1 = 0;
var allT2 = 0;

$.fn.menu = function(options){
	var caller = this;
	var options = options;

	var m = new Menu(caller, options);	
	allUIMenus.push(m);

	if (options.closer){
		$(this).hover(function(){
			m.killAll();
		});
	} else if (options.act=='hover'){
		$(this)
		.hover(
			function () {
				m.showMenu();
				allT1 = 1;
			},
			function () {
			}
		);
	} else {
		$(this)
		.click(function(){
			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>');
	this.menuOpen = false;
	this.menuExists = false;
	var options = jQuery.extend({
		content: null,
		act: 'click',
		width: 100, // 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
		},
		showSpeed: 200, // show/hide speed in milliseconds
		callerOnState: '', // class to change the appearance of the link/button when the menu is showing
		closer: false,
	// ----- multi-level menu defaults -----
		crossSpeed: 200, // cross-fade speed for multi-level menus
		backLink: true, // in the ipod-style menu: instead of breadcrumbs, show only a 'back' link
		flyOut: false // multi-level menus are ipod-style by default; this parameter overrides to make a flyout instead
	}, options);

	var killAllMenus = function(){
		$.each(allUIMenus, function(i){
			if (allUIMenus[i].menuOpen) { allUIMenus[i].kill(); };	
		});
	};
	
	this.killAll = function(){
		killAllMenus();
	}
	
	this.kill = function(){
		caller.removeClass('fg-menu-open').removeClass(options.callerOnState);	
		if (options.act=='hover'){
//			caller.css({backgroundImage: 'none', textDecoration: 'underline'});
			caller.removeClass('gl-hovered');
		}
		if (options.callerOnState) { caller.removeClass(options.callerOnState); };			
		if (container.is('.fg-menu-flyout')) { menu.resetFlyoutMenu(); };	
		container.parent().hide();	
		menu.menuOpen = false;
		$(document).unbind('click', killAllMenus);
		$(document).unbind('keydown');
		allT1 = 0;
		allT2 = 0;
	};

	this.showMenu = function(){
		killAllMenus();
		if (!menu.menuExists) { menu.create() };
		caller.addClass(options.callerOnState);
		container.parent().show().click(function(){menu.kill(); return false; });
		if (options.act=='hover'){
//			caller.css({backgroundImage: 'url(/images/bg_header.gif)', backgroundPosition: 'right top', textDecoration: 'none'});
			caller.addClass('gl-hovered');
		}
		if (options.act=='hover'){
			caller.mouseover(function(){allT1=1;allT2=0;});
			container.mouseover(function(){allT2=1;allT1=0;});
			caller.mouseout(function(){allT1=1;setTimeout(function(){allT1=0;},200)});
			container.mouseout(function(){allT2=1;setTimeout(function(){allT2=0;},200)});
			$(document).bind(
				'mouseover mouseout', function(){
					if (allT1==0 && allT2==0) {
						menu.kill();
						$(document).unbind('mouseover mouseout');
					}
				}
			);
		}
		menu.menuOpen = true;
		$(document).click(killAllMenus);
		$(document).keydown(function(event){
			var e;
			if (event.which !="") { e = event.which; }
			else if (event.charCode != "") { e = event.charCode; }
			else if (event.keyCode != "") { e = event.keyCode; }
			
			var menuType = ($(event.target).parents('div').is('.fg-menu-flyout')) ? 'flyout' : 'ipod' ;
			
			switch(e) {
				case 37: // left arrow 
					if (menuType == 'flyout') {
						$(event.target).trigger('mouseout');
					};
					
					if (menuType == 'ipod') {
						$(event.target).trigger('mouseout');
						if ($('.fg-menu-footer').find('a').size() > 0) { $('.fg-menu-footer').find('a').trigger('click'); };
						if ($('.fg-menu-header').find('a').size() > 0) { $('.fg-menu-current-crumb').prev().find('a').trigger('click'); };
						if ($('.fg-menu-current').prev().is('.fg-menu-indicator')) {
							$('.fg-menu-current').prev().trigger('mouseover');							
						};						
					};
					return false;
					break;
					
				case 38: // up arrow 
					return false;
					break;
					
				case 39: // right arrow 
					if ($(event.target).is('.fg-menu-indicator')) {						
						if (menuType == 'flyout') {
							$(event.target).next().find('a:eq(0)').trigger('mouseover');
						}
						else if (menuType == 'ipod') {
							$(event.target).trigger('click');						
							setTimeout(function(){
								$(event.target).next().find('a:eq(0)').trigger('mouseover');
							}, options.crossSpeed);
						};				
					}; 
					return false;
					break;
					
				case 40: // down arrow 
					return false;						
					break;
					
				case 27: // escape
					killAllMenus();
					break;
					
				case 13: // enter
					if ($(event.target).is('.fg-menu-indicator') && menuType == 'ipod') {							
						$(event.target).trigger('click');						
						setTimeout(function(){
							$(event.target).next().find('a:eq(0)').trigger('mouseover');
						}, options.crossSpeed);					
					}; 
					break;
			};			
		});
	};
	
	this.create = function(){	
		container.appendTo('body');
		container.find('a').click(function(){
			menu.chooseItem(this);
			return false;
		});
		menu.setPosition(container, caller, options);
		menu.menuExists = true;
	};
	this.chooseItem = function(item){
//		alert(item.html())
		if (options.act=='hover'){
			location.href = $(item).attr('href');
		} else {
			menu.kill();
		}
	};
};


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');		
		
		allSubLists.css({ left: linkWidth, width: linkWidth }).hide();
			
		$(this).find('a:eq(0)').html('<span>' + $(this).find('a:eq(0)').text() + '</span>');

	});
	
	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,
		ref_W: referrer.getTotalWidth(),
		ref_H: referrer.getTotalHeight(),
		refW: el.children('.d-content').width(),
		refH: el.children('.d-content').height()

	};	
	var options = options;
	var xVal, yVal;
	
	var helper = $('<div class="positionHelper"></div>');
	helper.css({ position: 'absolute', left: dims.refX, top: dims.refY, width: dims.refW, height: dims.refH });
	el.wrap(helper);
	
	el.css({ width: dims.refW, height: dims.refH });
	
	// get X pos
	switch(options.positionOpts.posX) {
		case 'left': 	xVal = 0; 
			break;				
		case 'center': xVal = dims.ref_W / 2;
			break;				
		case 'right': xVal = dims.ref_W;
			break;
	};
	
	// get Y pos
	switch(options.positionOpts.posY) {
		case 'top': 	yVal = 0;
			break;				
		case 'center': yVal = dims.ref_H / 2;
			break;				
		case 'bottom': yVal = dims.ref_H;
			break;
	};
	
	// add the offsets (zero by default)
	xVal += options.positionOpts.offsetX;
	yVal += options.positionOpts.offsetY;

	el.css({ bottom: 'auto', top: yVal });

	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('padding-right'), 10) + parseInt($(this).css('padding-left'), 10);
//	return $(this).width() + parseInt($(this).css('padding-right'), 10) + parseInt($(this).css('padding-left'), 10) + parseInt($(this).css('border-right-width'), 10) + parseInt($(this).css('border-left-width'), 10);
};

jQuery.fn.getTotalHeight = function(){
	return $(this).height() + parseInt($(this).css('padding-top'), 10) + parseInt($(this).css('padding-bottom'), 10);
//	return $(this).height() + parseInt($(this).css('padding-top'), 10) + parseInt($(this).css('padding-bottom'), 10) + parseInt($(this).css('border-top-width'), 10) + parseInt($(this).css('border-bottom-width'), 10);
};

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);
};
