/**
 * @fileoverview
 * atoolo - Cynapsis Kommunikationsagentur
 * @author Holger Veltrup
 * @version 0.9.3
 */

/**
 * Private Konstruktor. Instanzen des ResourceLoader werden ueber ResourceLoader.createLoader erzeugt.
 * @class
 * Ueber den ResourceLoader werden Resourcen wie JavaScript, Cascading-Stylesheets, VisualBasicScript oder
 * Dateien wie XML, JSON oder beliegige Text-Dateien geladen ueber HTTP mit Hilfe der Ajax-Technologie geladen
 * Der ResourceLoader arbeitet in zwei Varianten.<br>
 *	<ol>
 *		<li>
 *			Laden von Resourcen, die dem Browser zur Verfuegung gestellt werden sollen. Darunter Fallen
 *			JavaScript, Cascading-Stylesheets und VisualBasicScript. Der Inhalt der Datei wird geladen
 *			und in das Dokument eingefuegt, so das Beispielsweise geladene JavaScript-Funktionen verwendet
 *			werden koennen. Diese Resourcen werden mit der Methode {@link ResourceLoader#loadResourceList} geladen.
 *			Die angegebenen Resourcen werden <strong>asynchron</strong> geladen. Wird eine callback-Funktion mit
 *			angegeben, wird diese ausgefuehrt, wenn alle Resourcen komplett geladen und verarbeitet sind.
 *		</li>
 *		<li>
 *			Laden von Resourcen, die innerhalt vom JavaScript weiter verarbeitet werden sollen. Dazu gehoeren
 *			XML-Dateien, JSON-Strukturen und beliebige Text-Dateien. Diese Resourcen koennen mit den Methoden
 *			{@link ResourceLoader#getJsonObject},  {@link ResourceLoader#getDOMDocument}, {@link ResourceLoader#getContent},
 *			{@link ResourceLoader#getExternalDOMDocument}, {@link ResourceLoader#getExternalContent}, {@link ResourceLoader#getStorageFileContent}
 *			und {@link ResourceLoader#getStorageIdByName} geladen werden.
 *		</li>
 *	</ol>
 *
 * <strong>MSIE 6 workaround fuer CSS</strong>
 * <ul>
 *	<li>Lade mehrere CSS-Dateien asynchron</li>
 *	<li>Wenn alle geladen sind werden sie zu einem String zusammengefasst</li>
 *	<li>Wenn noch keine 20 Style-Tags erzeugt wurden, CSS-String in neuem Style-Tag dem Head hinzufuegen</li>
 *	<li>Wenn schon 20 Style-Tags existieren den Style-Tag mit den wenigsten Daten erweitern.</li>
 *	<li>Damit das Funktioniert muss noch folgendes Beachtet werden:
 *		<ol>
 *			<li>Da der CSS-Text den in dem DOM-Baum beim MSIE nicht mehr mit dem Originalen CSS-Daten uebereinstimmt (Expressions) muessen
 *				saemtliche CSS-Daten noch einam separat in Java-Script gehalten werden. Nur mit den Originaldaten koennen CSS erweitert werden, wenn die maximale Anzahl von Style-Tags erreicht ist.</li>
 *			<li>Einige Style-Angabe ueberschreiben schon definierte Style-Angaben. Damit die Reihenfolge in der die Style-Angaben ueberschrieben werden erhalten bleibt hab ich zwei Bereiche definiert:
 *				<dl>
 *					<dt><tt>base</tt></dt>
 *					<dd>Die Basis-CSS (in /css) diese werden zu erste geladen und koennen nur solange erweitert werden, bis Style-Angaben in dem zweitem Bereich (dependent) geschrieben wurden.</dd>
 *					<dt><tt>dependent</tt></dt>
 *					<dd>Dieses sind Tool-CSS. Sobald CSS in diesem Bereich enthalten sind, koennen in dem base-Bereich keine neuen CSS hinzugefuegt werden.</dd>
 *			</dl>
 *			</li>
 *		</ol>
 *	</li>
 * </ul>
 */
function ResourceLoader(id, asynchron, asynchronCallback, loadingLevel) {
	this.id = id;
	if (asynchron == null || !asynchron) {
		 this.asynchron = false;
	} else {
		this.asynchron = true;
		this.asynchronCallback = asynchronCallback;
		this.asynchronRequests = new Object();
		this.loadingLevel = loadingLevel;
		this.cssBatch = new Array();
	}
}

/**
 * Zaehler mit dem die id's des Loaders generiert werden
 * @private
 */
ResourceLoader.idCounter = 0;
/**
 * Id Prefix fuer die Loader Id's. Die Id's beginnen mit einem
 * Zeichen, damit kein Array sondern ein Objekt verwendet werden kann
 * Mit delete koennen die Loader dann entfernt werden
 * @private
 * @final
 */
ResourceLoader.ID_PREFIX = "loader";

/**
 * Gibt einen Timeout fuer eine Request an
 * @private
 * @final
 */
ResourceLoader.TIMEOUT = 2000;

/**
 * Funktionsdefinitionen generisch als CrossBrowser-Instanz
 * @private
 */
ResourceLoader.xml = {
  useActiveX:(typeof ActiveXObject != "undefined"),
  useDom:document.implementation && document.implementation.createDocument,
  useXmlHttp:(typeof XMLHttpRequest != "undefined")
};

/**
 * Zu verwendendende XML-HTTP-Version. Wird ueber ResourceLoader.createRequest() ermittelt
 * @private
 */
ResourceLoader.xmlHttpVersion = null;

/**
 * Moegliche XML-Http Versionen
 * @private
 */
ResourceLoader.ARR_XMLHTTP_VERS = [
	"MSXML2.XmlHttp.6.0",
	"MSXML2.XmlHttp.5.0",
	"MSXML2.XmlHttp.4.0",
	"MSXML2.XmlHttp.3.0",
	"MSXML2.XmlHttp",
	"Microsoft.XmlHttp"
];

