/*	Versie:					1.3
	Datum:					18 november 2004
	Auteur:					Koen Peters	
	Backwards compatible: 	t/m versie 1.1				
	Korte Beschrijving: 	Script waarmee een willkeurig aantal hoofdmenu's met een willekeurig aantal submenu's kan worden gebouwd
	Changes: 				instelling CONNECT_MENUS toegevoegd. 
	Dependencies:			DOMUtils.js			(moet compatible zijn met versie 1.0)
							findPosition.js		(moet compatible zijn met versie 1.0)
							cookieControl.js 	(moet compatible zijn met versie 1.1)
	
	Usage: De volgende dingen moeten worden ingesteld:
	1) 	In de onLoad van de pagina moet de volgende code worden opgenomen:	menuControl = new MenuControl(); menuControl.restoreMenuState();
		bv:
			<body id="primeline" onLoad="menuControl = new MenuControl(); menuControl.restoreMenuState();">

	2)	Ieder hoofdmenu moet worden gedefinieerd door het id van het containerelement een standaard prefix te geven (menu-) gevolgd door een uniek nummer 
		(oplopend beginnend bij 1, dus 1, 2, 3, etc). Ieder submenu van dit hoofdmenu heeft een anchor element binnen het containerelement van het hoofdmneu.
		Dit anchor is altijd een <A> tag. Iedere menuitem anchor moet als zodanig worden aangemerkt door het id van het element een standaard prefix te geven 
		(menuAnchor-), gevold door het id van het submenu dat moet worden geopend als op de anchor wordt geklikt. Dit id begint met het hoofdmenuvolgnr, gevolgd door
		een underscore (_) en dan het menunr. ook deze zijn oplopend, beginnend bij 1, bv 1_1, 1_2, etc)
		bv:
			<div id="menu-1">
				<a href="bla0.html" id="menuAnchor-1_1">test</a>
				<a href="bla1.html" id="menuAnchor-1_2">test</a>
			</div>
	
	3) Ieder submenu volgt een soortgelijk proces. Let erop dat de nummering goed staat ingesteld
		bv:
			<div id="menu-1_2">
				<a href="bla2.html" id="menuAnchor-1_2_1">test1.1</a>
				<a href="bla3.html">test1.2</a>
			</div>

			<div id="menu-1_2_1">
				<a href="bla4.html">test1.2.1</a>
				<a href="bla5.html">test1.2.2</a>
			</div>
			
	4) Zet de css definities goed voor ieder menu en menuitem.
	
	5) Zet de instellingen goed:
		- USE_FADE_EFFECT:			Wel of geen gebruik maken van het fade effect tijdens het zichtbaar maken van het submenu
									Opm: true of false. 
									Opm: Werkt alleen in IE.
		- FADE_EFFECT_DURATION:		De tijd die het moet duren voordat de fade is afgelopen. Dit is een duur in seconden. 
									Opm: Deze wordt alleen gebruikt als gebruikFadeEffect true is. 
									Opm: >= 0
		- USE_ROL_EFFECT:			Wel of geen gebruik maken van het vertical afrol effect. 
									Opm: true of false
		- ROL_EFFECT_SPEED:			De snelheid waarmee het roleffect moet plaatsvinden.
									Opm: Deze wordt alleen gebruikt als gebruikRolEffect true is. 
									Opm: >= 1 (traagste) en <= 100 (snelste)
		- HIDE_AFTER_TIMEOUT:		Wel of niet verbergen van het submenu na het verstrijken van een aantal seconden nadat het zichtbaar is gemaakt.
									Opm: true of false
		- TIMEOUT_TIME:				De tijd die moet verstrijken voordat een submenu weer wordt verborgen ndadat het zichtbaar is genmaakt.
									Opm: Deze wordt alleen gebruikt als verbergSubMenuNaTimeout true is. 
									Opm: >= 0
		- STORE_STATE:				Opslaan van de toestand van het menu, zodat deze kan worden hersteld na verversen van een pagina.
									Opm: Wanneer de state ook daadwerkelijk moet worden hersteld na een new request, dan moet in de onLoad van de body tag
										een aanroep naar restoreMenuState() staan.
									Opm: true of false
									Opm: Zet deze op false als je niet wil dat er gebruik wordt gemaakt van cookies.
		- BEHAVIOUR					De manier waarop het menu wordt gebruikt.
									0:	Wanneer showMenu wordt aangeroepen wordt het menu meteen uitgeklapt en opgeslagen in de state als uitgeklapt
									1:	(NOG NIET GEBOUWD) Wanneer showMenu wordt aangeroepen wordt het menu niet uitgeklapt, maar wel in de state als zodanig opgeslagen. Wanneer de pagina wordt
										ververst, dan klapt het menu alsnog uit.
		- EVENTTRIGGER				De manier waarop de menu's moeten verschijnen. 
									0: Het menu wordt geopend na een klik op het anchor.
									1: Het menu wordt geopend nadat de muis over de anchor wordt bewogen.
		- CONNECT_MENUS				Geeft aan of wanneer de eventtrigger van een hoofdmenu vuurt, alle hoofdmenu's daarop moeten reageren door hun submenu's in te klappen 
									of alleen het betreffende hoofdmenu
									true: Alle hoofdmenu's
									false: Alleen het hoofdmenu waarbinnen wordt gevuurt.

*/ 

