/* (c) 2005 Koen Peters */

var USE_FADE_EFFECT			= false;
var FADE_EFFECT_DURATION	= .5;
var USE_ROL_EFFECT			= true;
var ROL_EFFECT_SPEED		= 5;
var HIDE_AFTER_TIMEOUT		= true;
var TIMEOUT_TIME			= 1000;
var STORE_STATE				= false;
var BEHAVIOUR				= 0;
var EVENTTRIGGER			= 1;


/* Niets veranderen na deze tekst */
var MENU_PREFIX				= 'menu-';
var MENU_ANCHOR_PREFIX		= 'menuAnchor-';
var SUBMENU_DEVIDER			= '_';
var menuControl;

var HOOFDMENU				= 0;
var SUBMENU					= 1;

var EVENTTRIGGER_ARR		= ['onclick', 'onmouseover'];

var ADD						= 0;
var REMOVE					= 1;


function d(txt, sep) {
	document.getElementById('debug').childNodes[0].data += txt + (sep? sep: ' - ');
}
/************************************************************/
/*						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;
	
	/* 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++)
		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, null));
		else break;
		i++;
	}	
}

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;
}

function showMenuMenuControl(caller) {	
	var menunr =  this.extractMenuNr(caller);
	var hoofdmenuNr	= this.extractHoofdmenuNr(menunr);
	if (this.hoofdmenus.length >= hoofdmenuNr)
		this.hoofdmenus[hoofdmenuNr - 1].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 = 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, parent, gebruikFadeEffect, fadeEffectDuur, gebruikRolEffect, rolEffectSnelheid, verbergSubMenuNaTimeout, subMenuTimeoutTijd) {
	/* VELDEN */
	this.menuNr			= menuNr;
	this.menuObj		= menuObj;
	this.menuType		= menuType;
	this.parentMenu		= parent;
	this.height			= null;
	
	this.menuPrefix		= MENU_PREFIX + menuNr;
	this.submenus		= new Array();
	this.rows			= new Array();
	this.menuAnchorObj;
	this.menuCacheId;	// Dit veld altijd uitlezeb 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.setAnchorsOfMenu	= setAnchorsOfMenu;
	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();
	this.getSubmenuHeight();
}

function clearVerbergTimer() {
	if (this.verbergTimer) clearTimeout(this.verbergTimer);
}

/* Haalt alle elementen op waaraan een anchor hangt, die horen bij dit menu. Wat er in feite gebeurd id dat de subDOMtree van het menu wordt afgelopen, en alleen
 * die elementen worden toegevoegd waar een anchor id aan hangt en die niet bij een genestte menudefinitie horen.
 */
function setAnchorsOfMenu(node, beginNode, arr) {	
	if (node.nodeType == 1 || node.nodeType == 9) /* ELEMENT_NODE OF DOCUMENT_NODE */ {
		if (node.tagName == 'A')		
			arr.push(node);
		if (beginNode == node || !node.id || node.id.indexOf(MENU_PREFIX) != 0) {
			// 0) Doorzoeken als het de eerste node betreft.
			// 1) Alleen de subtree doorzoeken als dit geen subMenu is
			var children = node.childNodes;
			for (var i=0; i < children.length; i++) {				
				this.setAnchorsOfMenu(children[i], beginNode, arr);
			}
		}
	}	
}

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() {
	/* Er wordt van uit gegaan dat de menu-elementen onder elkaar staan */
	if (!this.totalHeight) {
		var tempHeight = 0;
		for (var i=0; i < this.rows.length; i++) 
			tempHeight += this.rows[i].offsetHeight; // Hoogte per rij bepalen
		this.totalHeight = tempHeight;
	}
	return this.totalHeight;
}

// Haalt alle submenu's op en menuregels binnen dit menu op.
function initMenu() {
	// 1) Alle rijen ophalen
	this.rows = new Array();
	this.setAnchorsOfMenu(this.menuObj, this.menuObj, this.rows);	
	
	// 2) Alle submenu's bepalen. Maximaal het aantal rijen in het menu	
	for (var i=1; i <= this.rows.length; i++) {
		var menuObj = document.getElementById(this.menuPrefix + SUBMENU_DEVIDER + i);		
		if (menuObj)
			this.submenus.push(new Menu(this.menuNr + SUBMENU_DEVIDER + i, menuObj, SUBMENU, this));
	}
	
	// 3) Events aan het menuobject koppelen
	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 de anchors koppelen
	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); };
	}
}

function closeAllMenu() {
	for (var i=0; i<this.submenus.length; i++)
		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';
		
		if (this.gebruikFadeEffect && !menuControl.restoringState) 
			// 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';		
	}
}

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.			
			if (this.menuObj.style.display == 'none' || this.menuObj.style.display == '')
				this.rolMenuAf();
		// Alle eventuele submenu's ook verwittigen van de showMenu actie.		
		for (var i=0; i<this.submenus.length; i++)
			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*/
	this.menuObj.style.display	= 'block';
	
	// Alle a's zichtbaar maken met een fade (fade werkt alleen in IE)
	if (this.gebruikFadeEffect && !menuControl.restoringState) {
		try { 
			this.menuObj.style.filter = 'progid:DXImageTransform.Microsoft.Fade(duration=' + this.fadeEffectDuur + ')';
			this.menuObj.filters[0].Apply(); 
		} catch (error) {/* DLL niet geregistreerd, of bestaat niet */}
		
		for (var i=0; i<this.rows.length; i++)
			this.rows[i].style.visibility = 'visible';		
		
		try { 
			this.menuObj.filters[0].Play();
		} catch (error) {/* DLL niet geregistreerd, of bestaat niet */}
	}
	if (this.parentMenu) {
		// De height van de het parent menu staat nog ingesteld op de hoogte die nodig is voor het uitrollen van dit menu. 
		// Het parentmenu moet zijn hoogte nu aanpassen aan de hoogte die dit menu gaat aannemen, anders is het menu
		// niet zichtbaar (overflow	= 'hidden'). Dit geldt overigens alleen bij een menu met genestte submenu's		
		this.parentMenu.menuObj.style.height = '';
		this.parentMenu.menuObj.style.overflow	= '';
	}
	this.showRow(0);
}

function startTimeoutMenu(menunr) {
	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) {
	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 (!this.gebruikRolEffect || menuControl.restoringState) 
		this.menuObj.style.height = '';
	else if (perc < 100) {
		perc						= perc + this.rolEffectSnelheid;
		this.menuObj.style.height	= Math.round((perc / 100 ) * this.getSubmenuHeight()) + 'px';		
		this.rolluit				= setTimeout('menuControl.menuCache[' + this.getMenuCacheId() + '].showRow(' + perc + ')', 10);	
	} 
}