﻿/*
	v1.0.0, 28.07.2009

	©2009 Tanyel Dogan, alle Rechte vorbehalten
*/

if(Cms4d == undefined)var Cms4d = new Object();
if(! Cms4d.GetChildElements)
{
	Cms4d.ElementBuffer = function()
	{
		this.elmbuffer = new Object();
		this.AddNode = function(mynode)
		{
			//Die Elemente werden als Eigenschaften im globalen Object gespeichert.
			//Dadurch kann einfacher auf die dahinterstehenden Arrays zugegriffen werden.
			var nn = mynode.nodeName.toLowerCase();
			//Ist zu einem Elemetnamen noch kein Array im Object vorhanden, dann wird diese Eigeschaften zunächst erzeugt.
			if(! this.elmbuffer[nn])
			{
				this.elmbuffer[nn] = new Array();
			}
			this.elmbuffer[nn].push(mynode);
		}
		//Filtert aus allen Elementen in elmbuffer die Gruppe mit dem gewünschten nodeName heraus.
		this.GetNodeList = function(nn)
		{
			//Diese Funktion liefert auf jeden Fall zumindest ein leeres Array zurück.
			var nlist = new Array();
			var test = this.elmbuffer[nn];
			if(test != undefined)nlist = test;
			return(nlist);
		}
		//Liefert alle Gruppen in einem einzigen Array zurück
		this.GetAll = function()
		{
			var r = new Array();
			for(var g in this.elmbuffer)
			{
				var mylist = this.elmbuffer[g];
				if(mylist.length)
				{
					for(var i = 0; i < mylist.length; i++)
					{
						var myelm = mylist[i];
						r.push(myelm);
					}
				}
			}
			return(r);
		}
		this.toString = function()
		{
			var t = '';
			for(var i in this.elmbuffer)
			{
				var mylist = this.elmbuffer[i];
				t += i + ': ' + mylist.length + '\n';
			}
			return(t);
		}
	}
	Cms4d.GetChildElements = function(pnode, tn, deep)
	{
		if(tn == undefined)tn = '*';
		//max. bis zu einer Verschachtelungstiefe von 127.
		//Über die Tiefe kann gesteuert werden, bis zu welcher Verschachtelungstiefe Nachkommen rekursiv
		//geparst werden sollen. Dies beschleunigt das Parsen, wenn bekannt ist, dass die gesuchten Elemente
		//sich ziemlich nah unter pnode befinden. Der Rest der Struktur wird dann ignoriert.
		//Wird -1 übergeben, so wird die volle Tiefe ausgeschöpft.
		if(deep == undefined)deep = 1;
		if(deep < 1 || deep > 128)deep = 128;

		//Um mit dem Platzhalter * efizienter alle Nachkommen vom Typ Element
		//in einem Zug sammeln zu können wird an die rekursive Funktion ein Puffer-Objekt übergeben.
		//Alle gefunden Elemente werden dort in Arrays gruppiert s.o..
		var elmlist = new Cms4d.ElementBuffer();
		this.GetChildElements_traverse(pnode, tn, deep, elmlist);
		return(elmlist);
	}
	Cms4d.GetChildElements_traverse = function(pnode, tn, deep, elmlist, lcounter)
	{
		if(lcounter == undefined)lcounter = 0;
		lcounter++;

		for(var i = 0; i < pnode.childNodes.length; i++)
		{
			var cnode = pnode.childNodes[i];
			var cn = cnode.nodeName.toLowerCase();
			var ct = cnode.nodeType;
			//Nur Elemente
			if(ct == 1)
			{
				//Wenn gewünschter Elementname, dann in Liste aufnehmen.
				//Wahlweise kann auch mit dem Platzhalter * jedes Element in die Liste aufgenommen werden.
				//Dies vereinheitlicht für alle Browser die Funktion direkt unterhalb eines Elementes alle
				//Nachkommen (nodeType=1, also NUR Elemente, keine #text u.ä.). Dies ist vergleichbar
				//mit der all-Collection im IE.
				if(cn == tn || tn == '*')
				{
					elmlist.AddNode(cnode);
				}

				//Wenn deep=true dann rekursiv in allen descendants weitersuchen
				if(lcounter < deep)
				{
					//=== REKURSION ===
					Cms4d.GetChildElements_traverse(cnode, tn, deep, elmlist, lcounter);
				}
			}
		}

		lcounter--;
	}
}






