/**
 *	Klasse:			Dropdown
 *	Datei:			dropdown.js
 *	Version:		2.0
 *	Beschreibung:	Macht aus einer verschachtelten Listenstruktur ein dynamisches Multi-Level-Dropdown-Menü
 *	Autor:			Dominic Meier
 *	Datum:			28.01.2010
 *	WICHTIG:		Funktioniert nur zusammen mit dem jQuery-Framework (http://jquery.com/)
 */

var Dropdown = function(root_node, user_options, url)
{
	
	// ----- Konstruktor -----------------------------------------------------------------------------
	var root;
	var hide_delay;
	var reaction_delay;
	var keyboard_control = false;
	var on_click_handlers = [];
	
	// Default-Werte aller Optionen.
	var options = {
		firstLevelHorizontal:	false,
		highlightClassName:		"highlighted",
		hoverClassName:			"hovered",
		levelClassName:			"submenu",
		firstLevelClassName:	"first_level",
		moreClassName:			"more",
		hideDelay:				1000,
		reactionDelay:			200,
		animationType:			"fade",
		animationDuration:		300,
		overlappingX:			10,
		overlappingY:			10,
		keyboardControl:		true
	};
	
	// Initialisiert das Dropdown-Menü.
	root = $(root_node);
	
	// Sofern eine URL übergeben wurde, lade die Navigationsstruktur vom Server.
	if (url) root.load(url + " ul:first");
	if (!root.find("ul").length) return;
	
	/*
		BEMERKUNG: Es folgt ein IE6-Hack! IE6 ist nicht fähig, zu viele <ul>-Verschachtelungen aufs Mal anzuzeigen.
		Deshalb werden im CSS-File zunächst alle <ul> dieser Navigation ausgeblendet. Folgende Zeile Code
		zeigt dann alle wieder der Reihe nach an, womit der IE6 offenbar zurecht kommt.
	*/
	$("ul", root).show();

	options = $.extend(options, user_options);
	set_up_structure();
	add_level_count(root.children("ul"), 1);
	flatten_out();
	set_up_handlers();
	
	
	// ----- Öffentliche Methoden --------------------------------------------------------------------
	
	this.onClick = function(func)
	{
		// Fügt einen neuen Event-Listener hinzu.
		on_click_handlers.push(func);
	};
	
	this.getHighlightedItems = function()
	{
		// Gibt ein Array zurück, welches Node-Referenzen auf die momentan markierten Links enthält.
		return root.find("a."+options.highlightClassName).get();
	};
	
	this.destroy = function()
	{
		// Entfernt die Navigation aus dem DOM.
		root.contents().remove();
	};
	
	
	// ----- Private Methoden ------------------------------------------------------------------------
	
	function set_up_structure()
	{
		// Weist jedem <a> das zugehörige Untermenü zu und umgekehrt.
		root.find("a").each(function(){
			var submenu = $(this).closest("li").find("ul").first();
			submenu.data("parent", $(this));
			$(this).data("submenu", submenu);
			if (submenu.length) $(this).addClass(options.moreClassName);
		});
	}
	
	function add_level_count(ul_list, level_count)
	{
		ul_list.each(function(){
			$(this).data("level", level_count);
			add_level_count($(this).children("li").children("ul"), level_count + 1);
		});
	}
	
	function flatten_out()
	{
		// Erstellt eine lineare Repräsentation aller Untermenüs.
		root.find("ul").css({position: "absolute", zIndex: 99999})
			.addClass(options.levelClassName)
			.data("visible", false)
			.hide().appendTo(root)
			// Das erste Menü muss immer sichtbar sein.
			.first().css({position: "relative", zIndex: 0})
				.data("visible", true)
				.addClass(options.firstLevelClassName).show();
	}
	
	function set_up_handlers()
	{
		// Event-Handler registrieren.
		$(document).click(function(e){
			// Bei Dokument-Klick oder ESC alle Untermenüs schliessen.
			if (root.find("ul, ul *").index($(e.target)) === -1) close_all();
		})
		.keydown((options.keyboardControl) ? on_key_pressed : $.noop)
		.mousemove(function(){
			keyboard_control = false;
		});
		
		root.find("a").click(on_click).mouseover(on_mouse_over).mouseout(on_mouse_out);
		root.find("ul").mouseleave(function(){
			clearTimeout(reaction_delay);
			hide_delay = setTimeout(function(){if (!keyboard_control) close_all();}, options.hideDelay);
		}).mouseenter(function(){
			clearTimeout(hide_delay);
		});
	}
	
	function close_all()
	{
		// Finde ein aktives <a>-Element auf oberster Ebene und schliesse dessen Untermenü.
		blur_all();
		clearTimeout(reaction_delay); clearTimeout(hide_delay);
		hide_submenu(root.find("a."+options.highlightClassName).first());
	}
	
	function on_key_pressed(e)
	{
		// Erlaube Tastatur-Navigation.
		if (e.which === 27) {close_all(); return;} // ESC
		var current = root.find("a."+options.hoverClassName).last();
		if (current.length)
		{
			keyboard_control = true;
			var on_first_level = current.closest("ul").data("level") === 1 && options.firstLevelHorizontal;
			var on_second_level = current.closest("ul").data("level") === 2 && options.firstLevelHorizontal;
			switch (e.which)
			{
				case 38: // Key Up
					if (on_second_level && current.closest("li").is(":first-child"))
						key_back(current);
					else if (!on_first_level)
						key_prev(current);
					break;
				case 40: // Key Down
					if (on_first_level) key_continue(current); else key_next(current); break;
				case 37: // Key Left
					if (on_first_level) key_prev(current); else if (!on_second_level) key_back(current); break;
				case 39: // Key Right
					if (on_first_level) key_next(current); else key_continue(current); break;
			}
		}
	}
	
	function on_mouse_over(e)
	{
		// Handler für das Event "onmouseover" der <a>-Elemente.
		blur_all();
		$(e.target).addClass(options.hoverClassName).focus();
		clearTimeout(reaction_delay); clearTimeout(hide_delay);
		
		if ($(this).closest("ul").data("visible"))
		{
			reaction_delay = setTimeout(function(){
				show_submenu($(e.target));
				$(e.target).closest("ul").find("a").each(function(){
					if (this != e.target) hide_submenu($(this));
				});	
			}, options.reactionDelay);
		}
	}
	
	function on_mouse_out(e)
	{
		// Deaktiviere diesen Menüpunkt.
		$(this).removeClass(options.hoverClassName);
	}
	
	function blur_all()
	{
		// Deaktiviere alle selektierten Menüpunkte.
		root.find("."+options.hoverClassName).mouseout();
	}

	function on_click(e)
	{
		// Handler für das Event "onclick" der <a>-Elemente.
		if (on_click_handlers.length)
		{
			e.preventDefault();
			$.each(on_click_handlers, function(){this(e.target);});
		}
	}
	
	function show_submenu(a_node)
	{
		// Untermenü anzeigen.
		var submenu_node = a_node.data("submenu");
		var pos = a_node.closest("li").offset();
		if (submenu_node.length)
		{
			a_node.addClass(options.highlightClassName);
			if (options.firstLevelHorizontal && !a_node.closest("ul").data("parent"))
			{
				// Wenn die erste Ebene horizontal ausgerichtet ist.
				submenu_node.css({top: pos.top + a_node.closest("li").outerHeight(true), left: pos.left});
			} 
			else
			{
				// Hat das Untermenü noch Platz im Browser-Fenster?
				var item_width = a_node.closest("li").outerWidth(true);
				var pos_left = pos.left + item_width;
				if ((pos_left + item_width) > $(window).width()) {
					pos_left = pos_left - 2*item_width;
				} else {
					pos_left -= options.overlappingX;
				}
				var pos_top = pos.top + options.overlappingY;
				var v_diff = $(window).height() - (pos_top + submenu_node.outerHeight(true));
				if (v_diff < 0) pos_top += v_diff - 15;
				submenu_node.css({top: pos_top, left: pos_left});
			}
			set_visible(submenu_node);
		}
	}
	
	function hide_submenu(a_node)
	{
		if (!a_node.is("a")) return;
		// Blendet alle momentan geöffneten Untermenüs aus (ausgehend von a_node).
		a_node.removeClass(options.highlightClassName);
		set_invisible(a_node.data("submenu").find("a").each(function(){hide_submenu($(this));}).end());
	}

	function key_prev(current){
		var next_target = current.closest("li").prev().find("a");
		if (next_target.length) next_target.mouseover();
	}
	
	function key_next(current){
		var next_target = current.closest("li").next().find("a");
		if (next_target.length) next_target.mouseover();
	}
	
	function key_continue(current){
		if (current.data("submenu").data("visible"))
		{
			var next_target = current.data("submenu").find("a").first();
			next_target.mouseover();
		}
	}
	
	function key_back(current){
		var next_target = current.closest("ul").data("parent");
		if (next_target.length)
		{
			hide_submenu(current);
			next_target.mouseover();
		}
	}
	
	function set_visible(submenu_node)
	{
		// Zeigt ein Untermenü grafisch an (evtl. mit Animation).
		submenu_node.data("visible", true).stop(true, true);
		switch (options.animationType)
		{
			case "fade":	submenu_node.fadeIn(options.animationDuration); break;
			case "slide":	submenu_node.show(options.animationDuration); break;
			default:		submenu_node.show();
		}
	}
	
	function set_invisible(submenu_node)
	{
		// Blendet ein Untermenü grafisch aus (evtl. mit Animation).
		submenu_node.data("visible", false).stop(true, true);
		switch (options.animationType)
		{
			case "fade":	submenu_node.fadeOut(options.animationDuration); break;
			case "slide":	submenu_node.hide(options.animationDuration); break;
			default:		submenu_node.hide();
		}
	}
};
