/*	file: core.js
	LJEX Basisklassen und Funktionen.
	© 2008-2010 t.schweda
*/
// {{{1 Dokumentation
/*
	Description:
	In dieser Datei sind alle essentiell notwendigen Bestandteile von LJEX definiert.	Mindestens diese Datei muss eingebunden werden, um LJEX überhaupt nutzen zu können.

	Provides:
	<ljex>, <ljex.Ajax>, <ljex.FX>, <ljex.locale>, <ljex.contextMenu>, <ljex.Window>

	Requires:
	json2.js
*/
// }}}1

/* namespace: ljex
  Basisklasse des gesamten Frameworks. Von dieser sind alle anderen abgeleitet.
*/
ljex = {
	main: "/ljex/main.php5",
	path: "/ljex",
	theme: 'lunacms',
	uniqueID: 1,
	lang: 'de'
};

/*	group: Global Functions */
// {{{1 Code
/*	function: $_
	Liefert einen statischen Text in der eingestellten Sprache aus einem ljex.locale Objekt.

	parameters:
	section (string) - Sektion (DB-Feld tx_section)
	variable (string) - Schlüssel für den Text (DB-Feld tx_var)

	returns:
	(string) - Text in eingestellter Sprache.
*/
function $_(section, variable){
	return ljex.locale[section][variable];
}


/* 	function: $
  	Wrapperfunktion für document.getElementById().

  	parameters:
  	id (string) - ID des HTML-Node-Objekts.

  	returns:
  	node (object) - Node-Objekt mit der angegebenen ID.
*/
function $(id) { return document.getElementById(id); }

/*	function: $create 
	Wrapperfunktion für document.createElement("div"). Liefert ein Node Objekt
	vom Typ "div"-Container.

	parameters:
	id (string) - optional. Wenn angegeben wird diese ID automatisch zugewiesen.

	returns:
	el (object) - Node Objekt.
*/
function $create(ident) {
	var el = document.createElement("div");
	if(ident!=undefined) el.id=ident;
	return el;
}

function $assimilate(args){
// {{{ Dokumentation
/*	function: $assimilate 
	Erzeugt automatisch Klasseneigenschaften aus den Schlüsseln und Werten eines JSON Arrays. Diese
	Funktion sollte möglichst zu Beginn einer Konstruktorfunktion aufgerufen werden und zwar mit dem Geltungsbereich
	der Objektinstanz (also mit call aufrufen).

	Beispiel:
	(code)
	$assimilate.call(this,{ id: '4711', name: 'Unbekannt' });
	(end)

	Dieser Aufruf fügt dem Objekt *this* die beiden Eigenschaften "id" und "name" hinzu und weist Ihnen die
	entsprechenden Werte zu.

	parameters:
	args (hash) - Schlüssel/Wert Paare, die die Eigenschaften darstellen (Schlüssel) und Ihnen Werte zuweist (Werte).
*/
// }}}
	for(key in args){
		this[key]=args[key];
	}	
}

/*	function: $genid
	Generiert eine eindeutige ID (Timestamp).

	returns:
	(int) - Timestamp als eindeutige ID.
*/
function $genid(){
	return new Date().getTime();
}

/*	function: $setdef
	Initialisiert Klasseneigenschaften mit default-Werten. Ebenso wie <$assimilate> muss auch diese
	Funktion im Geltungsbereich des Objekts aufgerufen werden (per call).

	parameters:
	defaults (hash) - JSON Array mit default-Werten.
*/
function $setdef(defaults){
	for(var key in defaults){
		if(this[key]==undefined) this[key]=defaults[key];
	}
}

/*	function: $shadow
	Erzeugt einen Text mit Schattenwurf.

	parameters:
	text (string) - Text der schattiert dargestellt werden soll.

	returns:
	(string) - HTML-String mit dem schattierten Text.
*/
function $shadow(text){
	var s = "<div class='lx-shadow'> <div class='lx-shadow-black'>"+text+" <div class='lx-shadow-white'>"+text+"</div> </div> </div>";
	return s;
}

/*	function: $alert
	Öffnet eine Alert Box. Diese Fkt. ist eine Wrapperfunktion für ein spezialisiertes
	<ljex.MessageBox> Objekt

	parameters:
	msg (string) - Die anzuzeigende Meldung.
	title (string) - Optionaler Titel.
*/
function $alert(msg,title){
	new ljex.MessageBox({
		msg: msg,
		title: title||"Info",
		type: ljex.MessageBox.INFO
	});
}

/*	function: $warn
	Öffnet eine Warnungs Alert Box. Diese Fkt. ist eine Wrapperfunktion für ein spezialisiertes
	<ljex.MessageBox> Objekt

	parameters:
	msg (string) - Die anzuzeigende Warnmeldung.
	title (string) - Optionaler Titel.
*/
function $warn(msg,title){
	new ljex.MessageBox({
		msg: msg,
		title: title||"Warn",
		type: ljex.MessageBox.WARN
	});
}

/*	function: $error
	Öffnet eine Fehler Alert Box. Diese Fkt. ist eine Wrapperfunktion für ein spezialisiertes
	<ljex.MessageBox> Objekt

	parameters:
	msg (string) - Die anzuzeigende Fehlermeldung.
	title (string) - Optionaler Titel.
*/
function $error(msg,title){
	new ljex.MessageBox({
		msg: msg,
		title: title||"Error",
		type: ljex.MessageBox.ERROR
	});
}

/* 	function: $addEvent
	Kapselt die browserspezifischen Unterschiede beim Hinzufügen von Event-Listenern.
	
	example:
	(code)
	var el = $create();
	$addEvent(el,"mouseclick",function(){
		alert("Hallo!");
	});
	(end)

	parameters:
	el (ptr)	-	HTML Element, für den ein neuer Listener registriert werden soll.
	ev (string)	-	Event-Bezeichner OHNE "on". Also "mouseclick" oder auch "mouseover".
	func (ptr)	-	Funktionspointer (Handlerfunktion, die bei Eintreten des Events aufgerufen wird)
*/
function $addEvent(el, ev, func){
	if(el.addEventListener){
		el.addEventListener(ev, func, false);
	}else if(el.attachEvent){
		el.attachEvent("on"+ev, func);
	}
}