Cms4d.Menu = {
	//gobaler fortlaufender ID-Zähler für alle zu steuernden Elemente.
	idcounter:new Date().getTime()

	//Globales Object, in dem alle Controls als Eigenschaten gepuffert werden.
	,Controls:new Object()

	,SetId:function(elm)
	{
		var new_id = '_' + Cms4d.Menu.idcounter++;
		elm.id += new_id;
		elm.Cms4dControlId = new_id;
	}
	,SetAllIds:function(elms)
	{
		//Alle Elemente erhalten einen forlaufenden duplikatsfreien ID-Zusatz.
		//Zudem wird die Kontrollid zum Element vermerkt. Diese Kontrollid wird zum globalen Cms4d.Menu.Controls-Object
		//hinzugefügt, um bei timergesteuerten Prozessen die Controls direkt evaulieren zu können ohne jedesmal
		//über document.getElementById das HTML-Eleent im DOM such zu müssen.s.u.
		var all_elms = elms.GetAll();
		for(var i = 0; i < all_elms.length; i++)
		{
			var new_id = '_' + Cms4d.Menu.idcounter++;
			var elm = all_elms[i];
			elm.id += new_id;
			elm.Cms4dControlId = new_id;
		}
	}

	,ParseDropdownMenu:function(containerid)
	{
		var cdiv = document.getElementById(containerid);
		if(cdiv != null)
		{
			//Es werden alle untergeordneten Elemente 1 Mal gesammelt. Die einzelnen Element-Gruppen können
			//können dann aus dem Puffer gefilter werden.
			var elms = Cms4d.GetChildElements(cdiv, '*', -1);
			Cms4d.Menu.SetAllIds(elms);

			var linodes = elms.GetNodeList('li');
			//Alle li-Elemente in Level-0 erhalten jeweils ein Steuerungsobjekt, die untergordneten
			//Element einen Verweis darauf.
			//BEACHTE: statt den browserspezifischen attachEvent-Implementierungen wird hier
			//die onmouseover/out-Eigenschaft des HTML-Elementes direkt auf eine Funktion gesetzt.
			//Dies vereinfacht zwar die Zuweisung von Event-handlern, da für alle Browser gleich,
			//jedoch kann so zu jedem Event nur ein handler definiert werden.
			for(var i = 0; i < linodes.length; i++)
			{
				var linode = linodes[i];
				var li_classname = linode.className.toLowerCase();
				if(li_classname == 'li_0')
				{
					var elm_ctrlid = linode.id;
					var new_control = new Cms4d.Menu.DropdownControl(linode, elm_ctrlid);
					//Nur wenn das Control erfolgreich erzeugt werden konnt.
					if(new_control.ctrl_ok == true)
					{
						//Control zu Element hinzufügen
						linode.Cms4dControl = new_control;

						//Die einmalige Kontrollid des Elementes/Controls wird im globalen Cms4d.Menu.Controls
						//Object als Eigenschaft hinzugefügt. So kann bei timergesteuerten Prozessen und event-Handlern
						//direkter auf auf das Control zugegriffen werden (s.u.).
						Cms4d.Menu.Controls[elm_ctrlid] = new_control;

						//Auf dem sub_pos DIV-Element sind 2 CSS-Klassen festgelegt, die es ausser im IE6
						//ermöglichen, dass das Menu auch bei Ausgeschaltetem JavaScript ein und ausblendet.
						//Damit die Menus bei eingeschaltetem JavaScript in allen Browsern identisch
						//funktionieren wird die 2. Klasse gelöscht, so das nur noch sub_pos
						//übrigbleibt. In der Folge wird für alle Browser das Ein- und Ausblenden der Untermenus
						//identisch durch das Control geregelt.
						new_control.sub_pos_div.className = 'sub_pos';

						//sub-Div als Beschneidungscontainer auf Grösse des Untermenus einstellen.
						new_control.calc_menu();
						with(new_control.sub_abs_div.style)
						{
							width = new_control.subw + 'px';
							height = new_control.ymin + 'px';
							overflow = 'hidden';
							//Visibility zuerst auf hidden setzen, damit die inneren Elemente zwar ausgemessen werden
							//können, aber nicht unter dem Registerbutton noch ein 1px hohes Stück davon zu sehen ist.
							//IE6 und andere Browser vertragen keine width/height-Angaben von 0px. Deshalb ist
							//die minimale Höhe des scrollcontainers 1px.
							visibility = 'hidden';
						}

						//Untermenu in den negativen Bereich setzen und dadurhc unsichtbar machen.
						//Dies dient als Startposition
						new_control.ul1.style.top = new_control.ymin + 'px';

//new_control.do_status();

						//Maushandler
						//linode.onmouseover = linode.Cms4dControl.on_mouseover;
						//linode.onmouseout = linode.Cms4dControl.on_mouseout;
						linode.onmouseover = Cms4d.Menu.Controls[elm_ctrlid].on_mouseover;
						linode.onmouseout = Cms4d.Menu.Controls[elm_ctrlid].on_mouseout;
					}
				}
			}

			elms = null;
		}//Ende cdiv!=null
	}//Ende ParseDropdownMenu

	,ParseTabBox:function(containerid, first_tabindex, blend_step, autoplay_delay)
	{
		var cdiv = document.getElementById(containerid);
		if(cdiv != null)
		{
			var elms = Cms4d.GetChildElements(cdiv, '*', -1);
			Cms4d.Menu.SetAllIds(elms);
			elms = null;

			var elm_controlid = cdiv.id;

			//Im Gegensatz zum DropdownControl bilden alle Tabs in diesem Container eine logische Gruppe.
			var new_control = new Cms4d.Menu.TabboxControl(cdiv, elm_controlid, first_tabindex, blend_step, autoplay_delay);
			//In globalem Objekt vermerken
			Cms4d.Menu.Controls[elm_controlid] = new_control;
		}//Ende cdiv!=0
	}//Ende ParseTabBox
}