/**
 * Moegliche DOM Versionen
 * @private
 */
ResourceLoader.ARR_DOM_VERS = [
	"MSXML2.DOMDocument.6.0",
	"MSXML2.DOMDocument.5.0",
	"MSXML2.DOMDocument.4.0",
	"MSXML2.DOMDocument.3.0",
	"MSXML2.DOMDocument",
	"Microsoft.XmlDom"
];

/**
 * Enthaelt alle bereits geladenen URLs
 * @private
 */
ResourceLoader.loadedUrls = new Object();

/**
 * Enthaelt alle aktiven Loader
 * @private
 */
ResourceLoader.loaderMap = new Object();

/**
 * Basis URL um realtive Pfade zu ermitteln
 * @private
 */
ResourceLoader.base = null;

/**
 * Die CSS soucen muessen wir auch noch cache :-(, weils der MSIE es nicht gesch...en kriegt.
 * @private
 */
ResourceLoader.cssBaseSources = new Array(); // MSIE6 workaround

/**
 * Die CSS soucen muessen wir auch noch cache :-(, weils der MSIE es nicht gesch...en kriegt.
 * @private
 */
ResourceLoader.cssDependentSources = new Object(); // MSIE6 workaround


/**
 * Id des Loaders
 * @private
 */
ResourceLoader.prototype.id = 0;

/**
 * Id des Loaders
 * @private
 */
ResourceLoader.prototype.asynchron = false;

/**
 * Id des Loaders
 * @private
 */
ResourceLoader.prototype.cssBatch = null; // MSIE6 workaround

/**
 * Ladelevel (base|dependent)
 * @private
 */
ResourceLoader.prototype.loadingLevel = null; // MSIE6 workaround

/**
 * Callback Methode, die aufgerufen wird, wenn alle Resourcen gelanden und
 * verarbeitet wurden.
 * @private
 */
ResourceLoader.prototype.asynchronCallback = false;

/**
 * XMLHttpRequests die asynchron geladen werden
 * @private
 */
ResourceLoader.prototype.asynchronRequests = null;

/**
 * Anzahl der asynchornen XMLHttpRequests
 * @private
 */
ResourceLoader.prototype.asynchronRequestCounter = 0;

/**
 * Timeout-Handler um den Timeout nach erfolgreichem Request zurueckzusetzten
 * @private
 */
ResourceLoader.prototype.timeoutHandler = null;

/**
 * Flag, das Anzeigt, das der ASYNCHRONE Request abgebrochen wurde
 * @private
 */
ResourceLoader.prototype.isAborted = false;

/**
 * Flag, das Anzeigt, das der ASYNCHRONE Request abgebrochen wurde
 * @private
 */
ResourceLoader.prototype.isFinished = false;

/**
 * Ermittle, die bereist im DOM-Baum enthaltenen Resourcen und traegt sie in einer
 * internen liste ein
 * @type void
 */
ResourceLoader.detectLoadedResouces = function() {

	ResourceLoader.loadedUrls = new Object();

	// AtooloConfig.getInstance().getVersion();


	// Urls aller schon geladener Resoucesn ermitteln
	for (var i = 0; i < document.styleSheets.length; i++) {
		/* Kracht ab und zu
		if (document.styleSheets[i].imports) {
			for (var j = 0; i < document.styleSheets[i].imports.length; j++) {
				var ref = document.styleSheets[i].imports[j].href;
				if (ref != null && ref != '') {
					var id = ResourceLoader.createId(ref);
					ResourceLoader.loadedUrls[id] = true;
				}
			}
		}
		*/
		var ref = document.styleSheets[i].href;
		if (ref != null && ref != '') {
			var id = ResourceLoader.createId(ref);
			ResourceLoader.loadedUrls[id] = true;
		}
		/*
		var rules = document.styleSheets[i].cssRules ? document.styleSheets[i].cssRules : document.styleSheets[i].rules;
		for (var j = 0; j < rules.length; j++) {
			AT.log("rules: " + rules);

		}
		*/

		for (var id in ResourceLoader.loadedUrls) {
//			AT.log("detect " + id);
		}
	}

	var scripts = document.getElementsByTagName("script");
	for (var i = 0; i < scripts.length; i++) {
		if (scripts[i].id != null && scripts[i].id != '') {
			ResourceLoader.loadedUrls[scripts[i].id] = true;
		}
	}
}


/**
 * Liefert einenn ResourceLoader fuer synchrone Ladevorgaenge
 * @return neuer Loader
 * @type ResourceLoader
 */
ResourceLoader.createLoader = function() {
	ResourceLoader.idCounter++;
	var loader = new ResourceLoader(ResourceLoader.ID_PREFIX + ResourceLoader.idCounter);
	ResourceLoader.loaderMap[loader.getId()] = loader;
	return loader;
}

/**
 * Liefert einenn ResourceLoader fuer asynchrone Ladevorgaenge
 * @param {String} loadingLevel Gibt die Stufe an, den die Resoucen haben. Es existieren zwei Stufen:<br>
 * <ol>
 *	<li><tt>base</tt> sind Basis-Resourcen die, wenn sie geladen wurden auch nicht mehr augefasst werden duerfen
 *	nachdem Resourcen der zweiten Stufe <tt>dependent</tt> geladen wurden</li>
 *	<li><tt>dependent</tt> sind Resouren die von den Basis-Resoucen abhaengen. Nachdem eine Dependent-Resource geladen wurde
 *	ist es nicht mehr moeglich eine Basis-Resourcen zu laden. Der Grund dafuer ist das Dependent-Resource CSS-Angaben der
 *	Basis-Resourcen ueberschreiben koennen. Wuerden dann noch weitere Basis-Resourcen geladen werden wuerden die
 *	CSS-Angaben der Dependent-Resource wieder von den Basis-Resourcen ueberschrieben werden. Die ist natuerlich nur notwendig,
 *	da der MSIE6 nur 30-Style-Tags kann</li>
 * </ol>
 * @param {Function} callback Callback-Funktion die aufgerufen wird, wenn die Asynchron zu ladenen Daten
 *	geladen sind.
 * @return neuer asynchroner Loader
 * @type ResourceLoader
 */
