/* clickMenu - v0.1.6
 * Copyright (c) 2007 Roman Weich
 * http://p.sohei.org
 *
 * Changelog: 
 * v 0.1.6 - 2007-09-06
 *	-fix: having a link in the top-level menu would not open the menu but call the link instead
 * v 0.1.5 - 2007-07-07
 *	-change/fix: menu opening/closing now through simple show() and hide() calls - before fadeIn and fadeOut were used for which extra functions to stop a already running animation were created -> they were 
 *			buggy (not working with the interface plugin in jquery1.1.2 and not working with jquery1.1.3 at all) and now removed
 *	-change: removed option: fadeTime
 *	-change: now using the bgiframe plugin for adding iframes in ie6 when available
 * v 0.1.4 - 2007-03-20
 *	-fix: the default options were overwritten by the context related options
 *	-fix: hiding a submenu all hover- and click-events were unbound, even the ones not defined in this plugin - unbinding should work now
 * v 0.1.3 - 2007-03-13
 *	-fix: some display problems ie had when no width was set on the submenu, so on ie the width for each submenu will be explicitely set
 *	-fix: the fix to the ie-width-problem is a fix to the "ie does not support css min-width stuff" problem too which displayed some submenus too narrow (it looked just not right)
 *	-fix: some bugs, when user the was too fast with the mouse
 * v 0.1.2 - 2007-03-11
 *	-change: made a lot changes in the traversing routines to speed things up (having better memory usage now as well)
 *	-change: added $.fn.clickMenu.setDefaults() for setting global defaults
 *	-fix: hoverbug when a main menu item had no submenu
 *	-fix: some bugs i found while rewriting most of the stuff
 * v 0.1.1 - 2007-03-04
 *	-change: the width of the submenus is no longer fixed, its set in the plugin now
 *	-change: the submenu-arrow is now an img, not the background-img of the list element - that allows better positioning, and background-changes on hover (you have to set the image through the arrowSrc option)
 *	-fix: clicking on a clickMenu while another was already open, didn't close the open one
 *	-change: clicking on the open main menu item will close it
 *	-fix: on an open menu moving the mouse to a main menu item and moving it fastly elsewere hid the whole menu
 * v 0.1.0 - 2007-03-03
 */

