// eMenu, The BRS Elemental Menu Generator (8.24.11)
// Copyright Blue Ridge Solutions, Inc.  www.blueridges.com
//
// INSTRUCTIONS
// Select all menu items that trigger associated drop menus and call eMenu() (ex. $('.menu_item').eMenu(); )
// All main menu items/links that trigger a drop menu must be assigned an id of choice. (ex. <a id="nav_main" class="menu_item" >)
// All drop menus must be assigned the same id as the main item id prepended with sub_.  (ex. <div id="sub_nav_main" class="drop_menu">)
// Not all main menu items require a drop menu. Set no-link triggers to '#'.
// Default setup: see 3 examples above 

(function($){ 
	// A reference to the currently open menu
	$currentMenu = $();

	// Default options
	var defaults = {
		matchWidth : 0, //	//	// [1] will set the drop menu width to match the main menu outer width (padding included)
		
		// eMenu placement
		axis : 'y', 	//	//	// [x] for flyout menu (left/right) or [y] for drop menu (up/down)
		place : 'bottom,left', 	// [bottom,left] [top,right] placement of the menu relative to the trigger 
		
		// To Do
		parent : null, 	//	//	// [#id] parent container to use for certain fx or adjustments such as contain
		contain : 0,	//	//	// [1] will keep menu inside parent container's width
		
		// Time
		showSpeed : 200,	//	// [200] animation speed for showing menus 
		hideSpeed : 300, 	//	// [300] animation speed for hiding menus
		showDelay : 200,	//	// [200] delay to open the drop menu after hover
		hideDelay : 800, 	//	// [800] delay to close the drop menu after hover
		
		// FX
		fx : 'fade',	//	//	// [slide,fade][custom] use predefined animations or pass in (custom) animations
		showAnimation : {},	// [{opacity:1,height:100}] pass in custom animations (show)
		hideAnimation : {},	// [{opacity:0,height:0}] pass in custom animations (hide)
		
		showCallback : null,	// [function(){ alert('done'); }] pass in custom callback function
		hideCallback : function(){ $(this).hide(); } // [function(){ $(this).hide(); }] pass in custom callback function, hide() is required
	};
	
	// Functions
	var methods = {
		/**
		 * Initialize the plugin for a set of elements
		 */
		init: function(options) {
			return this.each(function() {
				var $this = $(this);
				
				// Decide which version of eMenu is being used
				if($this.hasClass('drop_menu') || $this.hasClass('eMenu')) { // preferred way [passed in menu]
					var $eMenu = $this;
					var trigger = $eMenu.attr('id');
					$trigger = $('#'+trigger.replace('sub_',''));
				} else { // old way [passed in 'possible' trigger]
					var $trigger = $this;
					var eMenu = $this.attr('id');
					$eMenu = $('.drop_menu[id=sub_'+eMenu+']');
					if($eMenu.length < 1) return;
				}
				
				if (!$eMenu || !$trigger) return;
				
				// If this element has already been init-ed, then skip it
				if ($eMenu.data('eMenu')) return;
				
				// Set up defaults
				var set = $.extend(true, {}, defaults, options);
				var menu_w = $eMenu.outerWidth();
				var menu_h = $eMenu.outerHeight();
				
				// Predefined FX
				if(set.fx != 'custom') {
					if(set.fx.search('fade')!=-1) { $.extend(set.showAnimation, {opacity:1}); $.extend(set.hideAnimation, {opacity:0}); }
					if(set.fx.search('slide')!=-1 && set.axis=='y') { $.extend(set.showAnimation,{height:menu_h}); $.extend(set.hideAnimation,{height:0}); }
					if(set.fx.search('slide')!=-1 && set.axis=='x') { $.extend(set.showAnimation,{width:menu_w}); $.extend(set.hideAnimation,{width:0}); }
				}
				
				// Trigger Variables
				var tHeight = $trigger.outerHeight();
				var tWidth = $trigger.outerWidth();
				var tPosition = $trigger.position();
				var tLeft = tPosition.left;
				var tTop = tPosition.top;
				var tBottom = tTop + tHeight;
				
				// Menu Size and Position
				if(set.matchWidth==1) $eMenu.css({width:tWidth+'px'});
				var mHeight = $eMenu.outerHeight();
				var mWidth = $eMenu.outerWidth();
				
				$eMenu.css({position:'absolute'});
				if(set.axis=='x'){ // Horizontal Menu
					if(set.place.search('left')!=-1) $eMenu.css({left:tLeft-mWidth+'px'});
					if(set.place.search('right')!=-1) $eMenu.css({left:tLeft+tWidth+'px'});
					if(set.place.search('bottom')!=-1) $eMenu.css({top:tBottom-mHeight+'px'});
					if(set.place.search('center')!=-1) $eMenu.css({top:(tBottom-mHeight)/2+'px'});
					if(set.place.search('top')!=-1) $eMenu.css({top:tTop+'px'});
				} else { // Vertical Menu
					if(set.place.search('left')!=-1) $eMenu.css({left:tLeft+'px'});
					if(set.place.search('center')!=-1) $eMenu.css({left:tLeft+(tWidth - mWidth)/2+'px'});
					if(set.place.search('right')!=-1) $eMenu.css({left:tLeft+(tWidth - mWidth)+'px'});
					if(set.place.search('bottom')!=-1) $eMenu.css({top:tBottom+'px'});
					if(set.place.search('top')!=-1) $eMenu.css({top:tTop-mHeight+'px'});
				}
				
				// when the menu is moused over, then keep the menu from being hidden
				$eMenu.bind('mouseover', function(){ clearTimer($(this)); });
				// when the trigger is moused over, then show the menu
				$trigger.bind('mouseover', trigger_onMouseOver);
				// when either the trigger or the menu is moused out of, then hide the menu
				$trigger.add($eMenu).bind('mouseout', onMouseOut);
				// when sibling <a> tags of the $trigger are hovered over, then hide this menu
				$trigger.siblings('a').bind('mouseover', function(){ if (!$(this).data('eMenu.menu')) $currentMenu.eMenu('hide'); });
				
				// Save references from one to the other
				$trigger.data('eMenu.menu', $eMenu);
				$eMenu.data('eMenu.trigger', $trigger);
				// Save the settings to the menu
				$eMenu.data('eMenu.settings', set);
				
				// Hide the menu
				$eMenu.css('display', 'none');
			});
		},
		
		show: function(){
			var $eMenu = $(this),
				set = $eMenu.data('eMenu.settings');
			
			// If another menu was open, then CLOSE IT
			$currentMenu.eMenu('hide',1);
			
			$currentMenu = $eMenu;
			clearTimer($eMenu);
			if($eMenu.hasClass('showing')) return;
			$eMenu.stop().css(set.hideAnimation).css('z-index',1).show().addClass('showing').animate(set.showAnimation, set.showSpeed, set.showCallback);
		},
		
		hide: function(hasten){
			var $eMenu = $(this),
				set = $eMenu.data('eMenu.settings');
			if (!set) return;
			
			//if(hasten==1) set.hideSpeed = set.hideSpeed/2;
			
			$eMenu.stop().css({'z-index':0}).removeClass('showing').animate(set.hideAnimation, set.hideSpeed, set.hideCallback);

			$currentMenu = $();
			clearTimer($eMenu);
		}
	};
	
	function trigger_onMouseOver() {
		var $trigger = $(this),
			$eMenu = $trigger.data('eMenu.menu'),
			set = $eMenu.data('eMenu.settings'),
			fn = function(){ $eMenu.eMenu('show'); };
		// If the menu is already open, don't open it again
		if ($currentMenu[0] == $eMenu[0]) return clearTimer($eMenu);
		// Open the menu in [settings.showDelay] seconds
		if (set.showDelay) setTimer($eMenu, fn, set.showDelay);
		else fn();
	}
	
	function onMouseOut() {
		var $this = $(this),
			$eMenu = $this.data('eMenu.settings') ? $this : $this.data('eMenu.menu'),
			set = $eMenu.data('eMenu.settings'),
			fn = function(){ $eMenu.eMenu('hide'); };
		if (set.hideDelay) setTimer($eMenu, fn, set.hideDelay);
		else fn();
	}
	
	function clearTimer($eMenu) {
		clearTimeout($eMenu.data('eMenu.timerId'));
	}
	
	function setTimer($eMenu, fn, delay) {
		// always clear the timer before setting it again
		clearTimer($eMenu);
		// set the new timer
		var timerId = setTimeout(fn, delay);
		$eMenu.data('eMenu.timerId', timerId);
	}

	$.fn.eMenu = function(method) {
		// If the passed in method is a valid method, then call it along with any further passed parameters (other than the method name, of course)
		if (methods[method]) return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		// If the method is an object or doesn't exist, then pass it on to init
		else if (typeof method === 'object' || !method) return methods.init.apply(this, arguments);
		// Otherwise the method isn't recognized, so throw an error
		else $.error('Method ' + method + ' is not a valid method of eMenu');
	}
})(jQuery);

