var coreAppCounter = 0;

function app() {

	this.prefixPrefix = 'o';
	this.prefix = '';
	this.elementNodeType = 1;
	this.loadedTemplateFiles = {};
	this.loadedTemplateParts = {};
	this.events = new Array();
	this.lEvents = new Array();
	
//	this.eventTarget
	
	app.prototype.initApp = function(container, doc) {
		if (container === undefined || container === null) {
			container = document.createElement("DIV");
		}
	
		if(typeof(doc) == "object") this.document = doc;
		else                      this.document = document;
		this.container = this.getObj(container); // Funzt nur weil this.prefix noch NICHT gesetzt (bzw. "") ist! (??????)		
		
		coreAppCounter++;
		this.prefix = this.prefixPrefix + coreAppCounter;
		
		this.listeners = new Array();
		this.events = new Array();
		this.lEvents = new Array();
		// this.container.dataObj = {};
	}
/*
	app.prototype.thisObject = function(obj) {
		while(obj && !obj.dataObj) obj = obj.parentNode;
		if(obj) return obj.dataObj;
	}
*/
	/******
	 * Returns the DOM-Object with the given ID or NULL
	 * @param id String with id (without prefix) or DOM-Object
	 * @return DOM-Object if existing, else NULL
	 ***/
	app.prototype.getObj = function(id) {
		var obj = null;
		if(typeof(id) != "object") {		
			obj = this.document.getElementById(this.prefix+id);
			if (obj == null) {
				obj = std.getElementById(this.container, this.prefix+id);
			}
			if (obj == null) {
				alert("Template Fehler!\nId nicht gefunden : "+id+"\n\nIn:\n\n"+this.container.innerHTML);
			}			
		}	
		else {
			obj = id;
		}
		return obj;
	}
	
	app.prototype.translateEventNameForIE = function(event) {
		var eventName = 'on'+event;
		return eventName;
	}

	app.prototype.addEvent = function(obj, eventName, func) {
		obj = this.getObj(obj);
		
		if (obj.dataObj == undefined) obj.dataObj = {};
		if (obj.dataObj[eventName] != undefined && obj.dataObj[eventName] != this) {
			alert("ACHTUNG:\n\nEvents vom Typ: "+eventName+" auf "+obj.id+" wird schon von einem andern app-Objekt abgefangen!");
		}
		else {
			obj.dataObj[eventName] = this;
			if (obj.addEventListener) obj.addEventListener(eventName, func, false);
			else if (obj.attachEvent) obj.attachEvent('on'+eventName, func); // IE
		}
	}

	app.prototype.removeEvent = function(obj, eventName, func) {
		obj = this.getObj(obj);
		obj.dataObj[eventName] = undefined;
		if (obj.removeEventListener) {
			obj.removeEventListener(eventName, func, false);
		}
		else if (obj.detachEvent) {
			eventName = this.translateEventNameForIE(eventName);
			obj.detachEvent(eventName, func);
		}
	}

	/******
	 * Adds an event-listener to any DOM-Object. When the event happens, 
	 * the method eventWrapper calls the method given as argument
	 * @param obj DOM-Object or String of the Object-ID
	 * @param event Name (as given in Mozilla) of the Event
	 * @param method Name of the method which is to be called
	 ***/
	app.prototype.addCEvent = function(obj, event, method) {
		obj = this.getObj(obj);
		
		var existing = false; // checking if this event is previously set
		for(var n = 0; (n < this.events.length && existing == false); n++) {
			if(this.events[n].object == obj && this.events[n].event == event && this.events[n].method == method) {
				existing = true;
			}
		}
		if(! existing) {
			this.events[this.events.length] = {'object':obj, 'event':event, 'method':method };
			this.addEvent(obj, event, eventWrapper);
		}
	}

	/******
	 * Removes an event-listener set by this.addCEvent.
	 * The arguments obj, event and method are required to identify the
	 * event in this.events.
	 ***/
	app.prototype.removeCEvent = function(obj, event, method) {
		obj = this.getObj(obj);
		for(var n = 0; n < this.events.length; n++) {
			if (this.events[n]['object'] == obj && this.events[n]['event'] == event && this.events[n]['method'] == method) {
				this.events.splice(n, 1);
			}
		}
		this.removeEvent(obj, event, eventWrapper);
	}
	
	/******
	 * Removes all event-listeners set by this.addCEvent.
	 ***/
	app.prototype.removeAllCEvents = function() {
		for (var n = 0; n < this.events.length; n++) {
			this.removeEvent(this.events[n]['object'], this.events[n]['event'], eventWrapper);
		}
		this.events = [];
	}
	

	function eventWrapper(event) {
		var src = this;
		
		if(std.getBrowser() == std.IE) {
			event = window.event;
			if (event.srcElement != undefined) {
				src = event.srcElement;
				while (src.dataObj == undefined || src.dataObj[event.type] == undefined) {
					src = src.parentNode;
				}
			}
			else {
				src = undefined;
			}
		} 
		
		if (src != undefined) {
			var app = src.dataObj[event.type];

			if(typeof(app.events) != 'object') alert('leer');

			var goOn = true;
			for(var n = 0; (n < app.events.length && goOn != false); n++) {
				if(app.events[n]['object'] == src && app.events[n]['event'] == event.type) {
				
					if (typeof(app[app.events[n]['method']]) == "function") {
						var goOn = app[app.events[n]['method']](src, event);
					}
					else {
						alert('ACHTUNG: "'+app.events[n]['method']+"\" ist keine Funktion");
					}
				}
			}
			if (goOn == false) std.noBubbling(event)
		}
		return goOn;		
	}
		
	app.prototype.show = function() { }

	app.prototype.hide = function() { }

	app.prototype.display = function(state) { 
		if(state) this.container.style.display = state;
		return this.container.style.display;
	}
	
	app.prototype.stripPrefix = function(prefixedId) {
		return prefixedId.substr(this.prefix.length);
	}
	
	app.prototype.switchPrefix = function(html, newPrefix) {
		var allIds = this.getIds(html);		
		var oldPrefix = this.findActualPrefix(allIds);		
		// var regex = new RegExp("id[\\s]*=[\\s]*(['\"])("+oldPrefix+")", "g");
		var regex = new RegExp("id[\\s]*=[\\s]*(['\"]*)("+oldPrefix+")", "g");  // IE sichere Version
		var result = html.replace(regex, "id=$1"+newPrefix); //      "' (damit syntax-einfärbung nicht versaut wird) :-)		
		return result;
	}

	app.prototype.findActualPrefix = function(idList) {
		var candidateNr = coreAppCounter;
		var candidatePrefix = "";
		var found = false;
		while (! found && candidateNr > 0) {
			candidatePrefix = this.prefixPrefix + candidateNr;
			found = true;
			for (var n = 0; (n < idList.length && found == true); n++) {
				if (! idList[n].startsWith(candidatePrefix)) found = false;
			}
			candidateNr--;
		}
		
		if (found == false) candidatePrefix = "";
		return candidatePrefix;
	}
	
	app.prototype.getIds = function(html) {
		// var matches = html.match(/id=['"].*?['"]/g);		  		//"   (damit syntax-einfärbung nicht versaut wird) :-)
		
		var matches = html.match(/id=['"]*.*?['"\s>]/g);		    // IE sichere Version
		var id = [];
		if (typeof(matches) == "object" && matches != null) {
			for (var n = 0; n < matches.length; n++) {
//				id[n] = matches[n].replace(/id[\s]*=[\s]*(['"])/g, ""); 	//      " (damit syntax-einfärbung nicht versaut wird) :-)
//				id[n] = id[n].replace(/(['"])/g, "");                      	//      " (damit syntax-einfärbung nicht versaut wird) :-)
				
				id[n] = matches[n].replace(/id[\s]*=[\s]*(['"]*)/g, ""); 	  // IE sichere Version
				id[n] = id[n].replace(/(['">\s]*)/g, "");                      	  // IE sichere Version
			}
		}
		return id;		
	}
			
	app.prototype.doIdReplacing = function(txt) { 
		timer.startMessung("doIdReplacing");
		if(txt.search(/{pre}/) > -1) {
			// Alle {pre} Platzhalter ersetzen
			var html = txt.replace(/{pre}/g, "id='"+this.prefix);
		} 
		else {
			// Alle IDs und CSS # mit dem prefix versehen
			var txt2 = txt.replace(/id[\s]*=[\s]*(['"])/g, "id=$1"+this.prefix); //      "' (damit syntax-einfärbung nicht versaut wird) :-)
			var html = txt2.replace(/\#(\S+)\s*\{/g, "#"+this.prefix+"$1 {");
		}
		timer.stopMessung("doIdReplacing");
		return html;
	}
	//--------------------------------------------------
	app.prototype.readTemplate = function(url, part){		// Just a dummy for the global syncGetFile()
		var txt = syncGetFile(url);	
		return txt;
	}
	
	app.prototype.importTemplate = function(url, targetObj) {
		var txt = this.readTemplate(url);
		var html = this.doIdReplacing(txt);
		this.setContent(html, targetObj);
	}
	
	app.prototype.importTemplateString = function(txt, targetObj) {
		var html = this.doIdReplacing(txt);
		// alert("vor idReplacing\n\n"+txt+"\n\nnach idReplacing\n\n"+html);
		this.setContent(html, targetObj);
	}
	
	app.prototype.setContent = function(string, targetObj) {
		string = string.trim();
		if(typeof(targetObj) != "object") targetObj = this.getObj(targetObj);
		if (targetObj.getAttribute("replace") == "yes") {	// Ersetzt das targetObj durch den string			
			this.objectReplace(string, targetObj);
		}
		else {							// Fülle das targetObj mit dem string
			std.setInnerHTML(targetObj, string);
			if (targetObj == this.container) this.element = targetObj.firstChild;
		}
	}
	
	app.prototype.isTableElement = function(object) {
		var result = false;
		var tn = object.tagName.toLowerCase();
		if (tn == "table" || tn == "thead" || tn == "tbody" || tn == "tfoot" || tn == "tr") result = true;
		return result;
	}

	app.prototype.objectReplace = function(string, targetObj) {
		// targetObj.innerHTML = string.trim()	// To Avoid that targetObj.firstChild will be a Text-Node;
		std.setInnerHTML(targetObj, string.trim());
		//alert(targetObj.tagName+"\n\n"+targetObj.innerHTML);

		var childNodesCount = 0;
		for (var i = 0; i < targetObj.childNodes.length; i++) {
			if (targetObj.childNodes[i].nodeType == this.elementNodeType) {
				childNodesCount++
			}
		}
		if (childNodesCount == 1) {
			var parent = targetObj.parentNode;
			targetObj.firstChild.id = targetObj.id;
			if (targetObj == this.container) {
				this.container = targetObj.firstChild;
				this.element = targetObj.firstChild;
			}
			parent.replaceChild(targetObj.firstChild, targetObj);
		}
		else {		// replace wird ignoriert wenn das element aus mehreren nodes besteht!
			if (targetObj == this.container) this.element = targetObj.firstChild;			
		}
	}

	app.prototype.getStyle = function(obj, style) {
		var result = "";
		if (std.getBrowser() == std.IE) {
			var styleArr = style.split("-");
			var styleCamelCase = styleArr[0];
			for (var n = 1; n < styleArr.length; n++) {
				styleCamelCase+= styleArr[n].substring(0,1).toUpperCase();
				styleCamelCase+= styleArr[n].substring(1);
			}
			result = eval("obj.currentStyle."+styleCamelCase+";");
		}
		else {
			result = this.document.defaultView.getComputedStyle(obj, '').getPropertyValue(style); // nur Firefox
		}
		return result;
	}

	//--------------------------------------------------
	app.prototype.importCSS = function(url, cssHasIdTags) {
		if (cssHasIdTags == true) {
			var txt = syncGetFile(url);
			// Alle CSS #-IDs mit dem prefix versehen
			var style = txt.replace(/\#(\S+)\s*\{/g, "#"+this.prefix+"$1 {");
			this.document.getElementById('stylesheets').insertAdjacentHTML("beforeEnd", "<span>&nbsp;</span><STYLE DEFER>" + style + "</STYLE>");
		}
		else { 
			importCSS(url);
		}
	}
	
	app.prototype.addListener = function(listener, id) {
		if(id) this.id = id;
		var existing = false; // damit derselbe Listener nicht mehrmals gesetzt wird
		for(var n = 0; n < this.listeners.length; n++) {
			if(this.listeners[n] == listener) {
				existing = true;
				break;
			}
		}
		if(!existing) this.listeners[this.listeners.length] = listener;
	}
	
	app.prototype.notify = function(event) {
		for(var i = 0; i < this.lEvents.length; i++) {
			if(this.lEvents[i].type == event.type) {

				if (typeof(this.lEvents[i].listener[this.lEvents[i].method]) == "function") {
					this.lEvents[i].listener[this.lEvents[i].method](this, event);
				}
				else {
					alert('ACHTUNG: "'+this.lEvents[i].method+"\" ist keine Funktion");
				}
			}
		}
		for (var i = 0; i < this.listeners.length; i++) {
			if (typeof(this.listeners[i]['update']) == "function") {
				this.listeners[i].update(this, event);
			}
		}
	}	
	
	app.prototype.update = function(source, event) {
	}

	app.prototype.addLEvent = function(listener, type, method) {
		var existing = false; // damit derselbe Listener nicht mehrmals gesetzt wird
		for (var n = 0; n < this.lEvents.length; n++) {
			if(this.lEvents[n].listener == listener && this.lEvents[n].type == type && this.lEvents[n].method == method) {
				existing = true;
				break;
			}
		}
		if(!existing) this.lEvents[this.lEvents.length] = {'listener':listener, 'type':type, 'method':method };

	}

	app.prototype.removeLEvent = function(obj, event, method) {
		obj = this.getObj(obj);
		for (var n = 0; n < this.lEvents.length; n++) {
			if (this.lEvents[n]['listener'] == obj && this.lEvents[n]['type'] == event && this.lEvents[n]['method'] == method) {
				this.lEvents.splice(n, 1);
			}
		}
	}
	
	app.prototype.throwEvent = function(src, event) {
		debug.print("passing through Event: "+event['type']);
		this.notify(event)
	}
	
	app.prototype.setContainer = function(newContainer) {
		if (typeof(newContainer) == "object") {
			var DO = this.container.dataObj;
			 
			// Copy container-content node by node
			/*
			var copyCount = 0;
			for (var i in this.container.childNodes) {
				if (this.container.childNodes[i] != undefined && this.container.childNodes[i].nodeType == this.elementNodeType) {
					newContainer.appendChild(this.container.childNodes[i]);
					copyCount++;
				}
			}
			// HACK: Mit obiger Schleife alleine funktioniert Edit-Table nicht mehr.
			//       Grund: this.container.childNodes[i].nodeType ist undefined
			
			if(copyCount == 0) {  */
				
			//}
			newContainer.appendChild(this.element);
			this.container = newContainer;
			this.container.dataObj = DO;
		}
		else {
			alert("app.setContainer:\n\nAchtung: 'newContainer' ist kein Objekt!\nElement:\n"+this.container.innerHTML);
		}
	

	}
 	app.prototype.setPrefix = function(newPrefix) {	
 		var txt = this.container.innerHTML;
 		var idPattern = eval("/id=(['\"])"+this.prefix+"/g");
 		// var cssPattern = eval("/\#(\S+)\s*\{"++"/g");
		var txt2 = txt.replace(idPattern, "id=$1"+newPrefix);
		//var html = txt2.replace(/\#(\S+)\s*\{/g+this.prefix, "#"+this.prefix+"$1 {");

		this.container.innerHTML = txt2;
	}
	
}



js_window.prototype = new app();

function js_window(container, overlay, closeOnOutClick, closingEvents, title) {
	
	this.initJSWindow = initJSWindow;
	this.closeWindow = closeWindow;
	this.setModule = setModule
	this.addMover = addMover;
	this.startCloseingWindow = startCloseingWindow;
	this.stopCloseingWindow = stopCloseingWindow;
	
	this.mover = undefined;
	this.module = undefined;
	this.overlay = undefined;
	this.windowElement = undefined;
	this.contentElement = undefined;
	this.closingEvents = [];
		
	if(arguments.length > 0) this.initJSWindow(container, overlay, closeOnOutClick, closingEvents, title);

	function initJSWindow(container, overlay, closeOnOutClick, closingEvents, title) {
		this.initApp(container);
		if (typeof(closingEvents) == "object") this.closingEvents = closingEvents;
		if (typeof(closingEvents) == "string") this.closingEvents = [closingEvents];
		if (closeOnOutClick == undefined) closeOnOutClick = true;

		this.overlay = overlay;

		if (title != undefined) {
			this.importTemplate('scripts/core/templates/window.html', this.container);
			this.addCEvent('closer', 'mousedown', 'startCloseingWindow');
			this.windowElement = this.getObj('window');
			this.contentElement = this.getObj('content');
			this.getObj('title').innerHTML = title;
			this.addMover(this.getObj('window_caption'));
		}
		else {
			this.importTemplate('scripts/core/templates/titleLessWindow.html', this.container);
			this.windowElement = this.getObj('window');
			this.contentElement = this.getObj('window');
			
			if (this.closingEvents.length == 0) closeOnOutClick = true;
		}
		
		if (closeOnOutClick) {
			this.addCEvent(this.overlay, 'click', 'closeWindow');
		}
		
	}
	
	function startCloseingWindow(src, event) {
		this.addCEvent('closer', 'mouseout', 'stopCloseingWindow');
		this.addCEvent('closer', 'mouseup', 'closeWindow');
		return std.noBubbling(event);
	}
	
	function stopCloseingWindow(src, event) {
		this.removeCEvent('closer', 'mouseout', 'stopCloseingWindow');
		this.removeCEvent('closer', 'mouseup', 'closeWindow');
	}
	
	function closeWindow(src, event) {	
		if (this.module != null) {
			for (var n = 0; n < this.closingEvents.length; n++) {
				this.module.removeLEvent(this, this.closingEvents[n], 'closeWindow');
			}
		}
		if (this.mover != undefined) {
			this.mover.stopListening();
		}
		var container = this.windowElement.parentNode;
		container.removeChild(this.windowElement);
		
		if (this.overlay != undefined) {
			var overlayContainer = this.overlay.parentNode;
			overlayContainer.removeChild(this.overlay);
		}
	}	
	
	function setModule(module) {
		this.module = module;
		if (this.module != null) {
			this.module.setContainer(this.contentElement);

			for (var n = 0; n < this.closingEvents.length; n++) {
				this.module.addLEvent(this, this.closingEvents[n], 'closeWindow');
			}		
		}
	}
	
	function addMover(moverContainer) {
		this.mover = new mover(moverContainer, this.windowElement);
	}
}
