Resources/public/js/jquery.clickmenu.js
changeset 53 22377c9e2eae
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/public/js/jquery.clickmenu.js	Wed Dec 07 15:06:35 2011 +0100
@@ -0,0 +1,515 @@
+/* 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(){
+			$(this).find('>a').each(function(){
+				if ( this.href )
+				{
+					window.location = this.href;
+				}
+			});
+		},
+		arrowSrc: '',
+		subDelay: 300,
+		mainDelay: 10
+	};
+
+	$.fn.clickMenu = function(options) 
+	{
+		var shown = false;
+		var liOffset = ( ($.browser.msie) ? 4 : 2 );
+
+		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
+					$(getAllChilds(getOneChild(div, 'UL'), 'LI')).unbind('mouseover', liHoverIn).unbind('mouseout', liHoverOut).unbind('click', settings.onClick);
+					//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
+					$(getAllChilds(getOneChild(div, 'UL'), 'LI')).mouseover(liHoverIn).mouseout(liHoverOut).click(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 ( $.browser.msie ) //fixing a display-bug in ie6 and adding min-width
+					{
+						var cW = $(getOneChild(div, 'UL')).width();
+						if ( cW < 100 )
+						{
+							cW = 100;
+						}
+						$(div).css('width', cW);
+					}
+					div.timer = null;
+				}, delay);
+			}
+		};
+
+		//same as hover.handlehover in jquery - just can't use hover() directly - need the ability to unbind only the one hover event
+		var testHandleHover = function(e)
+		{
+			// Check if mouse(over|out) are still within the same parent element
+			var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+			// Traverse up the tree
+			while ( p && p != this )
+			{
+				try
+				{ 
+					p = p.parentNode;
+				}
+				catch(e)
+				{ 
+					p = this;
+				}
+			}
+			// If we actually just moused on to a sub-element, ignore it
+			if ( p == this )
+			{
+				return false;
+			}
+			return true;
+		};
+		
+		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
+			var lis = getAllChilds(this.parentNode, 'LI');
+			var pattern = new RegExp("(^|\\s)hover(\\s|$)");
+			for (var i = 0; i < lis.length; i++)
+			{
+				if ( pattern.test(lis[i].className) )
+				{
+					$(lis[i]).removeClass('hover');
+				}
+			}
+			$(this).addClass('hover');
+			if ( shown )
+			{
+				hoverIn(this, settings.mainDelay);
+			}
+		};
+
+		var liHoverIn = function(e)
+		{
+			if ( !testHandleHover(e) )
+			{
+				return false;
+			}
+			if ( e.target != this )
+			{
+				//look whether the target is a direct child of this (maybe an image)
+				if ( !isChild(this, e.target) )
+				{
+					return;
+				}
+			}
+			hoverIn(this, settings.subDelay);
+		};
+
+		var hoverIn = function(li, delay)
+		{
+			var innerDiv = getOneChild(li, 'DIV');
+			//stop running timers from the other menus on the same level - a little faster than $('>*>div', li.parentNode)
+			var n = li.parentNode.firstChild;
+			for ( ; n; n = n.nextSibling ) 
+			{
+				if ( n.nodeType == 1 && n.nodeName.toUpperCase() == 'LI' )
+				{
+					var div = getOneChild(n, 'DIV');
+					if ( div && div.timer && !div.isVisible ) //clear show-div timer
+					{
+						clearTimeout(div.timer);
+						div.timer = null;
+					}
+				}
+			}
+			//is there a timer running to hide one of the parent divs? stop it
+			var pNode = li.parentNode;
+			for ( ; pNode; pNode = pNode.parentNode ) 
+			{
+				if ( pNode.nodeType == 1 && pNode.nodeName.toUpperCase() == 'DIV' )
+				{
+					if (pNode.timer)
+					{
+						clearTimeout(pNode.timer);
+						pNode.timer = null;
+						$(pNode.parentNode).addClass('hover');
+					}
+				}
+			}
+			//highlight the current element
+			$(li).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!)
+			$(li.parentNode.getElementsByTagName('DIV')).each(function(){
+				if ( this != innerDiv && this.isVisible )
+				{
+					hideDIV(this, delay);
+					$(this.parentNode).removeClass('hover');
+				}
+			});
+			//show the submenu, if there is one
+			if ( innerDiv )
+			{
+				showDIV(innerDiv, delay);
+			}
+		};
+
+		var liHoverOut = function(e)
+		{
+			if ( !testHandleHover(e) )
+			{
+				return false;
+			}
+			if ( e.target != this )
+			{
+				if ( !isChild(this, e.target) ) //return only if the target is no direct child of this
+				{
+					return;
+				}
+			}
+			//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 )
+			{
+				$(this).removeClass('hover');
+			}
+			else 
+			{
+				if ( !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 ( !shown )
+			{
+				$(this).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()
+		{
+			var div = getOneChild(this, 'DIV');
+			if ( div && div.isVisible ) //clicked on an open main-menu-item
+			{
+				clean();
+				$(this).addClass('hover');
+			}
+			else
+			{
+				hoverIn(this, settings.mainDelay);
+				shown = true;
+				$(document).bind('mousedown', checkMouse);
+			}
+			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;
+					}
+				});
+			}
+			if ( !vis )
+			{
+				clean();
+			}
+		};
+
+		var clean = function()
+		{
+			//remove timeout and hide the divs
+			$('ul.clickMenu div.outerbox').each(function(){
+				if ( this.timer )
+				{
+					clearTimeout(this.timer);
+					this.timer = null;
+				}
+				if ( this.isVisible )
+				{
+					$(this).hide();
+					this.isVisible = false;
+				}
+			});
+			$('ul.clickMenu li').removeClass('hover');
+			//remove events
+			$('ul.clickMenu>li li').unbind('mouseover', liHoverIn).unbind('mouseout', liHoverOut).unbind('click', settings.onClick);
+			$(document).unbind('mousedown', checkMouse);
+			shown = false;
+		};
+
+		var getOneChild = function(elem, name)
+		{
+			if ( !elem )
+			{
+				return null;
+			}
+			var n = elem.firstChild;
+			for ( ; n; n = n.nextSibling ) 
+			{
+				if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
+				{
+					return n;
+				}
+			}
+			return null;
+		};
+
+		var getAllChilds = function(elem, name)
+		{
+			if ( !elem )
+			{
+				return [];
+			}
+			var r = [];
+			var n = elem.firstChild;
+			for ( ; n; n = n.nextSibling ) 
+			{
+				if ( n.nodeType == 1 && n.nodeName.toUpperCase() == name )
+				{
+					r[r.length] = n;
+				}
+			}
+			return r;
+		};
+
+		var findParentWithClass = function(elem, searchTag, searchClass)
+		{
+			var pNode = elem.parentNode;
+			var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
+			for ( ; 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|$)");
+			if ( pattern.test(elem.className) )
+			{
+				return true;
+			}
+			return false;
+		};
+		
+		var isChild = function(elem, childElem)
+		{
+			var n = elem.firstChild;
+			for ( ; n; n = n.nextSibling ) 
+			{
+				if ( n == childElem )
+				{
+					return true;
+				}
+			}
+			return false;
+		};
+
+	    return this.each(function()
+		{
+			//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);
+				};
+			}
+			//add class
+			if ( !checkClass(this, 'clickMenu') )
+			{
+				$(this).addClass('clickMenu');
+			}
+			//add shadows
+			$('ul', this).shadowBox();
+			//ie6? - add iframes
+			if ( $.browser.msie && (!$.browser.version || parseInt($.browser.version) <= 6) )
+			{
+				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)"/>');
+				}
+			}
+			//assign events
+			$(this).bind('closemenu', function(){clean();}); //assign closemenu-event, through wich the menu can be closed from outside the plugin
+			//add click event handling, if there are any elements inside the main menu
+			var liElems = getAllChilds(this, 'LI');
+			for ( var j = 0; j < liElems.length; j++ )
+			{
+				if ( getOneChild(getOneChild(getOneChild(liElems[j], 'DIV'), 'UL'), 'LI') ) // >div>ul>li
+				{
+					$(liElems[j]).click(mainClick);
+				}
+			}
+			//add hover event handling and assign classes
+			$(liElems).hover(mainHoverIn, mainHoverOut).addClass('main').find('>div').addClass('inner');
+			//add the little arrow before each submenu
+			if ( settings.arrowSrc )
+			{
+				$('div.inner div.outerbox', this).before('<img src="' + settings.arrowSrc + '" class="liArrow" />');
+			}
+
+			//the floating list elements are destroying the layout..so make it nice again..
+			$(this).wrap('<div class="cmDiv"></div>').after('<div style="clear: both; visibility: hidden;"></div>');
+	    });
+	};
+	$.fn.clickMenu.setDefaults = function(o)
+	{
+		$.extend(defaults, o);
+	};
+})(jQuery);
+
+(function($)
+{
+	$.fn.shadowBox = function() {
+	    return this.each(function()
+		{
+			var outer = $('<div class="outerbox"></div>').get(0);
+			if ( $(this).css('position') == 'absolute' )
+			{
+				//if the child(this) is positioned abolute, we have to use relative positioning and shrink the outerbox accordingly to the innerbox
+				$(outer).css({position:'relative', width:this.offsetWidth, height:this.offsetHeight});
+			}
+			else
+			{
+				//shrink the outerbox
+				$(outer).css('position', 'absolute');
+			}
+			//add the boxes
+			$(this).addClass('innerBox').wrap(outer).
+					before('<div class="shadowbox1"></div><div class="shadowbox2"></div><div class="shadowbox3"></div>');
+	    });
+	};
+})(jQuery);
\ No newline at end of file