/*	function: $loadScript
	Neues JavaScript während der Laufzeit einbinden. Scripts die über diese Funktion zur Laufzeit eingbunden werden MÜSSEN
	sich in einem Verzeichnis namens "scripts" befinden. Das Script wird im Header der Seite als letztes Element
	eingebunden.

	see also:
	<$onScriptLoaded>

	parameters:
	filename (string)	-	JS Dateiname OHNE Pfadangabe (muss ja im Verzeichnis <i>scripts</i> liegen).
	objectname (string)	-	Name der Klasse/Funktion die eingebunden wird.
	args (string)		-	Argument(e), die der Klasse/Funktion übergeben werden.
	*/
function $loadScript(filename, objectname, args){
	try{
		var isLoaded = eval(objectname);
	}catch(e){

		ljex.moduleArgs = args;
		var jScript=document.createElement("script");
		jScript.src="scripts/"+filename;
		document.getElementsByTagName('head')[0].appendChild(jScript);
	}

	return true;
}

/*	function: $onScriptLoaded
	Instanziiert ein dynamisch nachgeladenes JavaScript-Objekt. Bitte diese Funktion *am Ende* einer
	dynamisch zu ladenden Scriptdatei einfügen. Dies gewährleistet, das sofort nach erfolgreichem Laden der Klasse/Funktion
	diese auch ausgeführt/instanziiert wird.

	parameters:
	objectname (string)	-	Name der auszuführenden Funktion/der zu instanziierenden Klasse.
*/
function $onScriptLoaded(objectName){
		eval("new "+objectName+"(ljex.moduleArgs)");
}


// }}}1 END Global Functions

/*  group: Static Functions */
// {{{1 Code
/*	method: ljex.isIE 
	Ermittelt ob der Browserclient vom Typ Internet Explorer ist.

	returns:
	(bool)	-	true = Internet Explorer, false = Anderer Browser
*/
ljex.isIE = function(){
	if(navigator.appName=="Microsoft Internet Explorer"){
		return true;
	}else{
		return false;
	}
}

/*	method: ljex.isSafari 
	Ermittelt ob der Browserclient vom Typ Safari ist.

	returns:
	(bool)	-	true = Safari, false = Anderer Browser
*/
ljex.isSafari = function(){
	if(navigator.appVersion.match(/.*Safari.*/g)){
		return true;
	}else{
		return false;
	}
}

/*	method: ljex.init
	Initialisiert das Framework. Es werden die Sprache eingestellt und evtl. Attribute von
	HTML Elementen ausgewertet.

	parameters:
	lang (string) - Sprachkürzel (de,en,fr)
*/
ljex.init = function(lang){
	if(lang!=undefined) ljex.lang=lang;

	var bodyd = document.getElementsByTagName("body");
	if(ljex.isIE()){
		bodyd[0].attachEvent("onclick", function(){
			if(ljex.Form){
				if(typeof ljex.Form.openDropDown == "string"){
					ljex.Form.openDropDown = $(ljex.Form.openDropDown);
				}else if(ljex.Form.openDropDown!=null ){
					ljex.Form.openDropDown.style.display='none';
					ljex.Form.openDropDown=null;
				}
			}
		});
	}else{
		bodyd[0].addEventListener('click',function(){
			if(ljex.Form){
				if(typeof ljex.Form.openDropDown == "string"){
					ljex.Form.openDropDown = $(ljex.Form.openDropDown);
				}else if(ljex.Form.openDropDown!=null ){
					ljex.Form.openDropDown.style.display='none';
					ljex.Form.openDropDown=null;
				}
			}
		},false);
	}

	var divs = document.getElementsByTagName("div");
	var tiptext=[]; 
	for(i=0;i<divs.length;i++){
		var el=divs[i];
		if(el.getAttribute("ljex")){
			var params=el.getAttribute("ljex").split(";");
			for(j=0;j<params.length;j++){
				var param=params[j].split(":");
				switch(param[0]){
					case 'scrollbar':
					/*	if(param[1]=="custom"){
							el.style.overflow='hidden';
							if (window.addEventListener)
        						el.addEventListener('DOMMouseScroll', ljex.wheel, false);
							el.onmousewheel = document.mousewheel = ljex.wheel;

							el.style.position="relative";
							el.style.top=0;

						}*/
					break;
					case 'tooltip':
						tiptext[el.id]=param[1];
						el.onmouseover = function(e){
							var evt = (document.all) ? window.event : e;
							ljex.showToolTip(evt,tiptext[this.id]);
						}
					break;
				}
			}
		}
	}
}

/*	function: ljex.showToolTip
	Blendet ein ToolTip Kästchen ein. Diese Funktion ist zum Einsatz in mouseover Events
	gedacht. Sie erstellt automat. ein mouseout Event mit der Funktion sich selbst wieder zu entsorgen.
*/
// {{{2 Dokumentation
/*
	parameters:
	e (object) - mouseover Event Objekt.
	tipText (string) - Text für den ToolTip.
*/
// }}}2
ljex.showToolTip = function(e, tipText){
	var obj = (document.all) ? e.srcElement : e.target;


	tipDiv=document.createElement("div");
	tipDiv.id="lx-tooltip";
	tipDiv.style.position="absolute";
	tipDiv.style.zIndex=5000;

	document.body.appendChild(tipDiv);
	tipDiv.innerHTML='<div id="lx-tooltip-text" style="max-width:200px;background-color:#ffe;color:#000;border-style:dotted;border-color:#aaa;border-width:1px;padding:5px"	>'+tipText+'</div>';

	x = (document.all) ? window.event.clientX + document.body.scrollLeft : e.pageX;
    y = (document.all) ? window.event.clientY + document.body.scrollTop : e.pageY;

    posLeft = (x + 10) + "px";
    posTop   = (y + 10) + "px";

	tipDiv.style.left = posLeft;
	tipDiv.style.top = posTop;


	new ljex.FX({
			id : "lx-tooltip-text",
			fps : 25
		}).fadeIn();

	obj.onmouseout=function(){
		if($('lx-tooltip'))	document.body.removeChild($('lx-tooltip'));
	}
}