var USE_FADE_EFFECT			= false;
var FADE_EFFECT_DURATION	= 0;
var USE_ROL_EFFECT			= false;
var ROL_EFFECT_SPEED		= 100;
var HIDE_AFTER_TIMEOUT		= false;
var TIMEOUT_TIME			= 2000;
var STORE_STATE				= true;
var BEHAVIOUR				= 0;
var EVENTTRIGGER			= 0;
var CONNECT_MENUS			= true;

/* Niets veranderen na deze tekst */
var MENU_PREFIX				= 'menu-';
var MENU_ANCHOR_PREFIX		= 'menuAnchor-';
var MENU_ENDPOINT_PREFIX	= 'menuEndpoint-';
var SUBMENU_DEVIDER			= '_';
var menuControl;

var HOOFDMENU				= 0;
var SUBMENU					= 1;

var EVENTTRIGGER_ARR		= ['onclick', 'onmouseover'];

var ADD						= 0;
var REMOVE					= 1;



/************************************************************/
/*						MENUCONTROL CLASS					*/
/************************************************************/

function MenuControl() {
	/* VELDEN. */
	this.hoofdmenus								= new Array();
	this.menuCache								= new Array(); // hierin wordt een referentie bijgehouden naar alle menu objecten in de hele pagina. Ieder menu object meldt zich hier zelf bij aan.
	this.restoringState							= false;
	
	/* METHODEN */
	MenuControl.prototype.closeHoofdMenu		= closeHoofdMenu;
	MenuControl.prototype.closeAll				= closeAllMenuControl;
	MenuControl.prototype.extractHoofdmenuNr	= extractHoofdmenuNr;
	MenuControl.prototype.extractMenuNr			= extractMenuNr;
	MenuControl.prototype.getMenuState			= getMenuState;
	MenuControl.prototype.init					= initMenuControl;
	MenuControl.prototype.restoreMenuState		= restoreMenuState;
	MenuControl.prototype.showMenu				= showMenuMenuControl;
	MenuControl.prototype.startTimeout			= startTimeoutMenuControl;
	MenuControl.prototype.stopTimeout			= stopTimeoutMenuControl;
	MenuControl.prototype.storeMenuState		= storeMenuState;
	MenuControl.prototype.subscribeMenu			= subscribeMenu;
	MenuControl.prototype.showMenuByMenunr		= showMenuByMenunrMenuControl;
	
	/* INITIALISATIE */
	this.init();
}

function closeHoofdMenu(menunr) {
	if (this.hoofdmenus.length > menunr) 
		this.hoofdmenus[menunr].closeAll();
}

function closeAllMenuControl() {
	for (var i=0; i<this.hoofdmenus.length; i+=1)
		this.closeHoofdMenu(i);
}

function extractHoofdmenuNr(menunr) {
	var nrs = menunr.split(SUBMENU_DEVIDER);
	if (nrs.length >=1) return nrs[0];
	else return null;
}

function extractMenuNr(caller) {
	if (caller.id) {
		// Het betreft een element 
		if (caller.id.indexOf(MENU_ANCHOR_PREFIX) == 0) return caller.id.substring(MENU_ANCHOR_PREFIX.length);
		else return caller.id.substring(MENU_PREFIX.length);
	} else {
		// Het betreft geen element. Het is gewoon de waarde zelf
		return caller;
	}
}