ResourceLoader.createAsynchronLoader = function(loadingLevel, callback) {
	ResourceLoader.idCounter++;
	var loader = new ResourceLoader(ResourceLoader.ID_PREFIX + ResourceLoader.idCounter, true, callback, loadingLevel);
	ResourceLoader.loaderMap[loader.getId()] = loader;
	return loader;
}

/**
 * Loescht einen Loader aus der LoaderListe
 * @param {id} id Id des Loaders
 */
ResourceLoader.removeLoader = function(id) {
	if (ResourceLoader.loaderMap[id] != null) {
		ResourceLoader.loaderMap[id].abort();
		delete ResourceLoader.loaderMap[id];
	}
}

/**
 * Prueft, ob der Loader mit der angegebenen Id (noch) existiert
 * @param {id} id Id des Loaders, dessen Existenz geprueft werden soll
 * @return <code>true</code>, wenn der Loader existiert, sonst <code>false</code>
 * @type boolean
 */
ResourceLoader.exists = function(id) {
	return (typeof ResourceLoader.loaderMap[id] == "object");
}

/**
 * Liefert den Loader mit der angegebenen Id
 * @param {id} id Id des Loaders
 * @return Loader mit der angegebenen Id oder <code>null</code>, wenn kein Loader mit der Id, existiert
 * @type ResourceLoader
 */
ResourceLoader.getLoader = function(id) {
	return ResourceLoader.loaderMap[id];
}

/**
 * Liefert die Id des Loaders
 * @return Id des Loaders
 * @type String
 */
ResourceLoader.prototype.getId = function() {
	return this.id;
}

/**
 * Erzeut einen HTTP-Request fuer Ajax-Abfragen
 * @return Liefert ein Cross-Browser XMLHttpRequest zurueck
 * @type XMLHttpRequest
 * @private
 */
ResourceLoader.createRequest = function() {

	if (ResourceLoader.xml.useXmlHttp){
		return new XMLHttpRequest();
	} else if(ResourceLoader.xml.useActiveX) {
		if(!ResourceLoader.xmlHttpVersion) {
			for(var i = 0; i < ResourceLoader.ARR_XMLHTTP_VERS.length; i++) {
				try {
					new ActiveXObject(ResourceLoader.ARR_XMLHTTP_VERS[i]);
					ResourceLoader.xmlHttpVersion = ResourceLoader.ARR_XMLHTTP_VERS[i];
					break;
				} catch(oError) {} // Absicht: Hier soll kein Error geworfen werden!
				// es wird die "beste" Version fuer MSIE ermittelt
			}
		}
		if(ResourceLoader.xmlHttpVersion) {
			return new ActiveXObject(ResourceLoader.xmlHttpVersion);
		} else {
//			AT.log("atoolo: Could not create XML HTTP Request.", "error");
		}
	} else {
//		AT.log("atoolo: Your browser doesn't support an XMLHTTP Request.", "error");
	}

	return null;
}

/**
 * Liefert <code>true</code>, wenn der Browser Ajax unterstuetzt.
 * @return <code>true</code>, wenn der Browser Ajax unterstuetzt.
 * @type boolean
 */
ResourceLoader.isAjaxSupported = function() {
  return ResourceLoader.xml.useXmlHttp || ResourceLoader.xml.useActiveX;
}

/**
 * Bricht einen aktiven <strong>ansynchronen</strong> Request ab.
 * @type void
 */
ResourceLoader.prototype.abort = function() {
	try {
		for (key in this.asynchronRequests) {
			var req = this.asynchronRequests[key];
			if (req != null && !this.isAborted) {
				this.isAborted = true;
//				AT.log("ResourceLoader.prototype.abort(): abort request", "warn");
				req.abort();
			}
			this.asynchronRequests = new Object();
		}
	} catch (e) {
//		AT.log("ResourceLoader.prototype.abort(): " + e, "error", e);
	}
}

/**
 * Liefert den Fortschritt der Asynchron geladenen Rresoucen in Prozent
 * @return Fortschritt der Asynchron geladenen Rresoucen in Prozent
 * @type int
 */
ResourceLoader.prototype.getAsynchronLoadStatus = function() {

	var status = 0;

	if (this.asynchronRequestCounter == 0) {
		status = 100;
	} else {
		var processStatus = 0;
		var c = 0;
		for (id in this.asynchronRequests) {
			c++;
			var req = this.asynchronRequests[id];
			if(req.readyState == 1) {
				processStatus += 1;
			} else if(req.readyState == 2) {
				processStatus += 2;
			} else if(req.readyState == 3) {
				processStatus += 2;
			} else if(req.readyState == 4) {
				processStatus += 3;
			} else {
//				AT.log(c + " unknown state: " + id + ": " + req.readyState ,"error");
			}
		}

		if (this.asynchronRequestCounter != c) {
//			AT.log("invalid request counter " + this.asynchronRequestCounter + " != " + c, "error");
		}

		if (processStatus > (this.asynchronRequestCounter * 3)) {
//			AT.log("processStatus greater as counter " + processStatus, "error");
		}

		status = (processStatus * 100) / (this.asynchronRequestCounter * 3);
	}

	if (status >= 100) {
		if (status > 100) {
//			AT.log("calculation error: status = " + status, "error");
		}
		return 100;
	} else {
		return status;
	}
}

/**
 * Wird fuer jede, im Ladevorgang befindliche Resouces aufgerufen, wenn sich desses Lade-Status
 * geaendert hat
 * @type void
 * @private
 */