/*	function: ljex.prompt
	Bequemlichkeits-Wrapperfunktion für die Darstellung von Prompt Boxen.

	parameters:
	args (hash) - Konfigurationsoptionen. Vorbelegter Typ ist "ljex.MessageBox.ASK". Siehe: <ljex.MessageBox>
*/
ljex.prompt = function(args){
	args.type=ljex.MessageBox.ASK;
	args.height=150;
	new ljex.MessageBox(args);
}
// }}}1 End Static Functions

/*{{{1 Älterer Versuch mit Custom-Scrollbalken... vertagt...
ljex.handleMouseWheel = function(el,delta){
	var elTop = parseInt(el.style.top);
	if(delta<0){
		if( parseInt(el.parentNode.offsetHeight)-elTop < parseInt(el.offsetHeight) ){
			el.style.top=elTop-(-delta*30);
		}
	}else{
		if(elTop < 0){
			el.style.top=elTop+(delta*30);
		}
	}
}

ljex.wheel = function(event){
        var delta = 0;
        if (!event) 
                event = window.event;
        if (event.wheelDelta) {                 delta = event.wheelDelta/120;
                                if (window.opera)
                        delta = -delta;
        } else if (event.detail) {
                                delta = -event.detail;
        }
                if (delta)
                ljex.handleMouseWheel(this,delta);
                if (event.preventDefault)
                event.preventDefault();
	event.returnValue = false;
}
}}}1*/

/* class: ljex.locale */
// {{{1 Code
ljex.locale = {
	add: function(site, section, lang, callback, scope){
		var cb=callback;
		var sc=scope||this;
		var sect=[];

		if(typeof(section)=='string'){
			sect[0]=section;
		}else{
			sect=section;
		}

		new ljex.Ajax({
			url: '/lib/Locale.php5',
			method: 'POST',
			param: {
				site: site,
				section: JSON.stringify(sect),
				lang: lang
			},
			scope: this,
			callback: function(response){ 
				data = eval("("+response.responseText+")");

				for(sec in data['sections']){
					ljex.locale[sec]=data['sections'][sec];
				}

				if(cb) cb.call(sc);
			}
		}).request();
	}
};
// }}}1

ljex.ajaxRequests = [];

/* class: ljex.Ajax
  Wrapperklasse zur Vereinfachung des Umgangs mit XMLHttpRequest().
*/
//{{{1 Dokumentation
/*
  Beispiel:
  Hier ein ganz einfaches Beispiel für einen Ajax GET Request.

  (code)
	new ljex.Ajax({
		url: '/index.php5?action=updateVideos',
		method: 'GET',
		scope: this,
		callback: function(response){ 
			var data = eval("("+response.responseText+")");
		}
	}).request();
	(end)

*/
/* properties: Konfigurationsoptionen
 
  url (string) - URL des PHP-Skriptes, welches den Request verarbeitet.
  callback (function) -  Funktion, die nach dem Request aufgerufen werden soll. Bekommt als einzigem Parameter das Requestobjekt.
  method (string) -  Legt die Requestmethode fest (POST oder GET). Standardmäßig wird *GET* angenommen.
  param (hash) - JSON Array mit POST-Parametern. 
  scope (object) - Geltungsbereich (Scope) der callback Funktion .
  async (bool) - Bestimmt ob ein Request asynchron (true) oder synchron (false) ist. Standardwert ist *true*
*/ 
//}}}1
// {{{1 Code
/* constructor: ljex.Ajax
  Erzeugt ein ljex.Ajax Objekt und initialisiert es.
 
  parameters: 
  conf (hash) - JSON Array mit Config Optionen. Siehe: <Konfigurationsoptionen>.
*/
ljex.Ajax = function(conf){
	ljex.uniqueID++;

	ljex.ajaxRequests[ljex.uniqueID]=this;
	this.requestID=ljex.uniqueID;

	this.url = conf.url;
	this.callback = conf.callback || null;
	this.method = conf.method || 'GET';
	this.param = conf.param || null;
	this.scope = conf.scope || this;  
	this.async = (conf.async!=undefined)?conf.async:true;

	this.myRequest=0;
}

/* 	method: request
  	Führt einen XMLHttpRequest aus. Ruft ggf. die callback Funktion auf.

  	callback:
  	response (object) - XMLHttpRequest Objekt.

	returns:
  	response (object) - XMLHttpRequest Objekt, wenn Request synchron läuft. Sonst keine Rückgabe.
*/
ljex.Ajax.prototype.request = function(){ // {{{ Code
	var thisObj=this;
	//XMLHttpRequest erzeugen
 		try {
  			// ... für IE
	  		this.myRequest = new ActiveXObject("Microsoft.XMLHTTP");
	 	} catch (error) {
	  		// ... für Firefox, Opera, usw.
	  		this.myRequest = new XMLHttpRequest();
	 	}
//	    this.myRequest.prototype.requestID=this.requestID; 
		//Verbindung öffnen
		if(this.method=='GET'){
			this.myRequest.open("GET",this.url, this.async);
			if(!this.async){
				this.myRequest.send(null);
				return this.myRequest;
			}else{
				//Event Listener setzen
				this.myRequest.onreadystatechange =  function(){
					if(thisObj.myRequest.readyState == 4){
						thisObj.callback.call(thisObj.scope, thisObj.myRequest) ;
					}
				}
				//Anfrage senden
				this.myRequest.send(null);
			}
		}else{
			this.myRequest.open("POST",this.url, this.async);
			this.myRequest.setRequestHeader('Content-type','application/x-www-form-urlencoded');

			//POST Parameter basteln 
			var postString="";
			var first=true;
			for(key in this.param){
				if(!first){
					postString+="&"+key+"="+encodeURIComponent(this.param[key]);
				}else{
					postString=key+"="+encodeURIComponent(this.param[key]);
					first=false;
				}
			}

			if(!this.async){
				this.myRequest.send(postString);
				return this.myRequest;
			}else{
				//Event Listener setzen
				this.myRequest.onreadystatechange = function(){
					if(thisObj.myRequest.readyState == 4){
						thisObj.callback.call(thisObj.scope, thisObj.myRequest) ;
					}
				}
				this.myRequest.send(postString);
			}

		}
}
// }}}
// }}}1

