if (!window.CHAMAELEON) {
window.CHAMAELEON = {
	// Dummy-Eintrag, damit "JavascriptMergingFile" 
	
	USE: {
		__Translations: "chamaeleon:Translations"
	},

	prefixMap : null,		// Verzeichnispfade fÃ¼r Resource (wird von init gesetzt)

	data : {					// private Objekt-Daten
		resourceCallQueue : [],	// Warteschlange fÃ¼r via resourceCall getÃ¤tigte Aufrufe
		resources : {},			// Geladene Ressourcen (nicht notwendigerweise initialisiert)
		loadResourceQueue : [],	// Warteschlange fÃ¼r zu ladende Ressourcen
		loadedResourceName : null, // Name der Ressource, die momentan geladen wird
		
		initResourceQueue: [],	// Warteschlange fÃ¼r zu initialisierende Ressourcen
								// Hier werden prinzipiell alle Ressourcen nach dem Laden abgelegt
		
		isProcessingQueue : false,	// ob die Methode __continueResourceCallQueue gerade am AusfÃ¼hren ist;
									// verhindert rekursive Aufrufe
		resourceLoadingTimeout : null	// erzeugt Fehlermeldungen nach Ablauf des Timeouts
	},

	/*
		@DESCRIPTION
			Initialisiert die CHAMAELEON-Lib. Dieser Aufruf darf auch mehrfach auf einer Seite erfolgen, z.B. im
			Zusammenhang mit Portlets. Einzige Bedingung ist, dass derselbe Prefixe nicht auf unterschiedliche URLs
			abgebildet wird.
			
		@PARAM newPrefixMap
			Ein Hash, der Prefixe (z.B. "chamaeleon") auf URLs (z.B. "http://www.example.com/js" oder "../scripts") abbildet.
			Der chamaeleon-Prefix MUSS angegeben werden und auf die CHAMAELEON-Lib (also auf das Verzeichnis,
			in dem u.a. dieses Skript hier liegt) verweisen.
			
		@PARAM developMode
			Gibt an, ob das Skript im Entwicklungs-Modus aufgerufen wird (1) oder nicht (0) (= Produktiv-Modus).
			Der Unterschied ist, dass im Entwicklungsmodus auch Warnungen, also potenzielle Probleme, ausgegeben werden
			(z.B. wenn ein Skript nach 5 Sekunden immer noch nicht geladen ist). Fehlt dieser Parameter, wird
			kein Entwicklungs-Modus angenommen.
	*/
	init : function(newPrefixMap, developMode) {
		this.developMode = this.developMode || developMode;
		if (this.prefixMap) {
			this.__loadPrefixes(newPrefixMap);
		} else {
			this.prefixMap = {};
			this.__loadPrefixes(newPrefixMap);
			this.resourceCall("chamaeleon:Translations", null);
		}
	},
	
	__loadPrefixes : function(newPrefixMap) {
		for (var prefix in newPrefixMap){
			if (newPrefixMap[prefix] != Object.prototype[prefix]) {
				if (this.prefixMap[prefix]) {
					/* URL bereits registriert */
					if (newPrefixMap[prefix] != this.prefixMap[prefix]) {
						throw new Error("Attempted to map prefix '" + prefix + "' to '" + newPrefixMap[prefix] + "' which has already been mapped to '" + this.prefixMap[prefix] + "'");
					}
				} else if (newPrefixMap[prefix].indexOf("http:") == 0 || newPrefixMap[prefix].indexOf("https:") == 0 || newPrefixMap[prefix].indexOf("file:") == 0) {
					/* URL bereits absolut */
					this.prefixMap[prefix] = newPrefixMap[prefix];
				} else {
					var p = window.location.pathname;
					p = p.substring(0, p.lastIndexOf("/")) + "/";
					if (newPrefixMap[prefix].substring(0,1) == "/")
						p = "";
					p = p + newPrefixMap[prefix];
					p = p.split("/");
					n = new Array();
					for(var k = 0; k < p.length; k++){
						if (p[k] == ".."){
							n.length--;
						} else if (p[k] == ".") {
							n = n;
						} else {
							n[n.length] = p[k];
						}
					};
					n = n.join("/");
		
					this.prefixMap[prefix] = n;
				} 
			}
		}
	},
	
	ERROR : function(message){
		try {
			throw new Error(message);
		} catch (exception){
			if (exception.stack){
				var buf = exception.stack.toString().split('\n');
				for(var k=1; k < buf.length; k++){
					buf[k] = buf[k].substring(buf[k].indexOf('@') + 1);
				}
				buf[0] = message;
				alert(buf.join("\n"));
				throw new Error(exception.stack.toString());
			} else {
				alert(message + "\n" + this.getStackTrace(arguments.callee.caller));
				throw exception;
			}
		}
	},
	
	getStackTrace : function(f, recurMap){
		if (! f){
			return "";
		};
		if (f["CYCLIC_STACK_TRACE"]){
			return "";
		}
		f["CYCLIC_STACK_TRACE"] = true;
		
		var trace = this.getStackTrace(f.caller);
		if (f["NAME"]){
			if (trace != ""){
				trace = f["NAME"] + "\n" + trace;
			} else {
				trace = f["NAME"];
			}
		}
		f["CYCLIC_STACK_TRACE"] = null;
		return trace;
	},
	
	
	/**
	*	Die Methode resourceCall ruft asynchron eine Member-Methode einer
	*   CHAMAELEON-Resource auf. resourceCall-Aufrufe werden in der Reihenfolge
	*	ausgefÃ¼hrt, in der sie stattfanden. Allerdings wird beim Aufruf der
	*	resourceCall-Methode nicht notwendigerweise der Programmfluss der
	*	aufrufenden Funktion unterbrochen.
	*	
	*	@param resourceName
	*		Name der aufzurufenden Resourcen
	*		Der name besteht dabei aus "prefix" (inkl. Doppelpunkt) und
	*		relativen Dateipfad zur zum PrÃ¤fix gehÃ¶renden URL
	*		ohne die Datei-Erweiterung ".js"
	*   @param methodName
	*		Name der aufzurufenden Methode.
	*   @param params
	*		Alle weiteren Argumente werden als Parameter an den Methodenaufruf
	*		weitergegeben
	*/
	resourceCall : function(resourceName, methodName){
		// Dient der syntaktischen PrÃ¼fung
		this.resolve(resourceName);
		if (!this.data.resources[resourceName]) {
			this.data.loadResourceQueue.push(resourceName);
		}
		if (methodName) {
			var args = [];
			args.push.apply(args, arguments);
			this.data.resourceCallQueue.push(args);
		}
		this.__continueResourceCallQueue();
	},
	
	/**
	* Liefert eine bereits geladene Resource zurÃ¼ck. Diese Methode liefert null
	* zurÃ¼ck, wenn die Resource noch nicht geladen wurde.
	*/
	lookupResource : function(resourceName){
		return this.data.resources[resourceName];
	},
	
	/**
	* gibt true zurÃ¼ck, gdw. das Ã¼bergebene Objekt eine CHAMAELEON Resource ist
	*/
	isResource : function(object){
		return object.NAME && this.lookupResource(object.NAME) == object;
	},
	
	/**
	 *	Interne Methode zum Erzeugen einer Ressource, unabhÃ¤ngig davon, ob es sich um ein
	 *  Objekt oder eine Klasse handelt
	 */
	__createResource : function(resource, isClass){
		var name = this.data.loadedResourceName;
		this.data.loadedResourceName = null;

		if (name == null){
			throw new Error("data.loadedResourceName is null! Caller is " + arguments.callee.caller.caller);
		}
		if (this.data.resources[name]) {
			throw new Error("resource " + name + " has been registered before!");
		}

		resource.NAME = name;
		
		if (resource.SUPER) {
			resource.USE.SUPER = resource.SUPER;
		}
		
		for (var m in resource){
			if (resource[m] != Object.prototype[m]) {
				if (typeof(resource[m]) == "function"){
					resource[m]["NAME"] = name + ":" + m + "()";
				}
			}
		}
	
		for (var m in resource.USE){
			if (resource.USE[m] != Object.prototype[m]) {
				// Ressourcen:
				// === undefined: vÃ¶llig unbekannt
				// === null: in Warteschlange, aber nicht geladen
				// true-Wert: geladen
				if (this.data.resources[resource.USE[m]] === undefined) {
					this.data.resources[resource.USE[m]] = null;
					this.data.loadResourceQueue.push(resource.USE[m]);
				}
			}
		}
		
		this.data.initResourceQueue.push(resource.NAME);

		window.clearTimeout(this.data.resourceLoadingTimeout);
		
		if (isClass) {
			if (!resource.construct) {
				throw new Error("no constructor defined");
			}
	
			var constructor = resource.construct;
			constructor.prototype = resource;
			this.data.resources[name] = constructor;
		} else {
			if (! resource.getMessage){
				resource.getMessage = this.__resource_getMessage;
			}
			
			this.data.resources[name] = resource;
		}

		this.__continueResourceCallQueue(true);
	},
	
	/* Diese Funktion wird spÃ¤ter geladenen Objekten hinzugefÃ¼gt */
	__resource_getMessage : function() {
		var params = [this.NAME];
		params.push.apply(params, arguments);
		return CHAMAELEON.data.resources["chamaeleon:Translations"].getMessage.apply(
			CHAMAELEON.data.resources["chamaeleon:Translations"],
			params
		);
	},
	
	
	/**
	*   Diese Methode muss von jeder Resource aufgerufen werden, damit diese
	*	vom IONAS3-Objekt erfasst wird und die Verarbeitung fortgesetzt werden
	*   kann.
	*/
	RESOURCE : function(resource){
		this.__createResource(resource, false);
	},

	/**
	*   ï¿½hnlich wie RESOURCE, allerdings mit folgenden Unterschieden:
	*	* Die Ressource muss einen Konstruktor haben (das ist eine Methode namens construct, da sie angegeben werden muss, gibt es hier keine Vererbung)
	*	* Die Ressource kann eine Eigenschaft namens SUPER (String) haben, die die Oberklasse angibt
	*   * Anstatt eines Objektes erhï¿½lt man einen Konstruktor, mit dem neue Instanzen erzeugt werden kï¿½nnen
	*	* SUPER-Aufrufe sind innerhalb von Objektmethoden mï¿½glich per this.SUPERCALL("Methodenname", Parameter...);
	*	  Super-Konstruktoren kï¿½nnen entsprechend per this.SUPERCALL("construct", Parameter...) aufgerufen werden
	*/
	CLASS : function(resource){
		this.__createResource(resource, true);
	},
	
	/**
	* Die Methode resolve Ã¼berfÃ¼hrt einen qualified name in eine zugehÃ¶rige
	* URL. Dabei wird der Prefix anhand der bei Objekt-Initialisierung 
	* vorgenommen Abbildung aufgelÃ¶st durch den entsprechenden Pfad ersetzt 
	*/
	resolve : function(qualifiedName){
		if (typeof(qualifiedName) != "string") {
			throw new Error("Bad resource name '" + qualifiedName + "'");
		}
		var qn = qualifiedName.split(":", 2);
		if (qn.length < 2) {
			throw new Error("Bad resource name '" + qualifiedName + "'");
		}
		var resourcePath = this.prefixMap[qn[0]];
		if (resourcePath == null){
			throw new Error("Prefix of '" + qualifiedName + "' is unknown");
		}
		
		return resourcePath + qn[1];
	},
	
	__inPreloadMode : false,
	beginPreloading : function() {
		this.__inPreloadMode = true;
	},
	endPreloading : function() {
		this.__inPreloadMode = false;
		// Ressourcen nur nach Initialisierung laden
		if (this.prefixMap) {
			this.__continueResourceCallQueue(true);
		}
	},
	
	__loadPendingResources : function() {
		if (this.data.loadedResourceName != null){
			// Es wird bereits gerade eine Resource geladen!!!
			alert("nested resource loading: " +
					+ "\n" + this.data.loadedResourceName
					+ "\n" + this.data.loadResourceQueue[0]);
		}

		// Bereits geladene Ressourcen verwerfen
		do {
			if (!this.data.loadResourceQueue.length) {
				return false;
			}
			var resourceName = this.data.loadResourceQueue.shift();
		} while (this.data.resources[resourceName]);
		
		this.data.loadedResourceName = resourceName;

		// Scripte zum Nachladen fehlender JavaScript-Dateien erstellen
		if (window.quickLoadMode && !document.all) {
			/*
				Spezial-Modus fÃ¼r Firefox: evtl. vorhandene Fehler werden sofort gemeldet
			*/
			document.write('<script type="text/javascript" charset="utf-8" src="' + this.resolve(resourceName) + '.js"></script>');
			document.write('<script type="text/javascript">CHAMAELEON.__assertIsLoaded();</script>');
		} else {
			/*
				Standard-Modus
			*/
			var s = document.createElement("script");
			
			s.src = this.resolve(resourceName) + ".js";
			s.type = "text/javascript";
			s.charset = "utf-8";
			
			if (this.developMode) {
				this.data.resourceLoadingTimeout = window.setTimeout("alert('could not load: " + s.src +  "');", 5000);
			}
			document.getElementsByTagName("script")[0].parentNode.appendChild(s);
		}
		return true;
	},

	__initPendingResources : function() {
		var q = this.data.initResourceQueue;
		this.data.initResourceQueue = [];
		var resourceDeriveQueue = [];
		var resourceUnderived = {};
		
		// AbhÃ¤ngigkeiten auflÃ¶sen
		for (var i = 0; i < q.length; i++) {
			var resource = this.data.resources[q[i]];
			resource = resource instanceof Function ? resource.prototype : resource;
			if (resource.SUPER) {
				// Ressourcen ableiten
				resourceDeriveQueue.push({NAME: resource.NAME, SUPER: resource.SUPER});
				resourceUnderived[resource.NAME] = true;
			}
			for (var key in resource.USE){
				if (resource.USE[key] != Object.prototype[key]) {
					resource[key] = this.data.resources[resource.USE[key]];
					if (!resource[key]) {
						throw new Error("(Internal error) Resource " + key + " does not exist although it was expected to exist");
					}
				}
			}
		}

		// Ableitungen auflÃ¶sen
		var maxIter = resourceDeriveQueue.length*(resourceDeriveQueue.length+1)/2;
		while (resourceDeriveQueue.length) {
			if (maxIter-- <= 0) {
				throw new Error("Endless loop while deriving resources");
			}
		
			var item = resourceDeriveQueue.shift();
			
			// Oberklasse noch nicht abgeleitet? -> hinten anstellen und warten
			if (resourceUnderived[item.SUPER]) {
				resourceDeriveQueue.push(item);
				continue;
			}

			delete resourceUnderived[item.NAME];
			var r = this.data.resources[item.NAME];
			
			if (!(r.prototype.SUPER instanceof Function)) {
				throw new Error("Attempted to derive class " + item.NAME + " from a non-class object " + r.prototype.SUPER);
			}
			
			var newProto = r.prototype;
			
			var dummyProtoFunc = function(){};
			dummyProtoFunc.prototype = newProto.SUPER.prototype;
			
			r.prototype = new dummyProtoFunc();
			for (var i in newProto) {
				if (newProto[i] != Object.prototype[i]) {
					if (i != "prototype" && i != "SUPER" && i != "SUPERCALL") {
						r.prototype[i] = newProto[i];
						if (r.prototype[i] instanceof Function) {
							r.prototype[i].SUPER = newProto.SUPER.prototype;
						}
					}
				}
			}
			r.prototype.SUPERCALL = new Function("name", "var params = []; params.push.apply(params, arguments); return arguments.callee.caller.SUPER[name].apply(this, params.slice(1));");
		}

		// Ressourcen initialisieren
		for (var i = 0; i < q.length; i++) {
			var resource = this.data.resources[q[i]];
			resource = resource instanceof Function ? resource.prototype : resource;
			if (resource.INIT) {
				resource.INIT();
			}
		}
	},

	__continueResourceCallQueue : function(resume){
		if (this.__inPreloadMode || this.data.isProcessingQueue && !resume){
			return;
		}
		
		this.data.isProcessingQueue = true;
		
		// Es mÃ¼ssen noch Ressourcen geladen werden
		if (this.data.loadResourceQueue.length) {
			if (this.__loadPendingResources()) {
				return;
			}
		}
		
		// Es mÃ¼ssen noch Ressourcen initialisiert werden
		if (this.data.initResourceQueue.length) {
			this.__initPendingResources();
		}
		
		// Warteschlangen abarbeiten
		while (this.data.resourceCallQueue.length > 0){
			var queue = this.data.resourceCallQueue;
			this.data.resourceCallQueue = [];
			for (var k = 0; k < queue.length; k++) {
				var args = queue[k];
				var resourceName = args.shift();
				var methodName 	 = args.shift();

				// Ressource ist auf jeden Fall bereits geladen, wenn wir bis hierhin kommen
				
				if (methodName){
					var r = this.data.resources[resourceName];
					if (r instanceof Function) {
						if (methodName == 'construct') {
							var callStr = [];
							for (var j = 0; j < args.length; j++) {
								callStr[j] = "args[" + j + "]";
							}
							eval("new r(" + callStr.join(",") + ")");
						} else {
							throw new Error("Resource '" + resourceName + "' is a class, calling '" + methodName + "' is not permitted (use 'construct')!");
						}
					} else {
						if (!r[methodName]){
							throw new Error("Resource '" + resourceName + "' has no such member method '" + methodName + "'!");
						} else {
							r[methodName].apply(r, args);
						}
					}
				}
			}
		}
		this.data.isProcessingQueue = false;
	},
	
	// Erzeugt eine Fehlermeldung, falls eine Ressource nicht geladen werden konnte
	__assertIsLoaded : function() {
		if (this.data.loadedResourceName && !this.data.hasError) {
			this.data.hasError = true;
			alert('could not load ' + this.data.loadedResourceName);
		}
	}
};
}

