/************************************************************************************/
/* @TITLE
/*	jslib::Core - Core-Modul
/*
/* @SYNOPSIS
/*	<script type="text/javascript" src="/jslib/Core.js"></script>
/*
/* @DESCRIPTION
/*	Dieses Modul bietet einige grundlegende Funktionen an
/*
/* @AUTHOR
/*	Thilo Girmann
/*
/* @COPYRIGHT
/*	Copyright © 2004-2005 by CHAMAELEON AG (http://www.chamaeleon.de) - November 2005
/*************************************************************************************/


/************************************************************************************/
/* @DESCRIPTION
/*	Ist der aktuelle Browser ein Internet Explorer?
/************************************************************************************/
var isIE = navigator.userAgent && navigator.userAgent.indexOf("MSIE") > -1;
var isIE6 = navigator.userAgent && navigator.userAgent.indexOf("MSIE 6") > -1;

/************************************************************************************/
/* @DESCRIPTION
/*	Ist der aktuelle Browser ein Mozilla Firefox?
/************************************************************************************/
var isFirefox = navigator.userAgent && navigator.userAgent.indexOf("Firefox") > -1;


/* Erzeugt ein XMLHttpRequest-Objekt in IE 5 und 6 (aber nicht im IE 7, der braucht das nicht mehr) */

if (!window.XMLHttpRequest) {
	XMLHttpRequest = function() {
		try {
			return new ActiveXObject("Msxml2.XMLHTTP");
		}
		catch (e) {
			try {
				return new ActiveXObject("Microsoft.XMLHTTP");
			}
			catch (f) {
				throw new Error("Cannot create ActiveX object: " + e.message + " " + f.message);
			}
		}
	};
}