ResourceLoader.prototype.onreadystatechange = function(sWorkon, sUrl) {

	var sId = ResourceLoader.createId(sUrl);
	var req = this.asynchronRequests[sId];
	if (req) {
		if (req.readyState == 1) {
		} else if(req.readyState == 2) {
		} else if(req.readyState == 3) {
		} else if(req.readyState == 4) {
			var response = null;
			if (req.status == 200) {
				if ((Plappadu.Utils.Browser.isIE6 || Plappadu.Utils.Browser.isIE7) && sWorkon == 'addCss') { // MSIE6 workaround
					this.cssBatch.push(req.responseText);
				} else {
					response = this.workon(sWorkon, sUrl, req);
				}
				//AT.log(sUrl + " loaded");
			} else {
				response = "error";
//				AT.log(sUrl + " loading failed, status: " + req.status, "error");
			}
			delete this.asynchronRequests[sId];
			this.asynchronRequestCounter--;
			if (this.asynchronRequestCounter == 0 && this.isFinished) {
				if (Plappadu.Utils.Browser.isIE6 || Plappadu.Utils.Browser.isIE7) { // MSIE6 workaround
					this.executeCssBatch();
				}
				ResourceLoader.removeLoader(this.getId());
				if (this.asynchronCallback != null) {
					this.asynchronCallback(response);
				}
			}
		}
	} else {
//		AT.log("request " + sId + " not found", "error");
	}
}

/**
 * Fuert einen Request aus und Liefert je nach <code>sWorkon</code> ein Objekt zurueck.
 * Das <code>sWorkon</code> definiert wie der Request verarbeitet werden soll. Folgende Werte sind moeglich
 *	<table>
 *		<tr><th><code>sWorkon</code></th><th>Beschreibung</th></tr>
 *		<tr><td>addJs</td><td>Laed ein JavaScript und fuegt es in das Document ein. Der Rueckgabewert ist <code>null</code></td></tr>
 *		<tr><td>addCss</td><td>Laed eine CSS-Datei und fuegt es in das Document ein. Der Rueckgabewert ist <code>null</code></td></tr>
 *		<tr><td>addVb</td><td>Laed eine Visual-Basic-Datei und fuegt es in das Document ein. Der Rueckgabewert ist <code>null</code></td></tr>
 *		<tr><td>getFileContent</td><td>Liefert den Inhalt der Datei als String zurueck</td></tr>
 *		<tr><td>getXML</td><td>Liefert die Datei als XML-DOMDocument zurueck</td></tr>
 *		<tr><td>getJson</td><td>Evaluiert den Inhalt der Datei und liefert in als Java-Script-Objekt (JSON) zurueck</td></tr>
 *	</table>
 * @param {String} sMethod HTTP-Methode (GET|POST)
 * @param {String} sUrl URL auf die der Request ausgefuehrt werden soll
 * @param {Object} oContent Inhalt der gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @param {Array} aRequestHeader Mit zu sendende HTTP-Request Header
 * @param {String} sWorkon gibt an, wie der Request verarbeitet werden soll (addJs|addCss|addVb|getFileContent|getXml|getJson)
 * @return Je nach <code>sWorkon</code> wird ein Objekt oder <code>null</code> zurueckgeliefert:<br>
 *	<ul>
 *		<li><strong>getFileContent</strong>: liefert einen String mit dem Inhalt der Datei zurueck</li>
 *		<li><strong>getXML</strong>: liefert ein XMLDocument der angegebenen XML-Datei zurueck</li>
 *		<li><strong>getJson</strong>: liefert ein JavaScript-JSON-Objekt zurueck</li>
 *		<li>bei allen anderen <code>sWorkon</code> wird <code>null</code> zurueckgeliefert
 *	</ul>
 * @type String, XMLDocument, Object
 * @private
 */
ResourceLoader.prototype.doRequest = function(sMethod, sUrl, oContent, aRequestHeader, sWorkon) {
  if (this.oReq != null) {
		throw new Error("this loader (" + this.getId() + ") is already in use.");
	}

	sUrl = ResourceLoader.rewriteUrl(sUrl);
	// Url-Parameter, um Browser-Cache zu umgehen
	var cacheparam = "";
	// Im debug-Modues auch die js, css und vb immer mit einem Zeitstempel versehen,
	// um sie immer neu zu laden
	if (sUrl.indexOf("?") == -1) {
		sUrl = sUrl + "?" + cacheparam;
	} else {
		sUrl = sUrl + "&" + cacheparam;
	}

	var req = null;
	try {
		req = ResourceLoader.createRequest();
	} catch (e) {
//		AT.log("ResourceLoader.prototype.doRequest: create request " + sUrl, "error", e);
		return null;
	}
	// Asynchrones laden
	if (this.asynchron) {
		var sId = ResourceLoader.createId(sUrl);
		if (!this.asynchronRequests[sId]) {
			this.asynchronRequestCounter++;
			this.asynchronRequests[sId] = req;
		}
		req.onreadystatechange = new Function(
			"var loader = ResourceLoader.getLoader('" + this.getId() + "'); " +
			"if (loader != null) { " +
				"loader.onreadystatechange('" + sWorkon + "', '" + sUrl + "');" +
			"} else { " +
//				"AT.log('" + this.getId() + ": load not found','error');" +
			"}");
	}

	try {
		var t1 = new Date().getTime();
		try {
			// Request zusammenstellen
			if (oContent != null) {
				if (typeof(oContent) == "object") {
					oContent = ResourceLoader.getRequestBody(oContent);
//          console.log("content eingesammelt");
				}
			}
			req.open(sMethod, sUrl, this.asynchron);
//      console.log("sMethod:"+sMethod);
			if (sMethod == "POST") {
				req.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
			}
			if (aRequestHeader != null) {
				for (var param = 0; param < aRequestHeader.length; param++) {
					if (aRequestHeader[param].match(/^REQUEST\-HEADER.*/)) { // AT.log("yo");
						// set additional request header
						var aParam = aRequestHeader[param].split("|");
						// skip first aParam, it is used to determine the kind of the aRequestHeader[param]
						for (var each = 1; each < aParam.length; each++) {
							var aKeyValue = aParam[each].split(":");
							var sKey = aKeyValue.shift();
							req.setRequestHeader(sKey,aKeyValue.join(":"));
						}
					}
				}
			}
		} catch (e) {
//			AT.log("ResourceLoader.prototype.doRequest: prepare request " + sUrl, "error", e);
		}

		try {
			// abort() geht nur fuer asynchrone requests
			if (this.asynchron) {
				this.timeoutHandler = window.setTimeout("ResourceLoader.getLoader('" + this.getId() + "').abort()", ResourceLoader.TIMEOUT);
			}
			req.send(oContent);
		} catch (e) {
//			AT.log("ResourceLoader.prototype.doRequest: send error " + sUrl + "\n" + oContent, "error", e);
		} finally {
			if (this.timeoutHandler != null) {
				window.clearTimeout(this.timeoutHandler);
				this.timeoutHandler = null;
			}
		}
		if (this.isAborted) {
//			AT.log("request " + sUrl + " is aborted", "warn");
			return null;
		}
	} catch (e) {
//		AT.log("ResourceLoader.doRequest " + sUrl + ": " + e, "error", e);
	}


	// Nur bei synchronem Request workon ausfueren
	if (!this.asynchron) {
		return this.workon(sWorkon, sUrl, req);
	}
}