/* Retourneert een array van ids van submenu;s die zichtbaar moeten zijn volgens de opgeslagen state */
function getMenuState() {
	var arr = (readCookie("visibleMenuIds") == null? "": readCookie("visibleMenuIds")).split("-");	
	if (arr.length == 1 && arr[0] == "") {
		return new Array(0);
	} else {
		return arr;
	}
}

// Haalt alle hoofdmenu's op op deze pagina
function initMenuControl() {
	var i=1;
	for (;;) {
		var menuObj = document.getElementById(MENU_PREFIX + i);
		if (menuObj)
			this.hoofdmenus.push(new Menu(i, menuObj, HOOFDMENU));
		else
			break;
		i+=1;
	}	
}

function restoreMenuState() {
	var visibleMenuIds = this.getMenuState();
	// Aangeven dat het menu nu weer wordt opgebouwd na een refresh. Op deze manier kan worden voorkomen
	// dar alle effecten worden uitgevoed (dat vertraagd de boel alleen maar) en er kan voor worden gezorgd
	// dat menu's die worden uitgeklapt tijdens deze loop niet weer worden ingeklapt. (Dat kan gebeuren door
	// een verkeerde volgorde van ids. Een alternatief is dus sorteren van de lijst, maar dit is eenvoudiger.
	this.restoringState = true;
	for (var i=0; i < visibleMenuIds.length; i++) {
		this.showMenu(visibleMenuIds[i]);
	}
	this.restoringState = false;
	
	//this is for lasernederland only!!!!!!
	clearMenuInlineStyle();
}

function showMenuMenuControl(caller) {	
	this.showMenuByMenunr(this.extractMenuNr(caller));
}

function showMenuByMenunrMenuControl(menunr) {	
	var hoofdmenuNr	= this.extractHoofdmenuNr(menunr);
	for (var i=0; i < this.hoofdmenus.length; i++) {
		if (CONNECT_MENUS || i == (hoofdmenuNr - 1))
			this.hoofdmenus[i].showMenu(menunr);
	}
}

function startTimeoutMenuControl(caller) {
	var menunr = this.extractMenuNr(caller);
	var hoofdmenuNr	= this.extractHoofdmenuNr(menunr);
	if (this.hoofdmenus.length >= hoofdmenuNr)		
		this.hoofdmenus[hoofdmenuNr - 1].startTimeout(menunr);
}

function stopTimeoutMenuControl(caller) {
	var menunr = this.extractMenuNr(caller);
	var hoofdmenuNr	= this.extractHoofdmenuNr(menunr);
	if (this.hoofdmenus.length >= hoofdmenuNr)		
		this.hoofdmenus[hoofdmenuNr - 1].stopTimeout(menunr);
}

function storeMenuState(menuNr, actionType) {
	if (STORE_STATE) {
		var visibleMenuIds		= this.getMenuState();
		var newVisibleMenuIds	= new Array(visibleMenuIds.length + 1);
		var found				= false;
		for (var i=0; i < visibleMenuIds.length; i++) { 		
			found					= found || visibleMenuIds[i] == menuNr;
			newVisibleMenuIds[i]	= visibleMenuIds[i]; 
			if (actionType == REMOVE && visibleMenuIds[i] == menuNr)
				newVisibleMenuIds[i] = null;
		}
		if (actionType == ADD && !found)
			newVisibleMenuIds[visibleMenuIds.length + 1] = menuNr; 
		
		// Sla de toestand van het menu op. Als op een plek in het array null staat, dan wordt deze waarde niet opgeslagen. 
		var stateAsString = "";
		for (var i =0; i < newVisibleMenuIds.length; i++)
			stateAsString += newVisibleMenuIds[i] != null? (stateAsString != ""? "-": "") + newVisibleMenuIds[i]: "";
		createCookie("visibleMenuIds", stateAsString);
	}
}

function subscribeMenu(menu) {
	this.menuCache.push(menu);
	return this.menuCache.length - 1;
}


/************************************************************/
/*							MENU CLASS						*/
/************************************************************/