/* 	class: ljex.FX
	Stellt verschiedene Animationsarten für HTML-Elemente bereit.
*/
//{{{1 Dokumentation
/*  properties: Konfigurationsoptionen
	id (string|object) - Wird ein String übergeben, wird das Element mit dieser ID animiert. Ansonsten Objekt selbst.
	fps (int) - Animationsfrequenz (Frames per second). Standardwert: 25
	callback (function) - Wird aufgerufen, wenn Animation beendet ist.
	scope (object) - Geltungsbereich der callback Funktion.
*/
//}}}1
// {{{1 Code
/*  constructor: ljex.FX
	Initialisiert FX Objekt mit dem angegebenen HTML Element.

	parameters:
	config (hash) - JSON Objekt mit <Konfigurationsoptionen>
*/
ljex.FX = function(config){ // {{{

	if(!ljex.FX.Instances) ljex.FX.Instances = new Array();

	if(typeof(config.id)=="string"){
		this.id = config.id;
		this.el = $(config.id);
	}else{
		this.id = config.id.id;
		this.el = config.id;
	}

	ljex.FX.Instances[this.id]=this;

	this.aktiv=null;
	this.duration=0;
	this.fps= config.fps || 25;
	this.callback = config.callback || null;
	this.stepWidth= config.stepWidth || 0.05;
	this.scope=config.scope;
	this.INFADE=false;
	this.SHIFTOPEN=false;
	this.aktOpacity=0; // opacity für FireFox puffern, sonst klappt fade in nicht...

	this.targetHeight=0;
	this.aktHeight=0;
}
// }}}

ljex.FX.prototype.fade = function(){ // {{{

	if(this.INFADE){
		if(this.aktOpacity< ((ljex.isIE())?100:1) ){
			this.aktOpacity+=this.stepWidth;
			if(ljex.isIE()){
				this.el.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity="+this.aktOpacity+")";
			}else{
				this.el.style.MozOpacity=this.aktOpacity; // Workaround wg. FireFox fadein bug.
				this.el.style.opacity=this.aktOpacity;
			}
		}else{
			window.clearInterval(this.aktiv);
			if(this.callback!=null) this.callback(this.scope);
		}
	}else{
		if(this.aktOpacity >= 0.0){
			this.aktOpacity-=this.stepWidth;
			if(ljex.isIE()){
				this.el.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity="+this.aktOpacity+")";
			}else{
				this.el.style.MozOpacity=this.aktOpacity; // Workaround wg. FireFox fadein bug.
				this.el.style.opacity=this.aktOpacity;
			}
		}else{
		  this.el.style.display="none";
			window.clearInterval(this.aktiv);
			if(this.callback!=null) this.callback(this.scope);
		}

	}
}
// }}}

/*	method: fadeIn
	Blendet das HTML Element entsprechend der Konfiguration ein.
*/
ljex.FX.prototype.fadeIn = function(){ // {{{
	this.aktOpacity=0;

	if(ljex.isIE()){
		this.el.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
		this.stepWidth=5;
	}else{
		this.el.style.MozOpacity=0;
		this.el.style.opacity=0;
	}

	this.INFADE=true;
	this.aktiv=window.setInterval('ljex.FX.Instances["'+this.id+'"].fade()',this.fps);
}
// }}}

/*	method: fadeOut
	Blendet das HTML Element entsprechend der Konfiguration aus.
*/
ljex.FX.prototype.fadeOut = function(){ // {{{

	if(ljex.isIE()){
		this.aktOpacity=100;
		this.stepWidth=5;
		this.el.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
	}else{
		this.aktOpacity=1;
		this.el.style.MozOpacity=1;
		this.el.style.opacity=1;
	}

	this.INFADE=false;

	this.aktiv=window.setInterval('ljex.FX.Instances["'+this.id+'"].fade()',this.fps);
} // }}}

ljex.FX.prototype.shift = function(){ // {{{
	if(this.SHIFTOPEN){
		if(this.aktHeight<this.targetHeight){
			this.aktHeight+=this.stepWidth;
			this.el.style.height=this.aktHeight;
		}else{
			this.el.style.overflow=this.styleOverflow;
			window.clearInterval(this.aktiv);
			if(this.callback!=null) this.callback(this.scope);
		}
	}else{
		if(this.aktHeight>0){
			this.aktHeight-=this.stepWidth;
			this.el.style.height=this.aktHeight;
		}else{
			this.el.style.overflow=this.styleOverflow;
			this.el.style.display='none';
			window.clearInterval(this.aktiv);
			if(this.callback!=null) this.callback(this.scope);
		}
	}
} // }}}

/*	function: shiftOpen
	"Öffnet" ein HTML Element (meist div-container) mit einem Aufschiebeeffekt.

	parameters:
	targetHeight (int)	-	Höhe des Elements im aufgeschobenen Zustand
*/
ljex.FX.prototype.shiftOpen = function(targetHeight){ // {{{
	this.el.style.height=0;
	if(this.stepWidth<1) this.stepWidth=6;
	this.fps=12;
	this.targetHeight=targetHeight;

	this.styleOverflow=this.el.style.overflow;
	this.el.style.overflow='hidden';

	this.SHIFTOPEN=true;

	this.aktiv=window.setInterval('ljex.FX.Instances["'+this.id+'"].shift()',this.fps);

} // }}}

/*	function: shiftClose
	"Schliesst" ein HTML Element (meist div-container) mit einem Zuschiebeeffekt.

*/
ljex.FX.prototype.shiftClose = function(){ // {{{
	this.fps=12;
	if(this.stepWidth<1) this.stepWidth=6;

	this.styleOverflow=this.el.style.overflow;
	this.el.style.overflow='hidden';

	this.aktHeight=parseInt(this.el.style.height);

	this.SHIFTOPEN=false;

	this.aktiv=window.setInterval('ljex.FX.Instances["'+this.id+'"].shift()',this.fps);

} // }}}
// }}}1

/*	class:	ljex.buttonMenu
	Eine Art Kontextmenü für Buttons. 
*/
// {{{1 Code
ljex.buttonMenu = function(args, evt){
	$assimilate.call(this,args);
	var thisObj = this;
	this.items = args.items;
	this.evt=evt;
	this.el = $create(args.id);
	this.el.className="lx-context-ct";

	// Methode ausleihen
	ljex.buttonMenu.prototype.init = ljex.contextMenu.prototype.init;

	for(var i=0;i<this.items.length;i++){
		this.el.appendChild(this.init(this.items[i]));
	}

		
	x = (document.all) ? window.event.clientX + document.body.scrollLeft : this.evt.pageX;
 	y = (document.all) ? window.event.clientY + document.body.scrollTop : this.evt.pageY;

	posLeft = (x + 5) + "px";
	posTop   = (y + 5) + "px";

	this.el.style.left = posLeft;
	this.el.style.top = posTop;

	document.body.appendChild(this.el);
}
// }}}1 Code