Cms4d.Menu.TabboxControl = function(pelm, pctrlid, pfirst_tabindex, pblend_step, pautoplay_delay)
{
	this.elm = pelm;
	this.ctrlid = pctrlid;
	this.first_tabindex = pfirst_tabindex == undefined ? 0 : pfirst_tabindex;
	this.ctrl_ok = false;

	this.tablist = new Array();
	this.tcursor = 0;
	this.tcursor_max = 0;
	this.tabs_init = false;

	this.blend_step = pblend_step == undefined ? 20 : pblend_step;
	this.blend_counter = 0;
	this.blend_delay = 50;
	this.blend_timerid = null;
	this.front_tab = null;

	//Wird nichts oder 0 übergeben, so ist autoplay deaktiviert.
	if(pautoplay_delay == undefined)pautoplay_delay = 0;
	this.autoplay_delay = pautoplay_delay;
	//Das autoplay-Interval darf nicht kleiner als eine bestimmte Zeit sein, da sonst die tabs nie richtig
	//eingeblendet werden. Die Minimalzeit ergibt sich aus der Einblendschrittweite und Delay.
	this.autoplay_min_delay = this.blend_step * this.blend_delay;
	if(this.autoplay_delay > 0 && this.autoplay_delay < this.autoplay_min_delay)this.autoplay_delay = this.autoplay_min_delay;
	this.autoplay_timerid = null;

	//Registerbuttons und zugehörige Tabs suchen.
	//Für jeden Tab wird ein eigenes Object erzeugt, dass ihn Button und Tab zusammenfasst.
	this.TabObj = function(parent_control, pidx, pbutton, ptab)
	{
		//Ein Verweis auf das übergeordnete TabboxControl wird in jedem TabObj gespeichert.
		this.pcontrol = parent_control;
		this.idx = pidx;
		this.button = pbutton;
		this.tab = ptab;

		this.button.style.cursor = 'pointer';

		this.button_onclick = function(evt)
		{
			//BEACHTE: wid dieser Handler aufgerufen, so ist "this" das HTML-Element, NICHT dieses Object.
			//Deshalb muss auf das Control extra Bezug genommen werden.
			var ctrl = this.Cms4dControl;
			if(ctrl == null)return;
			var pctrl = ctrl.pcontrol;
			if(pctrl == null)return;
			//WICHTIG: autoplay auf jeden Fall deaktivieren sobald auf irgendeinem Tab onclick durch den Benutzer stattfindet.
			pctrl.autoplay_delay = 0;
			clearTimeout(pctrl.autoplay_timerid);
			//Wenn diese Registerkarte gerade die ative ist, dann ignorieren.
			//Andernfalls würde erneut ein Einblendvorgang gestartet.
			if(ctrl.idx == pctrl.tcursor)return;
			pctrl.move_cursor(ctrl.idx);
		}

		//Verweis auf dieses Objekt in den HTML-Elementen setzen
		this.button.Cms4dControl = this;
		this.tab.Cms4dControl = this;

		//Onclick-Handler wird direkt auf dem Element gesetzt. Dies wirkt in allen Browsern ohne attachEvent.
		this.button.onclick = this.button_onclick;

		this.toString = function()
		{
			var t = '';
			t += '\tpcontrol: ' + (this.pcontrol != null) + '\n';
			t += '\tidx: ' + this.idx + '\n';
			t += '\tbutton: ' + this.button + '\n';
			t += '\ttab: ' + this.tab + '\n';
			return(t);
		}
	}

	//Diese Funktion bewegt den Cursor um 1 vorwärts und aktiviert den nächsten Tab.
	//Wird new_index übergeben, so wird direkt zu diesem tab gesprungen.
	//Dies dient dazu die einzelnen Tabs auch automatisch im Loop wechseln zu können.
	this.move_cursor = function(new_index)
	{
		clearTimeout(this.autoplay_timerid);

		if(new_index == undefined)
		{
			this.tcursor++;
		}
		else
		{
			this.tcursor = new_index;
		}
		//Index-Loop
		if(this.tcursor < 0)this.tcursor = this.tcursor_max;
		if(this.tcursor > this.tcursor_max)this.tcursor = 0;

		//Tabs un-/sichtbar machen
		this.show_tabs();
//ausgabe.innerHTML = this.toString();
	}
	this.show_tabs = function()
	{
		clearTimeout(this.blend_timerid);

		//Die Funktion bezieht sich immer auf tcursor als den Tab, der aktuell aktiv sein soll.
		//Alle anderen werden im z-Stapel nach unten gesetzt, NICHT ausgeblendet.
		//Dadurch können die Tabs weich ineinandergeblendet werden.
		//Hierbei ergibt sich aber das Problem, dass der vor dem Umschalten vorderste
		//Tab auch dann noch zu sehen sein soll, wenn der jetzt Einzublendene ganz nach vorne
		//gesetzt und langsam über den vorherigen eingeblendet werden soll.
		//Die Reihenfolge kann also nicht starr durchgezählt werden. Der vor dem Umschalten
		//vorderste Tab wird vermerkt und im z-Index direkt hinter den neuen Vordergrundtab gesetzt.
		//Dadurch wird immer der Vorderste über den zuletzt Vorderen eingeblendet ohne dass es
		//Bildsprünge gibt.
		//Der Einfachheit halber werden alle Tabs vor dem Umschalten auf z-Index 0 gesetzt.
		//Dann die beiden relevante auf tcursor_max und tcursor_max - 1;

		//Alle zurücksetzen
		for(var i = 0; i < this.tablist.length; i++)
		{
			var mytab = this.tablist[i];
			mytab.tab.style.zIndex = 0;
			//Buttons deaktivieren
			mytab.button.className = 'tab_but';
			//Ist dieser Tab nicht der aktuell Vordere, so wird
			//seine Deckkraft auf 100% gesetzt. Dies soll verhindern, dass bei schnellem Umschalten
			//alle tabs nur teilweise transparent sind und dadurch der Seitenhintergrund durchscheint.
			//Der aktuell Vordere bleibt auf seiner aktuellen Transparenz stehen, damit es keine
			//Sprünge in der Sichtbarkeit gibt. Der neue Vordere wird weiter unten auf die neue Starttransparenz gesetzt.
			if(mytab != this.front_tab)
			{
				this.set_opacity(mytab.tab, 100);
			}
		}

		//Vorher vordersten Tab an zweithöchste Position setzen.
		//BEACHTE: front_tab ist nach der Initialisierung null bis zum Ersten Mal umgeschaltet wurde
		if(this.front_tab != null)
		{
			this.front_tab.tab.style.zIndex = this.tcursor_max - 1;
		}

		//Aktuellen Tab nach vorne setzen
		var act_tab = this.tablist[this.tcursor];
		with(act_tab.tab.style)
		{
			zIndex = this.tcursor_max;
			display = 'block';
		}
		//Button aktivieren
		act_tab.button.className = 'tab_but tab_but_jsact';

		//Neuen front_tab für nächstes Umschalten merken s.o.
		this.front_tab = act_tab;

		//Der erste Aufruf zum Umschalten erfolgt bei der Initialisierung des Controls.
		//Damit dann die erste Registerkarte sofort ohne Überblendung sichtbar wird, wird
		//nur überblendet, wenn das Flag false ist.
		if(this.tabs_init != true)
		{
			this.tabs_init = true;
			return;
		}

		//---

		this.blend_counter = 0;
		//aktuellen Tab komplett transparent machen
		this.set_opacity(act_tab.tab, this.blend_counter);
		//Einblenden starten
		this.do_blend();
	}
	this.do_blend = function()
	{
		clearTimeout(this.blend_timerid);
		this.blend_counter += this.blend_step;
		if(this.blend_counter > 100)this.blend_counter = 100;

		var act_tab = this.tablist[this.tcursor];
		this.set_opacity(act_tab.tab, this.blend_counter);

		//ggf. loop bis volle sichtbarkeit erreicht ist.
		if(this.blend_counter < 100)
		{
			var func_str = 'Cms4d.Menu.Controls.' + this.ctrlid + '.do_blend()';
			this.blend_timerid = setTimeout(func_str, this.blend_delay);
		}
		else if(this.autoplay_delay > 0)
		{
			//Ist das tab komplett sichtbar und autoplay aktiv, so wird der nächste Cursorschritt zeitverzögert ausgelöst.
			//Der cursor wird endlos geloopt s.o.
			var func_str = 'Cms4d.Menu.Controls.' + this.ctrlid + '.move_cursor()';
			this.autoplay_timerid = setTimeout(func_str, this.autoplay_delay);
		}
//ausgabe.innerHTML = this;
	}
	this.set_opacity = function(obj, a)
	{
		//Für style.opacity werden Werte zwische 0-1 benötigt, für filter.alpha Werte zwischen 0-100.
		//IE
		obj.style.filter = 'alpha(opacity=' + a + ')';
		//andere
		obj.style.opacity = (a / 100);
	}

	//---- Initialisierung ----
	//Buttons sammeln
	//BEACHTE: für die Gültigkeit des gesamten Controls muss zu jedem Button ein korrespondierens Tab vorhanden sein.
	//Sobald der erste Fehler auftritt ist das gesamte Control ungültig.
	this.tabs_ok = true;
	var linodes = Cms4d.GetChildElements(this.elm, 'li', 3).GetNodeList('li');
	for(var i = 0; i < linodes.length; i++)
	{
		var linode = linodes[i];
		var cn = linode.className.toLowerCase();
		if(cn.indexOf('tab_li') != -1)
		{
			//Button und Tab-Inhalt zu dieser Registerkarte suchen. Beide liegen direkt unterhalb des LI-Elementes
			var cnodes = Cms4d.GetChildElements(linode, '*', 1);
			var divnodes = cnodes.GetNodeList('div');
			var ulnodes = cnodes.GetNodeList('ul');
			if(divnodes.length == 0 || ulnodes.length == 0)
			{
				this.tabs_ok = false;
			}
			else
			{
				//Es wird erwartet, dass beide Element im Index 0 stehen und die entsprechende CSS-Klasse haben.
				var but_elm = divnodes[0];
				var tab_elm = ulnodes[0];
				var but_class = but_elm.className.toLowerCase();
				var tab_class = tab_elm.className.toLowerCase();
				if(but_class.indexOf('tab_but') != -1 && tab_class.indexOf('tab_cont') != -1)
				{
					var new_tab = new this.TabObj(this, this.tablist.length, but_elm, tab_elm);
					//Neues Tab Object zur globeln Liste hinzufügen
					this.tablist.push(new_tab);
				}
				else
				{
					this.tabs_ok = false;
				}
			}
		}
		if(this.tabs_ok == false)break;
	}
	if(this.tabs_ok == true)this.ctrl_ok = true;

	//ggf. Überlauf korrigieren
	this.tcursor_max = this.tablist.length - 1;
	if(this.first_tabindex < 0)this.first_tabindex = 0;
	if(this.first_tabindex >= this.tcursor_max)this.first_tabindex = this.tcursor_max;

	//Sind zu allen Reitern auch Tabs vorhanden werden die li-Klassen für die reine CSS-Funktionalität
	//gelöscht. Dann wird das Control in allen Browsern nur noch über das Control gesteuert.
	if(this.ctrl_ok == true)
	{
		for(var i = 0; i < linodes.length; i++)
		{
			var linode = linodes[i];
			linode.className = 'tab_li';
		}

		//Ersten Tab aktivieren. Dieser kann über die Parsefunktion bei der Initialisierung festgelegt werden.
		this.move_cursor(this.first_tabindex);
	}

	//Wenn autoplay != 0 ist, dann werden die Tabs solange automatisch im Loop
	//durchgewechselt, bis der Benutzer zum ersten Mal manuell einen Tab anklickt.
	//Die Funtkion move_cursor ist so ausgelegt, dass sie bei fehlendem Indexparameter
	//den Cursor in einem Loop durchschreitet. s.o.
	//BEACHTE: der erste automatische Bildwechsel findet erst NACH dem ersten Interval statt.
	//Ausserdem wird diese Instanz des Controls erst im nächsten Programmschrit in ParseTabBox()
	//zur globalen Liste der Controls hinzugefügt. Es wird davon ausgegangen, dass bis zum Aufruf
	//des Timers dsa Control dort in der Liste verfügbar ist.
	if(this.ctrl_ok == true)
	{
		var func_str = 'Cms4d.Menu.Controls.' + this.ctrlid + '.move_cursor()';
		this.autoplay_timerid = setTimeout(func_str, this.autoplay_delay);
	}

	//---

	this.toString = function()
	{
		var t = '';
		t += 'ctrlid: ' + this.ctrlid + '\n';
		t += 'first_tabindex: ' + this.first_tabindex + '\n';
		t += 'autoplay_delay: ' + this.autoplay_delay + '\n';
		t += 'tcursor: ' + this.tcursor + '\n';
		t += 'tcursor_max: ' + this.tcursor_max + '\n';
		t += 'ctrl_ok: ' + this.ctrl_ok + '\n';
		t += 'tabs_ok: ' + this.tabs_ok + '\n';
		t += 'blend_counter: ' + this.blend_counter + '\n';
		t += 'front_tab: ' + this.front_tab + '\n';
		t += 'tablist:\n';
		for(var i = 0; i < this.tablist.length; i++)
		{
			var mytab = this.tablist[i];
			t += mytab + '\n';
		}
		return(t);
	}
}