/**
 * Verarbeitet den Response
 * @param {String} sWorkon Methode, wie der Request weiter verarbeitet werden soll
 * @param {String} sUrl URL der Resource
 * @param {String} req HttpRequest-Objekt
 * @type Variable
 * @private
 */
ResourceLoader.prototype.workon = function(sWorkon, sUrl, req) {

	if (req.readyState == 4 && req.status == 200) {
		switch (sWorkon) {
			case "addJs":
				return ResourceLoader.addJs(req.responseText, sUrl);
			break;
			case "addCss":
				return ResourceLoader.addCss(req.responseText, sUrl);
			break;
			case "addVb":
				return ResourceLoader.addVb(req.responseText, sUrl);
			break;
			case "getFileContent":
				if (req.readyState == 4 && req.status == 200) {
					return req.responseText;
				} else {
					return null;
				}
			break;
			case "getJson":
				try {
//          AT.log("getJson:"+req.responseText);
					return eval('(' + req.responseText + ')');
				} catch (e) {
					return null;
				}
			break;
			case "getXml":
				if(isIE) {
					var tXDD = zXmlDom.createDocument();
//					tXDD.loadXML(req.responseXML.xml);
					tXDD.loadXML(req.responseText);
					return tXDD;
				} else {
//          return req.responseXML;
          var tXDD = zXmlDom.createDocument();
					tXDD.loadXML(req.responseText);
					return tXDD;
				}
			break;
			default:
//				AT.log("nothing to do after doRequest");
				return null;
			break;
		}
	} else {
//		AT.log(sWorkon + " state " + req.readyState + ", status: " + req.status);
		return null;
	}
}

/**
 * Fuegt die Session-Id an die URL an
 * @param {String} sUrl Url, die durch die Session-Id ergaenzt werden soll.
 * @return duch die Session-Id erweiterte URL
 * @type String
 */
ResourceLoader.rewriteUrl = function(sUrl) {

	var urlWithoutParams = sUrl;
	var urlParams = null;

	var paramStart = sUrl.indexOf("?");
	if (paramStart != -1) {
		urlWithoutParams = sUrl.substring(0, paramStart);
		urlParams = sUrl.substring(paramStart);
	}

	var sessionIdStart = urlWithoutParams.indexOf(";jsessionid");
	if (sessionIdStart != -1) {
		urlWithoutParams = urlWithoutParams.substring(0, sessionIdStart);
	}

	// Nur Sessions an .jsp, captcha, control und forward URL's anhaengen
	if (urlWithoutParams.indexOf("/captcha") != -1 ||
		urlWithoutParams.indexOf("/control") != -1 ||
		urlWithoutParams.indexOf("/forward") != -1 ||
		urlWithoutParams.indexOf(".jsp") == urlWithoutParams.length - 4) {

		urlWithoutParams = urlWithoutParams + ";jsessionid=" + AtooloDesktop.getInstance().getSessionId();
	}

	if (urlParams == null) {
		return urlWithoutParams;
	} else {
		return urlWithoutParams + urlParams;
	}
}

/**
 * Laed eine Liste von Resourcen
 * @param {String} loadingLevel Level der Resouce (base|dependent)
 * @param {Array} list Resourceliste aus der Konfiguration
 * @param {Function} callback Funktion, die ausgefuehrt werden soll, wenn alle Resourcen geladen sind
 * @type void
 */
ResourceLoader.loadResourceList = function(loadingLevel, list, callback) {

	if (list == null || list.length == 0) {
		if (callback != null) {
			callback();
		}
		return;
	}

	var loader = ResourceLoader.createAsynchronLoader(loadingLevel, callback);
	var reallyLoad = 0;
	var msiecss = new Array();
	for (var i = 0; i < list.length; i++) {
		if (list[i].browser == null || (list[i].browser != 'msie' && list[i].browser != 'msie7')) {
			var doLoad = loader.loadResource(list[i].type, list[i].url);
			if (doLoad) {
				reallyLoad++;
			}
		} else if ((Plappadu.Utils.Browser.isIE6 && list[i].browser == 'msie') || (Plappadu.Utils.Browser.isIE7 && list[i].browser == 'msie7')) {
			msiecss.push(list[i]);
		}
	}

	for (var i = 0; i < msiecss.length; i++) {
		var doLoad = loader.loadResource(msiecss[i].type, msiecss[i].url);
		if (doLoad) {
			reallyLoad++;
		}
	}
	if (reallyLoad == 0) {
		ResourceLoader.removeLoader(loader.getId());
		if (callback != null) {
			callback();
		}
		return null;
	} else {
		try {
		if (loader.asynchronRequestCounter == 0) {
			if (Plappadu.Utils.Browser.isIE6 || Plappadu.Utils.Browser.isIE7) { // MSIE6 workaround
				loader.executeCssBatch();
			}
			ResourceLoader.removeLoader(loader.getId());
			if (callback != null) {
				callback();
			}
			return null;
		} else {
			loader.isFinished = true;
			return loader;
		}
		} catch (e) {
//			AT.log("ResourceLoader.loadResourceList: " + e, "error", e);
		}
	}
}