/**
 *	Erzeugt eine Funktions-Referenz, die z.B. für Callback-Funktionen Verwendung findet.
 *	Parameter sind:	
 *	* Objekt, auf dem der Aufruf erfolgen soll (optional, Parameter kann auch weggelassen werden) 
 *  * Anzahl statischer Parameter
 *  * Die aufzurufende Funktion  
 */
window.$ref = function() {
	var obj = null;
	var staticParamCount, func;
	
	var i = 0;
	if (typeof(arguments[i]) != "number") obj = arguments[i++];		
	if (typeof(arguments[i]) != "number") throw new Error("Number expected but " + arguments[i] + " found");
	staticParamCount = arguments[i++];
	if (!(arguments[i] instanceof Function)) throw new Error("Function expected but " + arguments[i] + " found");
	func = arguments[i++];
	
	
	var f2 =
		'var args=arguments.callee.args; ' +
		'args=args.concat.apply(args, arguments); ' +
		'return arguments.callee.func.apply(arguments.callee.obj, args); ';
	
	var f = new Function(
		"if (arguments.length != " + staticParamCount + ") throw new Error('Wrong number of arguments, expected " + staticParamCount + " but got ' + arguments.length);" +   
		"var args=[]; args.push.apply(args,arguments);" +
		"var f = new Function('" + f2 + "');" +
		"f.args = args; f.obj = arguments.callee.obj || this; f.func = arguments.callee.func;" +
		"return f;"
	);
	
	f.func = func;
	f.obj = obj;

	return f;
};