/*  class: ljex.contextMenu 
	Erzeugt ein Kontextmenü zu einem HTML-Element. Dieses Objekt ist dafür vorgesehen
	aus einem beliebigen Event-Handler heraus aufgerufen zu werden.
*/
//{{{1 Dokumentation
/*	properties: Konfigurationsoptionen
	items (hash) - JSON Array mit den einzelnen Menüpunkten ( siehe <Items> ).
	id (string) - ID des Menücontainers.

	properties: Items
	id (string) - ID des Menüeintrags.
	text (string) - Beschriftung (kann auch HTML enthalten).
	onclick (function) - Handler Funktion bei Klick auf Eintrag.
*/
//}}}1
// {{{1 Code
/*	constructor: ljex.contextMenu
	Erzeugt das Kontextmenü und stellt es dar.

	parameters:
	args (hash) - JSON Array mit <Konfigurationsoptionen>
	evt (object) - Event Objekt, das die Darstellung des Menüs initiert.
*/
ljex.contextMenu = function(args, evt){
	$assimilate.call(this,args);
	var thisObj = this;
	if(ljex.contextMenu.open){
		return;
	}else{
		ljex.contextMenu.open=true;
	}
//	this.items = args.items;
	this.evt=evt;
	this.el = $create(args.id);
	this.el.style.zIndex=ljex.Window.zindex+1;
	this.el.className="lx-context-ct";

	for(var i=0;i<this.items.length;i++){
		this.el.appendChild(this.init(this.items[i]));
	}

	
	x = (document.all) ? window.event.clientX + document.body.scrollLeft : this.evt.pageX;
 	y = (document.all) ? window.event.clientY + document.body.scrollTop : this.evt.pageY;

	posLeft = (x + 5) + "px";
	posTop   = (y + 5) + "px";

	this.el.style.left = posLeft;
	this.el.style.top = posTop;


	var f = document.body.onclick;

	document.body.onclick = function(e){
		if($(args.id)){
			document.body.removeChild($(args.id));

			this.onclick = f; //function(){ return false; }
		}
		ljex.contextMenu.open=false;
	}

	document.body.appendChild(this.el);
}

ljex.contextMenu.open=false;

ljex.contextMenu.prototype.init = function(item){
	var thisObj=this;
	var itemEl = $create(item.id+'-itemel');
	itemEl.className="lx-context-item";

	itemEl.innerHTML=item.text;

	itemEl.onclick = function(){
		item.onclick.call(thisObj, item); // scope=ljex.contextMenu Object
		if(thisObj.el){
			document.body.removeChild(thisObj.el);
			ljex.contextMenu.open=false;
		}
	}

	itemEl.onmouseover = function(){ this.className='lx-context-item-hover'; }
	itemEl.onmouseout = function(){ this.className='lx-context-item'; }
		
	return itemEl;

}
// }}}1

/*	class: ljex.Window
	Erzeugt ein absolut positioniertes GUI Fenster. 
*/
//{{{1 Dokumentation
/*
	Beispiel:

	(code)
	this.win = new ljex.Window({
		title: this.title,
		id: this.id,
		width: this.width,
		height: 400,
		html: '<div>Hallo Welt</div>',
		parentId: 'content',
		center: true,
		focusOpacity: false,
		onclose: function(){
			alert("Fenster zu!");
		}
	});
	
	this.win.open();
	(end)
*/
/*	properties: Konfigurationsoptionen
	id (string) - ID des Fenster Containers (default = 'lx-window-'+new Date().getTime()).
	title (string) - Fenstertitel (default = ""). Wird kein Titel angegeben, wird auch keine Titelleiste gerendert.
	left (int) - Position links (default = 0).
	top (int) - Position oben (default=0).
	width (int) - Fensterbreite (default='auto').
	height (int) - Fensterhöhe.
	html (string) - Freies HTML, das direkt auf den Container gerendert wird.
	icon (string) - vollständiger Dateiname (mit Pfad) eines zu verwendenten Icons (16x16 Pixel).
	parentId (string) - ID des Eltern Containers.
	contentBgColor (string) - Hintergrundfarbe des Fensters (Bsp. #000).
	color (string) - Schriftfarbe (default='#000').
	center (bool) - true = Fenster zentrieren, false = nicht zentrieren (default).
	focusOpacity (bool) - true = Alles um das Fenster herum abdunkeln, false = nicht abdunkeln (default).
	onclose (function) - Eigene Funktion die bei Klick auf Schließen Symbol aufgerufen wird.
*/
//}}}1
// {{{1 Code
/* 	constructor: ljex.Window
	Initialisiert ein neues Fensterobjekt.

	parameters:
	config (hash) - JSON Array mit <Konfigurationsoptionen>
*/
ljex.Window = function(config){
	$assimilate.call(this,config);

	$setdef.call(this,{
		title: "",
		id: 'lx-window-'+$genid(),
		left: 0,
		top: 0,
		width: 'auto',
		height: null,
		html: "",
		icon: null,
		parentId: null,
		items: null,
		contentBgColor: null,
		color: '#000',
		center: false,
		focusOpacity: false,
		onclose: null
	});

}

ljex.Window.zindex=0;

/*	method: show
	Zeigt das Fenster an und initialisiert das Event Handling.
*/
ljex.Window.prototype.show = function(){ // {{{
	this.winEl.style.display='block';
	if(this.width=='auto'){
//		if(ljex.isIE()) offsetIE=2; else offsetIE=0;
//		this.winEl.style.width=($(this.id+'-content').offsetWidth+offsetIE)+'px';
	}
	if(this.center){
		this.winEl.style.left=(parseInt(this.parentEl.offsetWidth)/2)-(parseInt(this.winEl.offsetWidth)/2)+"px";
		if(!this.top) this.winEl.style.top=(parseInt(this.parentEl.offsetHeight)/2)-(parseInt(this.winEl.offsetHeight)/2)+"px";
	}

	this.connectEvents();
//	$(this.id+"-title").width=parseInt(this.winEl.clientWidth)-50;
/*	if(this.title){
		var wLeft = $(this.id+'-title-left').offsetWidth;
		var wRight = $(this.id+'-title-right').offsetWidth;
		var wWin = this.winEl.offsetWidth;
		$(this.id+'-title').style.width=(wWin-wLeft-wRight-4)+'px';
	}*/
} // }}}