/**
 * Laed eine Resource
 * @param {String} type Typ der Resource (js|css)
 * @param {String} url URL der Resource
 * @private
 * @type void
 */
ResourceLoader.prototype.loadResource = function(type, url) {

	var resourceId = ResourceLoader.createId(url);

	if (ResourceLoader.loadedUrls[resourceId]) {
		//AT.log(url + " already loaded");
		return false;
	} else {
		//AT.log(url + " loading");
		var f = null;
		if (type == 'js') {
			f = "addJs"
		} else if (type == 'css') {
			f = "addCss"
		} else if (type == 'vb') {
			f = "addVb"
		} else {
//			AT.log("ResourceLoader.loadResource: invalid type " + type, "error");
		}
		if (f != null) {
			if (f == "addJs" && url.indexOf("http") == 0) {
				ResourceLoader.addJs(null, url);
			// Im debug > 1 css als link laden, um sie im Firefox bearbeiten zu koennen
			} else if (f == "addCss" && (!Plappadu.Utils.Browser.isIE)) {
				ResourceLoader.addCss(null, url);
			} else {
				this.doRequest('GET', url, null, null, f);
			}
			ResourceLoader.loadedUrls[resourceId] = true;
		}
		return true;
	}
}

/**
 * Liefert ein JavaScript-Object (JSON) zurueck
 * @param {String} url URL der JSON-Datei
 * @param {Object} oContent Inhalt der gesendet werden soll oder <code>null</code>, wenn nicht gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @return JavaScript-Object der JSON-Datei
 * @type JSONObject
 */
ResourceLoader.getJsonObject = function(url, oContent) {
  // Plappadu.log("gettingJsonObject");
  var method = null;
  // Plappadu.log("url:"+url);
  // Plappadu.log("oContent:"+oContent);
	if (oContent == null) {
		method = "GET";
	} else {
		method = "POST";
	}
	// Plappadu.log("method:"+method);
	var loader = ResourceLoader.createLoader();
	try {
		return loader.doRequest(method, url, oContent, null, 'getJson');
	} finally {
		ResourceLoader.removeLoader(loader.getId());
	}
}

/**
 * Laed ein JavaScript-Object (JSON) asynchron und ruft eine Callback-Methode auf, wenn es geladen wurde
 * @param {String} url URL der JSON-Datei
 * @param {Object} oContent Inhalt der gesendet werden soll oder <code>null</code>, wenn nicht gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @param {String} callback Callback-Methode, die aufgerufen wird, wenn des JSON-Objekt geladen wurde. Der Methode wird das
 *	geladene JSON-Objekt uebergeben.
 * @type void
 */
ResourceLoader.loadJsonObject = function(url, oContent, callback) {
//  AT.log("gettingJsonObject");
  var method = null;
	if (oContent == null) {
		method = "GET";
	} else {
		method = "POST";
	}
	var loader = ResourceLoader.createAsynchronLoader(null, callback);
	loader.doRequest(method, url, oContent, null, 'getJson');
	loader.isFinished = true;
}

/**
 * Liefert ein XML-DOMDocument-Object (JSON) aus einer XML-Dateu zurueck
 * @param {String} url URL der XML-Datei
 * @param {Object} oContent Inhalt der gesendet werden soll oder <code>null</code>, wenn nicht gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @return XML-DOMDocument-Object der XML-Datei
 * @type DOMDocument
 */
ResourceLoader.getDOMDocument = function(url, oContent) {
	var method = null;
	if (oContent == null) {
		method = "GET";
	} else {
		method = "POST";
	}
	var loader = ResourceLoader.createLoader();
	try {
		return loader.doRequest(method, url, oContent, null, 'getXml');
	} finally {
		ResourceLoader.removeLoader(loader.getId());
	}
}

/**
 * Liefert den Inhalt einer Datei
 * @param {String} url URL der Datei
 * @param {Object} oContent Inhalt der gesendet werden soll oder <code>null</code>, wenn nicht gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @return Inhalt der Datei
 * @type String
 */
ResourceLoader.getContent = function(url, oContent) {
	var method = null;
	if (oContent == null) {
		method = "GET";
	} else {
		method = "POST";
	}
	var loader = ResourceLoader.createLoader();
	try {
		return loader.doRequest(method, url, oContent, null, 'getFileContent');
	} finally {
		ResourceLoader.removeLoader(loader.getId());
	}
}

/**
 * Liefert ein DOM-Document aus einer externen XML-Datei, ueber das forward-Servlet.
 * @param {String} url URL der XML-Datei
 * @param {Object} oContent Inhalt der gesendet werden soll oder <code>null</code>, wenn nicht gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @return XML-DOMDocument-Object der XML-Datei
 * @type DOMDocument
 */
ResourceLoader.getExternalDOMDocument = function(url, oContent) {
	var url = "/forward/?url="+encodeURIComponent(url);
	return ResourceLoader.getDOMDocument(url, oContent);
}

/**
 * Liefert den Inhalt einer Externen URL
 * @param {String} url URL der Datei
 * @param {Object} oContent Inhalt der gesendet werden soll oder <code>null</code>, wenn nicht gesendet werden soll.
 *	<code>oContent</code> kann ein HTML-Formular sein dessen Felder ausgelesen und denen ein URL-ParameterString generiert wird. Sonst muss
 *	<code>oContent</code> ein String sein der einen URL-ParameterString enthaehlt der per GET oder POST versendet werden kann
 * @return Inhalt der Datei
 * @type String
 */