function createHttpRequest(uri, method, params, onCompletion) {
/************************************************************************************/
/* @TITLE
/*	HTTP-Request erzeugen
/*
/* @SYNOPSIS
/*	// synchroner Aufruf
/*	var request = createHttpRequest("nachricht.txt");
/*	if (request.status == 200) {
/*		alert(request.responseText);
/*	}
/*
/*	// asynchroner Aufruf
/*	function alertFunction() {
/*		if (this.status == 200) {
/*			alert(this.responseText);
/*		} else {
/*			alert("Fehler: " + this.status);
/*		}
/*	}
/*	var request = createHttpRequest("nachricht.txt", "GET", "path=" + path, alertFunction);
/*
/* @DESCRIPTION
/*	Erzeugt ein XMLHttpRequest-Objekt und gibt es zurück.
/*	Der Aufruf erfolgt synchron, falls onCompletion fehlt (d.h. bei Beendigung der Funktion ist das Ergebnis des Aufrufs schon bekannt),
/*	ansonsten asynchron, falls onCompletion vorhanden ist (die Funnktion springt sofort zurück.
/*
/* @PARAM uri
/*	URI des zu ladenden Dokuments/Skripts
/*
/* @PARAM method
/*	"GET" oder "POST" (darf auch weggelassen werden, wenn params ebenfalls weggelassen wurde)
/*
/* @PARAM params
/*	Zu übergebender Parameter. Darf sein:
/*	* nichts
/*	* ein String -> wird direkt übergeben
/*	* (nur in Verbindung mit POST) ein Hash mit Schlüssel-Wert-Paaren -> diese werden als CGI-Parameter übernommen.
/*		Die Werte dürfen Strings oder Arrays von Strings sein, evtl. vorhandenen NULL-Werte werden einfach übersprungen.
/*
/* @PARAM onCompletion
/*	Falls vorhanden, wird ein asynchroner Aufruf durchgeführt und bei Abschluss der hiermit übergebene Code ausgeführt
/*
/*	Je nach Typ wird wie folgt verfahren:
/*
/*	* String: diesen Code bei Abschluss per eval() aufrufen, request ist das XMLHttpRequest-Objekt.
/*
/*	* Funktion: diese Funktion bei Abschluss aufrufen, erster Parameter ist das XMLHttpRequest-Objekt.
/*
/*	* Array: erstes Element ist die bei Abschluss aufzurufende Funktion, erster Parameter ist das XMLHttpRequest-Objekt,
/*	  weitere Parameter sind die dieser übergebenen Parameter.
/*
/*************************************************************************************/

	if (method == undefined && params == undefined) {
		method = "GET";
	}
	method = method.toUpperCase();
	if (method != "GET" && method != "POST") {
		throw new Error("Core::createHttpRequest(): invalid method passed (" + method + ")!\nOnly 'GET' and 'POST' allowed.");
	}

	try {
		var request = new XMLHttpRequest();
	} catch (e) {
		throw new Error("Core::createHttpRequest():\n\nYour browser does not support AJAX technology!\nPlease update or switch your browser to an appropriate version or product! (Error: " + e.message + ")");
	}


    if (request.overrideMimeType) {
        request.overrideMimeType('text/xml');
    }

    var postUrlEncoded = (method == "POST" && params instanceof Object);

    if (postUrlEncoded) {
    	var newparams = "";
    	for (var k in params) {
    	 	var v = params[k];
    	 	if (v instanceof Array) {
    	 		for (var i = 0; i < v.length; i++) {
    	 			if (v[i] != undefined) {
	    	 			newparams += encodeURIComponent(k) + "=" + encodeURIComponent(v[i]) + "&";
	    	 		}
    	 		}
    	 	} else if (v != undefined) {
    	 		newparams += encodeURIComponent(k) + "=" + encodeURIComponent(v) + "&";
    	 	}
    	}
    	params = newparams;
    }

	var requestObj = undefined;

	if (onCompletion) {
		var reqId = generateObjectId(request);
		var objectCode = "findObjectWithId(" + reqId + ")";
		var deleteCode = "deleteObjectWithId(" + reqId + ")";

		if (typeof onCompletion == "string") {
			eval("request.onreadystatechange = function() {var request = " + objectCode + "; if (request.readyState == 4) {if(1){" + onCompletion + "} " + deleteCode + "}}");
		} else if (typeof onCompletion == "function") {
			var completionFunction = generateOneTimeObjectCode(onCompletion);
			eval("request.onreadystatechange = function() {var request = " + objectCode + "; if (request.readyState == 4) { " + completionFunction + "(request); " + deleteCode + " }}");
		} else if (onCompletion instanceof Array) {
			var completionFunction = generateOneTimeObjectCode(onCompletion[0]);
			var funcParams = "request";
			for (var i = 1; i < onCompletion.length; i++) {
				funcParams += ", " + generateOneTimeObjectCode(onCompletion[i]);
			}
			eval("request.onreadystatechange = function() {var request = " + objectCode + "; if (request.readyState == 4) { " + completionFunction + "(" + funcParams + "); " + deleteCode + "}}");
		} else {
			throw new Error("Invalid onCompletion type " + onCompletion);
		}
	}

	request.open(method, uri, onCompletion ? true : false);
	if (postUrlEncoded) {
		request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
	}
	request.send(params);

	return request;
}

function downloadText(uri, method, params, silent) {
/************************************************************************************/
/* @TITLE
/*	Text herunterladen
/*
/* @SYNOPSIS
/*	alert(downloadText("nachricht.txt"));
/*
/* @DESCRIPTION
/*	Lädt einen Text per createHttpRequest() herunter.
/*
/* @PARAM uri
/*	URI des zu ladenden Dokuments/Skripts
/*
/* @PARAM method
/*	"GET" oder "POST" (darf auch weggelassen werden, wenn params ebenfalls weggelassen wurde)
/*
/* @PARAM params
/*	Zu übergebender Parameter. Kann ein String sein, kann aber auch weggelassen werden.
/*
/* @PARAM silent
/*	Falls true, wird im Fehlerfall <em>undefined</em> zurückgegeben, ansonsten wird eine Exception geworfen.
/*************************************************************************************/
	var request = createHttpRequest(uri, method, params);
	if (request.status == 200) {
		return request.responseText;
	} else if (silent) {
		return undefined;
	} else {
		throw new Error("Got status " + request.status + " while trying to fetch URI " + uri);
	}
}