(function($) {
	var defaults = {
		onClick: function(e){
			$(this).children('a').each(function(){
				if (this.onclick_saved && this.onclick_saved() === false) {
					return;
				}
				if (this.href) {
					switch (e.button) { 
						case 0: 
							window.location = this.href;
							return false; 
							break 
						case 1: 
							if(this.dispatchEvent) {
								var evt = document.createEvent('MouseEvents');
								evt.initMouseEvent('click',false,true,document.defaultView,1,0,0,0,0,false,false,false,false,1,null);
								this.dispatchEvent(evt);
							}
							return true; 
							break 
						default: 
							return false; 
					} 
				}
			});
		},
		subDelay: 300,
		mainDelay: 10
	};

	$.fn.clickMenu = function(options) {
		var settings = $.extend({}, defaults, options);

		var hideDIV = function(div, delay) {
			//a timer running to show the div?
			if ( div.timer && !div.isVisible )
				clearTimeout(div.timer);
			else if (div.timer)
				return; //hide-timer already running
			if ( div.isVisible ) {
				div.timer = setTimeout(function() {
					//remove events
					$('ul:first>li', div).unbind('.clm');
					//hide it
					$(div).hide();
					div.isVisible = false;
					div.timer = null;
				}, delay);
			}
		};

		var showDIV = function(div, delay) {
			if ( div.timer )
				clearTimeout(div.timer);
			if ( !div.isVisible ) {
				div.timer = setTimeout(function() {
					//check if the mouse is still over the parent item - if not dont show the submenu
					if ( !checkClass(div.parentNode, 'hover') )
						return;
					//assign events to all div>ul>li-elements
					$('>ul:first>li', div).not(".unbinded")
						.bind('mouseenter.clm', liHoverIn).bind('mouseleave.clm', liHoverOut).bind('click.clm', settings.onClick);
					//positioning
					if ( !checkClass(div.parentNode, 'main') )
						$(div).css('left', div.parentNode.offsetWidth - liOffset);
					//show it
					div.isVisible = true; //we use this over :visible to speed up traversing
					$(div).show();
					if($(div).offset().left < 0) {
						$(div).css({
							"left": "-1px"
						});
					}
					/*if ( $.browser.msie ) { //fixing a display-bug in ie6 and adding min-width
						var cW = Math.max($(getOneChild(div, 'UL')).width(), 100);
						//$(div).css('width', cW);
					}*/
					div.timer = null;
				}, delay);
			}
		};

		
		var mainHoverIn = function(e) {
			//no need to test e.target==this, as no child has the same event binded
			//its possible, that a main menu item still has hover (if it has no submenu) - thus remove it
			$(this).children('li').removeClass('hover');
			if(!$(this).closest("ul.clickMenu").is(".disabled"))
				$(this).parent().andSelf().addClass('hover');
			if ( $(this).data('shown') )
				hoverIn(this, settings.mainDelay);
		};

		var liHoverIn = function(e) {
			if (e.target != this && e.target.parentNode != this) //look whether the target is a direct child of this (maybe an image)
				return;
			hoverIn(this, settings.subDelay);
		};

		var hoverIn = function(li, delay) {
			//stop running timers from the other menus on the same level - a little faster than $('>*>div', li.parentNode)
			var innerDiv, ul = li.parentNode, divs = ul.getElementsByTagName('div'), divs2 = [];
			for (var i = 0; i < divs.length; i++) {
				var div = divs[i];
				if (div.parentNode.parentNode == ul) {
					if (!innerDiv && div.parentNode == li)
						innerDiv = div;
					if (div.timer && !div.isVisible) {
						clearTimeout(div.timer);
						div.timer = null;
					}
					divs2.push(div);
				}
			}

			//is there a timer running to hide one of the parent divs? stop it
			for (var pNode = ul; pNode; pNode = pNode.parentNode ) {
				if ( pNode.nodeType == 1 && pNode.nodeName == 'DIV' ) {
					if (pNode.timer) {
						clearTimeout(pNode.timer);
						pNode.timer = null;
						$(pNode.parentNode).addClass('hover');
					}
				}
			}
			//highlight the current element
			if (!$(div).closest("ul.clickMenu").is(".disabled")) {
				$([li, ul]).addClass('hover');
			}
			//is the submenu already visible?
			if ( innerDiv && innerDiv.isVisible ) {
				//hide-timer running?
				if ( innerDiv.timer ) {
					clearTimeout(innerDiv.timer);
					innerDiv.timer = null;
				} else {
					return;
				}
			}
			//hide all open menus on the same level and below and unhighlight the li item (but not the current submenu!)
			for (var i = 0; i < divs2.length; i++) {
				div = divs2[i];
				if (div != innerDiv && div.isVisible) {
					hideDIV(div, delay);
					$(div.parentNode).removeClass('hover');
				}
			}
			//show the submenu, if there is one
			if (innerDiv && !$(div).closest("ul.clickMenu").is(".disabled"))
				showDIV(innerDiv, delay);
		};

		var liHoverOut = function(e) {
			if (e.target != this && e.target.parentNode != this)
				return; //return only if the target is no direct child of this
			//remove the hover from the submenu item, if the mouse is hovering out of the menu (this is only for the last open (levelwise) (sub-)menu)
			var div = getOneChild(this, 'DIV');
			if (!div || !div.isVisible)
				$(this).removeClass('hover');
		};

		var mainHoverOut = function(e) {
			//no need to test e.target==this, as no child has the same event binded
			//remove hover
			var div = getOneChild(this, 'DIV');
			var relTarget = e.relatedTarget || e.toElement; //this is undefined sometimes (e.g. when the mouse moves out of the window), so dont remove hover then
			var p;
			if ( !$(this).data('shown') ) {
				$(this).parent().andSelf().removeClass('hover');
			} else if ( !div && relTarget ) { //menuitem has no submenu, so dont remove the hover if the mouse goes outside the menu
				p = findParentWithClass(e.target, 'UL', 'clickMenu');
				if ( p.contains(relTarget))
					$(this).removeClass('hover');
			} else if ( relTarget )	{
				//remove hover only when moving to anywhere inside the clickmenu
				p = findParentWithClass(e.target, 'UL', 'clickMenu');
				if ( !div.isVisible && (p.contains(relTarget)) )
					$(this).removeClass('hover');
			}
		};

		var mainClick = function(e)	{
			var div = getOneChild(this, 'DIV');
			//clicked on an open main-menu-item
			if ( div && div.isVisible && (e.target.tagName=="A" || e.target.className=="unbinded")) {
				//if (!($(div) ))
				if (!$(e.target).next('div').hasClass('outerbox'))
					clean();
				$(this).parent().andSelf().addClass('hover');
			} else {
				if(e.target.className!="unbinded") {
					$(this).parent().andSelf().css('z-index', '99000');
					hoverIn(this, settings.mainDelay);
					$(this).data('shown', true);
					$(document).bind('mousedown.clm', checkMouse);
					$(document).trigger('clickmenushown', [this]);
				}
			}
			return false;
		};

		var checkMouse = function(e) {
			//is the mouse inside a clickmenu? if yes, is it an open (the current) one?
			var vis = false;
			var cm = findParentWithClass(e.target, 'UL', 'clickMenu');
			if ( cm ) {
				$(cm.getElementsByTagName('DIV')).each(function(){
					if ( this.isVisible ) {
						vis = true;
						return false; // break each
					}
				});
			}
			if ( !vis )
				clean();
		};

		var clean = function() {
			var $ul = $('ul.clickMenu');
			//remove timeout and hide the divs
			$ul.find('div.outerbox').each(function(){
				if ( this.timer ) {
					clearTimeout(this.timer);
					this.timer = null;
				}
				if ( this.isVisible ) {
					$(this).hide();
					this.isVisible = false;
				}
			});
			$ul.find('li').andSelf().removeClass('hover');
			//if(!$("body").has(".ie7")) {
				$ul.children('li').andSelf().css('z-index', ie8 ? 'auto' : '');
			//}
			//remove events
			$ul.children('li').find('li').unbind('.clm');
			$(document).unbind('.clm');
			$ul.children('li.main').data('shown', false);
			$(document).trigger('clickmenuhide');
		};
			
		return this.each(function()	{
			var $this = $(this);
			if ($this.data('clickMenu'))
				return;
			$this.addClass('clickMenu').data('clickMenu', 1);
			var $uls = $this.find('ul');
			$uls.wrap('<div class="outerbox" style="position:absolute"/>').addClass('innerBox');
			if (!$.browser.msie)
				$uls.before('<div class="shadowbox1"/><div class="shadowbox2"/><div class="shadowbox3"/>');
			//ie6? - add iframes
			if (ie6) {
				if ( $.fn.bgiframe )
					$('div.outerbox', this).bgiframe();
				else
					/* thanks to Mark Gibson - http://www.nabble.com/forum/ViewPost.jtp?post=6504414&framed=y */
					$('div.outerbox', this).append('<iframe style="display:block;position:absolute;top:0;left:0;z-index:-1;filter:mask();' + 
									'width:expression(this.parentNode.offsetWidth);height:expression(this.parentNode.offsetHeight)"/>');
			}
			//add click event handling, if there are any elements inside the main menu
			$this.find('a').each(function(){
				if (this.onclick) {
					this.onclick_saved = this.onclick;
					this.onclick = null;
				}
			});
			//add hover event handling and assign classes
			$this.children('li').click(mainClick).hover(mainHoverIn, mainHoverOut).addClass('main').children('div').addClass('inner');

			//the floating list elements are destroying the layout..so make it nice again..
			//$this.wrap('<div class="cmDiv"/>').after('<div style="clear: both; visibility: hidden; line-height: 0; zoom: 1;"/>');
			$uls.eq(0).css('display', 'block');
	    });
	};
	$.fn.clickMenu.setDefaults = function(o) {
		$.extend(defaults, o);
	};

	var ie6 = $.browser.msie && (!$.browser.version || parseInt($.browser.version) <= 6);
	var ie8 = $.browser.msie && /trident\/\d/i.test(navigator.userAgent);
	var liOffset = $.browser.msie ? 4 : 2;
	
	var getOneChild = function(elem, name) {
		if ( !elem )
			return null;
		for (var n = elem.firstChild; n; n = n.nextSibling )
			if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
				return n;
		return null;
	};

	var findParentWithClass = function(elem, searchTag, searchClass) {
		var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
		for (var pNode = elem.parentNode; pNode; pNode = pNode.parentNode )
			if ( pNode.nodeType == 1 && pNode.nodeName.toUpperCase() == searchTag && pattern.test(pNode.className) )
				return pNode;
		return null;
	};
	
	var checkClass = function(elem, searchClass) {
		var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
		return pattern.test(elem.className);
	};
	
	var isDescendantButNotSubitem = function(el, ch) {
		while (ch) {
			if (ch.nodeName == 'UL')
				return false;
			if (ch == el)
				return true;
			ch = ch.parentNode;
		}
		return false;
	};
	
	//add .contains() to mozilla - http://www.quirksmode.org/blog/archives/2006/01/contains_for_mo.html
	if (window.Node && Node.prototype && !Node.prototype.contains)
		Node.prototype.contains = function(arg) {
			return !!(this.compareDocumentPosition(arg) & 16);
		};
	
})(jQuery);