/*	method: close
	Schließt das Fenster und entfernt es aus dem DOM Baum.
*/
ljex.Window.prototype.close = function(){ // {{{
	var thisObj=this;

	this.winEl.style.display='none';

	thisObj.initDrag=false;
//	thisObj.parentEl.onmousemove = function(){return};
	if(!$(thisObj.id)){
		$alert("Seltener, abgefangener Fehler: core.js / ljex.Window.prototype.close<br><br> Window Objekt nicht mehr im DOM. onclose() - Handler wird nicht mehr ausgeführt.");
	}else{
		thisObj.parentEl.removeChild($(thisObj.id));
		if(thisObj.focusOpacity){
			thisObj.parentEl.removeChild($(thisObj.id+'_special'));
		}
		
		if(thisObj.onclose!=null){
			thisObj.onclose();
		}
	}
} // }}}

/*	method: open
	Bastelt das Fenster und zeigt es an, wenn hidden=false/undefined.

	parameters:
	hidden (bool) - true = Fenster nicht anzeigen, false/undefined = Fenster anzeigen.
*/
ljex.Window.prototype.open = function(hidden){ // {{{
	var thisObj = this;
	if(this.parentId!=null){
		this.parentEl=$(this.parentId);
	}else{
		this.parentEl=document.body;
	}

	this.winEl=document.createElement("table");
	with(this.winEl){
		id=this.id;
		border=0;
		className="lx-window-frame";
		cellSpacing=0;
		cellPadding=0; 
	}
	ljex.Window.zindex++;
	this.winEl.style.zIndex=ljex.Window.zindex;
	var tbody = document.createElement("tbody");

	with(this.winEl.style){
		if(this.left) left=this.left+'px';
		if(this.top) top=this.top+'px';
		if(this.bgColor) backgroundColor=this.bgColor;
		if(this.border===false) border=0;
			
		if(this.width!='auto'){
			width=this.width+'px';
		}
		if(this.height) height=this.height+'px';
	}
	

	if(this.title){
		var titleCt = document.createElement("table"); //$create(
		titleCt.id=this.id+"-titlect";
		with(titleCt){
			cellSpacing=0;
			cellPadding=0;
			border=0;
		}
		titleCt.className='lx-window-title';

		var titleBody = document.createElement("tbody");

		var titleRow = document.createElement("tr");

		var titleLeft = document.createElement("td"); //$create(
		titleLeft.id=this.id+'-title-left';
		titleLeft.width=25;
//		titleLeft.style.width="23px";
		titleLeft.height=26;
		titleLeft.valign="middle";
		titleLeft.align="center";
		titleLeft.className='lx-window-title-left';
		titleRow.appendChild(titleLeft);

		if(this.icon){
			var titleIcon = document.createElement("img");
			titleIcon.src=this.icon;
			titleLeft.appendChild(titleIcon);
		}

		var titleCenter = document.createElement("td");
		titleCenter.id=this.id+"-title";
//		titleCenter.width="50%";
		titleCenter.height=26;
		titleCenter.className='lx-window-title-center';
//		titleCenter.style.paddingTop='7px';
		

		titleCenter.innerHTML=$shadow(this.title);
		titleRow.appendChild(titleCenter);

		var titleRight = document.createElement("td");
		titleRight.id=this.id+'-title-right';
		titleRight.width=25;
		titleRight.valign="middle";
	//	titleRight.style.width="25%";
		titleRight.height=26;
		titleRight.className='lx-window-title-right';

		var titleClose = document.createElement("img");
		titleClose.id=this.id+"-close";
		titleClose.style.cursor='pointer';
		titleClose.src=ljex.path+"/themes/"+ljex.theme+"/images/windows/close.png";

		titleRight.appendChild(titleClose);
		titleRow.appendChild(titleRight);
		titleBody.appendChild(titleRow);

		titleCt.appendChild(titleBody);

		var ttr = document.createElement("tr");
		var ttd = document.createElement("td");
		ttd.appendChild(titleCt);
		ttr.appendChild(ttd);
		tbody.appendChild(ttr);
	}

	if(this.focusOpacity){
		// div zum abdunkeln der page.
		var specialEl = document.createElement("div");

		with(specialEl){
			id=this.id+'_special';
			style.position='absolute';
			style.left='0px';
			style.top='0px';
			style.zIndex=ljex.Window.zindex-1;
			style.width=this.parentEl.offsetWidth+'px';
			style.height=this.parentEl.offsetHeight+'px';
			style.backgroundColor='#000';
			style.opacity='0.85';
			style.mozOpacity='0.85';
			style.filter="progid:DXImageTransform.Microsoft.Alpha(Opacity=85)";

		}

		this.parentEl.appendChild(specialEl);
	}

	// Fensterinhalt
	var bodyRow = document.createElement("tr");



	var ctEl = document.createElement("td");
	ctEl.id=this.id+'-content';
	ctEl.className='lx-window-content';
	ctEl.innerHTML=this.html;

	this.transEl = document.createElement("td");
	this.transEl.style.display='none';


	if(this.items!=null){
		for(var i=0;i<this.items.length;i++){
			if(!(this.items[i] instanceof ljex.Panel) && !(this.items[i] instanceof ljex.ToolBar) ){
				switch(this.items[i].xtype){
					case 'panel':
						var p = new ljex.Panel(this.items[i]);
						break;
					case 'treepanel':
						var p = new ljex.TreePanel(this.items[i]);
						break;
					case 'tabpanel':
						var p = new ljex.TabPanel(this.items[i]);
						break;
				}
				var pEl = p.render();
			}else{
				var pEl = this.items[i].render();
			}
			ctEl.appendChild(pEl);
		}
	}

	with(ctEl.style){
		if(this.width!='auto' && this.width!=undefined){
			width=this.width+'px'; //(this.width-4)+'px'; // border & margin abziehen
		}
		if(this.height!=null){
			height=(this.height-((this.title)?26:0))+"px";
		}
		if(this.contentBgColor!=null){
			backgroundColor=this.contentBgColor;
		}
	}

	bodyRow.appendChild(ctEl);
	bodyRow.appendChild(this.transEl);
	tbody.appendChild(bodyRow);
	this.winEl.appendChild(tbody);

	this.parentEl.appendChild(this.winEl);

	if(!hidden){
		this.show();
	}else{
		this.winEl.style.display='none';
	}
} // }}}