function downloadXML(uri, method, params, silent) {
/************************************************************************************/
/* @TITLE
/*	XML-Daten herunterladen
/*
/* @SYNOPSIS
/*	var xml = downloadXML("nachricht.txt");
/*
/* @DESCRIPTION
/*	Lädt XML-Daten per createHttpRequest() herunter.
/*
/* @PARAM uri
/*	URI des zu ladenden Dokuments/Skripts
/*
/* @PARAM method
/*	"GET" oder "POST" (darf auch weggelassen werden, wenn params ebenfalls weggelassen wurde)
/*
/* @PARAM params
/*	Zu übergebender Parameter. Kann ein String sein, kann aber auch weggelassen werden.
/*
/* @PARAM silent
/*	Falls true, wird im Fehlerfall undefined zur�eben, ansonsten wird eine Exception geworfen.
/*************************************************************************************/
	var request = createHttpRequest(uri, method, params);
	if (request.status == 200) {
		return request.responseXML;
	} else if (silent) {
		return undefined;
	} else {
		throw new Error("Got status " + request.status + " while trying to fetch URI " + uri);
	}
}


function HttpStream(uri, params, method, mode, callback){
/************************************************************************************/
/* @TITLE
/*	Core::HttpStream
/*
/* @DESCRIPTION
/*	Das Ganze nochmal als Klasse zwecks Signaturkompatibilität mit den übrigens Libs
/*
/* @AUTHOR
/*	Marcus Bloch
/*
/* @COPYRIGHT
/*	Copyright © 2004-2005 by CHAMAELEON AG (http://www.chamaeleon.de) - Oktober 2005
/*************************************************************************************/
	// Part 1: Konstruktor

	//
	// Vorbedingungen prüfen
	//
	if(!window.validateUri){
		alert("jslib::Core::HttpStream(): URI validation function not defined.\nPerhaps you forgot to include the jslib::validate.js?");
		return;
	}
	if(!validateUri(uri, true)){
		return false;
	}
	for(var key in params){
		if(!params[key].match(/^[\w\_]+\=\S*$/)){
			alert("jslib::Core::HttpStream(): invalid cgi parameter tuple for AJAX call found:\n["+ params[key] + "]");
			return false;
		}
	}


	mode = mode.toUpperCase();
	if(mode != "SYNC" && mode != "ASYNC"){
		alert("jslib::Core::HttpStream(): invalid mode passed [" + mode + "]!\nOnly 'SYNC' and 'ASYNC' allowed.");
		return false;
	}

	if(mode == "ASYNC" && !window[callback]){
		alert("jslib::Core::HttpStream(): The callback function given ('" + callback + "') could not be found at current window ('" + window.name + "')");
		msg = "";
		for(k in window){
			msg += k + ", ";
		}

		return false;
	}

	//
	// Für GET: URI um Parameter erweitern. Für POST: XML-Body zusammen setzen.
	//
	if(method == "GET"){
		var firstMark = (uri.indexOf("?") > 0 ? "&" : "?");
		uri += firstMark + params.join("&");
		var body = null;
	}
	else{
		var body = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<call>\n";
		var container;
		for(var key in params){
			container = params[key].split("=");
			body += " <param key=\"" + container[0] + "\" value=\"" + container[1] + "\" />\n";
		}
		body += "</call>\n";
	}

	//
	// Call absetzen
	//
	this.xmlhttp = createHttpRequest(uri, method, body, mode == "ASYNC" ? callback + "()" : undefined);

	// Part 2: Klassenmethoden

	this.isOK = function(){
		if(!this.xmlhttp){
			alert("jslib::Core::HttpStream::isOK(): Object has not been correctly initialized!");
			return false;
		}
		var msg = "";
		for(var k in this.xmlhttp){
			msg += k + ", ";
		}
		return (this.xmlhttp.status == 200);
	}


	this.fetch = function(){
		if(this.isOK()){
			return this.xmlhttp.responseText;
		}
		return "";
	}
}



var _libraries = {};
var _scriptPathStack = [];
var _scriptPath = "";