function Menu(menuNr, menuObj, menuType, gebruikFadeEffect, fadeEffectDuur, gebruikRolEffect, rolEffectSnelheid, verbergSubMenuNaTimeout, subMenuTimeoutTijd) {
	/* VELDEN */
	this.menuNr			= menuNr;
	this.menuObj		= menuObj;
	this.menuType		= menuType;

	this.menuPrefix		= MENU_PREFIX + menuNr;
	this.submenus		= new Array();
	this.rows			= new Array();
	this.menuAnchorObj;
	this.menuCacheId;	// Dit veld altijd uitlezen via getmenuCacheId();	
	this.verbergTimer;
	this.rolAfTimer;
	
	/* Indien niet gespecificeert, dan standaardwaarden instellen */
	this.gebruikFadeEffect			= gebruikFadeEffect? gebruikFadeEffect: USE_FADE_EFFECT;
	this.fadeEffectDuur				= fadeEffectDuur? fadeEffectDuur: FADE_EFFECT_DURATION;
	this.gebruikRolEffect			= gebruikRolEffect? gebruikRolEffect: USE_ROL_EFFECT;
	this.rolEffectSnelheid			= rolEffectSnelheid? rolEffectSnelheid: ROL_EFFECT_SPEED;
	this.verbergSubMenuNaTimeout	= verbergSubMenuNaTimeout? verbergSubMenuNaTimeout: HIDE_AFTER_TIMEOUT;
	this.subMenuTimeoutTijd			= subMenuTimeoutTijd? subMenuTimeoutTijd: TIMEOUT_TIME;	

	/* METHODEN */
	Menu.prototype.clearVerbergTimer= clearVerbergTimer;
	Menu.prototype.getMenuCacheId	= getMenuCacheId;
	Menu.prototype.getSubmenuHeight	= getSubmenuHeight;
	Menu.prototype.init				= initMenu;
	Menu.prototype.showMenu 		= showMenuMenu;
	Menu.prototype.closeAll 		= closeAllMenu;
	Menu.prototype.closeMenu 		= closeMenu;
	Menu.prototype.resetTimers 		= resetTimers;
	Menu.prototype.rolMenuAf 		= rolMenuAf;
	Menu.prototype.showRow 			= showRow;
	Menu.prototype.startTimeout		= startTimeoutMenu;
	Menu.prototype.stopTimeout		= stopTimeoutMenu;
	
	/* INITIALISATIE */
	this.init();
}

function clearVerbergTimer() {
	if (this.verbergTimer) clearTimeout(this.verbergTimer);
}

function getMenuCacheId() {
	if (!this.menuCacheId) 
		// Dit kan niet in de constructor, omdat het menuControl object dan nog niet is geinitialiseerd.
		this.menuCacheId = menuControl.subscribeMenu(this); 
	return this.menuCacheId;
}

function getSubmenuHeight() {
	var hoogte = 0;
	for (var i=0; i < this.rows.length; i++) 
		hoogte += this.rows[i].parentNode.offsetHeight;
	return 0;
}

