/* author: ravcyntek */
// http://codecanyon.net/user/ravcyntek/portfolio
(function($){
	$.fn.mjMenu = function(userSettings){	
		
		var defaultSettings={openClick: false,
				  openClickMouseHoverOpen: false,
				  documentClickClose: false,
				  enableKeyboard: false,
				  submenuCloseBeforeOpen: false,
				  autoArrow: true,
				  openAnimation: "slide",
				  closeAnimation: "slide",
				  moveBoxAnimationOpen: "fade",
				  moveBoxAnimationClose: "fade",				  
				  delayClose: 300, 
				  closeSpeed: 400,
				  delayOpenSubmenu: 500,				  
				  openSpeed: 500,
				  mouseEnableDelay: 600,
				  moveBoxSpeed: 1000,
				  afterOpenFnc: function(){},
				  beforeOpenFnc: function(){},
				  afterCloseFnc: function(){},
				  beforeCloseFnc: function(){},
				  afterMoveBoxOpenFnc: function(){},
				  beforeMoveBoxOpenFnc: function(){},
				  afterMoveBoxCloseFnc: function(){},
				  beforeMoveBoxCloseFnc: function(){}				  
		};

		// Merge defaultSettings and userSettings				
		settings = $.extend({}, defaultSettings, userSettings);		
		menuItems="";
		submenuItems="";
		submenuParents="";
		movingBox="";
		selectedItem="";
		submenuSelected="";
		closeDelay="";
		zIndex=1;
		delayOpenSubmenu="";
		menuPositionTop=$(this).offset().top;
		menuPositionLeft=$(this).offset().left;
			
		//Find menu items & parents
		$(this).removeClass('nojs');
		menuItems=$(this).children('li').addClass('menuItem');		
		menuParents = $(this).children('li:has(ul),li:has(div)').addClass('parent');											
		
		//Find submenu items
		submenuItems=$(this).children('li').find('li').addClass('submenuItem');
		submenuParents = $(this).children('li').children('ul').find('li:has(ul)');
		
		//Enable arrow
		if(settings.autoArrow)
			addArrow();
							
		createMovingBox();					
		
		enableMouse();
		if(settings.enableKeyboard)
			enableKeyboard();
	};
	
	/**
	 * Function change arrow in Menu
	 * @param item
	 * @param bool 
	 * @return void
	 */
	
	function arrow(item,open){
		if(settings.autoArrow){
			if(item.is('.menuItem')){
				if(open)						
					item.children('a').children('.arrow').addClass('arrowHover');
				else				
					item.children('a').children('.arrow').removeClass('arrowHover');
			}
			else if(item.is('.submenuItem')){
				if(open)
					item.children('a').children('.varrow').addClass('varrowHover');
				else				
					item.children('a').children('.varrow').removeClass('varrowHover');
			}
		}			
	}	
	
	/**
	 * Function switch menu animation 
	 * @param item
	 * @param bool 
	 * @return void
	 */
	
	function caseAnimation(item,open){
	   var value = open ? settings.openAnimation :
	       settings.closeAnimation;
		switch(value){
		case "slide":
			animateMenuSlide(item,open);
		break;
		case "show":
			animateMenuShow(item,open);
		break;
		default:
			animateMenuFade(item,open);
		break;					
		}						
	}
	
	/**
	 * Function that triggers the after open callback
	 *
	 * @return void
	 */
	
	function afterOpen(item){
		settings.afterOpenFnc.call(item.parent());
	}
	
	/**
	 * Function that triggers the before open callback
	 *
	 * @return void
	 */		
	
	function beforeOpen(item){
		settings.beforeOpenFnc.call(item.parent());
	}
	
	/**
	 * Function that triggers the after close callback
	 *
	 * @return void
	 */		
	function afterClose(item){
		settings.afterCloseFnc.call(item.parent());
	}
	
	/**
	 * Function that triggers the after before callback
	 *
	 * @return void
	 */		
	function beforeClose(item){
		settings.beforeCloseFnc.call(item.parent());
	}
	
	
	/**
	 * Function that triggers the after move box open callback
	 *
	 * @return void
	 */		
	function afterMoveBoxOpen(){
		settings.afterMoveBoxOpenFnc.call();
	}

	/**
	 * Function that triggers the before move box open callback
	 *
	 * @return void
	 */		
	function beforeMoveBoxOpen(){
		settings.beforeMoveBoxOpenFnc();
	}
	
	/**
	 * Function that triggers the after move box close callback
	 *
	 * @return void
	 */		
	
	function afterMoveBoxClose(){
		settings.afterMoveBoxCloseFnc.call();
	}
	
	/**
	 * Function that triggers the before move box close callback
	 *
	 * @return void
	 */	
	
	function beforeMoveBoxClose(){
		settings.beforeMoveBoxOpenFnc();
	}		
	
	/**
	 * Animation where the menu show
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */
	
	function animateMenuShow(item,open){
		if(open){
			beforeOpen(item);
			item.show();
			afterOpen(item);
		}
		else{
			beforeClose(item);
			item.hide();
			afterClose(item);
		}
	}
	
	/**
	 * Animation where the menu slide
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */		
	
	function animateMenuSlide(item,open){
		if(open){
			beforeOpen(item);
			item.slideDown(settings.openSpeed,function(){
				afterOpen(item);
			});				
		}
		else{
			beforeClose(item);
			item.slideUp(settings.openSpeed,function(){
				afterClose(item);
			});				
		}
	}	
	
	/**
	 * Animation where the menu fade
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */		
	
	function animateMenuFade(item,open){
		if(open){
			beforeOpen(item);
			item.fadeIn(settings.openSpeed,function(){
				afterOpen(item);
			});				
		}
		else{
			beforeClose(item);
			item.fadeOut(settings.openSpeed,function(){
				afterClose(item);
			});				
		}
	}		

	
	/**
	 * Animation where the moveBox fade
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */		
	
	function moveBoxFade(item,open){
		if(open){
			beforeMoveBoxOpen(item);
			$(movingBox).hide();
			$(movingBox).stop(true,true);
			$(movingBox).css('top',item.position().top);
			$(movingBox).css('left',item.position().left);
			$(movingBox).css('width',item.width());				
			$(movingBox).fadeIn(settings.moveBoxSpeed,function(){afterMoveBoxOpen(item);});
		}
		else{
			beforeMoveBoxClose(item);				
			$(movingBox).stop(true,true).fadeOut(settings.moveBoxSpeed,function(){afterMoveBoxClose(item);});				
		}
	}
	
	/**
	 * Animation where the moveBox slide
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */		
	
	function moveBoxSlide(item,open){
		if(open){
			beforeMoveBoxOpen(item);
			$(movingBox).hide().stop(true,true);
			$(movingBox).css('top',item.position().top);
			$(movingBox).css('left',item.position().left);
			$(movingBox).css('width',item.width());				
			$(movingBox).slideDown(settings.moveBoxSpeed,function(){afterMoveBoxOpen(item);});
		}
		else{
			beforeMoveBoxClose(item);
			$(movingBox).stop(true,true).slideUp(settings.moveBoxSpeed,function(){afterMoveBoxClose(item);});				
		}
	}
	
	/**
	 * Animation where the moveBox show
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */		
	
	function moveBoxShow(item,open){
		if(open){
			beforeMoveBoxOpen(item);
			$(movingBox).css('top',item.position().top);
			$(movingBox).css('left',item.position().left);
			$(movingBox).css('width',item.width());
			$(movingBox).show();
		}
		else{
			beforeMoveBoxClose(item);
			$(movingBox).hide();
			afterMoveBoxClose(item);
		}			
	}
	
	/**
	 * Animation where the moveBox move
	 *
	 * @param item
	 * @param bool
	 * @return void
	 */		
	
	function moveBoxMove(item,open){
		if(open){			
			$(movingBox).show();
			$(movingBox).css('top',item.position().top);
			$(movingBox).stop().animate({width: item.width(),left: item.position().left},settings.moveBoxSpeed);
		}
		else				
		{
			$(movingBox).stop().animate({width: 0,left: menuPositionLeft},settings.moveBoxSpeed,function(){
				$(this).hide();
			});
		}	
	}
	
	/**
	 * Function switch moveBox animation 
	 * @param item
	 * @param bool 
	 * @return void
	 */		
	
	function moveBox(item,open){
		var value = open ? settings.moveBoxAnimationOpen : settings.moveBoxAnimationClose;
			switch(value){
			case "slide":
				moveBoxSlide(item,open);
			break;
			case "show":
				moveBoxShow(item,open);
			break;
			case "move":
				moveBoxMove(item,open);
			break;
			default: 
				moveBoxFade(item,open);
			break;
			}				
	}		
	
	/**
	 * Function close all submenu  
	 * @param item 
	 * @return void
	 */		
	
	function closeAll(item){
		var other = item.siblings();
		
		$.each(other, function(i, val) {
			closeMenu($(other[i]).find('.activeSubmenu'));
			if($(other[i]).is('.open'))
				closeMenu($(other[i]));
		});
	}
	
	/**
	 * Function open menu & submenu 
	 * @param item
	 * @return void
	 */		
	
	function openMenu(item,e){
		item.removeClass('toClose');
		if(item.is('.menuItem')&&!item.is('.open')){
			closeAll(item);
			if(selectedItem!=""&&!selectedItem.is(item)){
				clearTimeout(closeDelay);
				closeMenu(selectedItem.find('.open'));
				selectedItem.removeClass('open');
				closeMenu(selectedItem);
				moveBox(item,true);
			}
			else
				moveBox(item,true);
			submenuSelected="";
			selectedItem=item;
			
			var itemChildren=item.children('ul').length ? item.children('ul') : item.children('div');
			itemChildren.css('z-index',zIndex++);
			caseAnimation(itemChildren,true);
			item.addClass('open');
			arrow(item,true);
		}
		else if(item.is('.submenuItem'))
		{				
			submenuSelected=item;
			if(settings.delayOpenSubmenu){					
				item.addClass('activeSubmenu');
				item.siblings().removeClass('activeSubmenu');
				if(settings.submenuCloseBeforeOpen)
					closeAll(item);
				delayOpenSubmenu=setTimeout(function(){
					if(item.is('.activeSubmenu')){
						if(!settings.submenuCloseBeforeOpen)
							closeAll(item);
						item.children('ul').css('top',$(item).position().top);
						arrow(item,true);;
						item.children('ul').css('z-index',zIndex++);
						caseAnimation(item.children('ul'),true);
						item.addClass('open');
					}
				},settings.delayOpenSubmenu);
			}
			else{
				item.addClass('activeSubmenu');
				item.children('ul').css('top',$(item).position().top-1);
				closeAll(item);
				caseAnimation(item.children('ul'),true);
				item.addClass('open');
			}
		}						
	}
	
	/**
	 * Function open menu & submenu 
	 * @param item
	 * @return void
	 */		
	
	function closeMenu(item){
		if(item.is('.menuItem')&&!item.is('.open')){
			var itemChildren=item.children('ul').length ? item.children('ul') : item.children('div');
			caseAnimation(itemChildren);
			arrow(item);
			closeMenu(item.find('.activeSubmenu'));
		}
		else if(item.is('.submenuItem')){
			item.removeClass('open');
			caseAnimation(item.children('ul'));
			item.removeClass('activeSubmenu');
			arrow(item);				
		}
	}
	
	/**	
	 * Handle mouseIn action 
	 * @return void
	 */		
	
	function mouseIn(){
		if(settings.openClickMouseHoverOpen){
			$(menuItems).unbind('mouseover').bind('mouseover',mouseIn);
		}
		var obj=$(this);
		openMenu(obj);
	}
	
	/**	
	 * Handle mouseOut action 
	 * @return void
	 */		
			
	function mouseOut(){							
		var obj=$(this);
		obj.addClass('toClose');
		if(!obj.is('.submenuItem')){
			var toClose=obj.find('li.open');				
			closeDelay=setTimeout(function(){
				if(obj.is('.toClose')){
					obj.removeClass('open');
					moveBox(toClose);
					closeMenu(toClose);
					closeMenu(obj);
				}
			},settings.delayClose);
		}
	}
	
	/**
	 * Function add arrow element to navigation 
	 * @return void
	 */
	
	function addArrow(){
		var varrow = jQuery(document.createElement('span')).addClass('varrow');
		$(submenuParents).children('a').append(varrow);
		
		var arrow = jQuery(document.createElement('span')).addClass('arrow');
		$(menuParents).children('a').append(arrow);
	}
	
	/**
	 * Function create movingBox 
	 * @return void
	 */				
	
	function createMovingBox(){
		movingBox=document.createElement('div');		
		movingBox.setAttribute('class','movingBox');
		$(movingBox).css('left',menuPositionLeft);
		$('body').append(movingBox);
	}
	
	/**
	 * Next menu action (when keyboard is enable)
	 * @return void
	 */		
	
	function nextMenu(){
		if(selectedItem!="")
			mouseOut.call(selectedItem);
		if(selectedItem!=""&&selectedItem.next().length)			
			mouseIn.call(selectedItem.next());
		else
			mouseIn.call(menuItems.first());
		$(document).scrollTop(menuPositionTop);

	}

	/**
	 * Prev menu action (when keyboard is enable)
	 * @return void
	 */				
	
	function prevMenu(){
		if(selectedItem!="")
			mouseOut.call(selectedItem);
		if(selectedItem!=""&&selectedItem.prev().length)			
			mouseIn.call(selectedItem.prev());
		else
			mouseIn.call(menuItems.last());
		$(document).scrollTop(menuPositionTop);
	}

	/**
	 * Prev submenu action (when keyboard is enable)
	 * @return void
	 */				
	
	function prevSubmenu(){			
		if(selectedItem!=""){
			if(submenuSelected!=""&&submenuSelected.prev().length){
				mouseIn.call(submenuSelected.prev());
			}
			else if(submenuSelected!=""){
				mouseIn.call(submenuSelected.parent().children('li:last'));
			}
			else mouseIn.call(selectedItem.children('ul').children('li:last'));
		}
		$(document).scrollTop(submenuSelected.position().top);
	}
	
	/**
	 * Next submenu action (when keyboard is enable)
	 * @return void
	 */				
	
	function nextSubmenu(){
		if(selectedItem!=""){
			if(submenuSelected!=""&&submenuSelected.next().length){
				mouseIn.call(submenuSelected.next());
			}
			else if(submenuSelected!=""){
				mouseIn.call(submenuSelected.parent().children('li:first'));
			}
			else mouseIn.call(selectedItem.children('ul').children('li:first'));
		}
		$(document).scrollTop(submenuSelected.position().top);
	}
	
	/**
	 * Go to url (when keyboard is enable)
	 * @return void
	 */				
	
	function enter(){
		if(selectedItem!=""){
			if(submenuSelected!="")
				window.location = submenuSelected.children('a').attr("href");
			else
				window.location = selectedItem.children('a').attr("href");
				
		}
	}
	
	/**
	 * Key action
	 * @param event
	 * @return void
	 */				
	
	function keyEvent(event){
		disableAction();
		var char = event.keyCode || event.charCode;
		switch (char){
			case 38:
				prevSubmenu();
			break;
			case 37:
				if(submenuSelected!=""&&submenuSelected.parent().parent().length&&!selectedItem.is(submenuSelected.parent().parent())){
					closeMenu(submenuSelected);
					mouseIn.call(submenuSelected.parent().parent());	
				}	
				else					
					prevMenu();
			break;
			case 39:
				if(submenuSelected!=""&&submenuSelected.children('ul').length){
					mouseOut.call(submenuSelected);
					mouseIn.call(submenuSelected.children('ul').children('li:first'));
				}
				else
					nextMenu();
			break;
			case 40:
					nextSubmenu();
			break;
			case 13:
					enter();
			break;
			case 27:
					mouseOut.call(selectedItem);
			break;
		};
		setTimeout(function(){
			enableMouse();
		},settings.mouseEnableDelay);
	}
	
	/**
	 * Function unbind all action
	 * @return void
	 */		
					
	function disableAction(){
		$(menuItems).unbind();
		$(submenuItems).unbind();		
	}
	
	/**
	 * Function enable mouse in menu
	 * @return void
	 */				
	
	function enableMouse(){
		$(submenuItems).hover(mouseIn,mouseOut);
		if(!settings.openClick){
			if(!settings.documentClickClose)
				$(menuItems).hover(mouseIn,mouseOut);
			else{
				$(menuItems).hover(mouseIn,function(){});
				$(document).click(function(){mouseOut.call(selectedItem);});
			}					
		}				
		else {				
			$(menuItems).bind('click',mouseIn);
			if(settings.documentClickClose){
				$(menuItems).mouseleave(function(){$(this).addClass('toClose');});
				$(document).click(function(){
					if(selectedItem&&selectedItem.is('.toClose')){
						mouseOut.call(selectedItem);
						$(menuItems).unbind('mouseover');
					}
				});
			}
			else $(menuItems).bind('mouseleave',mouseOut);				
		}		
	}
	
	/**
	 * Function enable keyboard in menu
	 * @return void
	 */				
	
	function enableKeyboard(){				
		$(document).keydown(keyEvent);
	}
	
	/**
	 * Menu constructor
	 * @return void
	 */	
						
	return this;
})(jQuery);