function require_once() {
/************************************************************************************/
/* @TITLE
/*	Bibliotheken nachladen
/*
/* @SYNOPSIS
/*	requires("foo.js", "bar.js");
/*
/* @DESCRIPTION
/*	Diese Funktion lädt eine Bibliothek nach. Das ist nur zur Ladezeit des Skriptes
/*	möglich. Vorsicht: Der Ladevorgang findet erst _nach_ dem aktuellen Skriptblock statt!
/*
/*	Pro Bibliothek kann nur ein Ladeversuch unternommen werden, weitere Versuche werden
/*	ignoriert, um unnötiges Mehrfachladen zu verhindern. Rekursives Nachladen (eine
/*	geladene Bibliothek lädt ihrerseits weitere Bibliotheken nach) ist ebenfalls möglich,
/*	Endlosschleifen können so nicht auftreten.
/*
/*	Es sind nur einfache Pfade und Namen erlaubt, keine kompletten URLs oder GET-Parameter
/*
/* @PARAM url
/*	URLs der zu ladenden Skripte. Beliebig viele Skripte können hier angegeben werden.
/*************************************************************************************/
	for (var i = 0; i < arguments.length; i++) {
		var script = arguments[i];
		if (script.substr(0,1) != "/") {
			script = _scriptPath + script;
		}
		var path = script.replace(/[^\/]+$/, "");
		if (!_libraries[script]) {
			_libraries[script] = 1;
			document.write(
				'<scr'+'ipt type="text/javascript">_scriptPathStack.push(_scriptPath); _scriptPath = "' + path + '";</scr' + 'ipt>'
			  + '<scr'+'ipt type="text/javascript" src="' + script + '"></scr' + 'ipt>'
			  + '<scr'+'ipt type="text/javascript">_scriptPath = _scriptPathStack.pop();</scr' + 'ipt>');
		}
	}
}

var _errors = "";

function warn(msg) {
/************************************************************************************/
/* @TITLE
/*	Warnung ausgeben
/*
/* @SYNOPSIS
/*	warn("Fehler");
/*
/* @DESCRIPTION
/*	Sammelt Fehler und gibt diese erst mit kurzer Verzögerung als Message Box aus.
/*
/* @PARAM msg
/*	Fehlermeldung
/*************************************************************************************/
	if (_errors == "") {
		setTimeout("_displayErrors()", 0);
	}
	_errors += msg;
	if (!_errors.match(/\n$/)) {
		_errors += "\n";
	}
}

function _displayErrors() {
	if (_errors != "") {
		var myErrors = _errors;
		_errors = "";
		alert(myErrors);
	}
}

var _idStorage = [];

function generateObjectId(object) {
/************************************************************************************/
/* @TITLE
/*	ID für Objekt generieren
/*
/* @DESCRIPTION
/*	Generiert eine eindeutige ID, über die das Objekt später wieder abgerufen werden kann
/*************************************************************************************/
	var id = _idStorage.length;
	_idStorage[id] = object;
	return id;
}

function deleteObjectWithId(id) {
/************************************************************************************/
/* @TITLE
/*	ID eines Objektes löschen
/*
/* @DESCRIPTION
/*	Wichtig, damit auch die Referenz auf das Objekt wieder freigegeben werden kann
/*************************************************************************************/
	delete _idStorage[id];
}

function findObjectWithId(id) {
/************************************************************************************/
/* @TITLE
/*	Objekt zu einer ID abrufen
/*
/* @DESCRIPTION
/*	Ruft ein Objekt zu einer zuvor generierten ID ab
/*************************************************************************************/
	return _idStorage[id];
}

function findAndDeleteObjectWithId(id) {
/************************************************************************************/
/* @TITLE
/*	Objekt abrufen und ID löschen
/*************************************************************************************/
	var result = _idStorage[id];
	delete _idStorage[id];
	return result;
}

function generateOneTimeObjectCode(object) {
/************************************************************************************/
/* @TITLE
/*	Code-String erzeugen, der einmalig bei Ausführung das Objekt zurückliefert
/*************************************************************************************/
	return "findAndDeleteObjectWithId(" + generateObjectId(object) + ")";
}