/*	method: connectEvents
	Initialisiert das Event Handling des Fensters. Diese Methode sollte für gewöhnlich
	niemals direkt aufgerufen werden. Dies erledigt <show>
*/
ljex.Window.prototype.connectEvents = function(){ // {{{
	var thisObj=this;

	if(this.title!=""){
		$(this.id+'-close').onclick= function(){
			thisObj.initDrag=false;
			thisObj.parentEl.onmousemove = function(){return}
			thisObj.parentEl.removeChild($(thisObj.id));
			if(thisObj.focusOpacity){
				thisObj.parentEl.removeChild($(thisObj.id+'_special'));
			}
			if(thisObj.onclose!=null){
				thisObj.onclose();
			}
		}

		$(this.id+'-title').onmousedown = function(e){
			if(!thisObj.initDrag){
				ljex.Window.zindex++;
				thisObj.startX = (document.all) ? window.event.clientX + thisObj.parentEl.scrollLeft : e.pageX;
				thisObj.startY = (document.all) ? window.event.clientY + thisObj.parentEl.scrollTop : e.pageY;
				thisObj.initDrag=true;
				thisObj.winEl.style.opacity='0.5';
				thisObj.winEl.style.filter="progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
				thisObj.winEl.style.zIndex=ljex.Window.zindex;

				var el=$(thisObj.id+'-content');
				var w=el.clientWidth; var h=el.clientHeight;

				thisObj.transEl.style.width=w+"px"; 
				thisObj.transEl.style.height=h+"px";

				el.style.display='none';
				thisObj.transEl.style.display='block';

			}

		}
		
		$addEvent(this.parentEl, "mouseup",
		function(){
			if(thisObj.initDrag){
				thisObj.initDrag=false;
				thisObj.winEl.style.opacity='1.0';
				thisObj.winEl.style.filter="progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
				var el=$(thisObj.id+'-content');

				el.style.display='block';
				thisObj.transEl.style.display='none';
				thisObj.transEl.style.width="0px"; 
				thisObj.transEl.style.height="0px";
			}
			return true;
		});

		$addEvent(this.parentEl, "mousemove",
		function(e){
			var winTitle = $(thisObj.id+'-title');
			if(thisObj.initDrag){
					aktX = (document.all) ? window.event.clientX + winTitle.scrollLeft : e.pageX ;
   		 			aktY = (document.all) ? window.event.clientY + winTitle.scrollTop : e.pageY;
				
					x = aktX-thisObj.startX;
					y = aktY-thisObj.startY;

					thisObj.startX=aktX; thisObj.startY=aktY;

				//	window.status=x+"/"+y+" "+winTitle.offsetLeft;
					with(thisObj.winEl.style){
						left=parseInt(left)+x+'px';

						top=parseInt(top)+y+'px';
						if(parseInt(top)<0) top='0px';
					}
			}	
			return true;
		});
	}

} // }}}
// }}}1 END CLASS ljex.Window

/* 	class: ljex.MessageBox
	Eine hübschere Variante des Standard Javascript Alerts/Prompts.
*/
//{{{1 Dokumentation
/*	properties: Konfigurationsoptionen
	id (string) - optional. ID für's Fenster.
	title (string) - Fenstertitel (default = LJEX MessageBox).
	width (int) - Breite (default = 350).
	height (int) - Höhe (default = 150).
	msg (string) - Nachrichtentext.
	type (const) - Type des Alerts. Mögliche Angaben hier siehe: <Alerttypen>. (default = ljex.MessageBox.WARN)
	parentId (string) - ID des Elterncontainers. (default = page)
	scope (object) - Geltungsbereich der callback Funktion. (default = this)
	callback (function) - callback Funktion. Bekommt als Parameter entweder "OK" oder "CANCEL". (optional)
*/
/*	properties: Alerttypen
	ljex.MessageBox.INFO (const) - Erzeugt eine Informations-/Hinweis Box
	ljex.MessageBox.STATUS (const) - Erzeugt eine Informations-/Hinweis Box ohne OK-Button. Sie muss also 
									 programmatisch geschlossen werden. Sinnvol z.B. um Ladebalken anzuzeigen.
	ljex.MessageBox.ERROR (const) - Erzeugt eine Fehlermeldungs Box
	ljex.MessageBox.WARN (const) - Erzeugt einen Alert für Warnungen
	ljex.MessageBox.ASK (const) - Erzeugt eine Prompt Box. Diese besitzt 2 Buttons: "Ja" und "Nein!"
*/
//}}}1
// {{{1 Code
/*	constructor: ljex.MessageBox
	Initialisiert die Box und zeigt sie an.

	parameters:
	args (hash) - JSON Array mit <Konfigurationsoptionen>.
*/
ljex.MessageBox = function(args){
	var thisObj = this;

	var defaults = {
		id: 'lx-win#'+$genid(),
		title: 'LJEX MessageBox',
		width: 350,
		height: 150,
		msg: 'LJEX - Leightweight Javascript EXtension',
		type: ljex.MessageBox.WARN,
		scope: this,
		focusOpacity: false,
		parentId: 'page'
	};

	$setdef.call(this, defaults);
	$assimilate.call(this, args);


	footerItems=[];

	var btnOK = { 
		id: 'ljex-msg-ok', 
		text: (this.type==ljex.MessageBox.ASK)?'Ja':'OK', 
		icon: '/ljex/images/ok.png'
	};

	if(this.type==ljex.MessageBox.ASK){
		btnOK.onclick = function(){
			thisObj.close();
			thisObj.callback.call(thisObj.scope,"OK");
		}

		var btnCANCEL ={ 
			id: 'ljex-msg-cancel', 
			text: '<span style="color:#f00;font-weight:bold;">Nein!</span>', 
			icon: '/ljex/images/cancel.png',
			onclick: function() { thisObj.close(); thisObj.callback.call(thisObj.scope,"CANCEL");  } 
		};
	}else{
		btnOK.onclick = function(){
			if(thisObj.callback){
				thisObj.callback.call(thisObj.scope,"OK");
			}
			thisObj.close();
		}

	}

	var msgHeight = this.height-62;
//	var msgWidth = this.width-60;
	this.win = new ljex.Window({
		title: this.title,
		id: this.id,
		width: this.width,
	//	height: this.height,
		html: '<div align=center style="background-color:#ddd;width:100%;height:100%;"><div align=left style="height:'+msgHeight+'px" class="'+this.type+'"><div class="lx-mbox-msg"><div style="padding-left:60px;padding-right:10px;">'+this.msg+'</div></div></div><table align=center><tr><td><div class="lx-mbox-footer" id="'+this.id+'-footer-ct'+'"></div></td></tr></table></div>',
		parentId: this.parentId,
		center: true,
		focusOpacity: this.focusOpacity,
		onclose: function(){
		//	cms.modules.wmc.running=false;
		}//,

//		items:[this.naviPanel, this.mainPanel]
	});
	
	this.win.open();
	this.win.winEl.style.zIndex=10000;

	// STATUS Boxen haben keine Buttons
	if(this.type!=ljex.MessageBox.STATUS){
		footerItems[0]=btnOK;
//		footer.appendChild(btnOK.render());
	}
	
	if(this.type==ljex.MessageBox.ASK){
		footerItems[1]=btnCANCEL;
//		footer.appendChild(btnCANCEL.render());
	}

	var footer = new ljex.ToolBar({
		id: this.id+'-footer',
		renderTo: this.id+'-footer-ct',
		items: footerItems
	});
//	if(this.type==ljex.MessageBox.ASK){
//		$("ljex-msg-ok").style.cssFloat='left';
//		$("ljex-msg-ok").style.marginRight='5px';
	//	$("ljex-msg-cancel").style.cssFloat='right';
//	}

	footer.render();

	ljex.MessageBox.instance=this;
	if(this.autoclose) window.setTimeout("ljex.MessageBox.instance.close()",this.autoclose*1000);

}