Cms4d.Menu.DropdownControl = function(pelm, pctrlid)
{
	this.elm = pelm;
	this.ctrlid = pctrlid;
	this.ctrl_ok = false;

	//BEACHTE: die timerid wird über das HTML-Element gesteuert, so dass dieser auch
	//direkt in einem event-Handler ohne Control verfügbar ist.s.u.
	this.mouseout_delay = 100;
	this.elm.mouseout_timerid = null;

	//Jedes Control steuert autark das Auf- und Zuscrollen des submenus über einen eigenen timer.
	this.dir = 0;
	this.step = 20;
	this.subw = 0;
	this.subh = 0;
	this.ypos = 0;
	this.ymin = 1;
	this.ymax = 0;
	this.y = 0;
	this.movetimerid = null;
	this.movetimer_delay = 30;

	//---

	//A-Element in Level-0 des Registerbuttons ermitteln
	this.a0 = null;
	var anodes = Cms4d.GetChildElements(this.elm, 'a', 1).GetNodeList('a');
	for(var i = 0; i < anodes.length; i++)
	{
		var anode = anodes[i];
		var cn = anode.className.toLowerCase();
		//BEACHTE: die Registerkarten können noch mehrere Klassennamen im HTML class-Attribute haben,
		//z.B. um per CSS unterschiedliche Hintergrundgrafiken zu setzen.
		//Deshalb wird hier auf indexOf geprüft und nicht auf ==
		if(cn.indexOf('lvl_0') != -1)
		{
			this.a0 = anode;
			break;
		}
	}

	//Untergeordnete Container für ein und ausblenden ermitteltn
	this.sub_pos_div = null;
	this.sub_abs_div = null;
	this.sub_div = null;
	var divnodes = Cms4d.GetChildElements(this.elm, 'div', 3).GetNodeList('div');
	for(var i = 0; i < divnodes.length; i++)
	{
		var divnode = divnodes[i];
		var cn = divnode.className.toLowerCase();
		if(this.sub_pos_div == null && cn.indexOf('sub_pos') != -1)this.sub_pos_div = divnode;
		if(this.sub_abs_div == null && cn.indexOf('sub_abs') != -1)this.sub_abs_div = divnode;
		if(this.sub_div == null && cn.indexOf('sub_graph') != -1)this.sub_div = divnode;
	}

	//Erste UL mit Submenu
	this.ul1 = null;
	var ulnodes = Cms4d.GetChildElements(this.elm, 'ul', 4).GetNodeList('ul');
	for(var i = 0; i < ulnodes.length; i++)
	{
		var ulnode = ulnodes[i];
		var cn = ulnode.className.toLowerCase();
		if(cn == 'ul_1')
		{
			this.ul1 = ulnode;
			break;
		}
	}

	if(this.a0 != null && this.sub_pos_div != null && this.sub_abs_div != null && this.sub_div != null && this.ul1 != null)this.ctrl_ok = true;

	//---

	this.calc_menu = function()
	{
		//Die Abmessungen des ul-Elementes, dass das Untermenu enthält.
		//ACHTUNG: hierzu muss das Element sichtbar sein, da bei display:none immer 0 gemessen wird.
		this.subw = this.sub_div.offsetWidth;
		this.subh = this.sub_div.offsetHeight;

		//min/max
		this.ymax = this.subh;

		//aktuelle höhe des Beschneidungscontainers
		this.ypos = this.sub_abs_div.offsetHeight;
	}
this.do_status = function(s)
{
	var t = '';
	t += 'subw: ' + this.subw + '\n';
	t += 'subh: ' + this.subh + '\n';
	t += 'ymin: ' + this.ymin + '\n';
	t += 'ymax: ' + this.ymax + '\n';
	t += 'ypos: ' + this.ypos + '\n';
	t += 'y: ' + this.y + '\n';
	if(s != undefined)t += s;
	ausgabe.innerHTML = t;
}
	//BEACHTE: wird in diesem Objekt in Event-handlern auf 'this' Bezug genommen, so ergibt dies
	//einen Verweis auf das auslösende HTML-Element. Zur Steuerung muss aber auf das Cms4dControl
	//des aufrufenden HTML-Elementes Bezug genommen werden.
	this.on_mouseover = function(evt)
	{
		clearTimeout(this.mouseout_timerid);
		var ctrl = this.Cms4dControl;
		if(ctrl == null)return;

		ctrl.calc_menu();
//ctrl.do_status();

		//Immer Richtung auf Öffnen setzen. Falls ein Untermenu gerade zuklappt klappt es bei erneutem Mouseover wieder auf.
		ctrl.dir = 1;
		if(ctrl.ypos <= ctrl.ymin)
		{
			ctrl.y = ctrl.ypos;
			ctrl.move();
		}
	}
	this.move = function()
	{
		clearTimeout(this.movetimerid);
		this.calc_menu();

		var new_y = this.y + (this.dir * this.step);
		if(new_y < this.ymin)new_y = this.ymin;
		if(new_y > this.ymax)new_y = this.ymax;
		this.y = new_y;

		with(this.sub_abs_div.style)
		{
			//Sicherstellen, dass der Beschneidungscontainer immer so breit ist, wie das Untermenu
			width = this.subw + 'px';
			height = this.y + 'px';
			visibility = 'visible';
		}

		//Solange keine der beiden Grenzen erreicht ist wird
		//timergesteuert die Bewegung fortgesetzt.
		var func_str = '';
		if(this.y > this.ymin && this.y < this.ymax)
		{
			func_str = 'Cms4d.Menu.Controls.' + this.ctrlid + '.move()';
			this.movetimerid = setTimeout(func_str, this.movetimer_delay);
		}
		else
		{
			//Ist das Zusammnenrollen abgeschlossen wird der Container invisible gemacht, da sonst
			//immer noch ein 1px hohes Stück zu sehen ist. Es wird NICHT display:none verwendet, da
			//dann der Container uns seiner inneren Elemente beim Ausmessen nur noch 0-Werte ergeben.
			//Bei visibility hingegen nehmen sie ihren normalen Platz im Layout ein, werden aber nicht gezeichnet.
			if(this.y <= this.ymin)
			{
				this.sub_abs_div.style.visibility = 'hidden';
			}
		}
//this.do_status('func_str: ' + func_str + '\n');
	}

	this.on_mouseout = function(evt)
	{
		clearTimeout(this.mouseout_timerid);
		var ctrl = this.Cms4dControl;

		//BEACHTE: wird die Maus innerhalb eines Level-0 LI-Elemntes über untergeordnete Links
		//bewegt, so wird ständig mouseover/mouseout gefeuert. Deshalb wird das Untermenu nicht sofort
		//ausgeblendet, sondern zuerst bei onmouseover alle laufenden timer zu diesem Control gecancelt.
		//Hier bei onmouseout wird das Ausbleden zeitverzögert aktiviert.
		//Wird direkt im Anschluss an mouseout auf dem selben Control wieder mouseover gefeuert, so
		//hat der gestartete Timer keine Chance ausgeführt zu werden, da er ja wieder sofort gecancelt wird.
		//Erst wenn der letzte mouseout auf dem LI stattgefunden hat und die Maus komplett ausserhalb des Controls ist
		//hat der Timer eine Chance die Funktion aufzufrufen, die das Untermenu zu diesem Control ausblendet.
		//var func_str = 'document.getElementById("' + this.id + '").Cms4dControl.hide_sub()';
		//var func_str = 'Cms4d.Menu.Controls[' + this.Cms4dControlId + '].hide_sub()';
		var func_str = 'Cms4d.Menu.Controls.' + this.Cms4dControlId + '.hide_sub()';
//alert(func_str);
		this.mouseout_timerid = setTimeout(func_str, this.Cms4dControl.mouseout_delay);
	}
	this.hide_sub = function()
	{
		//BEACHTE: hier ist 'this' tatsächlich das Control selbst, nicht das HTML-Element.
		clearTimeout(this.elm.mouseout_timerid);

		this.dir = -1;

		//Falls das Untermenu schon komplett ausgeklappt ist dann Einklappen starten.
		this.move();
	}
}





function center_control(container_id)
{
	this.ctrlid = 'dropdown_' + new Date().getTime();
	this.timerid = null;
	this.container_id = container_id;
	this.container_div = null;
	this.pdiv = null;
	this.center_menu = function()
	{
		clearTimeout(this.timerid);
		this.container_div = document.getElementById(this.container_id);
		if(this.container_div != null)
		{
			this.pdiv = this.container_div.parentNode;
		}

		if(this.pdiv != null)
		{
			var pw = this.pdiv.offsetWidth;
			var cw = this.container_div.offsetWidth;
			var cx = Math.round((pw - cw) / 2);
			if(cx < 0)cx = 0;
			{
				this.container_div.style.left = cx + 'px';
			}
		}
	}
	document[this.ctrlid] = this;
	this.center_menu();
}