// Haalt alle submenu's en menuregels binnen dit menu op.
function initMenu() {
	// 1) Alle submenu's bepalen
	var i=1;
	for (;;) {
		var submenuObj	= document.getElementById(this.menuPrefix + SUBMENU_DEVIDER + i);
		if (submenuObj) {
			this.submenus.push(new Menu(this.menuNr + SUBMENU_DEVIDER + i, submenuObj, SUBMENU));
		} else break;
		i++;
	}
	
	// 2) Alle rijen ophalen en opslaan
	this.rows = getElementsByTagNameFromStartNode('A', this.menuObj);
	if (!this.rows) 
		this.rows = new Array();

	// 3) Events aan het menuobject koppelen, zodat dichtklappen van de bijbehorende submenu's (on)gedaan kan worden gemaakt
	if (this.menuType != HOOFDMENU) {
		// onMouseOver
		var old1 = (this.menuObj[EVENTTRIGGER_ARR[EVENTTRIGGER]]) ? this.menuAnchorObj[EVENTTRIGGER_ARR[EVENTTRIGGER]] : function () {};
		this.menuObj[EVENTTRIGGER_ARR[EVENTTRIGGER]] = function () {old1(); menuControl.stopTimeout(this); };
		// onMouseOut
		var old2 = (this.menuObj.onmouseout) ? this.menuAnchorObj.onmouseout : function () {};
		this.menuObj.onmouseout = function () {old2(); menuControl.startTimeout(this); };
	}	

	// 4) Events aan het anchor dat bij dit menu hoort koppelen, zodat dit menu wordt uitgeklapt als er op het anchor wordt geklikt.
	this.menuAnchorObj = document.getElementById(MENU_ANCHOR_PREFIX + this.menuNr);
	// In het geval van type == HOOFDMENU zal er waarschijnlijk geen element gevonden worden.
	if (this.menuAnchorObj) {		
		// onMouseOver
		var old3 = (this.menuAnchorObj[EVENTTRIGGER_ARR[EVENTTRIGGER]]) ? this.menuAnchorObj[EVENTTRIGGER_ARR[EVENTTRIGGER]] : function () {};
		this.menuAnchorObj[EVENTTRIGGER_ARR[EVENTTRIGGER]] = function () {old3(); menuControl.showMenu(this); };
		// onMouseOut
		var old4 = (this.menuAnchorObj.onmouseout) ? this.menuAnchorObj.onmouseout : function () {};
		this.menuAnchorObj.onmouseout = function () {old4(); menuControl.startTimeout(this); };
	}
	
	// 5) Bepalen of er endpoints zijn voor dit hoofdmenuitem
	if (this.menuType == HOOFDMENU) {
		// Omdat de hoofdmenuitems altijd zichtbaar zijn en er dus altijd op kan worden geklikt, moet er in het geval van een
		// hoofdmenu zonder submenu een endPoint zijn gedefinieerd. Dit endpoint wil zoveel zeggen als: "Er is geen submenu, 
		// maar als er op mij wordt geklikt, dan moeten de andere submenu's worden afgesloten."
		this.menuEndpointObj = document.getElementById(MENU_ENDPOINT_PREFIX + this.menuNr);
		if (this.menuEndpointObj) {
			// onMouseOver
			var old5 = (this.menuEndpointObj[EVENTTRIGGER_ARR[EVENTTRIGGER]]) ? this.menuEndpointObj[EVENTTRIGGER_ARR[EVENTTRIGGER]] : function () {};
			this.menuEndpointObj[EVENTTRIGGER_ARR[EVENTTRIGGER]] = function () {old5(); menuControl.showMenu(this); };
		}
	}
}

function closeAllMenu() {
	for (var i=0; i<this.submenus.length; i+=1)
		this.submenus[i].closeAll();
	this.closeMenu();
}

function closeMenu() {	
	if (this.menuType != HOOFDMENU) {
		// Alleen niet hoofdmenu menu's kunnen worden afgesloten
		this.menuObj.style.display = 'none';
		
		// Alle a's onzichtbaar maken om ze straks zichtbaar te maken met een fade. Deze fade werkt met een DLL specifiek 
		// voor IE, en zorgt ervoor dat de oude inhoud van een element wordt gefaded naar de nieuwe inhoud van een element. 
		// In dit geval is de oude inhoud dus de onzichtbare a's en de nieuwe inhoud de zichtbare a's.
		for (var i=0; i < this.rows.length; i++) {
			this.rows[i].style.visibility = 'hidden';		
		}
		if (this.menuType != HOOFDMENU)
			menuControl.storeMenuState(this.menuNr, REMOVE);		
	}
}

function showMenuMenu(menunr) {
	this.resetTimers();
	if (BEHAVIOUR == 0) {
		if (menunr.indexOf(this.menuNr) != 0) {
			// Als de menuPrefix van dit menu niet voorkomt in het menunr dan is het blijkbaar 
			// geen parent in de menustructuur en dan moet ie dus worden afgesloten.			
			if (!menuControl.restoringState) 
				// Wanneer de state wordt gerestored, dan moeten alle tot nu toe zichtbare 
				// menu's zichtbaar blijven (zie menuControl.restoreState())
				this.closeMenu();
		} else if (menunr == this.menuNr || (menuControl.restoringState && this.menuType != HOOFDMENU))
			// Het betreft dit menu, of de state wordt gerestored. Omdat het noodzakelijk 
			// is dat de parent van een menu zichtbaar is op het moment dat een menu
			// zichtbaar wordt gemaakt (ivm plaatsbepaling), wordt in het geval van een 
			// restore iedere parent alvast zichtbaar gemaakt.
			this.rolMenuAf();
			
		// Alle eventuele submenu's ook verwittigen van de showMenu actie.		
		for (var i=0; i<this.submenus.length; i+=1)
			this.submenus[i].showMenu(menunr);		
	} else {
		// Nog te bouwen
	}
}