ResourceLoader.getExternalContent = function(url, oContent, tidy) {
  if((tidy == null) || (tidy == "false")) { tidy = false; }
	var url = "/forward/?url="+encodeURIComponent(url)+"&tidy="+tidy;
	return ResourceLoader.getContent(url, oContent);
}

/**
 * Liefert den Inhalt einer Datei aus Atoolo-Disk
 * @param {String} fileName Name der Datei die geladen werden soll
 * @param {String} storageId Id der Datei die geladen werden soll
 * @return Inhalt der Datei
 * @type String
 */
ResourceLoader.getStorageFileContent = function(fileName, storageId, parentId, tidy) {
  if(tidy == null) { tidy = false; }
  if(storageId == null) {
		storageId = ResourceLoader.getStorageIdByName(fileName, parentId);
	}
	var url = "/control/"+encodeURIComponent(fileName)+"?sys_mt_storage_action=load&mt_storage_id="+storageId+"&sys_mt_jsp_Handler=com.cynapsis.myTools.http.handler.HttpStorageHandler";
  if(tidy) { url += "&tidy=true"; }
	var loader = ResourceLoader.createLoader();
	try {
		return loader.doRequest('GET', url, null, null, 'getFileContent');
	} finally {
		ResourceLoader.removeLoader(loader.getId());
	}
}

/**
 * Liefert das DOMDocument aus Atoolo-Disk
 * @param {String} fileName Name der Datei die geladen werden soll
 * @param {String} storageId Id der Datei die geladen werden soll
 * @return Inhalt der Datei
 * @type String
 */
ResourceLoader.getStorageDOMDocument = function(fileName, storageId, parentId, tidy) {
  if(tidy == null) { tidy = false; }
	if (storageId == null) {
		storageId = ResourceLoader.getStorageIdByName(fileName, parentId);
	}
	var url = "/control/"+encodeURIComponent(fileName)+"?sys_mt_storage_action=load&mt_storage_id="+storageId+"&sys_mt_jsp_Handler=com.cynapsis.myTools.http.handler.HttpStorageHandler";
  if(tidy) { url += "&tidy=true"; }
	var loader = ResourceLoader.createLoader();
	try {
		return loader.doRequest('GET', url, null, null, 'getXml');
	} finally {
		ResourceLoader.removeLoader(loader.getId());
	}
}

/**
 * Holt die StorageId einer Datei von Atoolo-Disk
 * @param {String} fileName Name der Datei die geladen werden soll
 * @param {String} parentId Id des Verzeichnisses, in dem die Datei liegt
 * @return Id der Atolo-Disk-Datei
 * @type String
 */
ResourceLoader.getStorageIdByName = function(fileName, parentId) {
	var url = "/json/getFileByName.jsp?fileName="+fileName;
	if (parentId != null) {
		url += "&parentId=" + parentId;
	}
	var loader = ResourceLoader.createLoader();
	try {
		var file = loader.doRequest('GET', url, null, null, 'getJson');
		if (file == null || file == 'undefined' || file == '' || file.id == null) {
			return null;
		} else {
			return file.id;
		}
	} finally {
		ResourceLoader.removeLoader(loader.getId());
	}
}

/**
 * Erzeugt aus einem HTML-Formular eine POST-Body-String
 * @param {HTMLFormElement} oForm HTML-Formular-Element
 * @return POST Body
 * @type String
 * @private
 */
ResourceLoader.getRequestBody = function(oForm) {
	var aParams = new Array();
	var addField = function(name,value) {
		var sParam = encodeURIComponent(name);
		sParam += "=";
		sParam += encodeURIComponent(value);
		aParams.push(sParam);
	};
	for (var i=0 ; i < oForm.elements.length; i++) {
		var el = oForm.elements[i];
		if (!el.disabled) {
			switch(el.type) {
				case 'text': case 'password': case 'hidden': case 'textarea': case 'file':
					addField(el.name, el.value);
					break;
				case 'select-one':
					if (el.selectedIndex>=0) {
						addField(el.name,el.options[el.selectedIndex].value);
					}
					break;
				case 'select-multiple':
					for (var j=0; j<el.options.length; j++) {
						if (el.options[j].selected) {
							addField(el.name,el.options[j].value);
						}
					}
					break;
				case 'checkbox': case 'radio':
					if (el.checked) {
						addField(el.name,el.value);
					}
					break;
			}
		}
	}
	return aParams.join("&");
}

/**
 * Erzeugt aus einer URL eine eindeutige Id
 * @param {String} sUrl URL des JavaScripts
 * @return Id der URL
 * @type String
 * @private
 */
ResourceLoader.createId = function(sUrl) {

//	if (ResourceLoader.base == null && AtooloConfig.getInstance) {
//		ResourceLoader.base = AtooloConfig.getInstance().getRoot().base;
//	}
//
//	if (sUrl.indexOf(ResourceLoader.base) == 0) {
//		sUrl = sUrl.substring(ResourceLoader.base.length - 1, sUrl.length);
//	}
//
//	if (sUrl.indexOf("?") != -1) {
//		if (sUrl.indexOf("?browser") != -1) {
//			if (sUrl.indexOf("&") != -1) {
//				sUrl = sUrl.substring(0, sUrl.indexOf("&"));
//			}
//		} else {
//			sUrl = sUrl.substring(0, sUrl.indexOf("?"));
//		}
//	}
	var id = "id" + sUrl.replace(/[^a-zA-Z0-9_]/g, "");
	return id;
}