function reposition(x, y){
	/********************************************************************************
	/* @TITLE
	/*	Skalieren und Repositionieren eines Fensters
	/*
	/* @DESCRIPTION
	/*	Diese Methode skaliert das aktive Fenster zunächst auf die Länge der x- und y-
	/*	Angaben und positioniert es dann mittig auf dem Bildschirm.
	/*
	/* @PARAM x
	/*	Soll-Breite des Fensters
	/* @PARAM y
	/*	Soll-Höhe des Fensters
	/*
	/* @SYNOPSIS
	/*	<body onLoad="reposition(100,200)">...</body>
	/********************************************************************************/
	if(isFirefox){
		y += 18;	// Der Firefox lässtt die Statusbar nicht ausblenden!
	}
	window.resizeTo(x, y);
	window.moveTo((screen.width - x) / 2, (screen.height - y) / 2);
	window.focus();
}


var scroll_y = 0;
function scrollstep(scrollableElement, speed){
	/********************************************************************************
	/* @TITLE
	/*	Text durchscrollen lassen
	/*
	/* @DESCRIPTION
	/*	Diese Methode wird mit der ID (oder einer Referenz) auf den
	/*	zu scrollenden DIV-Bereich sowie einer Angabe zur Scroll-Geschwindigkeit
	/*  aufgerufen. Der Text wird sofort beginnend durchgescrollt.
	/*
	/* @PARAM scrollableElement
	/*	ID oder Referenz auf das zu scrollende DIV-Element
	/*
	/* @PARAM speed
	/*	Scroll-Geschwindigkeit. Entspricht den Millisekunden Wait-Time zwischen den
	/*	Einzelschritten. 33 ergibt eine mittlere Scroll-Geschwindigkeit.
	/*
	/* @SYNOPSIS
	/*	setTimeout("scrollstep('myScrollDiv', 33)", 2000);	// Bsp.: warte anfangs 2 Sekunden
	/********************************************************************************/

	var scrolltext = getElement(scrollableElement);
	var h = 100;
	scroll_y += 1;
	if(scroll_y == scrolltext.offsetHeight){
		scroll_y = -h;
	}
	scrolltext.style.clip = "rect(" + scroll_y + "px, 400px," + (scroll_y + h) + "px, 0px)";
	scrolltext.style.top = (-scroll_y) + "px";
	setTimeout("scrollstep(" + generateOneTimeObjectCode(scrollableElement) + ", " + speed + ")", speed);
}


function setErrorHandlerURL(callbackURL) {
	/********************************************************************************
	/* @TITLE
	/*	Benutzerdefinierte Fehlerbehandlung per Rückmeldung an Server
	/*
	/* @DESCRIPTION
	/*	Falls eine Exception es bis nach oben schafft, wird dies im Normalfall als
	/*	JavaScript-Fehler angezeigt. Durch Aufruf dieser Methode wird eine URL
	/*	hinterlegt, die im Fehlerfall mit 4 Parametern (desc, page, line, char)
	/*	aufgerufen wird, die die Fehlerquelle beschreiben.
	/*
	/*	Vom Server wird erwartet, dass dieser eine XML-Struktur zur�fert.
	/*	Deren Wurzelknoten kann folgende Attribute enthalten:
	/*
	/*	* Attribut "message": Diese Nachricht wird dem Benutzer gezeigt falls vorhanden
	/*	* Attribut "function": Diese Funktion wird aufgerufen (mit der XML-Struktur
	/*	  als Parameter)
	/*	* Attribut "code": Dieser Code wird ausgef�per eval, die XML-Struktur
	/*	  steht in der Variable xml zur Verfügung
	/*
	/*	Eventuelle Unterknoten und weitere Attribute werden ignoriert und sind nur
	/*	für die eventuell aufgerufene Funktion oder Code relevant.
	/********************************************************************************/
	if (callbackURL != undefined) {
		eval("window.onerror = function(a,b,c,d) {return __xmlErrorHandler(a,b,c,d,findObjectWithId(" + generateObjectId(callbackURL) + "))}");
	} else {
		window.onerror = null;
	}
}