function resetTimers() {
	this.clearVerbergTimer();
	if (this.rolAfTimer) clearTimeout(this.rolAfTimer);	
}

function rolMenuAf() {
	menuControl.storeMenuState(this.menuNr, ADD);

	var x = findPosX(this.menuAnchorObj);
	var y = findPosY(this.menuAnchorObj);		
	
	this.menuObj.style.left		= x + 'px';
	this.menuObj.style.top		= y + 'px';
	this.menuObj.style.height	= '0';
	this.menuObj.style.overflow	= 'hidden'; /* clip werkt niet in Mozilla, dus vandaar deze constructie met overflow en height*/
	
	//uitgezet voor laser nederland!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	//if (this.menuObj.tagName == 'UL')
		//this.menuObj.style.display	= 'inline'; //block werkt niet voor UL elementen
	//else 
		this.menuObj.style.display	= 'block';
	// Deze is nu even uitgecommentarieerd omdat dat anders render fouten geeft bij Directa.
	//this.menuObj.style.filter	= 'progid:DXImageTransform.Microsoft.Fade(duration=' + this.fadeEffectDuur + ')';
	
	// Alle a's zichtbaar maken met een fade (fade werkt alleen in IE)
	if (this.gebruikFadeEffect && !menuControl.restoringState) 
		try { this.menuObj.filters[0].Apply(); } 
		catch (error) {/* DLL kan niet zijn geregistreerd, of niet bestaan */}
	
		for (var i=0; i<this.rows.length; i++) {
			this.rows[i].style.visibility = 'visible';		
		}
		if (this.gebruikFadeEffect && !menuControl.restoringState) 
		try { this.menuObj.filters[0].Play(); } 
		catch (error) {/* DLL kan niet zijn geregistreerd, of niet bestaan */}
	
	this.showRow(0);
}

function startTimeoutMenu(menunr) {
	if (this.verbergSubMenuNaTimeout) {
		for (var i=0; i<this.submenus.length; i++)
			this.submenus[i].startTimeout(menunr);		
		if (menunr.indexOf(this.menuNr) == 0) {
			// Als de menuPrefix van dit menu voorkomt in het menunr dan is het een parent in de menustructuur en dan moet ie dus ook verdwijnen als zijn kind moet verdwijnen			
			this.clearVerbergTimer();				
			this.verbergTimer = setTimeout('menuControl.menuCache[' + this.getMenuCacheId() + '].closeMenu()', this.subMenuTimeoutTijd);	
			// Dit menu zal weg gaan, dus het kan al als weg worden opgeslagen.
			if (this.menuType != HOOFDMENU)
				menuControl.storeMenuState(this.menuNr, REMOVE);
		}
	}
}

function stopTimeoutMenu(menunr) {
	if (this.verbergSubMenuNaTimeout) {
		for (var i=0; i<this.submenus.length; i++)
			this.submenus[i].stopTimeout(menunr);		
		if (menunr.indexOf(this.menuNr) == 0) {
			// Als de menuPrefix van dit menu voorkomt in het menuNr dan is het een parent in de menustructuur en dan mag hij als parent ook niet verdwijnen
			this.clearVerbergTimer();
			// Dit menu zal niet weg gaan, dus het kan al als niet weg worden opgeslagen.
			if (this.menuType != HOOFDMENU)
				menuControl.storeMenuState(this.menuNr, ADD);
		}
	}
}

function showRow(perc) {	
	if (perc < 100) {
		var hoogte					= this.getSubmenuHeight();
		perc						= perc + ((this.gebruikRolEffect && !menuControl.restoringState)? this.rolEffectSnelheid: 100);
		this.menuObj.style.height	= Math.round((perc / 100 ) * hoogte) + 'px';		
		this.rolluit				= setTimeout('menuControl.menuCache[' + this.getMenuCacheId() + '].showRow(' + perc + ')', 10);	
	}
}