/**
 * Fuegt ein JavaScipt dynamisch in die Seite ein
 * @param {String} sContent JavaScript Source
 * @param {String} sUrl URL des JavaScripts
 * @return liefert <code>true</code> wenn das JavaScript hinzugefuegt wurde oder <code>false</code>, wenn
 *	das JavaScript bereits enthalten ist
 * @type boolean
 * @private
 */
ResourceLoader.addJs = function(sContent, sUrl) {
	try {
	var sId = ResourceLoader.createId(sUrl);
	var scriptNode = document.getElementById(sId);
	if (scriptNode == null) {
		var scriptNode = document.createElement("script");
		scriptNode.type = "text/javascript";
		scriptNode.id = "JS" + sId;
		//scriptNode.charset = "UTF-8";
		if (sContent == null) {
			scriptNode.src = sUrl;
		}
		//document.getElementsByTagName("body")[0].appendChild(scriptNode);
		document.getElementById("scripts").appendChild(scriptNode);
		if (sContent != null) {
			scriptNode.text = sContent;
		}
		return true;
	} else {
		return false;
	}
	return scriptNode;
	} catch (e) {

  }
}

/**
 * Fuegt ein VB-Script dynamisch in die Seite ein
 * @param {String} sContent VB-Script Source
 * @param {String} sUrl URL des VB-Script
 * @return liefert <code>true</code> wenn das VB-Script hinzugefuegt wurde oder <code>false</code>, wenn
 *	das VB-Script bereits enthalten ist
 * @type boolean
 * @private
 */
ResourceLoader.addVb = function(sContent, sUrl) {
	var sId = ResourceLoader.createId(sUrl);
	var scriptNode = document.getElementById(sId);
	if (scriptNode == null) {
		var scriptNode = document.createElement("script");
		scriptNode.type = "text/vbscript";
		scriptNode.id = "VB" + sId;
		scriptNode.charset = "UTF-8";
		if (sContent == null) {
			scriptNode.src = sUrl;
		}
		document.getElementsByTagName("head")[0].appendChild(scriptNode);
		if (sContent != null) {
			scriptNode.text = sContent;
		}
		return true;
	} else {
		return false;
	}
}

/**
 * Fuegt alle gesammelten CSS in den DOM-Baum
 * @type void
 * @private
 */
ResourceLoader.prototype.executeCssBatch = function() { // MSIE6 workaround

	try {
	if (this.cssBatch == null || this.cssBatch.length == 0) {
		return;
	}

	var t1 = new Date().getTime();

	if (this.loadingLevel == "base") {
		var cssNode = document.getElementById("base");
		if (cssNode == null) {
			cssNode = document.createElement("style");
			cssNode.id = "base";
			cssNode.type = "text/css";
			document.getElementsByTagName("head")[0].appendChild(cssNode);
		}
		if (ResourceLoader.cssDependentSources["dependent1"]) {
//			AT.log("rewrite base css, possible overwrite dependent css", "warn");
		}
		ResourceLoader.cssBaseSources.push(this.cssBatch.join("\n"));
		cssNode.styleSheet.cssText = ResourceLoader.cssBaseSources.join("\n"); // ist ja eh nur fuer'n MSIE
	} else if (this.loadingLevel == 'dependent') {
		var smallest = null;
		var min = -1;
		var counter = 0;
		for (key in ResourceLoader.cssDependentSources) {
			counter++;
			if (min == -1 || min < ResourceLoader.cssDependentSources[key].length) {
				smallest = key;
				min = ResourceLoader.cssDependentSources[key].length;
			}
		}

		var cssNode = null;
		var key = null;

		// verwende maximal 20 Style-Tags
		if (counter == 0 || counter < 1) {
			key = "dependent" + (counter + 1);
			cssNode = document.getElementById(key);
			if (cssNode == null) {
				cssNode = document.createElement("style");
				cssNode.id = key;
				cssNode.type = "text/css";
				document.getElementsByTagName("head")[0].appendChild(cssNode);
			}
		} else {
			key = smallest;
			cssNode = document.getElementById(smallest);
		}

		var css = null;
		if (ResourceLoader.cssDependentSources[key]) {
			css = ResourceLoader.cssDependentSources[key] + "\n" + this.cssBatch.join("\n");
		} else {
			css = this.cssBatch.join("\n");
		}
		ResourceLoader.cssDependentSources[key] = css;
		cssNode.styleSheet.cssText = css; // ist ja eh nur fuer'n MSIE
	}

	//AT.log("addCss bad version TIMER: " + (new Date().getTime() - t1), "info");
	} catch (e) {
//		AT.log("ResourceLoader.executeCssBatch: " + e, "error", e);
	}
}


/**
 * Fuegt ein CSS dynamisch in die Seite ein
 * @param {String} sUrl URL des CSS
 * @return liefert <code>true</code> wenn das CSS hinzugefuegt wurde oder <code>false</code>, wenn
 *	das CSS bereits enthalten ist
 * @type boolean
 * @private
 */
ResourceLoader.addCss = function(sContent, sUrl) {

	var sId = ResourceLoader.createId(sUrl);

	var cssNode = document.getElementById(sId);
	if (cssNode == null) {
		var t1 = new Date().getTime();
		if (sContent == null) {
			cssNode = document.createElement("link");
			cssNode.id = sId;
			cssNode.type = "text/css";
			cssNode.rel = "stylesheet";
			cssNode.href = sUrl;
			document.getElementsByTagName("head")[0].appendChild(cssNode);
		} else {
			cssNode = document.createElement("style");
			cssNode.id = sId;
			cssNode.type = "text/css";
			document.getElementsByTagName("head")[0].appendChild(cssNode);
			if (cssNode.styleSheet) { // MSIE
				cssNode.styleSheet.cssText = sContent;
			} else { // DOM
				var cssText = document.createTextNode(sContent);
				cssNode.appendChild(cssText);
			}
		}
		//AT.log("addCss good version TIMER: " + (new Date().getTime() - t1), "info");
		return true;
	} else {
		return false;
	}
}