/*	method: close
	Schließt die MessageBox.
*/
ljex.MessageBox.prototype.close =function(){
	if(this.win.winEl) this.win.close();
}

ljex.MessageBox.instance=null;
ljex.MessageBox.INFO = "lx-mbox-body lx-mbox-info";
// keine Buttons, status ist fake-css! Dient blos zur Unterscheidung von info.
ljex.MessageBox.STATUS = "lx-mbox-body lx-mbox-info status"; 
ljex.MessageBox.ERROR = "lx-mbox-body lx-mbox-error";
ljex.MessageBox.WARN = "lx-mbox-body lx-mbox-warn";
ljex.MessageBox.ASK = "lx-mbox-body lx-mbox-ask"; 
// }}}1

/*	class:	ljex.FormWindow
	Erweitert die Window Klasse um die Möglichkeit direkt bei der Definition
	eine Formulardeklaration mit anzugeben.
*/
// {{{1 Dokumentation
/*
	Beispiel:
	(code)
	var frmWindow = new ljex.FormWindow({
		// ljex.Window Optionen:
		title: 'Meine Einstellungen',
		width: 400,
		//  Ab hier die Formularkonfiguration:
		form: { 
			id: 'cms-user-prefs',
			labelWidth: 130,
			formLoader: { // Formular aus der Datenbank laden
				frm_name: 'pref_cms_users_de',
				frm_table: 'cms_users',
				frm_lang: 'de', 
				ds_lang: args.ds_lang,
				ds_id: cms.userid,
				ds_id_field: 'us_id'
			},

			buttons:[{
				id: 'cms-user-prefs-save',
				submit: 'ajax',
				icon: '/images/icons/save.png',
				text: "Speichern", 
				handler: function(response){
					data = eval("("+response.responseText+")");
					// Hier kann was sinnvolles passieren.
				}
			}]
		}
	});

	frmWindow.open();
	(end)
*/
/*	properties: Konfigurationsoptionen
	form (hash) - Konfigurationsoptionen des Formulars. Siehe auch <ljex.Form>. Für weitere Properties siehe auch <ljex.Window>
*/
//}}}1
// {{{1 Code
/*	constructor:	ljex.FormWindow
	Erzeugt eine neue Instanz. Das Fenster wird jedoch nicht automat. geöffnet. Siehe <open>

	parameters:
	args (hash) - JSON Array mit Konfigurationsoptionen.
*/
ljex.FormWindow = function(args){
	var thisObj = this;
	$assimilate.call(this, args);
	this.uid = $genid();

	var defaults = {
		id: 'lx-win#'+this.uid,
		title: 'LJEX FormWindow',
		width: 'auto',
		parentId: 'page',
		center: true,
		focusOpacity: false,
		contentBgColor: '#ddd',
		onclose: function(){}
	};

	$setdef.call(this, defaults);
}

/*	method: close
	Schließt das FormWindow.
*/
ljex.FormWindow.prototype.close = function(){
	this.win.close();
}

/*	method: open
	Öfnnet das FormWindow und rendert das Formular.
*/
ljex.FormWindow.prototype.open = function(){
	var thisObj=this;
	this.form.renderTo = "form-ct#"+this.uid;
	if(this.form.onrender){
		var onrenderFunc=this.form.onrender;
	}else{
		var onrenderFunc=function(){ return true; }
	}
	this.form.onrender = function(){
		thisObj.win.show();
		onrenderFunc();
	}
	this.formular = new ljex.Form(this.form);

	this.win = new ljex.Window({
		title: this.title,
		id: this.id,
		width: this.width,
		icon: this.icon,
		height: this.height,
		html: '<div id="form-ct#'+this.uid+'" ></div>',
		parentId: this.parentId,
		center: this.center,
		focusOpacity: this.focusOpacity,
		contentBgColor: this.contentBgColor,
		onclose: this.onclose
	});
	
	if(this.top) this.win.top=this.top;
	if(this.left) this.win.left=this.left;

	this.win.open(true);

	if(this.formular.formLoader){
		this.formular.loadForm();
	}else{
		this.formular.render();
	}

}
// }}}1 END CLASS
