﻿/* charset-encoding="UTF-8" */

/*
  Copyright 2013 Persis GmbH
*/

/**
 * Basis-Datei der Persis-JavaScript-Bibliothek.
 * - Globale Initialisierungen
 * - Globale Funktionen
 * 
 * [13.10.2009] rj: Erstellung
 * [08.11.2009] lo: Initialkonstrukt geändert
 * [16.12.2009] lo: Start Redesign
 * [19.01.2010] lo: Prototype als Abhängigkeit zur Verfügung gestellt
 * [19.01.2010] lo: neue persis.dom.xxx Funktionen ergänzt
 * [04.02.2010] lo: FocusHandler ergänzt
 * [18.02.2010] lo: persis.forms.validation ergänzt
 * [01.06.2010] lo: neue persis.dom.xxx Funktionen ergänzt
 * [10.06.2010] lo: Google-Closure-Library Abhängigkeiten ergänzt
 * [10.06.2010] lo: Fehlende Übersetzung gekennzeichnet
 * [10.06.2010] lo: relative/absolute/externe Addressierung von Script-Dateien ergänzt
 * [22.06.2010] lo: Prototype und Scriptaculous nach /js verschoben und Abhängigkeiten ergänzt
 * [04.08.2010] lo: persis.ui.menus ergänzt
 * [04.08.2010] lo: persis.dump und persis.traceDump ergänzt
 * [12.08.2010] lo: persis.scripts.load(): Validator ist nun optional
 * [12.08.2010] lo: Wegen Nachladeproblem von Google Closure (deps.js) eine "postLoad"-Handlerfunkion ergänzt, um Script-spezifische Aufgaben durchzuführen. Weitere Handlerfunktionen folgen.
 * [30.08.2010] lo: persis.dom.css Paket ergänzt. (TextMetrics)
 * [01.09.2010] lo: persis.async.events Paket ergänzt. (UserEventTrigger)
 * [01.09.2010] lo: persis.async Paket ergänzt. (Job)
 * [01.09.2010] lo: persis.async.ajax Paket ergänzt. (AjaxLoaderJob, IFrameLoaderJob)
 * [06.09.2010] lo: persis.async.Queue ergänzt.
 * [08.09.2010] lo: Umbenennen von Job -> Task, Loader -> TaskProcessor, AjaxLoaderJob -> AjaxRequest, IFrameLoaderJob -> IFrameLoader
 * [08.09.2010] lo: persis.deps.Dependency Klasse ergänzt. Damit Abhängigkeiten von anderen Ressourcen ermöglicht (persis.deps.stylesheet()).
 * [15.09.2010] lo: page_templ.js in persis.modules.system ergänzt.
 * [15.09.2010] lo: Namespace für alte Script-Dateien ergänzt (persis.old_scripts)
 * [15.09.2010] lo: parser.js, form.js ergänzt (persis.old_scripts.parser_js, persis.old_scripts.form_js)
 * [23.09.2010] lo: persis.ui.trees ergänzt.
 * [15.09.2010] lo: dhtml.js ergänzt (persis.old_scripts.dhtml_js)
 * [27.09.2010] lo: Bugfix: persis.dump() funktionierte nicht rekursiv
 * [27.09.2010] lo: persis.ui.overlay ergänzt.
 * [27.09.2010] lo: persis.Disposable ergänzt.
 * [12.10.2010] lo: findBaseScriptPath(): Regulären Ausdruck korrigiert. (Fehler beim Nachladen von Prototype)
 * [18.11.2010] lo: Unterstützung für PDF-Object Library ergänzt.
 * [30.11.2010] lo: mouse.js ergänzt (persis.old_scripts.mouse_js)
 * [30.11.2010] lo: calendar.js ergänzt (persis.old_scripts.calendar_js)
 * [01.12.2010] lo: parser.js braucht form.js
 * [01.12.2010] lo: date.js ergänzt (persis.old_scripts.date_js)
 * [02.12.2010] lo: persis.types.components.formfields ergänzt.
 * [07.01.2011] lo: string.js, coding.js ergänzt (persis.old_scripts.string_js, persis.old_scripts.coding_js)
 * [03.02.2011] lo: JSDoc Tags ergänzt
 * [03.02.2011] lo: PrimitveCompareable ergänzt
 * [01.03.2011] lo: Weitere Voraussetzungen für persis.types.components.formfields ergänzt.
 * [01.03.2011] lo: validation.js ergänzt (persis.old_scripts.validation_js)
 * [08.03.2011] lo: persis.ui.dialogs ergänzt
 * [09.03.2011] lo: persis.ui.frames ergänzt
 * [05.04.2011] lo: Google Closure Library r790 testweise eingebunden.
 * [07.07.2011] lo: popup.js und uebernahme.js ergänzt (persis.old_scripts.popup_js, persis.old_scripts.uebernahme_js)
 * [19.07.2011] lo: persis.ui.menus.TabCtrl ergänzt
 * [21.07.2011] lo: persis.ui.frames.iframe ergänzt
 * [21.07.2011] lo: IFrameConnector in persis.ui.frames.iframe verschoben
 * [09.09.2011] lo: persis.ui.dialogs.InputDialog ergänzt
 * [12.09.2011] lo: dialogs.js braucht jetzt parser.js
 * [19.10.2011] lo: autocomplete.js ergänzt
 * [30.11.2011] lo: toolbars.js ergänzt
 * [02.12.2011] lo: persis.dom braucht jetzt Google
 * [05.12.2011] lo: menuctrl.js ergänzt
 * [06.12.2011] lo: Google Closure Library r1446 testweise eingebunden.
 * [06.12.2011] lo: persis.constants Namespace ergänzt für setzen/abfragen globaler Konstanten.
 * [06.12.2011] lo: persis.deps.constant() ergänzt, um im Rahmen der Abhängigkeitsprüfung Konstanten zu setzen.
 * [07.12.2011] lo: persis.utils mit ThirdPartyWrapper ergänzt.
 * [13.12.2011] lo: persis.ui.menus, persis.ui.toolbars aktualisiert
 * [13.12.2011] lo: persis.ui.menus.MenuButton ergänzt
 * [13.12.2011] lo: persis.inherits um experimentelle Mehrfachvererbung erweitert
 * [19.12.2011] lo: persis.ui.inputs ergänzt
 * [19.12.2011] lo: persis.ui.inputs.Slider ergänzt
 * [20.12.2011] lo: persis.ui.container ergänzt
 * [20.12.2011] lo: persis.ui.container.PageFlip ergänzt
 * [20.12.2011] lo: persis.async.ajax.ImageLoader ergänzt
 * [20.12.2011] lo: TEST: Testweise den Validierungsmechanismus von persis.scripts.load verändert. Es werden nun keine Timer mehr verwendet.
 * [21.12.2011] lo: persis.dom.graphics ergänzt
 * [21.12.2011] lo: persis.dom.graphics.CanvasRenderer ergänzt
 * [21.12.2011] lo: Excanvas kann nun auch mit persis.require("Excanvas") geladen werden.
 * [22.12.2011] lo: persis.types.Rect ergänzt
 * [28.12.2011] lo: persis.types.Dim ergänzt
 * [05.01.2012] lo: persis.types.Pos ergänzt
 * [09.01.2012] lo: persis.dom.sound ergänzt
 * [09.01.2012] lo: persis.dump() verursachte in Chrome ab und zu einen Konvertierungsfehler.
 * [23.01.2012] lo: "persis.dom" braucht jetzt "persis.css"
 * [24.01.2012] lo: "CanvasRenderer" ist jetzt von "UIElement" abgeleitet
 * [14.02.2012] lo: persis.ui.layout ergänzt
 * [15.02.2012] lo: jQuery 1.7.1 verlinkt
 * [16.02.2012] lo: persis.obj.extend() hat nun einen weiteren Parameter "follow".
 * [17.02.2012] lo: persis.types.iter.Collection ergänzt.
 * [17.02.2012] lo: persis.Cloneable ergänzt.
 * [17.02.2012] lo: mit inherits() "mehrfachvererbte Klassen" können nun mit obj.instanceOf(baseClass) prüfen, ob eine Vererbung vorliegt.
 * [21.02.2012] lo: persis.ui.container.ImageGallery ergänzt.
 * [05.03.2012] lo: persis.dom.animation.Renderer ergänzt.
 * [07.03.2012] lo: persis.utils.algorithms.BSpline ergänzt.
 * [08.03.2012] lo: persis.utils.algorithms.Interpolator ergänzt.
 * [18.05.2012] lo: Äbhängigkeiten geordnet
 * [25.06.2012] kw: synchronizableMenu ergänzt.
 * [06.07.2012] kw: synchronizableToolbar ergänzt (fehlt aber noch für akte_viewer).
 * [11.07.2012] lo: in persis.ui.elements.Image fehlte Abhängigkeit zu persis.async.ajax.ImageLoader
 * [24.07.2012] wb: persis.modules.md und persis.modules.md.stmdkattn ergänzt
 * [26.07.2012] lo: persis.ui.elements.Progress ergänzt
 * [26.07.2012] wb: persis.modules.md.stmdkattn erweitert
 * [26.07.2012] lo: persis.fn.paramValue() ergänzt
 * [27.07.2012] lo: persis.instanceOf() ergänzt
 * [27.07.2012] wb: persis.ui.inputs.TriStateCheckbox ergänzt
 * [21.08.2012] lo: Teile von persis.errorMessage() nach persis.createErrorMessage() ausgelagert
 * [29.08.2012] lo: Refactoring: Name korrigiert (Synchronizable)
 * [03.09.2012] kw: persis.modules.dipa.AkteRegTree ergänzt, persis.ui.trees mit tree_ctrl.css erweitert
 * [04.09.2012] lo: onDocIsReady() ergänzt
 * [13.09.2012] lo: persis.async.timer.IdleHandler ergänzt
 * [17.09.2012] lo: persis.fn.delayBind() behandelte die Parameter nicht richtig
 * [17.09.2012] lo: persis.async.Synchronizable benötigt nun persis.types.iter.Collection
 * [29.11.2012] lo: PDFObject 1.2 eingebunden.
 * [03.12.2012] wb: persis.modules.md.aufg eingebunden.
 * [03.12.2012] wb: persis.ui.container.Accordion eingebunden.
 * [05.12.2012] kw/lo: persis.ui.menus.Menu.Item und persis.ui.menus.Menu.SubMenu, persis.ui.menus.Menu.Separator als required für persis.ui.toolbars.SynchronizableToolbar aufgenommen.
 * [10.12.2012] lo: persis.ui.container.Tabs ergänzt
 * [21.12.2012] lo: persis.str.camelize()/persis.str.capitalize() ergänzt
 * [28.12.2012] lo: persis.storage ergänzt
 * [31.01.2013] kw: persis.modules.dipa.AkteViewer benötigt nun persis.types.iter.Collection
 * [20.02.2013] lo: storage.removeItem() kann nun optional alle Listener zu diesem Item entfernen.
 * [25.02.2013] lo: persis.ui.elements.DynElement ergänzt
 * [05.04.2013] wb: persis.modules.md.gsp_erf eingebunden.
 * [11.04.2013] sl: persis.ui.progress eingebunden.
 * [12.04.2013] lo: editor.js für persis.require() ergänzt (persis.old_scripts.editor_js)
 * [21.05.2013] lo: Storage-Typ-Erkennung geändert
 * [23.05.2013] lo: persis.async.ajax.ImageLoader benötigt jetzt persis.dom
 * [31.05.2013] lo: persis.fn.isValid() ergänzt
 * [31.05.2013] lo: Storage prüft nun ob die Listener gültig sind bevor diese benachrichtigt werden.
 * [04.06.2013] lo: Storage: "Verbindung abgerissen"-Meldung entfernt
 * [10.06.2013] lo: Storage: Skriptfehler im IE behoben
 * [10.06.2013] lo: persis.modules.dipa.ImageCanvasRenderer ergänzt
 * [10.06.2013] lo: persis.modules.dipa.AkteViewer benötigt nun persis.modules.dipa.ImageCanvasRenderer.
 * [13.06.2013] lo: jQuery Jcrop Plugin ergänzt
 * [24.06.2013] lo: Markierungsfunktionen des Akte-Viewers ausgelagert nach persis.modules.dipa.AkteDokMarkingsTarget
 * [26.06.2013] lo: persis.browser.isStandardsStrictMode() und isInternetExplorerInCompatibilityMode() ergänzt
 * [03.07.2013] wb: persis.ui.inputs.tokeninput und jquery-tokeninput eingebunden
 * [03.07.2013] lo: Persis-Storage fehlertoleranter gemacht
 * [04.07.2013] lo: Persis-Storage verwendet im IE nun ab Version 10 das HTML-Storage.
 * [04.07.2013] lo: Verbesserte Versionserkennung und Erkennung des Kompatibilitätsmodus des Internet Explorers
 * [04.07.2013] lo: date.js braucht parser.js
 * [26.07.2013] lo: Dritter Parameter von removeEventListener ist im FF erst ab Version 6 optional
 * [29.07.2013] lo: In storage.removeItem() gab es im IE einen Fehler aufgrund einer nicht vorhandenen Variablen "value".
 * [30.07.2013] lo: Damit persis.fn.isValid() im IE zuverlässiger funktioniert, wird im Function-Prototypen eine Hilfsfunktion "validate()" ergänzt.
 * [31.07.2013] lo: Function-Prototyp um "context()" erweitert.
 * [31.07.2013] lo: persis.Exception-Klasse ergänzt
 * [01.08.2013] lo: Weitere Verbesserung des Persis-Storages im Internet Explorer.
 * [15.08.2013] lo: persis.ui.views.GenericViewTemplate ergänzt
 * [22.08.2013] fk: GenericViewTemplate ist von /css/persis_units.css und von persis.ui.dialogs.Dialog abhängig
 * [23.08.2013] fk: GenericViewTemplate ist von persis.async.ajax.AjaxRequest abhängig
 * [23.09.2013] lo: jquery.event.drag und jquery.event.drop testweise eingebunden
 * [--.--.2014] --: Überwachen des Nachladens aktiviert
 * [18.02.2014] wb: jquery-fullcalendar und momentjs aufgenommen
 * [19.02.2014] wb: persis.ui.calendars und persis.ui.calendars.FullCalendar aufgenommen
 * [19.02.2014] sts:Weiche für Kompatibilitätsmodus ab IE11 in persis.browser.detect, sowie /Trident\ in persis.browser Statement und Trident7/IE11 Variable in persis.browser.isInternetExplorerInCompatibilityMode ergänzt
 * [26.02.2014] wb: persis.modules.ma und persis.modules.ma.kalender aufgenommen
 * [27.02.2014] la: persis.ui.calendars.DatePicker aufgenommen
 * [28.02.2014] la: persis.ui.calendars.DateTimePicker aufgenommen 
 * [28.02.2014] la: jQueryFullCalendar.persisViews aufgenommen 
 * [06.03.2014] wb: persis.modules.ma.kalender.dialogs aufgenommen
 * [09.04.2014] la: persis.str.template ergänzt
 * [10.04.2014] wb: persis.ui.inputs.ColoredCheckbox ergänzt
 * [27.06.2014] ah: persis.ui.inputs.Combobox und persis.ui.inputs.SQLCombobox ergänzt
 * [20.07.2014] ah: persis.ui.inputs.autocomplete ergänzt
 */


/**
 * Namespace für alle Persis Script-Funktionen.
 * @namespace
 */
var persis = persis || {};



/**
 * Namespace für alte Script-Dateien.
 * Bspw.: persis.old_scripts.parser_js = true;
 * @namespace
 */
persis.old_scripts = persis.old_scripts || {};



/**
 * Globaler Adressraum. I.d.R. das aktuelle window-Objekt, in dem die globalen Variablen/Funktionen gültig sind.
 */
persis.global = this;



/**
 * Prüft, ob das aktuelle HTML-Dokument bereits vollständig geladen wurde.
 * Dies ist unabhängig von eventuellen dynamischen Ajax-Aufrufen und
 * bezieht sich nur auf den Hauptinhalt.
 * @param {Document} doc - (optional)
 * @return {Boolean}
 * @since 2013/1
 */
persis.isDocReady = function(doc) {
	doc = doc || persis.global.document;
	return (typeof doc != 'undefined' && doc.readyState == "complete");
};

/**
 * Diese Hilfsfunktion bietet eine Möglichkeit eine Callback-Funktion
 * zu registrieren, welche erst nach der vollständigen Verfügbarkeit
 * des Dokuments (persis.isDocReady()) ausgeführt wird.
 * Ist das Dokument bereits "bereit", wird die Handler-Funktion
 * sofort ausgeführt.
 * @param {Function} callback - function(event) { ... }; Event-Bezeichnung ist "ready".
 * @since 2013/1
 */
persis.onDocIsReady = function() {
	var registeredListeners = [ ];
	var eventHandlerRegistered = false;
	var onReady = function(event) {
		var i;
		for(i = 0; i < registeredListeners.length; i++) {
			var listener = registeredListeners[i];
			try {
				listener("ready");
			}
			catch(e) {
				persis.errorMessage(String(/* persis-id="53445" */ 'Beim Ausführen des Event-Handlers ("ready") ist ein Skript-Fehler aufgetreten!'), e);
			}
		}
		unregisterEventHandler();
		registeredListeners = [ ];
	};
	var addListener = function(listener) {
		if(!eventHandlerRegistered) {
			registerEventHandler();
		}
		registeredListeners.push(listener);
	};
	var registerEventHandler = function() {
		if(persis.global.addEventListener) {
			persis.global.addEventListener("load", onReady);
		}
		else if(persis.global.attachEvent) {
			persis.global.attachEvent("onload", onReady);
		}
		eventHandlerRegistered = true;
	};
	var unregisterEventHandler = function() {
		if(persis.global.removeEventListener) {
			// 3. Parameter ist im FF erst ab Version 6 optional
			persis.global.removeEventListener("load", onReady, false);
		}
		else if(persis.global.detachEvent) {
			persis.global.detachEvent("onload", onReady);
		}
		eventHandlerRegistered = false;
	};

	return function(callback) {
		if(persis.isDocReady()) {
			callback.call(this, "ready");
		}
		else {
			addListener(callback);
		}
	};
}();


/**
 * Namespace für grundlegende Objekt-Hilfsfunktionen
 * @namespace
 */
persis.obj = persis.obj || {};

/**
 * Kopiert alle Attribute des Quell-Objekts in das Ziel-Objekt.
 * @param {Object} destination
 * @param {Object} source
 * @param {Boolean} follow - Wenn "true", dann werden Objekt-Attribute in Destination nicht einfach überschrieben, sondern auch nur ergänzt.
 * @return {Object} das Ziel-Objekt
 */
persis.obj.extend = function(destination, source, follow) {
	follow = follow || false;
	if(source) {
		for(var property in source) {
			if(follow && typeof destination[property] == "object")
				persis.obj.extend(destination[property], source[property]);
			else
				destination[property] = source[property];
		}
	}
	return destination;
};

/**
 * Erzeugt eine Kopie des angegebenen Objektes indem alle Attribute des
 * Objektes in ein neues übertragen werden.
 * @param {Object} source
 * @return {Object}
 */
persis.obj.clone = function(source) {
	return persis.obj.extend({}, source);
};



/**
 * Namespace für grundlegende Array-Hilfsfunktionen
 * @namespace
 * @since 2013/1
 */
persis.array = persis.array || { };

/**
 * Prüft, ob das angegebene Objekt ein Array ist.
 * @param {Object} array
 * @return {Boolean}
 * @since 2013/1
 */
persis.array.isArray = function(array) {
	return (typeof array == "array" ||
		typeof array.length == "number" && typeof array.slice == "function" &&
		 typeof array.push == "function" && typeof array.pop == "function");
};

/**
 * Sucht im angegebenen Array nach dem angegebenen Eintrag.
 * @param {Array} arr
 * @param {Object} value
 * @param {Function} comp - (optional) Vergleichsfunktion ( function(arrayElement, value) { return true; } )
 * @return {Number} - Array-Index, oder "-1" wenn nicht gefunden
 * @since 2013/1
 */
persis.array.find = function(array, value, comp) {
	comp = comp || function(a, b) { return a === b; };
	if(persis.array.isArray(array) && typeof value != "undefined" && value != null) {
		var i;
		for(i = 0; i < array.length; i++) {
			if(comp(array[i], value))
				return i;
		}
	}
	return -1;
};

/**
 * Erstellt eine Kopie des angegebenen Arrays.
 * @param {Array} arr
 * @return {Array}
 * @since 2013/1
 */
persis.array.clone = function(array) {
	if(persis.array.isArray(array)) {
		var copy = [ ];
		var i;
		for(i = 0; i < array.length; i++) {
			copy.push(array[i]);
		}
		return copy;
	}
	return false;
};



/**
 * Namespace für grundlegende String-Hilfsfunktionen
 * @namespace
 * @since 2013/1
 */
persis.str = persis.str || { };

/**
 * Schneidet Whitespace am Anfang und Ende des angegebenen Strings ab.
 * @param {String} str
 * @return {String}
 * @since 2013/1
 */
persis.str.trim = function(str) {
	return /^\s*(.*?)\s*$/.exec(str)[1];
};

/**
 * Prüft, ob der angegebene String mit dem angegebenen Präfix beginnt.
 * @param {String} str
 * @param {String} prefix
 * @return {Boolean}
 * @since 2013/1
 */
persis.str.startsWith = function(str, prefix) {
	return RegExp("^" + prefix + ".*$").test(str);
};

/**
 * Prüft, ob der angegebene String mit dem angegebenen Postfix endet.
 * @param {String} str
 * @param {String} postfix
 * @return {String}
 * @since 2013/1
 */
persis.str.endsWith = function(str, postfix) {
	return RegExp("^.*" + postfix + "$").test(str);
};

/**
 * Wandelt den angegebenen String in Groß-/Kleinschreibung um.
 * Dabei wird das folgende Zeichen nach einem "-" groß geschrieben, danach wieder klein.
 * Das Trennzeichen wird entfernt.
 * @param {String} str
 * @return {String}
 * @since 2013/1
 */
persis.str.camelize = function(str) {
	if(!str)
		return str;
	var parts = str.toString().split('-');
	var len = parts.length;
	
	if(len == 1)
		return parts[0];
	
	var camelized = str.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0];
	
	var i;
	for(i = 1; i < len; i++)
		camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
	
	return camelized;
};

/**
 * Wandelt den angegebenen String in Groß-/Kleinschreibung um.
 * Dabei wird das erste Zeichen groß geschrieben, alles danach folgende klein.
 * @param {String} str
 * @return {String}
 * @since 2013/1
 */
persis.str.capitalize = function(str) {
	if(!str)
		return str;
	return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
};


/**
 * Ersetzt in einem String Felder der Syntax #{Feld} durch Werte aus einem JSON-Objekt
 * 
 * var t = persis.str.template('<div class="#{classname}">#{content}</div>', { 'classname' : 'my-class', 'content' : 'My content.' });
 *
 * @param {type} tmpl Das Template mit den Platzhaltern
 * @param {type} vals Die Werte im JSON-Objekt
 * @returns {String} 
 */
persis.str.template = function(tmpl, vals) {
	var rgxp, repr;

	// default to doing no harm
	tmpl = tmpl || '';
	vals = vals || {};

	// regular expression for matching our placeholders; e.g., #{my-cLaSs_name77}
	rgxp = /#\{([^{}]*)}/g;

	// function to making replacements
	repr = function (str, match) {
		return typeof vals[match] === 'string' || typeof vals[match] === 'number' ? vals[match] : str;
	};

	return tmpl.replace(rgxp, repr);
};



/**
 * Erzeugt eine benutzerspezifische Exception.
 * @param {String} name
 * @param {String} message
 * @param {String} description
 * @param {Number} number
 * @param {Object} stack - Array von Strings
 * @param {Object | persis.Exception | String} rootException
 * @constructor
 */
persis.Exception = function(exceptionOrName, message, description, number, stack, rootException) {
	if(name)
		this.name = name;
	if(message)
		this.message = message;
	if(description)
		this.description = description;
	if(number)
		this.number = number;
	if(stack)
		this.stack = persis.array.isArray(stack) ? stack : [ stack ];
};


/**
 * String-Repräsentation der Ausnahme. Ggf. mit "name" und "number".
 * @return {String}
 */
persis.Exception.prototype.toString = function() {
	var str = this.message || "Unknown error!";
	if(this.name)
		str = this.name + ": " + str;
	if(typeof this.number != "undefined")
		str += " (" + this.number + ")";
		return str;
};



/**
 * Wandelt die angegebene Browser-Exception in einen "lesbaren"
 * Text um, welcher in Message-Boxen gezeigt werden kann.
 * @param {String} message
 * @param {Exception} exception
 * @return {String}
 * @since 2013/1
 */
persis.createErrorMessage = function(message, exception) {
	message = message || "";
	if(typeof exception != 'undefined') {
		if(typeof exception == "string")
			message += (message ? "\n" : "") + exception;
		else if(persis.instanceOf(exception, persis.Exception)) {
			message += (message ? "\n" : "") + exception.toString();
			if(exception.description && exception.description != exception.message)
				message += (message ? "\n" : "") + exception.description;
			if(exception.stack)
				message += (message ? "\n\n" : "") + "[" + exception.stack + "]";
		}
		else {
			if(exception.message)
				message += (message ? "\n" : "") + (exception.name ? exception.name + ": " : "") + exception.message + (exception.number ? " (" + exception.number + ")" : "");
			if(exception.description && exception.description != exception.message)
				message += (message ? "\n" : "") + exception.description;
			if(exception.stack)
				message += (message ? "\n\n" : "") + "[" + exception.stack + "]";
		}
		if(!message)
			message = exception.toString();
	}
	return message;
};

/**
 * Gibt eine Fehlermeldung aus. Optional mit Details aus der angegebenen Exception.
 * @param {String} message
 * @param {Exception} exception
 */
persis.errorMessage = function(message, exception) {
	alert(persis.createErrorMessage(message, exception));
};

/**
 * Gibt eine Meldung an der Debug-Konsole des Browsers aus.
 * @param {String} message
 * @param {Boolean} alertWhenNoConsole
 */
persis.trace = function(message, alertWhenNoConsole) {
	var msg = (new Date()) + ": " + message;
	if(typeof(console) != "undefined")
		console.log(msg);
	else if(alertWhenNoConsole)
		alert(msg);
};

/**
 * Wandelt das angegebene Objekt zu Debug-Zwecken in einen String um.
 * @param {Object} obj   Das betroffene Objekt
 * @param {Boolean} recurse   geht ggf. rekursiv vor.
 * @param {Boolean} methods   gibt ggf. den Inhalt von Unterfunktionen aus.
 */
persis.dump = function(obj, recurse, methods) {
	if(typeof obj == "object") {
		var str = "{ ";
		var attr;
		for(attr in obj) {
			str += "\"" + attr + "\": ";
			var value = typeof obj[attr] == "function" && !methods ? "function() {...}" : obj[attr];
			if(recurse)
				str += persis.dump(value, recurse);
			else if(value && value.toString)
				str += value.toString();
			else {
				// führte im Chrome ab und zu zu einem Fehler (DomStringMap)
				try {
					str += value;
				}
				catch(e) {
					str += "<" + String(/* persis-id="51945" */ "Umwandlungfehler!") + ">";
				}
				
			}
			str += ", ";
		}
		str += " }";
		return str;
	}
	return obj;
};

/**
 * Gibt eine Meldung an der Debug-Konsole des Browsers aus mit dem Inhalt (Dump) des angegebenen Objekts).
 * @param {String} message
 * @param {Object} obj   Das betroffene Objekt
 * @param {Boolean} recurse   geht ggf. rekursiv vor.
 * @param {Boolean} methods   gibt ggf. den Inhalt von Unterfunktionen aus.
 * @param {Boolean} alertWhenNoConsole
 */
persis.traceDump = function(message, obj, recurse, methods, alertWhenNoConsole) {
	recurse = recurse || false;
	methods = methods || false;
	alertWhenNoConsole = alertWhenNoConsole || false;
	var msg = message + ": " + persis.dump(obj, recurse, methods);
	persis.trace(msg, alertWhenNoConsole);
};

/**
 * Gibt das benannte Objekt im angegebenen Basis-Objekt zurück.
 * Ist das Basis-Objekt nicht angegeben, wird im Globalen Adressraum gesucht.
 * @param {String} name
 * @param {Object} baseObject   Das Basis(-Namespace)-Objekt.
 */
persis.byName = function(name, baseObject) {
	var parts = name.split('.');
	var base = baseObject || persis.global;
	var i;
	for(i = 0; i < parts.length; i++) {
		var object = base[parts[i]];
		if(typeof object != 'undefined') {
			base = object;
		}
		else {
			// require?
			return null;
		}
	}
	return base;
};

/*persis._pResolveRequirements = function(requirements, callback) {
	if(persis.deps.verifyRequirements(nameOrPackage))
		persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile), persis.fn.curry(persis.byName, nameOrPackage), callback);
};

persis._pRequireCallback = function(nameOrPackage, deps, callback) {
	if(persis.deps.verifyRequirements(nameOrPackage))
		persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile), persis.fn.curry(persis.byName, nameOrPackage), callback);
};*/


/**
 * Dynamische Anforderung einer Persis JavaScript Klasse, Funktion oder eines Packages/Namespace.
 * @param {persis.deps.Dependency | String} nameOrPackage
 * @param {Function} callback - (optional) function(object) { ... }
 * @return {Boolean}
 */
persis.require = function(nameOrPackage, callback) {
	//persis.trace("require: " + nameOrPackage);
	if(nameOrPackage instanceof persis.deps.Dependency) {
		return nameOrPackage.resolve(callback);
	}
	else {
		var object = persis.byName(nameOrPackage);
		if(object) {
			//persis.trace(nameOrPackage + " already available");
			// Objekt ist bereits verfügbar
			if(persis.scripts._pVERSION == 2) {
				if(typeof callback != 'undefined')
					return callback(object);
			}
			return true;
		}
		else {
			// Objekt nachladen
			var deps = persis.deps.getDependency(nameOrPackage);
			if(deps) {
				//persis.trace("resolving dependencies for " + nameOrPackage);
				/*var requirements = persis.obj.clone(deps.requires);
				persis._pResolveRequirements(requirements, persis._pRequireCallback);*/
				var req;
				//var foo = persis.fn.curry(persis.scripts.load, persis.scripts.getPersisScriptPath(deps.scriptFile), persis.fn.curry(persis.byName, nameOrPackage), callback);
				if(persis.scripts._pVERSION == 2) {
					if(!persis.isDocReady()) {
						// Das HTML-Dokument ist noch nicht vollständig geladen.
						// Scripte werden per "document.write" nachgeladen und es ist gewährleistet,
						// dass die Reihenfolge eingehalten wird.
						
						/*var requirements = persis.deps.getUnresolvedRequirements(nameOrPackage);
						if(requirements) {
							var i;
							for(i = 0; i < requirements.length, i++) {
							}
						}*/
						
						for(req in deps.requires) {
							//persis.trace(nameOrPackage + " requires " + req);
							if(deps.requires[req] instanceof persis.deps.Dependency)
								persis.require(deps.requires[req]);
							else
								persis.require(req);
						}
						
						// Script für angefordertes Objekt laden
						if(deps.scriptFile)
							persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile), persis.fn.curry(persis.byName, nameOrPackage), callback);
						
						if(typeof deps.options.postLoad != "undefined") {
							try {
								persis.trace("postLoad: " + nameOrPackage);
								deps.options.postLoad();
							}
							catch(e) {
								// Fehler in Handler-Funktion
								persis.errorMessage(String(/* persis-id="46731" */ "Fehler in postLoad()-Methode beim Nachladen von '" + deps.scriptFile + "'!"), e);
							}
						}
					}
					else {
						// Das HTML-Dokument ist bereits vollständig geladen.
						// Scripte werden durch Anlegen von <script> Blöcken nachgeladen.
						// Da hier nicht gewährleistet dass die Reihenfolge eingehalten wird,
						// müssen die Scripte "nach und nach" geladen werden. D.h. die
						// Script-Datei um angeforderten Objekt wird erst geladen, wenn
						// die Voraussetzungen erfüllt wurden.
						
						// Manche Third-Party-Libraries (z.B. Google Closure) können nicht dynamisch
						// nachladen. Dies wird mit dieser Option abgefangen.
						if(deps.options.preventLoadingAfterDomIsReady) {
							persis.errorMessage(String(/* persis-id="53446" */ "Das angeforderte Skript kann nicht dynamisch nachgeladen werden!") + " ('" + deps.scriptFile + "', '" + nameOrPackage + "')", e);
							return false;
						}
						
						// Hilfs-Callback, damit die Script-Datei des angeforderten Objekts erst geladen wird,
						// nachdem alle Voraussetzungen erfüllt wurden.
						// Diese Funktion wird bei allen Voraussetzungen als Callback eingetragen,
						// "feuert" aber erst los, wenn alle erfüllt sind.
						var foo = function() {
							if(persis.deps.verifyRequirements(nameOrPackage)) {
								//persis.trace("recheck dependencies for " + nameOrPackage + " ... solved");
								// Script für angefordertes Objekt laden
								if(deps.scriptFile)
									persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile),
										persis.fn.curry(persis.byName, nameOrPackage), callback);
								
								if(typeof deps.options.postLoad != "undefined") {
									try {
										deps.options.postLoad();
									}
									catch(e) {
										// Fehler in Handler-Funktion
										persis.errorMessage(String(/* persis-id="46731" */ "Fehler in postLoad()-Methode beim Nachladen von '" + deps.scriptFile + "'!"), e);
									}
								}
							}
							//else
								//persis.trace("recheck dependencies for " + nameOrPackage + " ... not solved");
						};
						// Voraussetzungen auflösen
						for(req in deps.requires) {
							//persis.trace(nameOrPackage + " requires " + req);
							if(deps.requires[req] instanceof persis.deps.Dependency)
								persis.require(deps.requires[req], foo);
							else
								persis.require(req, foo);
						}
					}
				}
				else {
				for(req in deps.requires) {
						/*if(persis.scripts._pVERSION == 2) {
							if(deps.requires[req] instanceof persis.deps.Dependency)
								persis.require(deps.requires[req], persis.fn.curry(persis._pRequireCallback, nameOrPackage, deps, callback));
							else
								persis.require(req, persis.fn.curry(persis._pRequireCallback, nameOrPackage, deps, callback));
						}
						else {*/
					if(deps.requires[req] instanceof persis.deps.Dependency)
						persis.require(deps.requires[req]/*, persis.fn.curry(persis._pRequireCallback, nameOrPackage, deps, callback)*/);
					else
						persis.require(req/*, persis.fn.curry(persis._pRequireCallback, nameOrPackage, deps, callback)*/);
						//}
						//persis.require(req, foo);
				}
					/*if(persis.scripts._pVERSION == 2)
						persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile), persis.fn.curry(persis.byName, nameOrPackage), callback);
					else*/
				if(deps.scriptFile)
					persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile), persis.fn.curry(persis.byName, nameOrPackage)/*, callback*/);
				if(typeof deps.options.postLoad != "undefined") {
					try {
						deps.options.postLoad();
					}
					catch(e) {
						// Fehler in Handler-Funktion
						persis.errorMessage(String(/* persis-id="46731" */ "Fehler in postLoad()-Methode beim Nachladen von '" + deps.scriptFile + "'!"), e);
					}
				}
			}
			}
			else {
				persis.errorMessage(String(/* persis-id="46172" */ 'Die Abhängigkeit für "' + nameOrPackage + '" konnte nicht aufgelöst werden!'));
			}
		}
	}
	return false;
};

/*persis.with = function(baseObject, foo) {
	return foo(baseObject);
};*/



/**
 * Namespace für Browser-Funktionen
 * @namespace
 * @since 2013/1
 */
persis.browser = persis.browser || {};

/**
 * @since 2013/1
 */
persis.browser.Products = {
	UNKNOWN: "UNKNOWN",
	INTERNET_EXPLORER: "INTERNET_EXPLORER",
	EXPLORER: "INTERNET_EXPLORER",
	IE: "INTERNET_EXPLORER",
	MSIE: "INTERNET_EXPLORER",
	FIREFOX: "FIREFOX",
	FF: "FIREFOX",
	OPERA: "OPERA",
	CHROME: "CHROME",
	SAFARI: "SAFARI",
	WEBKIT: "WEBKIT",
	MOZILLA: "MOZILLA"
};
/**
 * @since 2013/1
 */
persis.browser.ProductName = {
	"UNKNOWN": String(/* persis-id="53447" */ "unbekannt"),
	"INTERNET_EXPLORER": "Internet Explorer",
	"FIREFOX": "Firefox",
	"OPERA": "Opera",
	"CHROME": "Chrome",
	"SAFARI": "Safari",
	"WEBKIT": "WebKit",
	"MOZILLA": "Mozilla"
};
/**
 * @since 2013/1
 */
persis.browser.ProductVendor = {
	"UNKNOWN": String(/* persis-id="53447" */ "unbekannt"),
	"INTERNET_EXPLORER": "Microsoft",
	"FIREFOX": "Mozilla Foundation",
	"OPERA": "Opera Software",
	"CHROME": "Google",
	"SAFARI": "Apple",
	"WEBKIT": String(/* persis-id="53447" */ "unbekannt"),
	"MOZILLA": String(/* persis-id="53447" */ "unbekannt")
};
persis.browser._pDetRegExpr = [
	/msie ([\w.]+)/i,
	/Trident\/.*rv\:([\w.]+)/i,
	/firefox[ \/]([\w.]+)/i,
	/opera(?:.*version)?[ \/]([\w.]+)/i,
	/chrome[ \/]([\w.]+)/i,
	/version[ \/]([\w.]+).*?(?:(mobile)[ \/](?:[\w.]+).*?)?safari[ \/]([\w.]+)/i,
	/webkit[ \/]([\w.]+)/i,
	/mozilla[ \/]([\w.]+)/i
];
persis.browser._pDetProduct = [
	persis.browser.Products.INTERNET_EXPLORER,
	persis.browser.Products.INTERNET_EXPLORER,
	persis.browser.Products.FIREFOX,
	persis.browser.Products.OPERA,
	persis.browser.Products.CHROME,
	persis.browser.Products.SAFARI,
	persis.browser.Products.WEBKIT,
	persis.browser.Products.MOZILLA
];

/**
 * Erkennt um welchen Browser es sich handelt.
 * @since 2013/1
 */
persis.browser.detect = function() {
	var b = {};
	/*b.agent = navigator.userAgent;
	b.platform = navigator.platform;
	b.cookies = navigator.cookieEnabled;
	b.java = navigator.javaEnabled();
	b.plugins = navigator.plugins;
	
	b.environment = /\(([^)]+)/i.exec(b.agent)[1].split(";");
	var i;
	for(i = 0; i < b.environment.length; i++)
		b.environment[i] = /^\s*(.*?)\s*$/.exec(b.environment[i])[1];*/
	
	/*

	Internet Explorer 10:
		
		HTML 4.01 Strict-Mode:
		
			Browsermodus: IE10; Dokumentmodus: Standards;
				"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
				"5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
			
			Browsermodus: IE10-Kompatibilitätsansicht; Dokumentmodus: IE7-Standards;
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)" 
				"4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)" 
			
			Browsermodus: IE9; Dokumentmodus: IE9-Standards;
				"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
				"5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
			
			Browsermodus: IE8; Dokumentmodus: IE8-Standards;
				"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
			
			Browsermodus: IE7; Dokumentmodus: IE7-Standards;
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"


		HTML 4.01 Transitional-Mode:
		
			Browsermodus: IE10; Dokumentmodus: Quirks;
				"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
				"5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
		
			Browsermodus: IE10-Kompatibilitätsansicht; Dokumentmodus: IE5-Quirks;
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
		
			Browsermodus: IE9; Dokumentmodus: IE5-Quirks;
				"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
				"5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
		
			Browsermodus: IE8; Dokumentmodus: IE5-Quirks;
				"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
		
			Browsermodus: IE7; Dokumentmodus: IE5-Quirks;
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"


		HTML Default: (identisch zu Transitional)
		
			Browsermodus: IE10; Dokumentmodus: Quirks;
				"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
				"5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"
		
			Browsermodus: IE10-Kompatibilitätsansicht; Dokumentmodus: IE5-Quirks;
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
		
			Browsermodus: IE9; Dokumentmodus: IE5-Quirks;
				"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
				"5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
		
			Browsermodus: IE8; Dokumentmodus: IE5-Quirks;
				"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
		
			Browsermodus: IE7; Dokumentmodus: IE5-Quirks;
				"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
				"4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; BRI/2)"
		
		Internet Explorer 11:
		
			"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; InfoPath.3; .NET4.0E; rv:11.0) like Gecko"
	 */
	
	var agent = navigator.userAgent;
	var expr = [];
	for(i = 0; i < persis.browser._pDetRegExpr.length; i++) {
		expr[i] = persis.browser._pDetRegExpr[i].exec(agent);
	}
	var det = -1;
	for(i = 0; i < expr.length; i++) {
		if(expr[i]) {
			det = i;
			break;
		}
	}
	var prod;
	var ver;
	if(det < 0) {
		prod = persis.browser.Products.UNKNOWN;
		ver = navigator.appVersion || 0;
	}
	else {
		prod = persis.browser._pDetProduct[det];
		ver = expr[det][1];
	}
	
	var ieTridentVersion = 0;
	if(prod == persis.browser.Products.INTERNET_EXPLORER) {
		var trident = /Trident\/(\d+(?:\.\d+)?)/i.exec(agent);
		if(trident && trident.length == 2) {
			ieTridentVersion = Number(trident[1]);
		}
		
		// Weiche zur korrekten Anzeige des IEs ab Version 11, wenn Browser sich in Kompatibilitätsmodus befindet
		var _rv = /Trident\/.*rv\:([\w.]+)/i.exec(agent);
		var _revision = (_rv != null && _rv.length >= 2) ? _rv[1] : "-1";
		if(ieTridentVersion == 7 && _revision == '-1')
		{
			ver = '11.0';
		}
	}
	
	var major, minor, build;
	var v = /^\s*(\d+)(?:[ \/_.](\d+)(?:[ \/_.](\d+))?)?/.exec(ver);
	major = v ? Number(v[1] || 0) : 0;
	minor = v ? Number(v[2] || 0) : 0;
	build = v ? Number(v[3] || 0) : 0;
	
	//if(prod == persis.browser.Products.SAFARI)
	
	var docmode = {
		language: "",
		version: "",
		mode: "",
		engine: ""
	};
	if(document.doctype && document.doctype.publicId) {
		docmode.mode = "STANDARDS";
		var dm = /\/DTD\s+(\w+)\s+([0-9\.]+)(?:\s+(\w+))?\//.exec(document.doctype.publicId.toUpperCase());
		if(dm) {
			docmode.language = dm[1];
			docmode.version = dm[2];
			docmode.interpretation = dm[3] || "STRICT";
		}
	}
	else {
		if(document.compatMode == "BackCompat")
			docmode.mode = "QUIRKS";
		else /*if(document.compatMode == "CSS1Compat")*/
			docmode.mode = "STANDARDS";
		if(document.documentElement && document.documentElement.tagName.toUpperCase() == "HTML")
			docmode.language = "HTML";
		// IE < 9 bietet kein "doctype"-Tag und trotz Transitional-Angabe bleibt er im Quirks-Mode
		// deshalb kann man im "STANDARDS"-Mode im IE vom Strict-Mode ausgehen.
		if(docmode.language == "HTML" && docmode.mode == "STANDARDS" && prod == persis.browser.Products.INTERNET_EXPLORER && v.major < 9)
			docmode.interpretation = "STRICT";
		else
			docmode.interpretation = "";
	}
	if(document.documentMode)
		// IE only (Explorer-Modus "7", "8", "9")
		// Im Kompatibilitätsmodus wird auch "7" verwendet
		docmode.engine = String(document.documentMode);
	else
		docmode.engine = "";
	
	
	b.product = prod;
	b.name = persis.browser.ProductName[prod];
	b.vendor = persis.browser.ProductVendor[prod];
	b.version = ver;
	b.v = {};
	b.v.major = major;
	b.v.minor = minor;
	b.v.build = build;
	if(prod == persis.browser.Products.INTERNET_EXPLORER)
		b.v.trident = ieTridentVersion;
	b.docmode = docmode;
	//b.language = navigator.language;
	return b;
};

/**
 * Cache für den ermittelten Browser. Sollte nicht direkt ausgelesen werden,
 * sondern die Prüfroutinen im "persis.browser"-Package verwenden (isProduct(),
 * ...)
 * @since 2013/1
 */
persis.browser.DETECTED = persis.browser.detect();

/**
 * Prüft, ob der aktuell erkannte Browser dem Produkt mit
 * *mindestens* der angegebenen Versionsnummer entspricht.
 * @param {persis.browser.Products} product
 * @param {Number} major - (optional)
 * @param {Number} minor - (optional)
 * @return {Boolean}
 * @since 2013/1
 */
persis.browser.isProduct = function(product, major, minor) {
	major = major || 0;
	minor = minor || 0;
	return product == persis.browser.DETECTED.product && persis.browser.DETECTED.v.major >= major && persis.browser.DETECTED.v.minor >= minor;
};

/**
 * Prüft, ob das aktuelle Dokument im Standards-compliant Mode
 * geladen wurde, oder nicht (Quirks).
 * @return {Boolean}
 * @since 2013/1
 */
persis.browser.isStandardsMode = function() {
	return persis.browser.DETECTED.docmode.mode == "STANDARDS";
};

/**
 * Prüft, ob das aktuelle Dokument im Standards-Strict Mode
 * geladen wurde, oder nicht (Quirks).
 * @return {Boolean}
 * @since 2013/1
 */
persis.browser.isStandardsStrictMode = function() {
	return persis.browser.isStandardsMode() && persis.browser.DETECTED.docmode.interpretation == "STRICT";
};

/**
 * Prüft, ob aktuell der Internet Explorer im Kompatibilitätsmodus
 * ausgeführt wird.
 * @return {Boolean}
 * @since 2013/1
 */
persis.browser.isInternetExplorerInCompatibilityMode = function() {
	if(persis.browser.isProduct(persis.browser.Products.INTERNET_EXPLORER)) {
		var tridentBrowserVersion = 7;
		if(!persis.browser.DETECTED.v.trident || persis.browser.DETECTED.v.trident < 4)
			tridentBrowserVersion = 7;
		else if(persis.browser.DETECTED.v.trident < 5)
			tridentBrowserVersion = 8;
		else if(persis.browser.DETECTED.v.trident < 6)
			tridentBrowserVersion = 9;
		else if(persis.browser.DETECTED.v.trident < 7)
			tridentBrowserVersion = 10;
		else
			tridentBrowserVersion = 11;
		return persis.browser.DETECTED.v.major != tridentBrowserVersion;
	}
	return false;
	/*return persis.browser.isProduct(persis.browser.Products.INTERNET_EXPLORER)
		 && persis.browser.DETECTED.docmode.engine == 5;*/
};

/**
 * Gibt die Rendering-Engine-Kennung des aktuellen Browsers zurück.
 * (derzeit nur im IE verwendet)
 * @return {String}
 * @since 2013/1
 */
persis.browser.docEngine = function() {
	return persis.browser.DETECTED.docmode.engine;
};


/**
 * Namespace für Funktions-Hilfsfunktionen
 * @namespace
 */
persis.fn = persis.fn || {};

/**
 * Erweitert den Funktionsprototypen um eine Validierungsroutine,
 * mit der festgestellt werden kann, ob eine Funktion gültig ist,
 * oder nicht.
 * @return {Boolean} - "true", wenn valide. Ansonsten fliegt eine Exception.
 */
Function.prototype.validate = function() {
	// zusätzlich prüfen, ob die Funktion über einen gültigen Context verfügt
	if(!this.context())
		throw persis.obj.createException(
				"InvalidContextException",
				"Invalid function context!",
				"The context of the function is not valid. The window or location may have been left."
		);
	return true;
};

/**
 * Erweitert den Funktionsprototypen um die Möglichkeit den Context (Window)
 * in dem die Funktion definiert ist abzufragen.
 * @return {Window}
 */
Function.prototype.context = (function(context) {
	var _href = context.location.href;
	return function() {
		if(context) {
			// prüfen, ob die geladene Seite noch die gleiche wie beim Starten ist,
			// ansonsten ist der Context nicht mehr gültig
			if(_href == context.location.href)
				return context;
			else
				// Überschreiben, damit ein Wechsel zurück die Funktion nicht wieder "gültig" macht.
				context = false;
		}
		return false;
	}
})(persis.global);

/**
 * Prüft, ob die angegebene Funktion gültig ist. Dies ist nützlich
 * um zu prüfen, ob eine Funktion (siehe IE) dadurch ungültig wurde,
 * weil deren Fenster geschlossen wurde.
 * @param {Function} functionToTest
 * @return {Boolean}
 */
persis.fn.isValid = function(functionToTest) {
	if(functionToTest) {
		if(typeof functionToTest == "function" || typeof functionToTest == "object") {
			try {
				//functionToTest.toString();
				functionToTest.validate();
				return true;
			}
			catch(e) { }
		}
	}
	return false;
};

/**
 * Erzeugt eine neue temporäre Funktion, an die die weiteren angegebenen
 * Argumente "gebunden" sind. Der Kontext ("this") bleibt unverändert.
 * @param {Function} functionToBind
 * @param {Object} ... weitere Parameter
 * @return {Function}
 */
persis.fn.curry = function(functionToBind) {
	if(arguments.length < 2)
		return functionToBind;	// es gibt keine Parameter zu binden
	var $method = functionToBind;
	var $args = Array.prototype.slice.call(arguments, 1);	// Array-Methode "slice" auf "arguments" anwenden
	var foo = function() {
		var args = $args.concat(Array.prototype.slice.call(arguments, 0));
		return $method.apply(this, args);
	};
	return foo;
};

/**
 * Erzeugt eine neue temporäre Funktion, an die die weiteren angegebenen
 * Argumente "gebunden" sind. Der Kontext ("this") wird mit dem 2. Parameter
 * "thisContext" spezifiziert.
 * @param {Function} functionToBind
 * @param {Object} thisContext
 * @param {Object} ... weitere Parameter
 * @return {Function}
 */
persis.fn.bind = function(functionToBind, thisContext) {
	if(arguments.length < 2)
		return functionToBind;	// es gibt keine Parameter zu binden
	var $method = functionToBind;
	var $this = thisContext;
	var $args = Array.prototype.slice.call(arguments, 2);	// Array-Methode "slice" auf "arguments" anwenden
	var foo = function() {
		var args = $args.concat(Array.prototype.slice.call(arguments, 0));
		return $method.apply($this, args);
	};
	return foo;
};

/**
 * Verzögert den Aufruf der angegebenen Funktion um die angegebene Anzahl an
 * Millisekunden. Die Funktion selbst darf keine Parameter erwarten. Der
 * Rückgabewert ist eine window.setTimeout()-Id, mit der die Verzögerung auch
 * mittels window.clearTimeout() gelöscht werden kann.
 * @param {Function} functionToDelay
 * @param {Number} delayMillis - (optional) wenn nicht angegeben, wird 1 angenommen.
 * @return {Object} Rückgabewert ist eine window.setTimeout()-Id, mit der die
 * Verzögerung auch mittels window.clearTimeout() gelöscht werden kann.
 */
persis.fn.delay = function(functionToDelay, delayMillis) {
	delayMillis = Math.max(1, delayMillis || 1);
	return window.setTimeout(functionToDelay, delayMillis);
};

/**
 * Verzögert den Aufruf der angegebenen Funktion um die angegebene Anzahl an
 * Millisekunden. Es können noch weitere Parameter angegeben werden, die an die
 * Funktion "gebunden" werden. Der Kontext ("this") bleibt unverändert.
 * @param {Function} functionToDelay
 * @param {Number} delayMillis
 * @param {Object} ... weitere Parameter
 * @return {Object} Rückgabewert ist eine window.setTimeout()-Id, mit der die
 * Verzögerung auch mittels window.clearTimeout() gelöscht werden kann.
 */
persis.fn.delayCurry = function(functionToDelay, delayMillis) {
	var foo = persis.fn.curry(functionToDelay, Array.prototype.slice.call(arguments, 2));
	return persis.fn.delay(foo, delayMillis);
};

/**
 * Verzögert den Aufruf der angegebenen Funktion um die angegebene Anzahl an
 * Millisekunden. Der Kontext ("this") wird mit dem 3. Parameter "thisContext"
 * spezifiziert. Es können noch weitere Parameter angegeben werden, die an die
 * Funktion "gebunden" werden.
 * @param {Function} functionToDelay
 * @param {Number} delayMillis
 * @param {Object} thisContext
 * @return {Object} Rückgabewert ist eine window.setTimeout()-Id, mit der die
 * Verzögerung auch mittels window.clearTimeout() gelöscht werden kann.
 */
persis.fn.delayBind = function(functionToDelay, delayMillis, thisContext) {
	var args = Array.prototype.slice.call(arguments, 2);
	args.unshift(functionToDelay);
	var foo = persis.fn.bind.apply(this, args);
	return persis.fn.delay(foo, delayMillis);
};

/*
persis.fn.defer = function() {
	
};
*/

/**
 * Diese Hilfsfunktion prüft, ob ein gültiger Wert an einen
 * Funktionsparameter übergeben wurde. Ungültig würde in diesem
 * Kontext bedeuten: der Wert ist "undefined", oder "null",
 * oder "false". Während "", oder 0 gültig wären.
 * @param {Object} value - Parameterwert
 * @param {Object} defaultValue - Was soll zurückgegeben werden, wenn der Parameterwert ungültig ist (Default ist "false").
 * @return {Object} - der Parameterwert, oder defaultValue, wenn ungültig
 * @since 2013/1
 */
persis.fn.paramValue = function(value, defaultValue) {
	if(arguments.length < 2)
		defaultValue = false;
	return typeof value == "undefined" || typeof value == "boolean" && !value || value == null ?
		defaultValue : value;
};



/**
 * Überträgt die Prototypen-Methoden der angegebenen Basisklasse in
 * die neue Kindklasse, die erben soll.
 * Beispiel:
 * <pre>
 * function A(a, b) { ... }
 * 
 * A.prototype.foo = function(a) { ... }
 *
 * function B(a, b, c) {
 *     // Konstruktor der Basisklasse aufrufen
 *     A.call(this, a, b);
 *     ...
 * }
 * persis.inherits(B, A);
 *
 * var obj = new B('a', 'b', 'c');
 * obj.foo();   // foo() ist verfügbar
 * </pre>
 *
 * Zusätzlich wird die Basisklasse (Super-Klasse) wie folgt zur Verfügung gestellt:
 *
 * <pre>
 * B.prototype.foo = function(a) {
 *     // rufe Variante der Basisklasse auf
 *     B.superClass.foo.call(this, a);
 *     ...
 * };
 * </pre>
 *
 *
 * Mehrfachvererbung: (experimentell!)
 *
 * Es ist möglich der "inherits"-Funktion weitere Klassen als Parameter anzugeben,
 * welche Funktionalitäten bieten, die ebenfalls in die neue Child-Klasse übernommen
 * werden soll (Mehrfachvererbung).
 *
 * Hierbei werden alle Elemente einfach aus dem Prototypen-Objekt kopiert.
 * Achtung: Dabei können auch Elemente aus der Basisklasse überschrieben werden!
 * Da lediglich die Elemente aus dem Prototypen kopiert werden funktioniert hierauf
 * das JavaScript Konstrukt "instanceof" nicht! Dies wirkt lediglich auf die "superClass".
 *
 * Es kann jedoch alternativ mit der neuen Methode "obj.instanceOf()" entsprechend
 * geprüft werden. "instanceOf()" wird durch "inherits" ebenfalls in die "childClass"
 * aufgenommen.
 *
 * Diese Klassen werden in "childClass.extendedClasses" (einem Array) in der "childClass"
 * Klasse gespeichert. Die Konstruktoren können folgendermaßen aufgerufen werden:
 *
 * childClass.extendedClasses[0].call(this, ...);
 *
 * Die Reihenfolge der Parameter bestimmt damit automatisch auch die Reihenfolge
 * der Klassen in dem extendedClasses-Array.
 *
 * Achtung: Durch den Aufruf der weiteren Konstruktoren kann es vorkommen, wenn es
 * bei den Erweiterungsklassen gemeinsame Basisklassen gibt, dass diese Konstruktoren
 * mehrfach ausgeführt werden! Dies wird nicht automatisch verhindert, sondern muss
 * selbst organisiert werden.
 *
 * @param {Function} childClass - Neue Kindklasse, die von der Basisklasse erben soll.
 * @param {Function} parentClass - Basisklasse.
 * @param {Function} ... - Erweiterungsklassen
 */
persis.inherits = function(childClass, parentClass) {
	function tempClass() {};
	if(typeof parentClass == "undefined")
		persis.trace("Fehler: Basisklasse ist undefiniert! Abhängigkeit ist vermutlich nicht aufgelöst." + " (#1)");
	tempClass.prototype = parentClass.prototype;
	childClass.superClass = parentClass.prototype;
	childClass.prototype = new tempClass();
	
	childClass.extendedClasses = [ ];
	var i;
	for(i = 2; i < arguments.length; i++) {
		if(typeof arguments[i] == "undefined")
			persis.trace("Fehler: Basisklasse ist undefiniert! Abhängigkeit ist vermutlich nicht aufgelöst." + " (#" + i + ")");
		persis.obj.extend(childClass.prototype, arguments[i].prototype);
		childClass.extendedClasses.push(arguments[i]);
	}
	
	childClass.prototype.constructor = childClass;
	childClass.prototype.instanceOf = function(baseClass) {
		if(typeof baseClass == "function") {
			if(this instanceof baseClass)
				return true;
			else if(this.constructor && this.constructor === baseClass)
				return true;
			var i;
			for(i = 0; i < this.constructor.extendedClasses.length; i++) {
				if(this.constructor.extendedClasses[i].prototype.instanceOf) {
					if(this.constructor.extendedClasses[i].prototype.instanceOf.call(this.constructor.extendedClasses[i].prototype, baseClass))
						return true;
				}
				else if(this.constructor.extendedClasses[i] === baseClass)
					return true;
			}
			if(this.constructor.superClass && this.constructor.superClass.instanceOf)
				if(this.constructor.superClass.instanceOf.call(this.constructor.superClass, baseClass))
					return true;
		}
		return false;
	};
};

/**
 * Prüft ob das angegebene Objekt von der angegebenen Klasse abgeleitet ist.
 * @param {Object} obj
 * @param {Object} clazz
 * @return {Boolean}
 * @since 2013/1
 */
persis.instanceOf = function(obj, clazz) {
	if(obj && obj.instanceOf)
		return obj.instanceOf(clazz);
	else if(obj && obj.constructor && obj.constructor === clazz)
		return true;
	else
		return obj instanceof clazz;
};

/**
 * Basisklasse für alle Objekte, die "freigegeben" werden können.
 * @constructor
 */
persis.Disposable = function() {
	this._pDisposed = false;
};

/**
 * Liefert zurück, ob das aktuelle Objekt noch gültig ist, oder
 * bereits "freigegeben" wurde.
 * @return {Boolean}
 */
persis.Disposable.prototype.isDisposed = function() {
	return this._pDisposed;
};

/**
 * Gibt alle reservierten Unterobjekte des aktuellen Objekts wieder frei.
 * Abgeleitete Klassen müssen hier Elemente/Objekte löschen.
 * @return {Boolean} - liefert "false" zurück, wenn das Objekt bereits disposed wurde.
 */
persis.Disposable.prototype.dispose = function() {
	if(this.isDisposed())
		return false;
	this._pDisposed = true;
	return true;
};

persis.Disposable.prototype.toString = function() {
	return "persis.Disposable(" + this.isDisposed() + ")";
};


/**
 * Hilfsfunktion, die prüft, ob das angegebene Objekt eine dispose()
 * Funktion zur Verfügung stellt.
 * @param {Object} obj
 * @param {...} ...
 * @return {Boolean}
 * @since 2013/1
 */
persis.dispose = function(obj) {
	if(obj && typeof obj.dispose == "function") {
		var args = Array.prototype.slice.call(arguments, 1);	// Array-Methode "slice" auf "arguments" anwenden
		return obj.dispose.apply(obj, args);
	}
	return false;
}


/**
 * Basisklasse für alle Objekte, die "geklont" werden können.
 * @constructor
 */
persis.Cloneable = function() {
};

/**
 * Erstellt eine Kopie des aktuellen Objekts.
 * Abgeleitete Klassen müssen hier ein neues Objekt anlegen, und den Inhalt übertragen.
 * @return {persis.Disposable} - das neue Objekt
 */
persis.Cloneable.prototype.clone = function() {
	throw String(/* persis-id="51946" */ "Cloneable.clone() nicht implementiert!");
};

persis.Cloneable.prototype.toString = function() {
	return "persis.Cloneable()";
};


/**
 * Namespace für globale Konstanten.
 * @namespace
 */
persis.constants = persis.constants || {};
persis.constants._pConstantSpace = persis.constants._pConstantSpace || {};

/**
 * Setzt die globale Konstante auf den angegebenen Wert.
 * @param {String} constantName - Name der Konstante
 * @param {Object} value - Wert
 */
persis.constants.set = function(constantName, value) {
	persis.constants._pConstantSpace[constantName] = value;
};
/**
 * Gibt den Wert der globalen Konstante zurück.
 * @param {String} constantName - Name der Konstante
 * @returns {Object}
 */
persis.constants.get = function(constantName) {
	return persis.constants._pConstantSpace[constantName];
};


/**
 * Namespace für Script-Abhängigkeiten.
 * @namespace
 */
persis.deps = persis.deps || {};

/**
 * Abhängigkeiten auf Basis der Skript-Datei.
 * Welche Voraussetzungen hat die angegebene Datei? Was bietet die Datei?
 * Aufbau:
 * persis.deps._pFileIndex[scriptFile] = {
 *    scriptFile: 'scriptFile',
 *    provides: { 'class1': true, 'function2': true, ... },
 *    requires: { 'namespace': true, 'function1': true, ... }
 *    options: { 'postLoad': ..., ... }
 * };
 * @private
 */
persis.deps._pFileIndex = {};

/**
 * Abhängigkeiten auf Basis der Objekte.
 * Welche Dateiabhängigkeiten gilt für dieses Objekt?
 * Aufbau:
 * persis.deps._pObjectIndex[objectName] -> persis.deps._pFileIndex[...];
 * @private
 */
persis.deps._pObjectIndex = {};


/**
 * Hilfsklasse für Objekte, welche dynamisch nachgeladen werden können.
 * @param {String} ressourceId - Eine eindeutige Ressourcen-Kennung, die sich nicht mit anderen überschneiden darf.
 * @constructor
 */
persis.deps.Dependency = function(ressourceId) {
	this._pRessourceId = ressourceId;
	this._pCallbacks = [ ];
};
persis.deps.Dependency._pResolved = {};


/**
 * Gibt die Ressourcen-Kennung zurück.
 * @return {String}
 */
persis.deps.Dependency.prototype.getId = function() {
	return this._pRessourceId;
};

/**
 * Gibt zurück, ob die Abhängigkeit bereits aufgelöst wurde.
 * Abgeleitete Klassen sollten dies durch eigene Kriterien überschreiben.
 * Die Basis Implementierung prüft nur die globale Kennung zur
 * Ressourcen-Id, welche in "resolve" gesetzt wird.
 * @return {Boolean}
 */
persis.deps.Dependency.prototype.isResolved = function() {
	return (persis.deps.Dependency._pResolved[this.getId()] ? true : false);
};

/**
 * Ergänzt die angegebene Callback-Funktion. Diese Funktionen
 * werden implizit durch "resolve" Benachrichtigt, sobald die
 * Abhängigkeit aufgelöst wurde.
 * Es muss jedoch nach dem Hinzufügen "resolve" aufgerufen
 * werden.
 * Identische Callback-Funktionen können nicht mehrfach hinzugefügt
 * werden.
 * @param {Function} callback - function(dependency) { ... };
 * @return {Boolean} - "true", wenn die Callback-Funktion registriert wurde
 * @since 2013/1
 */
persis.deps.Dependency.prototype.addCallback = function(callback) {
	if(persis.array.find(this._pCallbacks, callback) < 0) {
		this._pCallbacks.push(callback);
		return true;
	}
	return false;
};

/**
 * Benachrichtigt die registrierten Callbacks, nachdem die Abhängigkeit aufgelöst wurde.
 * @since 2013/1
 */
persis.deps.Dependency.prototype._pNotifyCallbacks = function() {
	var i;
	for(i = 0; i < this._pCallbacks.length; i++) {
		var callback = this._pCallbacks[i];
		try {
			callback(this);
		}
		catch(e) {
			persis.errorMessage(String(/* persis-id="53449" */ "Beim Ausführen der Benachrichtigungsfunktion für das Auflösen der Abhängigkeit ist ein Script-Fehler aufgetreten!") + " (" + this.getId() + ")", e);
		}
	}
};

/**
 * Führt die Auflösung der Abhängigkeit durch bzw. leitet den Vorgang
 * ein (wird von "resolve" aufgerufen).
 * Abgeleitete Klassen müssen diese Methode überschreiben.
 * Die Basis Implementierung setzt eine globale Kennung zur
 * Ressourcen-Id. Abgeleitete Klassen müssen deshalb immer zusätzlich
 * die Basis-Variante aufrufen.
 * @since 2013/1
 */
persis.deps.Dependency.prototype._pResolveInternal = function() {
	persis.deps.Dependency._pResolved[this.getId()] = true;
};

/**
 * Löst die Abhängigkeit auf, bzw. leitet den entsprechenden Vorgang ein.
 * @param {Function} callback - (optional) function(dependency) { ... }; Diese Callback-Funktion wird aufgerufen, sobald die Abhängigkeit aufgelöst ist.
 * @return {Boolean} - "true", wenn die Abhängigkeit (bereits) aufgelöst ist. Bei "false" bekommt man die Rückmeldung erst nach/durch die Callback-Funktion.
 */
persis.deps.Dependency.prototype.resolve = function(callback) {
	//persis.trace("resolving " + this.toString() + " ...");
	if(callback)
		this.addCallback(callback);
	if(!this.isResolved())
		this._pResolveInternal();
	if(this.isResolved()) {
		this._pNotifyCallbacks();
		return true;
	}
	return false;
};

/**
 * Text-Repräsentation des Objekts.
 * @return {String}
 */
persis.deps.Dependency.prototype.toString = function() {
	return "persis.deps.Dependency(" + this.getId() + ")";
};


/**
 * Abhängigkeit zu einer CSS-Datei.
 * @param {String} pathToStylesheet
 * @param {String} media
 * @constructor
 */
persis.deps.Stylesheet = function(pathToStylesheet, media) {
	this._pPath = pathToStylesheet || "";
	this._pMedia = media || "";
	
	var id = "CSS:" + this._pPath + '#' + this._pMedia;
	persis.deps.Stylesheet.superClass.constructor.call(this, id);
};
persis.inherits(persis.deps.Stylesheet, persis.deps.Dependency);

/**
 * Erstellt dynamisch einen <link>-Tag, mit dem die angegebene CSS-Datei geladen wird.
 * @param {String} pathToCss
 * @param {String} media (optional)
 */
persis.deps.Stylesheet.load = function(pathToStylesheet, media) {
	//<link rel="stylesheet" href="css/general.css" />
	
	media = media || "";
	var doc = persis.global.document;
	if(typeof doc != 'undefined' && typeof doc.write != 'undefined') {
		if(!persis.isDocReady()) {
			persis.trace('loading "' + pathToStylesheet + '"');
			doc.writeln('<link rel="stylesheet" '+ (media != '' ? 'media="' + media + '" ' : '') + 'href="' + pathToStylesheet + '" />');
			//doc.writeln('<link rel="stylesheet" '+ (media != '' ? 'media="' + media + '" ' : '') + 'href="' + pathToStylesheet + '" type="text/css" />');
		}
		else {
			var styleElement = doc.createElement("link");
			styleElement.setAttribute("rel", "stylesheet");
			if(media)
				styleElement.setAttribute("media", media);
			//styleElement.setAttribute("type", "text/css");
			styleElement.setAttribute("href", pathToStylesheet);
			doc.body.appendChild(styleElement);
		}
	}
	else {
		persis.errorMessage(String(/* persis-id="47047" */ "Fehler beim Nachladen von '" + pathToStylesheet + "'!\nDocument-Bezug fehlt."));
	}
};


/**
 * Gibt die Adresse der CSS-Datei zurück.
 * @return {String}
 */
persis.deps.Stylesheet.prototype.getPath = function() {
	return this._pPath;
};

/**
 * Gibt die Media-Angabe des Stylesheets zurück.
 * @return {String}
 */
persis.deps.Stylesheet.prototype.getMedia = function() {
	return this._pMedia;
};

/**
 * Gibt zurück, ob die Abhängigkeit bereits aufgelöst wurde.
 * Abgeleitete Klassen sollten dies durch eigene Kriterien überschreiben.
 * Die Basis Implementierung prüft nur die globale Kennung zur
 * Ressourcen-Id, welche in "resolve" gesetzt wird.
 * @return {Boolean}
 */
/*persis.deps.Stylesheet.prototype.isResolved = function() {
	var href = this.getPath();
	var media = this.getMedia() || "screen";
	var i;
	for(i = 0; i < document.styleSheets.length; i++) {
		var sheet = document.styleSheets[i];
		if(persis.str.endsWith(sheet.href, href))
			var mediaText = typeof sheet.media == "string" ? sheet.media : (sheet.media && sheet.media.mediaText ? sheet.media.mediaText : "") || "screen";
			if(mediaText == media)
				return true;
	}
	return false;
};*/

/**
 * Führt die Auflösung der Abhängigkeit durch bzw. leitet den Vorgang
 * ein (wird von "resolve" aufgerufen).
 * Abgeleitete Klassen müssen diese Methode überschreiben.
 * Die Basis Implementierung setzt eine globale Kennung zur
 * Ressourcen-Id. Abgeleitete Klassen müssen deshalb immer zusätzlich
 * die Basis-Variante aufrufen.
 * @since 2013/1
 */
persis.deps.Stylesheet.prototype._pResolveInternal = function() {
	persis.deps.Stylesheet.load(persis.scripts.getPersisScriptPath(this.getPath()), this.getMedia());
	persis.deps.Stylesheet.superClass._pResolveInternal.call(this);
};

/**
 * Text-Repräsentation des Objekts.
 * @return {String}
 */
persis.deps.Stylesheet.prototype.toString = function() {
	return "persis.deps.Stylesheet(" + this.getId() + ")";
};


/**
 * Erstellt ein Dependency-Objekt für das angegebene Stylesheet.
 */
persis.deps.css = function(pathToStylesheet, media, callback) {
	var dep = new persis.deps.Stylesheet(pathToStylesheet, media);
	if(callback)
		dep.addCallback(callback);
	return dep;
};


/**
 * Abhängigkeit zu einer JavaScript-Datei.
 * @param {String} pathToScript
 * @constructor
 * @since 2013/1
 */
persis.deps.Script = function(pathToScript) {
	this._pPath = pathToScript || "";
	
	var id = "JS:" + this._pPath;
	persis.deps.Script.superClass.constructor.call(this, id);
};
persis.inherits(persis.deps.Script, persis.deps.Dependency);


/**
 * Gibt die Adresse der JavaScript-Datei zurück.
 * @return {String}
 */
persis.deps.Script.prototype.getPath = function() {
	return this._pPath;
};

/**
 * Gibt zurück, ob die Abhängigkeit bereits aufgelöst wurde.
 * Abgeleitete Klassen sollten dies durch eigene Kriterien überschreiben.
 * Die Basis Implementierung prüft nur die globale Kennung zur
 * Ressourcen-Id, welche in "resolve" gesetzt wird.
 * @return {Boolean}
 */
/*persis.deps.Script.prototype.isResolved = function() {
	...
};*/

/**
 * Führt die Auflösung der Abhängigkeit durch bzw. leitet den Vorgang
 * ein (wird von "resolve" aufgerufen).
 * Abgeleitete Klassen müssen diese Methode überschreiben.
 * Die Basis Implementierung setzt eine globale Kennung zur
 * Ressourcen-Id. Abgeleitete Klassen müssen deshalb immer zusätzlich
 * die Basis-Variante aufrufen.
 */
persis.deps.Script.prototype._pResolveInternal = function() {
	persis.deps.Script.load(persis.scripts.getPersisScriptPath(this.getPath()), this.getMedia());
	persis.deps.Script.superClass._pResolveInternal.call(this);
};

/**
 * Text-Repräsentation des Objekts.
 * @return {String}
 */
persis.deps.Script.prototype.toString = function() {
	return "persis.deps.Script(" + this.getId() + ")";
};


/**
 * Erstellt ein Dependency-Objekt für die angegebene Script-Datei.
 * @since 2013/1
 */
persis.deps.script = function(pathToScript, callback) {
	var dep = new persis.deps.Script(pathToScript);
	if(callback)
		dep.addCallback(callback);
	return dep;
};



/**
 * Hilfskonstrukt, um über die Abhängigkeiten globale Konstanten zu setzen.
 * Dies wird z.B. für die aktuellen Versionen von 3rd-Party-Libraries verwendet.
 * @param {String} constantName
 * @param {Object} value
 * @constructor
 */
persis.deps.Constant = function(constantName, value) {
	this._pConstantName = constantName;
	this._pValue = value || "";
	
	persis.deps.Constant.superClass.constructor.call(this, this._pConstantName);
};
persis.inherits(persis.deps.Constant, persis.deps.Dependency);


/**
 * Gibt den Namen der Konstanten zurück.
 * @return {String}
 */
persis.deps.Constant.prototype.getConstantName = function() {
	return this._pConstantName;
};

/**
 * Gibt den zugewiesenen Wert der Konstanten zurück.
 * @return {Object}
 */
persis.deps.Constant.prototype.getValue = function() {
	return this._pValue;
};

/**
 * Führt die Auflösung der Abhängigkeit durch bzw. leitet den Vorgang
 * ein (wird von "resolve" aufgerufen).
 * Abgeleitete Klassen müssen diese Methode überschreiben. Die Basis-
 * Implementierung wirft einen Script-Fehler.
 * @since 2013/1
 */
persis.deps.Constant.prototype._pResolveInternal = function() {
	persis.constants.set(this._pConstantName, this._pValue);
	persis.deps.Constant.superClass._pResolveInternal.call(this);
};

/**
 * Text-Repräsentation des Objekts.
 * @return {String}
 */
persis.deps.Constant.prototype.toString = function() {
	return "persis.deps.Constant(" + this.getId() + ")";
};

/**
 * Setzt im Rahmen der Abhängigkeitsprüfung den Wert der globalen Konstanten.
 * Es ist nicht möglich den Wert über diese Funktion zweimal zu setzen!
 * @param {String} constantName
 * @param {Object} value
 */
persis.deps.constant = function(constantName, value) {
	return new persis.deps.Constant(constantName, value);
};


/**
 * Ergänzt Abhängigkeiten der angegebenen Skript-Datei.
 * @param {String} scriptFile - Absolute Adressen beginnen mit einem "/"
 * @param {Array} provides - String (Objekt-Namen), daraus wird bestimmt welche Skripte geladen werden müssen. 
 * @param {Array} requires - String (Objekt-Namen) oder persis.deps.Dependency (Stylesheet, Constant)
 * @param {Object} options - { "postLoad", "preventLoadingAfterDomIsReady" }
 */
persis.deps.addDependency = function(scriptFile, provides, requires, options) {
	var deps = persis.deps._pFileIndex[scriptFile] || {};
	deps.scriptFile = scriptFile;
	var prov = deps.provides || {};
	var req = deps.requires || {};
	var opt = options || {};
	var i;
	if(typeof provides != 'undefined') {
		for(i = 0; i < provides.length; i++) {
			prov[provides[i]] = true;
			persis.deps._pObjectIndex[provides[i]] = deps;
		}
	}
	deps.provides = prov;
	if(typeof requires != 'undefined') {
		for(i = 0; i < requires.length; i++) {
			if(requires[i] instanceof persis.deps.Dependency)
				req['Dependency:' + requires[i].getId()] = requires[i];
			else
				req[requires[i]] = true;
		}
	}
	deps.requires = req;
	deps.options = opt;
	persis.deps._pFileIndex[scriptFile] = deps;
};

/**
 * Liefert die Abhängigkeit zu dem angegebenen Objekt.
 * @param {String} objectNameOrFile
 */
persis.deps.getDependency = function(objectNameOrFile) {
	var deps = persis.deps._pObjectIndex[objectNameOrFile];
	if(typeof deps != 'undefined')
		return deps;
	else {
		deps = persis.deps._pFileIndex[objectNameOrFile];
		if(typeof deps != 'undefined')
			return deps;
		else
			return false;
	}
};

/**
 * Prüft, ob die Voraussetzungen für das angegebene Objekt erfüllt sind.
 * @param {String | persis.deps.Dependency} objectNameOrDependency
 * @return {Boolean}
 */
persis.deps.verifyRequirements = function(objectNameOrDependency) {
	var deps = persis.deps.getDependency(objectNameOrDependency);
	if(deps) {
		var req;
		for(req in deps.requires) {
			if(persis.instanceOf(req, persis.deps.Dependency) && !req.isResolved())
				return false;
			else if(!persis.byName(req))
				return false;
		}
		return true;
	}
	return false;
};

/**
 * Generiert eine Validierungsfunktion für eine Script-Datei anhand der "provides"-Eintragungen.
 * @param {String} pathToScript
 * @return {Function}
 * @since 2013/1
 */
persis.deps.genValidateFunc = function(pathToScript) {
	var deps = persis.deps.getDependency(pathToScript);
	if(deps) {
		return function() {
			var prov;
			for(prov in deps.provides) {
				if(!persis.deps.isResolved(prov))
					return false;
			}
			return true;
		};
	}
	return false;
};

/**
 * Prüft, ob die aktuelle Abhängigkeit bereits aufgelöst ist.
 * @param {String | persis.deps.Dependency} objectNameOrDependency
 * @return {Boolean}
 * @since 2013/1
 */
persis.deps.isResolved = function(objectNameOrDependency) {
	if(persis.instanceOf(objectNameOrDependency, persis.deps.Dependency))
		return objectNameOrDependency.isResolved();
	else
		return persis.byName(objectNameOrDependency) ? true : false;
};

/**
 * Prüft, ob die aktuelle Abhängigkeit aufgelöst werden kann (d.h. alle Voraussetzungen erfüllt sind),
 * oder bereits aufgelöst ist.
 * @param {String | persis.deps.Dependency} objectNameOrDependency
 * @return {Boolean}
 * @since 2013/1
 */
persis.deps.isResolvable = function(objectNameOrDependency) {
	if(!persis.deps.isResolved(objectNameOrDependency)) {
		var deps = persis.deps.getDependency(objectNameOrDependency);
		if(deps) {
			var req;
			for(req in deps.requires) {
				if(persis.instanceOf(deps.requires[req], persis.deps.Dependency))
					req = deps.requires[req];
				if(!persis.deps.isResolved(req)) // && !persis.deps.isResolvable(req))
					return false;
			}
		}
	}
	return true;
};

/**
 * Gibt alle Voraussetzungen zurück, welche benötigt werden, um die
 * angegebene Abhängigkeit aufzulösen (inklusive Rekursion).
 * @param {String | persis.deps.Dependency} objectNameOrDependency
 * @param {Array} requirements - (optional) vorhandenes Voraussetzungsarray. Es werden nur Voraussetzungen ergänzt, welche noch nicht darin enthalten sind.
 * @return {Array}
 * @since 2013/1
 */
persis.deps.getRequirements = function(objectNameOrDependency, requirements) {
	var deps = persis.deps.getDependency(objectNameOrDependency);
	if(deps) {
		requirements = requirements || [];
		var req;
		for(req in deps.requires) {
			if(persis.instanceOf(deps.requires[req], persis.deps.Dependency))
				req = deps.requires[req];
			var foo = function(arrayElement, value) { return arrayElement.getId() == value; };
			if(persis.array.find(requirements, req, foo) < 0) {
				var reqDeps = persis.deps.getDependency(req);
				persis.deps.getRequirements(req, requirements);
				requirements.push(persis.deps.script(reqDeps.scriptFile));
			}
		}
		return requirements;
	}
	else
		return false;
};

/**
 * Gibt alle Voraussetzungen zurück, welche benötigt werden, um die
 * angegebene Abhängigkeit aufzulösen (inklusive Rekursion). Bereits erfüllte
 * Voraussetzungen werden weggelassen.
 * @param {String | persis.deps.Dependency} objectNameOrDependency
 * @param {Array} requirements - (optional) vorhandenes Voraussetzungsarray. Es werden nur Voraussetzungen ergänzt, welche noch nicht darin enthalten sind.
 * @return {Array}
 * @since 2013/1
 */
persis.deps.getUnresolvedRequirements = function(objectNameOrDependency, requirements) {
	requirements = requirements || [];
	if(!persis.deps.isResolved(objectNameOrDependency)) {
		var deps = persis.deps.getDependency(objectNameOrDependency);
		if(deps) {
			var req;
			for(req in deps.requires) {
				if(persis.instanceOf(deps.requires[req], persis.deps.Dependency))
					req = deps.requires[req];
				if(!persis.deps.isResolved(req)) {
					if(persis.array.find(requirements, req) < 0) {
						persis.deps.getUnresolvedRequirements(req, requirements);
						requirements.push(req);
					}
				}
			}
		}
		else
			return false;
		//requirements.push(objectNameOrDependency);
	}
	return requirements;
};

/**
 * Nimmt aus dem angegebenen Array von Voraussetzungen die bereits aufgelösten
 * heraus. Sie werden dabei aus dem Ursprungsarray entfernt.
 * @param {Array} requirements
 * @return {Array}
 * @since 2013/1
 */
persis.deps.filterResolved = function(requirements) {
	var resolved = [];
	if(persis.array.isArray(requirements)) {
		var i;
		for(i = 0; i < requirements.length; i++) {
			if(persis.deps.isResolved(requirements[i])) {
				resolved.push(requirements[i]);
				requirements.splice(i, 1);
				i--;
			}
		}
		return resolved;
	}
	else
		return false;
};

/**
 * Nimmt aus dem angegebenen Array von Voraussetzungen die direkt auflösbaren
 * heraus. Sie werden dabei aus dem Ursprungsarray entfernt.
 * Bereits aufgelöste bleiben unangetastet.
 * @param {Array} unresolvedRequirements
 * @return {Array}
 * @since 2013/1
 */
persis.deps.filterResolvable = function(requirements) {
	var resolvable = [];
	if(persis.array.isArray(requirements)) {
		var i;
		for(i = 0; i < requirements.length; i++) {
			if(persis.deps.isResolvable(requirements[i])) {
				resolvable.push(requirements[i]);
				requirements.splice(i, 1);
				i--;
			}
		}
		return resolvable;
	}
	else
		return false;
};

/**
 * Löst die im Array enthaltenen direkt auflösbaren Voraussetzungen
 * auf.
 * @param {Array} requirements
 * @param {Function} callback
 * @return {Boolean} - "true", wenn alle Voraussetzungen (bereits) aufgelöst sind.
 * @since 2013/1
 */
persis.deps.resolveRequirements = function(requirements, callback) {
	if(persis.array.isArray(requirements)) {
		var notResolvable = persis.array.clone(requirements);
		var resolved = persis.deps.filterResolved(notResolvable);
		var resolvable = persis.deps.filterResolvable(notResolvable);
		var i;
		for(i = 0; i < resolvable.length; i++) {
			var req = resolvable[i];
			//persis.require(req, function(req) { return function() { persis.traceDump("!", req); }; }(req));
			var innerCallback = function(req) { return function() {
				//persis.trace("resolve callback "+req);
				persis.deps.resolveRequirements(notResolvable, callback);
			};}(req);
			if(persis.instanceOf(req, persis.deps.Dependency))
				req.resolve(innerCallback);
			else {
				var deps = persis.deps.getDependency(req);
				
				// Manche Third-Party-Libraries (z.B. Google Closure) können nicht dynamisch
				// nachladen. Dies wird mit dieser Option abgefangen.
				if(persis.isDocReady() && deps.options.preventLoadingAfterDomIsReady) {
					persis.errorMessage(String(/* persis-id="53446" */ "Das angeforderte Skript kann nicht dynamisch nachgeladen werden!") + " ('" + deps.scriptFile + "')");
					return false;
				}
				if(deps.scriptFile)
					persis.scripts.load(persis.scripts.getPersisScriptPath(deps.scriptFile),
									persis.fn.curry(persis.byName, req), innerCallback);
				if(typeof deps.options.postLoad != "undefined") {
					try {
						persis.trace("postLoad: " + nameOrPackage);
						deps.options.postLoad();
					}
					catch(e) {
						// Fehler in Handler-Funktion
						persis.errorMessage(String(/* persis-id="46731" */ "Fehler in postLoad()-Methode beim Nachladen von '" + deps.scriptFile + "'!"), e);
					}
				}
			}
		}
		if(notResolvable.length <= 0) {
			if(callback)
				callback();
			return true;
		}
	}
	return false;
};


/**
 * Namespace für Script-Nachlade-Funktionalität.
 * @namespace
 */
persis.scripts = persis.scripts || {};

/**
 * Name der aktuellen (Haupt-)Skriptdatei von Persis. Anhand dieses Namens
 * wird der Pfad zu den dynamisch nachzuladenden Skripts ermittelt.
 * @private
 * @constant
 */
persis.scripts._pBASE_SCRIPT_PATH_REG_EXP = new RegExp("(.*[=/])persis\\.js(.*)", "i");

/**
 * Basis-Pfad zu den Persis-Skripten. Wird von findBaseScriptPath() gesetzt.
 * Das Array beinhaltet sowohl den Präfix des "<script src"-Attributs ([0]), als
 * auch den Postfix ([1]).
 * -> var pathToScript = persis.scripts._pBaseScriptPathParts[0] + scriptName + persis.scripts._pBaseScriptPathParts[1];
 * @private
 */
persis.scripts._pBaseScriptPathParts = [];

/**
 * Such nach dem Pfad zu der Skript-Datei "persis.js" und speichert ihn
 * in "persis.scripts._pBaseScriptPathParts".
 * @return {Array}
 */
persis.scripts.findBaseScriptPath = function() {
	if(persis.scripts._pBaseScriptPathParts.length > 0)
		return persis.scripts._pBaseScriptPathParts;
	
	var doc = persis.global.document;
	if(typeof doc != 'undefined' && typeof doc.write != 'undefined') {
		var scripts = doc.getElementsByTagName('script');
		var i;
		for(i = 0; i < scripts.length; i++) {
			var script = scripts[i];
			var parts = persis.scripts._pBASE_SCRIPT_PATH_REG_EXP.exec(script.src);
			if(parts && parts.length > 1) {
				persis.scripts._pBaseScriptPathParts = parts.slice(1);
				break;
			}
		}
	}
	return persis.scripts._pBaseScriptPathParts;
};

/**
 * Prüft, ob es sich beim Parameter um eine externe URL handelt.
 * (beginnt mit "http:", oder "https:")
 * @param {String} scriptName
 * @return {Boolean}
 */
persis.scripts.isExternalScriptUrl = function(scriptName) {
	if(scriptName && scriptName.length > 6) {
		var lowerCase = scriptName.toLowerCase();
		return lowerCase.substring(0, 5) == "http:" || lowerCase.substring(0, 6) == "https:";
	}
	return false;
}

/**
 * Prüft, ob es sich beim Parameter um eine externe URL handelt.
 * (beginnt mit "/")
 * @param {String} scriptName
 * @return {Boolean}
 */
persis.scripts.isAbsoluteScriptUrl = function(scriptName) {
	if(scriptName && scriptName.length > 1) {
		return scriptName.substring(0, 1) == "/";
	}
	return false;
}

/**
 * Ergänzt um den angegebenenen relativen Skript-Namen den vollständigen
 * Pfad für Persis-Skript-Dateien.
 * @param {String} scriptName - relative, absolute, oder externe Script-URL
 * @return {String}
 */
persis.scripts.getPersisScriptPath = function(scriptName) {
	// um welche Art von Script-Verweis handelt es sich?
	if(persis.scripts.isExternalScriptUrl(scriptName)) {
		// Externer Verweis (vollständige URL)
		return scriptName;
	}
	else if(persis.scripts.isAbsoluteScriptUrl(scriptName)) {
		// absoluter Verweis (innerhalb der Web-Site)
		return scriptName.substring(1);
	}
	else {
		// relativer Verweis (zum Persis-Basis-Script-Pfad)
		var pathParts = persis.scripts.findBaseScriptPath();
		if(pathParts.length > 0) {
			var pathToScript = pathParts[0] + scriptName + pathParts[1];
			return pathToScript;
		}
	}
	// Fallback
	return scriptName;
};

/**
 * Nach wie vielen Millisekunden soll geprüft werden, ob eine Skript-Datei erfolgreich geladen wurde.
 * @private
 */
persis.scripts._pSCRIPT_LOADED_VERIFY_DELAY = 10;

/**
 * Wie lange wird maximal gewartet, bis eine Skript-Datei erfolgreich geladen wurde (in Millisekunden)?
 * @private
 */
persis.scripts._pSCRIPT_LOADED_VERIFY_TIMEOUT = 10000;


/**
 * Script-Dateien, die aktuell geladen werden.
 * @private
 */
// TODO: Parallelzugriff absichern
persis.scripts._pLoading = persis.scripts._pLoading || {};

/**
 * Sucht nach dem (ersten) Script-Tag, das die angegebene
 * Script-Datei lädt (src-Attribut).
 * @param {String} pathToScript
 * @return {Node}
 * @since 2013/1
 */
persis.scripts.findScriptTag = function(pathToScript) {
	var doc = persis.global.document;
	var tags = doc.getElementsByTagName("script");
	if(tags) {
		var i;
		for(i = 0; i < tags.length; i++) {
			if(tags[i].src) {
				if(tags[i].getAttribute("src") == pathToScript)
					return tags[i];
			}
		}
	}
	return false;
};

/**
 * Version=2: Überwachen des Nachladens aktiviert (Beta)
 * @since BETA
 */
persis.scripts._pVERSION = 1;
/**
 * @private
 * @since 2013/1
 */
persis.scripts._pASK_AFTER_MESSAGES = 3;
/**
 * @private
 * @since 2013/1
 */
persis.scripts._pLoadingErrors = 0;
/**
 * @private
 * @since 2013/1
 */
persis.scripts._pHideLoadingErrors = false;

/**
 * Zeigt eine Fehlermeldung an, wenn beim Laden einer Script-Datei
 * ein Fehler aufgetreten ist. Dies ist gekapselt, da weitere
 * Fehlermeldungen (nach 3 Fehlern) übergangen werden können.
 * @param {String} message
 * @param {Exception} exception - (optional)
 * @since 2013/1
 */
persis.scripts._pShowLoadingError = function(message, exception) {
	persis.scripts._pLoadingErrors++;
	persis.trace(message);
	if(persis.scripts._pHideLoadingErrors)
		return;
	if((persis.scripts._pLoadingErrors % persis.scripts._pASK_AFTER_MESSAGES) == 0) {
		if(confirm(persis.createErrorMessage(message, exception)
					 + "\n\n" + String(/* persis-id="53450" */ "Sollen weitere Ladefehler übersprungen werden?")
					 + "\n" + String(/* persis-id="53451" */ "Es sind bereits " + persis.scripts._pLoadingErrors + " Ladefehler aufgetreten."))) {
			persis.scripts._pHideLoadingErrors = true;
			return;
		}
	}
	persis.errorMessage(message, exception);
};

/**
 * Erstellt dynamisch ein <script>-Tag, mit dem die angegebene Script-Datei geladen wird.
 * "validator" ist eine Callback-Funktion, die prüft, ob die Script-Datei geladen wurde (Rückgabe: true/false).
 * "callback" wird aufgerufen, wenn die Script-Datei geladen wurde.
 * @param {String} pathToScript
 * @param {Function} validator
 * @param {Function} callback
 * @param {Document} doc - (optional)
 */
persis.scripts.load = function(pathToScript, validator, callback, doc) {
	var loaded = false;
	if(typeof validator != 'undefined') {
		try {
			loaded = validator();
		}
		catch(e) {
			// Fehler in Validator-Funktion
			persis.scripts._pShowLoadingError(String(/* persis-id="45346" */ "Fehler in validator()-Methode beim Nachladen von '" + pathToScript + "'!"), e);
			// nicht weiter versuchen
			return false;
		}
	}
	else {
		// TODO: gibt es andere registrierte Validierungsroutinen?
		// Wenn eine Script-Datei bereits geladen wurde, muss dies ja nicht erneut passieren.
	}
	if(loaded) {
		// Script bereits geladen
		if(typeof callback != 'undefined') {
			try {
				callback();
			}
			catch(e) {
				// Fehler in Callback-Funktion
				persis.scripts._pShowLoadingError(String(/* persis-id="45347" */ "Fehler in callback()-Methode beim Nachladen von '" + pathToScript + "'!"), e);
			}
		}
		return true;
	}
	else {
		// Laden vorbereiten
		var loading = persis.scripts._pLoading[pathToScript];
		if(typeof loading != 'undefined') {
			// Script wird bereits geladen -> Callback-Routine anhängen
			if(typeof callback != 'undefined')
				loading.callbacks.push(callback);
		}
		else {
			// Script muss geladen werden
			var scriptTag = persis.scripts.findScriptTag(pathToScript);
			if(scriptTag) {
				// Script wurde bereits geladen, müsste sich aber u.U. refreshen?
				// (persis.old_scripts.xyz)
				persis.trace("Script wurde bereits geladen: " + pathToScript);
				//scriptTag.parentNode.removeChild(scriptTag);
			}
			
			persis.scripts._pLoading[pathToScript] = {
					start: (new Date()).getTime(),
					validator: validator,
					timeoutId: null,
					loaded: false,
					callbacks: [ ]
			};
			if(typeof callback != 'undefined')
				persis.scripts._pLoading[pathToScript].callbacks.push(callback);
			
			doc = doc || persis.global.document;
			if(typeof doc != 'undefined' && typeof doc.write != 'undefined') {
				persis.trace('loading "' + pathToScript + '"');
				if(!persis.isDocReady(doc)) {
					// Wenn der DOM noch nicht fertig geladen wurde, werden die Script-Tags
					// einfach per "document.write()" im HTML-Code ergänzt.
					// Hier ist dann auch keine weitere Maßnahme von nöten um Abhängigkeiten
					// korrekt abhandeln zu können. Das nächste Script-Tag wird vom Browser
					// erst angegangen, wenn das vorige zu Ende geladen ist.
					if(persis.scripts._pVERSION == 2) {
						//if(!scriptTag) {
							// Script-Tag + anschließendes Validieren
							/*
							 * <script ... src="xyz.js"></script>
							 * <script ...>persis.scripts._pScriptLoadedVerify("xyz.js");</script>
							 */
							doc.writeln('<script language="JavaScript" type="text/javascript" src="' +
								pathToScript + /*(scriptTag ? "&r=" + Math.random() : "") +*/
								'"></script><script language="JavaScript" type="text/javascript">' +
								/*(scriptTag ? '</script><script language="JavaScript" type="text/javascript">' : '') +*/
								'persis.scripts._pScriptLoadedVerify("' + pathToScript + '");</script>');
						/*}
						else {
							// bestehendes Script-Tag "touchen" und nur die Validierung rausschreiben
							scriptTag.src = "";
							scriptTag.src = pathToScript;
							doc.writeln('<script language="JavaScript" type="text/javascript">' +
								'persis.scripts._pScriptLoadedVerify("' + pathToScript + '");</script>');
						}*/
					}
					else {
				// Script-Tag schreiben
				doc.writeln('<script language="JavaScript" type="text/javascript" src="' +
					pathToScript + '"></script>');
					}
				}
				else {
					// Wenn der DOM bereits fertig geladen ist, müssen dynamisch Script-Tags
					// erzeugt und in den DOM eingefügt werden.
					// Um Abhängigkeiten korrekt behandeln zu können, müssen abhängige Scripts
					// gezielt nacheinander geladen werden. Dies wird versucht über den Event
					// "readystatechange" (oder alternativ "load") zu überwachen und zu steuern.
					var scriptElement = doc.createElement("script");
					scriptElement.setAttribute("language", "JavaScript");
					scriptElement.setAttribute("type", "text/javascript");
					scriptElement.setAttribute("src", pathToScript);
					if(persis.scripts._pVERSION == 2) {
						if(scriptElement.readyState) {
							if(scriptElement.addEventListener) {
								scriptElement.addEventListener("readystatechange", function() {
									if(scriptElement.readyState == "complete") {
										var loading = persis.scripts._pLoading[pathToScript];
										if(loading) {
											loading.loaded = true;
											//persis.fn.delayCurry(persis.scripts._pScriptLoadedVerify, 10, pathToScript);
											persis.scripts._pScriptLoadedVerify(pathToScript);
										}
									}
								});
							}
							else if(scriptElement.attachEvent) {
								scriptElement.attachEvent("onreadystatechange", function() {
									if(scriptElement.readyState == "complete") {
										var loading = persis.scripts._pLoading[pathToScript];
										if(loading) {
											loading.loaded = true;
											//persis.fn.delayCurry(persis.scripts._pScriptLoadedVerify, 10, pathToScript);
											persis.scripts._pScriptLoadedVerify(pathToScript);
										}
									}
								});
							}
						}
						else {
							if(scriptElement.addEventListener) {
								scriptElement.addEventListener("load", function() {
									var loading = persis.scripts._pLoading[pathToScript];
									if(loading) {
										loading.loaded = true;
										//persis.fn.delayCurry(persis.scripts._pScriptLoadedVerify, 10, pathToScript);
										persis.scripts._pScriptLoadedVerify(pathToScript);
									}
								});
							}
						}
						/*var scriptElement2 = doc.createElement("script");
						scriptElement2.setAttribute("language", "JavaScript");
						scriptElement2.setAttribute("type", "text/javascript");
						scriptElement2.appendChild(doc.createTextNode('persis.traceDump("verify ' + pathToScript + '", persis.old_scripts);'));*/
						doc.body.appendChild(scriptElement);
						//doc.body.appendChild(scriptElement2);
					}
					else
						doc.body.appendChild(scriptElement);
					return false;
				}
				//persis.scripts._pLoading[pathToScript].timeoutId = persis.fn.delayCurry(persis.scripts._pScriptLoadedVerify, persis.scripts._pSCRIPT_LOADED_VERIFY_DELAY, pathToScript);
			}
			else {
				persis.scripts._pShowLoadingError(String(/* persis-id="45348" */ "Fehler beim Nachladen von '" + pathToScript + "'!\nDocument-Bezug fehlt."));
			}
		}
		return false;
	}
};

/**
 * Prüfung, ob die Script-Datei mittlerweile geladen wurde.
 * @param {Object} pathToScript
 * @private
 */
persis.scripts._pScriptLoadedVerify = function(pathToScript) {
	//persis.trace('verifying: ' + pathToScript);
	var loading = persis.scripts._pLoading[pathToScript];
	if(!loading)
		return;
	var loaded = false;
	if(typeof loading.validator != "undefined") {
		try {
			loaded = loading.validator();
			if(loaded)
				persis.trace(pathToScript + ' verified');//, true);
		}
		catch(e) {
			// Fehler in Validator-Funktion
			persis.scripts._pShowLoadingError(String(/* persis-id="45346" */ "Fehler in validator()-Methode beim Nachladen von '" + pathToScript + "'!"), e);
		}
	}
	else {
		loaded = true;
		persis.trace(pathToScript + ' loaded (without validation)');
	}
	if(loaded) {
		// Script fertig geladen und validiert
		var i;
		for(i = 0; i < loading.callbacks.length; i++) {
			try {
				loading.callbacks[i]();
			}
			catch(e) {
				// Fehler in Callback-Funktion
				persis.scripts._pShowLoadingError(String(/* persis-id="45347" */ "Fehler in callback()-Methode beim Nachladen von '" + pathToScript + "'!"), e);
			}
		}
		delete persis.scripts._pLoading[pathToScript];
	}
	else {
		if(persis.scripts._pVERSION == 2) {
			if(loading.loaded) {
				// Script konnte nicht geladen werden - es wird auch nicht weiter versucht.
				persis.scripts._pShowLoadingError(String(/* persis-id="51947" */ "Fehler beim Nachladen von '" + pathToScript + "'!\nValidierung war nicht erfolgreich."));
				delete persis.scripts._pLoading[pathToScript];
				return;
			}
			var scriptTag = persis.scripts.findScriptTag(pathToScript);
			if(!scriptTag) {
				persis.scripts._pShowLoadingError(String(/* persis-id="53452" */ "Fehler beim Nachladen von '" + pathToScript + "'!\nDazu gehörendes Script-Tag nicht gefunden!"));
				//delete persis.scripts._pLoading[pathToScript];
			}
			else {
				if(scriptTag.readyState && scriptTag.readyState != "complete") {
					//persis.scripts._pShowLoadingError(String(/* persis-id="53453" */ "Fehler beim Nachladen von '" + pathToScript + "'!)") + "\n(" + scriptTag.readyState + ")");
					if(scriptTag.addEventListener) {
						persis.trace(pathToScript + ' delayed');//, true);
						scriptTag.addEventListener("readystatechange", function() {
							if(scriptTag.readyState == "complete")
								persis.scripts._pScriptLoadedVerify(pathToScript);
						});
						return;
					}
					else if(scriptTag.attachEvent) {
						persis.trace(pathToScript + ' delayed');//, true);
						scriptTag.attachEvent("onreadystatechange", function() {
							if(scriptTag.readyState == "complete")
								persis.scripts._pScriptLoadedVerify(pathToScript);
						});
						return;
					}
				}
				else {
					// Script konnte nicht geladen werden - es wird auch nicht weiter versucht.
					persis.scripts._pShowLoadingError(String(/* persis-id="51947" */ "Fehler beim Nachladen von '" + pathToScript + "'!\nValidierung war nicht erfolgreich."));
					delete persis.scripts._pLoading[pathToScript];
					return;
				}
			}
		}
		//persis.trace(pathToScript + ' delayed');//, true);
		// Script noch nicht fertig
		var now = (new Date()).getTime();
		if((now - loading.start) <= persis.scripts._pSCRIPT_LOADED_VERIFY_TIMEOUT) {
			// erneut warten
			//loading.timeoutId = persis.fn.delayCurry(persis.scripts._pScriptLoadedVerify, persis.scripts._pSCRIPT_LOADED_VERIFY_DELAY, pathToScript);
			persis.scripts._pShowLoadingError(String(/* persis-id="51947" */ "Fehler beim Nachladen von '" + pathToScript + "'!\nValidierung war nicht erfolgreich."));
		}
		else {
			// Script konnte nicht geladen werden - es wird auch nicht weiter versucht.
			persis.scripts._pShowLoadingError(String(/* persis-id="45349" */ "Fehler beim Nachladen von '" + pathToScript + "'!\nTimeout abgelaufen."));
			delete persis.scripts._pLoading[pathToScript];
		}
	}
};



/**
 * Globaler Skript-Speicherbereich für Persis. Verwendet wenn möglich
 * HTML 5 Web-Storage. Wenn nicht möglich (z.B. IE) wird eine
 * eigene Speicher-Routine etabliert. Die Schnittstellen bleiben dabei
 * identisch.
 * @since 2013/1
 */
persis.storage = persis.storage || (function() {
	/**
	 * Private Listener-Map.
	 */
	var _listeners = { };
	/**
	 * Prüft, ob ein konkreter Listener bereits für den
	 * "change"-Event registeriert ist.
	 * @param {String} item
	 * @param {Function} handler
	 * @return {Boolean}
	 */
	var _isAlreadyRegistered = function(item, handler) {
		var listeners = _listeners[item];
		if(listeners) {
			var i;
			for(i = 0; i < listeners.length; i++) {
				if(listeners[i] === handler)
					return true;
			}
		}
		return false;
	};
	/**
	 * Fügt einen Listener für den "change"-Event hinzu.
	 * @param {String} item
	 * @param {Function} handler
	 */
	var _addListener = function(item, handler) {
		_listeners[item] = _listeners[item] || [ ];
		// doppeltes Hinzufügen verhindern
		if(!_isAlreadyRegistered(item, handler)) {
			_listeners[item].push(handler);
		}
	};
	/**
	 * Entfernt einen Listener für den "change"-Event.
	 * @param {String} item
	 * @param {Function} handler
	 */
	var _removeListener = function(item, handler) {
		var listeners = _listeners[item];
		if(listeners) {
			var i;
			for(i = 0; i < listeners.length; i++) {
				if(listeners[i] === handler) {
					listeners.splice(i, 1);
					i--;
				}
			}
		}
	};
	/**
	 * Entfernt alle Listener für den "change"-Event für ein Item.
	 * @param {String} item
	 */
	var _removeAllListeners = function(item) {
		_listeners[item] = undefined;
	};
	/**
	 * Benachrichtigt ("change"-Event) die Listener auf dem angegebenen Item.
	 * (nur wenn sich die Werte geändert haben)
	 * @param {String} item
	 * @param {String} valueNew
	 * @param {String} valueOld
	 */
	var _notify = function(item, valueNew, valueOld) {
		if(_listeners[item] && valueNew != valueOld) {
			var i;
			for(i = 0; i < _listeners[item].length; i++) {
				var listener = _listeners[item][i];
				if(persis.fn.isValid(listener)) {
					try {
						listener("change", item, valueNew, valueOld);
					}
					catch(e) {
						persis.errorMessage(String(/* persis-id="53454" */ 'Beim Ausführen eines Event-Handlers ("change"-Event) des Persis-Skript-Storage ist ein Skript-Fehler aufgetreten!'), e);
					}
				}
				else {
					// Listener ist nicht mehr gültig. Dazugeörendes Fenster wurde geschlossen oder Seite verlassen.
					// Listener entfernen
					_removeListener(item, listener);
					i--;
				}
			}
		}
	};
	/**
	 * Überträgt die in diesem Storage-Objekt gespeicherten Listener in
	 * das angegebene neue. Wird bei einem Persis-Storage Objekt benötigt,
	 * wenn aufgrund eines "Verbindungsabrisses" ein neues Storage-Objekt
	 * erstellt wurde. Gleichzeitig werden die Listener aus dam aktuellen
	 * Storage-Objekt entfernt.
	 * @param {persis.storage} newStorage
	 */
	var _transfer = function(newStorage) {
		if(_listeners) {
			var item;
			for(item in _listeners) {
				var itemListener = _listeners[item];
				while(itemListener && itemListener.length) {
					var listener = itemListener[0];
					newStorage.addChangeListener(item, listener);
					_removeListener(item, listener);
				}
			}
		}
	};
	
	var _type = "PERSIS-STORAGE";
	/*
	 Der Internet Explorer unterstütz zwar theoretisch ab Version 8 das Web-Storage, Storage-Events aber erst
	 ab Version 9. Und dort auch nur im STANDARDS (Strict) Mode. In Version 10 scheint dies jedoch korrekt
	 unterstützt zu sein.
	 */
	if(persis.global.localStorage && (!persis.browser.isProduct(persis.browser.Products.INTERNET_EXPLORER) ||
			persis.browser.isProduct(persis.browser.Products.INTERNET_EXPLORER, 10)))
		_type = "WEB-STORAGE";
	
	var _storage;
	if(_type == "WEB-STORAGE") {
		persis.trace(String(/* persis-id="53455" */ "Verwende HTML 5 Web-Storage!"));
		
		// nativen HTML 5 Web-Storage Storage-Event abfangen
		
		/**
		 * HTML 5 Web-Storage Storage-Event-Handler.
		 * @param {Event} event
		 */
		var _onStorageEvent = function(event) {
			event = event || window.event;
			_notify(event.key, event.newValue, event.oldValue);
		};
		
		// nur einen Event-Listener auf den "storage"-Event registrieren
		if(persis.global.addEventListener) {
			// 3. Parameter ist im FF erst ab Version 6 optional
			persis.global.removeEventListener("storage", _onStorageEvent, false);
			persis.global.addEventListener("storage", _onStorageEvent, false);
		}
		else {
			persis.global.detachEvent("onstorage", _onStorageEvent);
			persis.global.attachEvent("onstorage", _onStorageEvent);
		}
		
		_storage = {
			/**
			 * Setzt das angegebene Item im Storage.
			 * @param {String} item
			 * @return {Boolean}
			 */
			setItem: function(item, value) {
				if(!persis.browser.isProduct(persis.browser.Products.INTERNET_EXPLORER) ) {
					var old = _storage.getItem(item);
					persis.global.localStorage.setItem(item, value);
					
					// bei der Verwendung von HTML 5 Web-Storage werden bei Änderungen im
					// Firefox und Chrome nur die Listener anderer Windows benachrichtigt.
					// Beim Internet Explorer werden die Listener anderer UND die des eigenen
					// Windows benachrichtigt.
					// Hier werden die eigenen manuell benachrichtigt.
					_notify(item, value, old);
				}
				else
					persis.global.localStorage.setItem(item, value);
				return _storage;
			},
			/**
			 * Holt das angegebene Item aus dem Storage.
			 * @param {String} item
			 * @return {String}
			 */
			getItem: function(item) {
				var value = persis.global.localStorage.getItem(item);
				return typeof value != "undefined" && value != null ? value : undefined;
			},
			/**
			 * Löscht das angegebene Item aus dem Storage.
			 * @param {String} name
			 * @param {Boolean} removeAllListeners - (optional)
			 * @return {persis.storage} - das Storage-Objekt
			 */
			removeItem: function(item, removeAllListeners) {
				if(!persis.browser.isProduct(persis.browser.Products.INTERNET_EXPLORER)) {
					var old = _storage.getItem(item);
					persis.global.localStorage.removeItem(item);
					
					// bei der Verwendung von HTML 5 Web-Storage werden bei Änderungen im
					// Firefox und Chrome nur die Listener anderer Windows benachrichtigt.
					// Beim Internet Explorer werden die Listener anderer UND die des eigenen
					// Windows benachrichtigt.
					// Hier werden die eigenen manuell benachrichtigt.
					_notify(item, undefined, old);
				}
				else
					persis.global.localStorage.removeItem(item);
				if(removeAllListeners)
					_removeAllListeners(item);
				return _storage;
			},
			/**
			 * Prüft, ob das angegebene Item im Storage enthalten ist.
			 * @param {String} item
			 * @return {Boolean}
			 */
			itemExists: function(item) {
				return typeof _storage.getItem(item) != "undefined";
			},
			/**
			 * Registriert einen Event-Listener für die Änderung eines Wertes.
			 * @param {String} item
			 * @param {Function} listener - function(event, item, valueNew, valueOld) { ... }
			 * @return {persis.storage} - das Storage-Objekt
			 */
			addChangeListener: function(item, listener) {
				_addListener(item, listener);
				return _storage;
			},
			/**
			 * Entfernt einen Event-Listener für die Änderung eines Wertes.
			 * @param {String} item
			 * @param {Function} listener - function(event, item, valueNew, valueOld) { ... }
			 * @return {persis.storage} - das Storage-Objekt
			 */
			removeChangeListener: function(item, listener) {
				_removeListener(item, listener);
				return _storage;
			},
			/**
			 * Text-Repräsentation des Objekts.
			 * @return {String}
			 */
			toString: function() {
				return "HTML 5 Web-Storage";
			}
		};
	}
	else {
		// Persis-Skript-Storage
		
		// suche in übergeordneten Frames
		var _detectPersisStorage = function(scope) {
			try {
				scope = scope || self;//persis.global;
				//if(typeof scope.persis != "undefined") {
				if(typeof scope.persis == "object") {	// Achtung: Wenn die Verbindung abgerissen ist, kommt hier ggf. mal "Erlaubnis verweigert". Typ war dann teilweise "unknown".
					if(scope.persis.storage)
						return scope.persis.storage;
				}
				var storage = false;
				if(scope.parent && scope.parent !== scope)
					storage = _detectPersisStorage(scope.parent);
				if(!storage && scope.opener && scope.opener !== scope)
					storage = _detectPersisStorage(scope.opener);
				return storage;
			}
			catch(e) { }
			return false;
		};
		_storage = _detectPersisStorage(persis.global);
		
		/**
		 * Erstellt ein neues Persis-Storage Objekt.
		 * @return {persis.storage} - das Storage-Objekt
		 */
		var _createPersisStorage = function() {
			persis.trace(String(/* persis-id="53456" */ "Erstelle neues Persis-Skript-Storage Objekt!"));
			
			/**
			 * Private Daten-Map.
			 */
			var _data = { };
			return {
				setItem: function(item, value) {
					var old = _data[item];
					_data[item] = value;
					if(old !== value)
						_notify(item, value, old);
				},
				getItem: function(item) {
					var value = _data[item];
					return typeof value != "undefined" && value != null ? value : undefined;
				},
				removeItem: function(item, removeAllListeners) {
					var old = _data[item];
					_data[item] = undefined;
					_notify(item, undefined, old);
					if(removeAllListeners)
						_removeAllListeners(item);
				},
				itemExists: function(item) {
					return typeof _storage.getItem(item) != "undefined";
				},
				addChangeListener: function(item, listener) {
					_addListener(item, listener);
					return _storage;
				},
				removeChangeListener: function(item, listener) {
					_removeListener(item, listener);
					return _storage;
				},
				_pTransferListenersTo: function(newStorage) {
					_transfer(newStorage);
				},
				toString: function() {
					return "Persis-Script-Storage";
				}
			};
		};
		/**
		 * Ersetzt in allen (geeigneten) übergeordneten Frames das angegebene
		 * Persis-Storage Objekt. Dabei werden alle dort registrierten Listener
		 * übernommen.
		 * @param {Window | Frame} scope
		 * @param {persis.storage} storage
		 */
		var _replacePersisStorage = function(scope, storage) {
			try {
				scope = scope || self;//persis.global;
				//if(typeof scope.persis != "undefined") {
				if(typeof scope.persis == "object") {	// Achtung: Wenn die Verbindung abgerissen ist, kommt hier ggf. mal "Erlaubnis verweigert". Typ war dann teilweise "unknown".
					if(scope.persis.storage) {
						// Listener übernehmen
						scope.persis.storage._pTransferListenersTo(storage);
						// neues Storage-Objekt aktivieren
						scope.persis.storage = storage;
					}
				}
				if(scope.parent && scope.parent !== scope)
					_replacePersisStorage(scope.parent, storage);
				if(!storage && scope.opener && scope.opener !== scope)
					_replacePersisStorage(scope.opener, storage);
			}
			catch(e) { }
		};
		
		if(!_storage) {
			_storage = _createPersisStorage();
			_replacePersisStorage(false, _storage);
		}
		else if(!_storage.isWrapped) {
			// Zugriff auf globales Storage-Objekt absichern gegen "abgerissene Verbindung"
			// TODO: ging nicht?
			// TODO: was ist denn "isWrapped"?
			// TODO: _pTransferListenersTo() einfach kopiert - der Script-Fehler ist weg, aber ist das richtig? Bzw. geht es "richtiger"?
			_storage = (function (globalStorage) {
				return {
					// doppeltes wrappen verhindern
					mainStorage: globalStorage,
					setItem: function(item, value) {
						try {
							return globalStorage.setItem(item, value);
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.setItem(item, value);
						}
					},
					getItem: function(item) {
						try {
							return globalStorage.getItem(item);
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.getItem(item);
						}
					},
					removeItem: function(item) {
						try {
							return globalStorage.removeItem(item);
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.removeItem(item);
						}
					},
					itemExists: function(item) {
						try {
							return globalStorage.itemExists(item);
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.itemExists(item);
						}
					},
					addChangeListener: function(item, listener) {
						try {
							return globalStorage.addChangeListener(item, listener);
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.addChangeListener(item, listener);
						}
					},
					removeChangeListener: function(item, listener) {
						try {
							return globalStorage.removeChangeListener(item, listener);
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.removeChangeListener(item, listener);
						}
					},
					_pTransferListenersTo: function(newStorage) {
						_transfer(newStorage);
					},
					_pIsValid: function() {
						try {
							globalStorage.getItem("_storage_validity_test");
							return true;
						}
						catch(e) {
							return false;
						}
					},
					toString: function() {
						try {
							return "(" + globalStorage.toString() + ")";
						}
						catch(e) {
							persis.trace("Fehler: Verbindung zum Haupt-Storage ist abgerissen! Erstelle neues Persis-Storage-Objekt");
							_storage = _createPersisStorage();
							_replacePersisStorage(false, _storage);
							return _storage.toString();
						}
					}
				};
			})(_storage.mainStorage ? _storage.mainStorage : _storage);
		}
	}
	return _storage;
})();



var DEPENDENCY_BLOCK_MARKER = 1;
/**
 * Abhängigkeiten (bitte sortieren!)
 * 
 * persis.deps.addDependency(scriptFile, provides, requires, options)
 * scriptFile - Absolute Adressen beginnen mit einem "/". Mit "false" können Abhängigkeiten deklariert werden, welche keine eigenen Script-Files voraussetzen.
 * provides - String (Objekt-Namen), daraus wird bestimmt welche Skripte geladen werden müssen. 
 * requires - String (Objekt-Namen) oder persis.deps.Dependency (Stylesheet, Constant)
 * options - { "postLoad", "preventLoadingAfterDomIsReady" }
 */
persis.deps.addDependency('/main?fn=format_settings_js', [ 'getDateFormat' ], [ ]);

persis.deps.addDependency('calendar.js', [ 'persis.old_scripts.calendar_js' ], [ 'persis', 'persis.old_scripts.mouse_js' ]);
persis.deps.addDependency('coding.js', [ 'persis.old_scripts.coding_js' ], [ 'persis' ]);
persis.deps.addDependency('date.js', [ 'persis.old_scripts.date_js' ], [ 'persis', 'persis.old_scripts.parser_js' ]);
persis.deps.addDependency('dhtml.js', [ 'persis.old_scripts.dhtml_js' ], [ 'persis' ]);
persis.deps.addDependency('editor.js', [ 'persis.old_scripts.editor_js' ], [ 'persis' ]);
persis.deps.addDependency('form.js', [ 'persis.old_scripts.form_js' ], [ 'persis' ]);
persis.deps.addDependency('json.js', [ 'persis.json' ], ['persis']);
persis.deps.addDependency('mouse.js', [ 'persis.old_scripts.mouse_js' ], [ 'persis' ]);
persis.deps.addDependency('parser.js', [ 'persis.old_scripts.parser_js' ], [ 'persis', 'persis.old_scripts.form_js' ]);
persis.deps.addDependency('persis.js', [ 'persis' ], [  ]);
persis.deps.addDependency('popup.js', [ 'persis.old_scripts.popup_js' ], [ 'persis', 'persis.old_scripts.coding_js', 'persis.old_scripts.string_js' ]);
persis.deps.addDependency('string.js', [ 'persis.old_scripts.string_js' ], [ 'persis' ]);
persis.deps.addDependency('tooltips.js', [ 'persis.old_scripts.tooltips_js' ], [ 'persis', 'persis.old_scripts.dhtml_js', 'persis.old_scripts.mouse_js' ]);
persis.deps.addDependency('uebernahme.js', [ 'persis.old_scripts.uebernahme_js' ], [ 'persis' ]);
persis.deps.addDependency('validation.js', [ 'persis.old_scripts.validation_js' ], [ 'persis' ]);
persis.deps.addDependency('watch.js', [ 'persis.old_scripts.watch_js' ], [ 'persis', 'jQuery', 'getDateFormat' ]);

persis.deps.addDependency('async/async.js', [ 'persis.async' ], [ 'persis' ]);
persis.deps.addDependency('async/processor.js', [ 'persis.async.TaskProcessor' ], [ 'persis.async', 'persis.async.events.UserEventTrigger', 'persis.async.Task', 'persis.async.timer.IdleHandler', 'persis.types.iter.PriorityQueue', 'Prototype', persis.deps.css('/js/persis/async/processor.css') ]);
persis.deps.addDependency('async/synchronizable.js', [ 'persis.async.Synchronizable' ], [ 'persis.async', 'persis.async.events.UserEventTrigger', 'persis.types.iter.Collection' ]);
persis.deps.addDependency('async/task.js', [ 'persis.async.Task' ], [ 'persis.async', 'persis.async.events.UserEventTrigger', 'persis.types.PrimitiveCompareable', 'Prototype' ]);
persis.deps.addDependency(false, [ 'persis.async.activateGlobalTaskProcessor' ], [ 'persis.async.TaskProcessor', 'persis.ui.elements.Progress' ]);
persis.deps.addDependency('async/ajax/ajax.js', [ 'persis.async.ajax', 'persis.async.ajax.AjaxRequest', 'persis.async.ajax.IFrameLoader', 'persis.async.ajax.ImageLoader' ], [ 'persis.async', 'persis.async.Task', 'persis.types.Dim', 'persis.dom', 'Prototype', persis.deps.css('/js/persis/async/ajax/ajax.css') ]);
persis.deps.addDependency('async/ajax/synchronizable.js', [ 'persis.async.ajax.AjaxSynchronizable' ], [ 'persis.async.ajax', 'persis.async.Synchronizable', 'persis.async.ajax.AjaxRequest', 'persis.async.TaskProcessor', 'persis.types.iter.Collection' ]);
persis.deps.addDependency('async/events/events.js', [ 'persis.async.events', 'persis.async.events.UserEventTrigger' ], [ 'persis.async', 'Prototype' ]);
persis.deps.addDependency('async/timer/timer.js', [ 'persis.async.timer', 'persis.async.timer.Timer' ], [ 'persis.async', 'persis.async.events.UserEventTrigger', 'persis.types.iter.Collection', 'Prototype' ]);
persis.deps.addDependency('async/timer/idle.js', [ 'persis.async.timer.IdleHandler' ], [ 'persis.async.timer.Timer' ]);

persis.deps.addDependency('dom/dom.js', [ 'persis.dom', 'persis.dom.createElement', 'persis.dom.get', 'persis.dom.show', 'persis.dom.hide', 'persis.dom.visible', 'persis.dom.toggle', 'persis.dom.addClassName', 'persis.dom.removeClassName', 'persis.dom.hasClassName', 'persis.dom.findNextSiblingElement', 'persis.dom.findParentElement' ], [ 'persis', 'persis.types.Dim', 'persis.types.Pos', 'persis.types.Rect', /*'persis.dom.css', */'Prototype', 'goog' ]);
persis.deps.addDependency('dom/css/css.js', [ 'persis.dom.css', 'persis.dom.css.TextMetrics', 'persis.dom.css.Color' ], [ 'persis.dom', 'Prototype', persis.deps.css('/js/persis/dom/css/css.css') ]);
persis.deps.addDependency('dom/animation/animation.js', [ 'persis.dom.animation' ], [ 'persis.dom' ]);
persis.deps.addDependency('dom/animation/renderer.js', [ 'persis.dom.animation.Renderer' ], [ 'persis.dom.animation', 'persis.async.timer.Timer', 'persis.types.iter.Collection', 'persis.utils.algorithms.Interpolator', 'persis.types.Pos', 'persis.types.Dim', 'persis.dom.css.Color' ]);
persis.deps.addDependency('dom/events/events.js', [ 'persis.dom.events', 'persis.dom.events.DOMEventHandler' ], [ 'persis.dom' ]);
persis.deps.addDependency('dom/events/focus.js', [ 'persis.dom.events.FocusHandler' ], [ 'persis.dom.events' ]);
persis.deps.addDependency('dom/events/keycodes.js', [ 'KeyEvent' ], [ ]);
persis.deps.addDependency('dom/events/mouse.js', [ 'persis.dom.events.MouseHoverHandler', 'persis.dom.events.MouseToggleHandler' ], [ 'persis.dom.events' ]);
persis.deps.addDependency('dom/graphics/canvas.js', [ 'persis.dom.graphics.CanvasRenderer' ], [ 'persis.dom.graphics', 'persis.ui.UIElement', 'Excanvas', 'Prototype', persis.deps.css('/js/persis/dom/graphics/canvas.css') ]);
persis.deps.addDependency('dom/graphics/graphics.js', [ 'persis.dom.graphics' ], [ 'persis.dom' ]);
persis.deps.addDependency('dom/mouse/mouse.js', [ 'persis.dom.mouse', 'persis.dom.mouse.MousePositionHandler' ], [ 'persis.dom', 'persis.dom.events.DOMEventHandler' ]);
persis.deps.addDependency('dom/sound/sound.js', [ 'persis.dom.sound' ], [ 'persis.dom', 'Prototype' ]);

persis.deps.addDependency('forms/forms.js', [ 'persis.forms' ], [ 'persis' ]);
persis.deps.addDependency('forms/inputinfo.js', [ 'persis.forms.attachInputInfoText', 'persis.forms.isInputInfoTextAttached', 'persis.forms.isInputInfoTextShown' ], [ 'persis.forms', 'persis.dom.events.FocusHandler' ]);
persis.deps.addDependency('forms/validation/validation.js', [ 'persis.forms.validation' ], [ 'persis.forms' ]);

persis.deps.addDependency('modules/modules.js', [ 'persis.modules' ], [ 'persis' ]);
persis.deps.addDependency('modules/dipa/akte_dok.js', [ 'persis.modules.dipa.AkteDok', 'persis.modules.dipa.AkteDok.Page' ], [ 'persis.modules.dipa', 'persis.ui.container.ImageGallery', 'persis.ui.container.PageFlip', 'persis.async.ajax.AjaxSynchronizable', 'persis.dom.events.MouseHoverHandler', 'persis.modules.dipa.AkteDokMarkingsTarget', persis.deps.css('/js/persis/modules/dipa/akte_dok.css') ]);
persis.deps.addDependency('modules/dipa/akte_viewer.js', [ 'persis.modules.dipa.AkteViewer' ], [ 'persis.modules.dipa', 'persis.types.iter.Collection', 'persis.ui.container.Tabs', 'persis.ui.container.PageFlip', 'persis.modules.dipa.ImageCanvasRenderer', 'persis.ui.LoaderOverlay', 'persis.modules.dipa.AkteDok', 'persis.ui.menus.SynchronizableMenu', 'persis.ui.tooltips.Tooltip', 'persis.async.activateGlobalTaskProcessor', 'persis.old_scripts.coding_js', 'persis.old_scripts.popup_js', 'persis.modules.dipa.AkteRegTree'/*, persis.deps.css('/js/persis/modules/dipa/akte_viewer.css')*/ ]);
persis.deps.addDependency('modules/dipa/akte_reg_tree.js', [ 'persis.modules.dipa.AkteRegTree' ], [ 'persis.modules.dipa', 'persis.async.Task', 'persis.ui.trees', 'persis.ui.LoaderOverlay', 'persis.old_scripts.coding_js', 'persis.old_scripts.string_js', persis.deps.css('/js/persis/modules/dipa/akte_reg_tree.css')]);
persis.deps.addDependency('modules/dipa/dipa.js', [ 'persis.modules.dipa' ], [ 'persis.modules', 'persis.async.events.UserEventTrigger', 'persis.async.ajax.AjaxRequest' ]);
persis.deps.addDependency('modules/dipa/image_renderer.js', [ 'persis.modules.dipa.ImageCanvasRenderer' ], [ 'persis.modules.dipa', 'persis.ui.container.ImageCanvasRenderer', 'persis.modules.dipa.AkteDokMarkingsTarget' ]);
persis.deps.addDependency('modules/dipa/markings.js', [ 'persis.modules.dipa.AkteDokMarkingsTarget' ], [ 'persis.modules.dipa', 'persis.dom.events.MouseHoverHandler', 'persis.old_scripts.date_js' ]);
persis.deps.addDependency('modules/system/page_templ.js', [ 'persis.modules.system.page_templ', 'persis.modules.system.page_templ.setPage', 'persis.modules.system.page_templ.setPageSize', 'persis.modules.system.page_templ.gotoPage' ], [ 'persis.modules.system', 'persis.old_scripts.parser_js' ]);
persis.deps.addDependency('modules/system/main.js', [ 'persis.modules.system.main' ], [ 'persis.modules.system', 'persis.modules.md.aufg' ]);
persis.deps.addDependency('modules/system/system.js', [ 'persis.modules.system' ], [ 'persis.modules' ]);
persis.deps.addDependency('modules/ma/ma.js', [ 'persis.modules.ma' ], [ 'persis.modules' ]);
persis.deps.addDependency('modules/ma/ma_kalender.js', [ 'persis.modules.ma.kalender' ], [ 'persis.modules.ma', 'persis.modules.ma.kalender.Cache', 'persis.ui.calendars.FullCalendar', persis.deps.css('/js/persis/modules/ma/ma_kalender.css') ]);
persis.deps.addDependency('modules/ma/ma_kalender_dialogs.js', [ 'persis.modules.ma.kalender.dialogs' ], [ 'persis.modules.ma.kalender', 'persis.ui.dialogs', 'persis.ui.calendars.DatePicker', 'persis.ui.calendars.TimePicker', 'persis.types.date.Converter',  persis.deps.css('/js/persis/modules/ma/ma_kalender_dialogs.css') ]);
persis.deps.addDependency('modules/ma/ma_kalender_cache.js', [ 'persis.modules.ma.kalender.Cache' ], [ ]);
persis.deps.addDependency('modules/ma/ma_kalender_panel.js', [ 'persis.modules.ma.kalender.Panel' ], [ 'persis.modules.ma.kalender' ]);
persis.deps.addDependency('modules/md/md.js', [ 'persis.modules.md' ], [ 'persis.modules' ]);
persis.deps.addDependency('modules/md/types.js', [ 'persis.modules.md.types' ], [ 'persis.modules', 'persis.modules.md' ]);
persis.deps.addDependency('modules/md/st_st_md_kat_tn.js', [ 'persis.modules.md.stmdkattn' ], [ 'jQuery', 'persis.modules.md', 'persis.modules.md.types' , 'persis.async.ajax.AjaxRequest', 'persis.async.TaskProcessor', 'persis.dom.events.DOMEventHandler', 'persis.ui.inputs.TriStateCheckbox', persis.deps.css('/js/persis/modules/md/stmdkattn.css') ]);
persis.deps.addDependency('modules/md/md_aufg.js', [ 'persis.modules.md.aufg' ], [ 'jQuery', 'persis.modules.md', 'persis.ui.container.Accordion', 'persis.ui.LoaderOverlay', 'persis.dom', 'persis.dom.events', 'persis.old_scripts.mouse_js', 'persis.old_scripts.dhtml_js', 'persis.old_scripts.tooltips_js', 'persis.ui.inputs.tokeninput', persis.deps.css('/js/persis/modules/md/aufg.css') ]);
persis.deps.addDependency('modules/md/md_gsp_erf.js', [ 'persis.modules.md.gsp_erf' ], [ 'jQuery', 'persis.modules.md', 'persis.dom.events.DOMEventHandler', persis.deps.css('/js/persis/modules/md/gsp_erf.css') ]);
persis.deps.addDependency('modules/md/md_gsp_zielvereinbarung_anderem_ma_add.js', [ 'persis.modules.md.gsp_zielvereinbarung_anderem_ma_add' ], [ 'jQuery', 'persis.modules.md', 'persis.ui.dialogs.MessageBox', persis.deps.css('/js/persis/modules/md/gsp_zielvereinbarung_anderem_ma_add.css') ]);

persis.deps.addDependency('parser/parser.js', [ 'persis.parser' ], [  ]);

persis.deps.addDependency('types/dim.js', [ 'persis.types.Dim' ], [ 'persis.types' ]);
persis.deps.addDependency('types/pos.js', [ 'persis.types.Pos' ], [ 'persis.types' ]);
persis.deps.addDependency('types/rect.js', [ 'persis.types.Rect' ], [ 'persis.types', 'persis.types.Pos', 'persis.types.Dim' ]);
persis.deps.addDependency('types/types.js', [ 'persis.types', 'persis.types.Compareable', 'persis.types.PrimitiveCompareable' ], [ 'persis', 'persis.async.events.UserEventTrigger' ]);
persis.deps.addDependency('types/iter/collection.js', [ 'persis.types.iter.Collection', 'persis.types.iter.Collector' ], [ 'persis.types.iter' ]);
persis.deps.addDependency('types/iter/iter.js', [ 'persis.types.iter' ], [ 'persis.types' ]);
persis.deps.addDependency('types/iter/queue.js', [ 'persis.types.iter.Queue', 'persis.types.iter.PriorityQueue' ], [ 'persis.types.iter', 'Prototype' ]);

persis.deps.addDependency('ui/layout.js', [ 'persis.ui.layout' ], [ 'persis.ui', 'persis.dom', 'jQuery.layout' ]);
persis.deps.addDependency('ui/overlay.js', [ 'persis.ui.OverlayPane', 'persis.ui.LoaderOverlay' ], [ 'persis.ui', 'persis.dom', 'persis.dom.events.DOMEventHandler', 'Prototype', persis.deps.css('/js/persis/ui/overlay.css') ]);
persis.deps.addDependency('ui/ui.js', [ 'persis.ui', 'persis.ui.UIElement' ], [ 'persis', 'persis.async.events.UserEventTrigger', 'persis.dom.events.DOMEventHandler' ]);
persis.deps.addDependency('ui/container/accordion.js', [ 'persis.ui.container.Accordion' ], [ 'persis.ui.container', 'jQuery', 'jQuery.ui', persis.deps.css('/js/persis/ui/container/accordion.css') ]);
persis.deps.addDependency('ui/container/container.js', [ 'persis.ui.container' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/container/image_gallery.js', [ 'persis.ui.container.ImageGallery' ], [ 'persis.ui.container', 'persis.types.iter.Collector', 'persis.async.timer.IdleHandler', 'jQuery', 'jQuery.fn.mousewheel', persis.deps.css('/js/persis/ui/container/image_gallery.css') ]);
persis.deps.addDependency('ui/container/image_renderer.js', [ 'persis.ui.container.ImageCanvasRenderer' ], [ 'persis.ui.container', 'persis.dom.graphics.CanvasRenderer', 'jQuery.Jcrop', persis.deps.css('/js/persis/ui/container/image_renderer.css') ]);
persis.deps.addDependency('ui/container/pageflip.js', [ 'persis.ui.container.PageFlip' ], [ 'persis.ui.container', 'persis.dom.graphics.CanvasRenderer', 'persis.types.Dim', 'persis.types.Rect', 'persis.ui.elements.Image', 'persis.async.TaskProcessor', 'persis.dom.events.MouseHoverHandler', 'Prototype', 'persis.dom.sound', persis.deps.css('/js/persis/ui/container/pageflip.css') ]);
persis.deps.addDependency('ui/container/tabs.js', [ 'persis.ui.container.Tabs' ], [ 'persis.ui.container', 'persis.types.iter.Collection', 'persis.dom', 'persis.ui.LoaderOverlay', 'jQuery.ui', persis.deps.css('/js/persis/ui/container/tabs.css') ]);
persis.deps.addDependency('ui/decorators/decorators.js', [ 'persis.ui.decorators', 'persis.ui.decorators.table' ], [ 'persis.ui', 'jQuery', 'Prototype' ]);
persis.deps.addDependency('ui/dialogs/dialogs.js', [ 'persis.ui.dialogs', 'persis.ui.dialogs.Dialog', 'persis.ui.dialogs.MessageBox', 'persis.ui.dialogs.InputDialog' ], [ 'goog', 'persis.ui', 'persis.ui.frames.iframe', 'persis.old_scripts.parser_js', 'persis.dom.events.DOMEventHandler', 'persis.async.ajax.AjaxRequest', persis.deps.css('/js/persis/ui/dialogs/dialogs.css') ]);
persis.deps.addDependency('ui/elements/elements.js', [ 'persis.ui.elements' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/elements/image.js', [ 'persis.ui.elements.Image' ], [ 'persis.ui.elements', 'persis.async.ajax.ImageLoader', persis.deps.css('/js/persis/ui/elements/image.css') ]);
persis.deps.addDependency('ui/elements/progress.js', [ 'persis.ui.elements.Progress' ], [ 'persis.ui.elements', 'jQuery.ui', persis.deps.css('/js/persis/ui/elements/progress.css') ]);
persis.deps.addDependency('ui/frames/frames.js', [ 'persis.ui.frames' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/frames/iframes.js', [ 'persis.ui.frames.iframe', 'persis.ui.frames.iframe.IFrameWrapper' ], [ 'persis.ui.frames', 'persis.ui.UIElement', 'Prototype', 'persis.dom', 'persis.dom.css', 'persis.dom.events.DOMEventHandler', 'persis.types.Dim', 'persis.async.timer.Timer' ]);
persis.deps.addDependency('ui/fx/dragndrop.js', [ 'persis.ui.fx.dragndrop', 'persis.ui.fx.dragndrop.Dragger' ], [ 'persis.ui.fx' ]);
persis.deps.addDependency('ui/fx/dynamic.js', [ 'persis.ui.fx.DynElement' ], [ 'persis.ui.fx', 'persis.async.ajax.AjaxRequest', persis.deps.css('/js/persis/ui/fx/dynamic.css') ]);
persis.deps.addDependency('ui/fx/fx.js', [ 'persis.ui.fx' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/inputs/inputs.js', [ 'persis.ui.inputs' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/inputs/slider.js', [ 'persis.ui.inputs.Slider' ], [ 'persis.ui.inputs', 'jQuery.ui', 'jQuery.fn.mousewheel', persis.deps.css('/js/persis/ui/inputs/slider.css') ]);
persis.deps.addDependency('ui/inputs/tristatecheckbox.js', [ 'persis.ui.inputs.TriStateCheckbox' ], [ 'persis.ui.inputs', 'jQuery', persis.deps.css('/js/persis/ui/inputs/tristatecheckbox.css') ]);
persis.deps.addDependency('ui/inputs/tokeninput.js', [ 'persis.ui.inputs.tokeninput' ], [ 'persis.ui.inputs', 'jQuery', 'persis.async.events.UserEventTrigger' ]);
persis.deps.addDependency('ui/inputs/coloredcheckbox.js', [ 'persis.ui.inputs.ColoredCheckbox' ], [ 'persis.ui.inputs', 'jQuery', 'jQuery.ui', 'persis.async.events.UserEventTrigger', persis.deps.css('/js/persis/ui/inputs/coloredcheckbox.css') ]);
persis.deps.addDependency('ui/menus/context_menu.js', [ 'persis.ui.menus.ContextMenu' ], [ 'persis.ui.menus', 'Prototype', 'persis.dom.events.DOMEventHandler' ]);
persis.deps.addDependency('ui/menus/menu.js', [ 'persis.ui.menus.Menu', 'persis.ui.menus.Menu.Element', 'persis.ui.menus.Menu.Control', 'persis.ui.menus.Menu.Separator', 'persis.ui.menus.Menu.SubMenu', 'persis.ui.menus.Menu.Item', 'persis.ui.menus.Menu.Select', 'persis.ui.menus.MenuButton' ], [ 'persis.ui.menus', 'goog', 'Prototype', 'persis.dom', 'persis.utils.GoogleClosureCustomRenderedComponentWrapper', persis.deps.css('/js/persis/ui/menus/menu.css') ]);
persis.deps.addDependency('ui/menus/menus.js', [ 'persis.ui.menus' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/menus/synchronizable_menu.js', [ 'persis.ui.menus.SynchronizableMenu' ], [ 'persis.ui.menus', 'persis.ui.menus.Menu', 'persis.async.ajax.AjaxSynchronizable' ]);
persis.deps.addDependency('ui/menus/tabctrl.js', [ 'persis.ui.menus.TabCtrl', 'persis.ui.menus.TabCtrl.Tab', 'persis.ui.menus.TabCtrl.Tab.PreloaderTask' ], [ 'persis.ui.menus', 'persis.async.events.UserEventTrigger', 'Prototype', 'persis.async.TaskProcessor', 'persis.async.Task', 'persis.ui.LoaderOverlay', 'persis.async.ajax.IFrameLoader', 'persis.async.ajax.AjaxRequest' ]);
persis.deps.addDependency('ui/progress/progress.js', [ 'persis.ui.progress' ], ['jQuery', 'jQuery.ui', 'persis.ui.elements.Progress', 'persis.json', 'persis.ui.tooltips', persis.deps.css('/js/persis/ui/progress/progress.css') ]);
persis.deps.addDependency('ui/trees/trees.js', [ 'persis.ui.trees', 'persis.ui.trees.TreeCtrl', 'persis.ui.trees.TreeCtrl.Node', 'persis.ui.trees.TreeCtrl.Node.DataColumn' ], [ 'persis.ui', 'persis.async.events.UserEventTrigger', 'persis.async.TaskProcessor', 'persis.ui.LoaderOverlay', 'persis.old_scripts.dhtml_js', 'Prototype', persis.deps.css('/js/persis/ui/trees/tree_ctrl.css') ]);
persis.deps.addDependency('ui/toolbars/toolbars.js', [ 'persis.ui.toolbars.Toolbar', 'persis.ui.toolbars.Toolbar.Element', 'persis.ui.toolbars.Toolbar.Control', 'persis.ui.toolbars.Toolbar.Button', 'persis.ui.toolbars.Toolbar.ToggleButton', 'persis.ui.toolbars.Toolbar.Menu', 'persis.ui.toolbars.Toolbar.Select', 'persis.ui.toolbars.Toolbar.Separator', 'persis.ui.toolbars.Toolbar.Static', 'persis.ui.toolbars.Toolbar.Group' ], [ 'persis.ui', 'goog', 'Prototype', 'persis.dom', 'persis.utils.GoogleClosureCustomRenderedComponentWrapper', 'persis.ui.menus.Menu.Item', persis.deps.css('/js/persis/ui/toolbars/toolbars.css') ]);
persis.deps.addDependency('ui/toolbars/synchronizable_toolbar.js', [ 'persis.ui.toolbars.SynchronizableToolbar' ], ['persis.ui.toolbars.Toolbar', 'persis.async.ajax.AjaxSynchronizable', 'persis.ui.menus.Menu.Item', 'persis.ui.menus.Menu.SubMenu', 'persis.ui.menus.Menu.Separator' ]);
persis.deps.addDependency('ui/tooltips/tooltips.js', [ 'persis.ui.tooltips', 'persis.ui.tooltips.Tooltip' ], [ 'persis.ui', 'persis.async.ajax.AjaxRequest', 'goog', 'Prototype', persis.deps.css('/js/persis/ui/tooltips/tooltips.css') ]);
persis.deps.addDependency('ui/views/views.js', [ 'persis.ui.views' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/views/generic_view.js', [ 'persis.ui.views.GenericViewTemplate' ], [ 'persis.ui.views', 'persis.dom', 'persis.ui.toolbars.Toolbar', 'persis.ui.menus.SynchronizableMenu', 'persis.ui.toolbars.SynchronizableToolbar', 'persis.ui.dialogs.Dialog', 'persis.async.ajax.AjaxRequest', persis.deps.css('/css/persis_units.css') ]);
persis.deps.addDependency('ui/calendars/calendars.js', [ 'persis.ui.calendars' ], [ 'persis.ui' ]);
persis.deps.addDependency('ui/calendars/fullcalendar.js', [ 'persis.ui.calendars.FullCalendar' ], [ 'persis.ui', 'persis.ui.calendars', 'jquery-fullcalendar', persis.deps.css('/js/persis/ui/calendars/fullcalendar.css') ]);
persis.deps.addDependency('ui/calendars/datepicker.js', [ 'persis.ui.calendars.DatePicker' ], [ 'persis.ui', 'persis.ui.calendars', 'jQuery.ui', 'persis.types.date.Converter' ]);
persis.deps.addDependency('ui/calendars/datetimepicker.js', [ 'persis.ui.calendars.DateTimePicker' ], [ 'persis.ui', 'persis.ui.calendars.DatePicker', 'jQuery.ui.datetimepicker' ]);
persis.deps.addDependency('ui/calendars/timepicker.js', [ 'persis.ui.calendars.TimePicker' ], [ 'persis.ui.calendars.DatePicker', 'jQuery.ui.timepicker', 'persis.old_scripts.watch_js']);
persis.deps.addDependency('ui/inputs/combobox.js', [ 'persis.ui.inputs.Combobox' ], [ 'jQuery.ui','persis.ui.inputs' ]);
persis.deps.addDependency('ui/inputs/autocomplete.js', [ 'persis.ui.inputs.AutoComplete' ], [  'jQuery.ui','persis.ui.inputs',persis.deps.css('/js/persis/ui/inputs/autocomplete.css') ]);
persis.deps.addDependency('ui/inputs/autocomplete_dropdown.js', ['persis.ui.inputs.AutoCompleteDropdown'], ['persis.ui.inputs.AutoComplete']);

persis.deps.addDependency('utils/closure_wrapper.js', [ 'persis.utils.GoogleClosureEventTargetWrapper', 'persis.utils.GoogleClosureComponentWrapper', 'persis.utils.GoogleClosureCustomRenderedComponentWrapper' ], [ 'persis.utils.ThirdPartyObjectWrapper', 'persis.utils.ThirdPartyComponentWrapper', 'goog' ]);
persis.deps.addDependency('utils/third_party_wrapper.js', [ 'persis.utils.ThirdPartyObjectWrapper', 'persis.utils.ThirdPartyComponentWrapper' ], [ 'persis.utils' ]);
persis.deps.addDependency('utils/utils.js', [ 'persis.utils' ], [ 'persis' ]);
persis.deps.addDependency('utils/algorithms/algorithms.js', [ 'persis.utils.algorithms' ], [ 'persis.utils' ]);
persis.deps.addDependency('utils/algorithms/bspline.js', [ 'persis.utils.algorithms.BSpline' ], [ 'persis.utils.algorithms' ]);
persis.deps.addDependency('utils/algorithms/interpolator.js', [ 'persis.utils.algorithms.Interpolator' ], [ 'persis.utils.algorithms' ]);


// Third-Party
persis.deps.addDependency('/js/codemirror/2.33/lib/codemirror.js', [ 'CodeMirror' ], [ persis.deps.css('/js/codemirror/2.33/lib/codemirror.css') ]);
persis.deps.addDependency('/js/closure-library/r1446/trunk/closure/goog/base.js', [ 'goog' ], [ persis.deps.constant("GOOGLE_CLOSURE_LIBRARY_REVISION", 1446) ], { 'preventLoadingAfterDomIsReady': true, 'postLoad': function() { persis.scripts.load(persis.scripts.getPersisScriptPath('/js/closure-library/r1446/trunk/closure/goog/deps.js')); } });

persis.deps.addDependency('/js/excanvas/excanvas.js', [ 'Excanvas' ], [  ], { 'postLoad': function() { persis.global.Excanvas = true; } });

persis.deps.addDependency('utils/moment/moment-timezone_0_5_3/moment-timezone-with-data.min.js', [ 'moment.tz' ], [ 'momentjs' ]);
persis.deps.addDependency('utils/moment/moment_2_12_0/moment-with-locales.min.js', [ 'momentjs' ], [  ]);
persis.deps.addDependency('/js/jquery/1.7.1/jquery/jquery-1.7.1.js', [ 'jQuery' ], [ ], { 'postLoad': function() { persis.scripts.load(persis.scripts.getPersisScriptPath('/js/jquery/1.7.1/jquery.noconflict.js')); } });persis.deps.addDependency('/js/jquery/1.7.1/jquery-ui-1.8.17.custom.min.js', [ 'jQuery.ui' ], [ 'jQuery', persis.deps.css('/js/jquery/1.7.1/css/jquery.ui.all.css') ] );
persis.deps.addDependency('/js/jquery/1.7.1/jquery.event.drag-2.2/jquery.event.drag-2.2.js', [ 'jQuery.fn.drag' ], [ 'jQuery' ] );
persis.deps.addDependency('/js/jquery/1.7.1/jquery.event.drop-2.2/jquery.event.drop-2.2.js', [ 'jQuery.fn.drop' ], [ 'jQuery' ] );
persis.deps.addDependency('/js/jquery/1.7.1/jquery.layout-latest.min.js', [ 'jQuery.layout' ], [ 'jQuery.ui', persis.deps.css('/js/jquery/1.7.1/css/layout-default-latest.css') ] );
persis.deps.addDependency('/js/jquery/1.7.1/jquery.mousewheel.min.js', [ 'jQuery.fn.mousewheel' ], [ 'jQuery' ]);
persis.deps.addDependency('/js/jquery/1.7.1/jquery.tinyscrollbar.min.js', [ 'jQuery.fn.tinyscrollbar' ], [ 'jQuery' ]);
persis.deps.addDependency('/js/jquery/1.7.1/jquery.Jcrop.min.js', [ 'jQuery.Jcrop' ], [ 'jQuery', persis.deps.css('/js/jquery/1.7.1/jquery.Jcrop.min.css') ]);
persis.deps.addDependency('/js/jquery/1.7.1/jquery-fullcalendar-persisviews/jquery-fullcalendar-persisviews.js', [ 'jQueryFullCalendar.persisViews' ], [  ]);
persis.deps.addDependency('/js/jquery/1.7.1/jquery-fullcalendar/fullcalendar.js', [ 'jquery-fullcalendar' ], [ 'jQuery', 'jQuery.ui', 'momentjs','moment.tz', 'jQueryFullCalendar.persisViews', persis.deps.css('/js/jquery/1.7.1/jquery-fullcalendar/fullcalendar.css') ]);
persis.deps.addDependency('/js/jquery/datetimepicker/jquery-ui-sliderAccess.js', [ 'jQuery.ui.sliderAccess' ], [ 'jQuery.ui' ]);
persis.deps.addDependency('/js/jquery/datetimepicker/jquery-ui-timepicker-addon.min.js', [ 'jQuery.ui.datetimepicker' ], [ 'jQuery.ui', 'jQuery.ui.sliderAccess', persis.deps.css('/js/jquery/datetimepicker/jquery-ui-timepicker-addon.min.css') ]);
persis.deps.addDependency('/js/jquery/timepicker/jquery.ui.timepicker.js', [ 'jQuery.ui.timepicker' ], [ 'jQuery.ui', persis.deps.css('/js/jquery/timepicker/jquery.ui.timepicker.css') ]);



persis.deps.addDependency('/js/pdfobject/1.2/pdfobject.js', [ 'PDFObject' ], [  ]);

//persis.deps.addDependency('prototype/prototype.js', [ 'Prototype' ], [  ]);
persis.deps.addDependency('/js/prototype/prototype.js', [ 'Prototype' ], [  ]);

persis.deps.addDependency('/js/scriptaculous/scriptaculous.js', [ 'Scriptaculous' ], [ 'Prototype' ]);


persis.deps.addDependency('types/date.js', [ 'persis.types.date.Converter' ], [ 'persis.types', 'getDateFormat', 'persis.old_scripts.parser_js' ]);



/*

Packages:
- klein geschrieben
- Unterpackages werden in entsprechenden Unterverzeichnissen geführt.
- Verzeichnisnamen entspricht dem Packagenamen.
- Versuchen die Package-Dateien nicht zu groß werden zu lassen -> Unterpackages machen
Klassen:
- Beginnt mit Großbuchstaben
- "Thematisch" in Dateien zusammenfassen
- Dateinamen entspricht dem "Thema" (kleingeschrieben)





persis								- Basisfunktionen (JS-Nachladen, OOP, ...) -> persis.js
persis.async						- Jobs, Queue, Loader, ...
persis.async.ajax					- Kommunikation via Ajax?
persis.async.events					- User-Events
persis.browser						- Browser-Informationen, Cookies, Fenstersteuerung, ...
persis.debug						- Debugging-Funktionen, Tracer, Logger, ...
persis.debug.tests   		 		- automatisierte Funktionstests (wird nicht ausgeliefert)
persis.dom							- gekapselter Zugriff auf den Browser DOM
persis.dom.css						- CSS-Funktionen
persis.dom.events					- DOM-Events
persis.forms						- Formular-Handhabung
persis.forms.validation				- Validierungen
persis.graphics						- Bild-/Grafik-Funktionen
persis.i18n							- Fremdsprachenfunktionen, Internationalisierung?
persis.i18n.curr					- Währungen
persis.io							- Upload, Ajax?, ...
persis.math							- Mathematische Funktionen
persis.plugins						- ActiveX, Plugins, Applets, ...
persis.types     					- Datentypen
persis.types.date					- Datumsfunktionen
persis.types.iter					- Arrays, Collections, ...
persis.types.str					- String-Funktionen
persis.types.num					- Numeric-Funktionen
!!! persis.types.components				- Hilfs-/Basisfunktionen für Persis Komponenten ("Prototypen")
!!! persis.types.components.formfields	- Formular-Eingabefelder (Datumsfelder, Datumsbereich, ...)
persis.parser						- Parse-Funktionen
persis.parser.json					- Handling von JSON, ...
persis.ui							- Widgets, Tree, Grid, ...
persis.ui.fx						- Drag&Drop, Animationen, ...
persis.ui.dialogs					- Übernahmen, Meldungen, Popups, ...
persis.ui.editor					- Editor
persis.ui.frames					- Frames, Frameset, ...
persis.ui.menus						- Menü-Funktionen/-Klassen
persis.ui.templates					- Templates für Persis-Komponenten
persis.modules						- Modulspezifische JavaScript-Dateien/Funktionen/Bibliotheken/Klassen
persis.modules.ab					- Modul "AB" - Ausbildungsmanager
persis.modules.ma					- Modul "MA" - Personalmanager
persis.modules...					- ...
persis.validation					- Generelle Validierungsfunktionen



persis
	async
		ajax
		events
	browser
	debug
		tests
	dom
		css
		events
	forms
		validation
	graphics
	i18n
		curr
	io
	math
	plugins
	types
		date
		iter
		str
!!!		components
!!!			formfields
	parser
		json
	ui
		fx
		dialogs
		editor
		frames
		menus
		templates
	modules
		ab
		bm
		sw
		ma
		dipa
	validation

*/

var Persis = Persis || {};

/*
Persis.Form = {};
Persis.Form.ResizeTextarea = function(elem){
	this.elem = elem;
	this.mouseupHandlerBuffer;
	this.mousemoveHandlerBuffer;
	this.startYPos = 0;
	this.currentYPos = 0;
	this.startXPos = 0;
	this.currentXPos = 0;
	this.doDestroyResizer;
	$ResizeTextarea = this;
}

Persis.Form.ResizeTextarea.prototype.setHandler = function(){
	this.rescueResizer();
	this.mouseupHandlerBuffer = document.onmouseup;
	this.mousemoveHandlerBuffer = document.onmousemove;
	document.onmouseup = function(e) {eval(document.onmouseup);  $ResizeTextarea.resetHandler();}; 
	document.onmousemove = function(e) {onMouseMove(e); $ResizeTextarea.resizeIt()};
}

Persis.Form.ResizeTextarea.prototype.resetHandler = function(){
	document.onmouseup = this.mouseupHandlerBuffer;
	document.onmousemove = this.mousemoveHandlerBuffer;
}

Persis.Form.ResizeTextarea.prototype.resizeIt = function(){
	this.rescueResizer();
	if (document.getElementById('dhtmlResizeTextarea')){
		
		if (this.startYPos == 0){
			this.startYPos = cmouseY;
		}
		this.currentYPos = cmouseY;

		if (this.currentYPos-this.startYPos > 12){
			this.elem.rows++;
			this.startYPos = this.currentYPos;
		}
		else if (this.currentYPos-this.startYPos < -12){
			this.elem.rows--;
			this.startYPos = this.currentYPos;				
		}

		
		
		if (this.startXPos == 0){
			this.startXPos = cmouseX;
		}
		this.currentXPos = cmouseX;

		if (this.currentXPos-this.startXPos > 3){
			this.elem.cols++;
			this.startXPos = this.currentXPos;
		}
		else if (this.currentXPos-this.startXPos < -3){
			this.elem.cols--;
			this.startXPos = this.currentXPos;				
		}
		this.positionResizer(document.getElementById('dhtmlResizeTextarea'));
	}
}

Persis.Form.ResizeTextarea.prototype.showResizer = function(objName){
	this.rescueResizer();
	if(document.getElementById('dhtmlResizeTextarea')){
		dhtmlDestroyDiv('dhtmlResizeTextarea');
	}				
	var div = dhtmlCreateDiv('dhtmlResizeTextarea', 'dhtmlResizeTextarea');
	div.setAttribute('onmousedown', 'javascript: ' + objName + '.setHandler(' + objName + ');', 0);
	div.setAttribute('onmouseover', 'javascript: ' + objName + '.rescueResizer();', 0);
	div.setAttribute('onmouseout', 'javascript: ' + objName + '.hideResizer(\'' + objName + '\');', 0);
	this.positionResizer(div);
}

Persis.Form.ResizeTextarea.prototype.positionResizer = function(div){
	var elemPos = dhtmlFindElementPos(this.elem);
	div.style.left = elemPos[0]+this.elem.offsetWidth + 'px';
	div.style.top = elemPos[1]+this.elem.offsetHeight + 'px';				
}

Persis.Form.ResizeTextarea.prototype.hideResizer = function(objName){
	this.doDestroyResizer = true;
	var fnName = objName + '.destroyResizer()';
	eval('window.setTimeout("' + fnName + '", 500)');
}			

Persis.Form.ResizeTextarea.prototype.destroyResizer = function(){
	if (this.doDestroyResizer){
		if(document.getElementById('dhtmlResizeTextarea')){
			dhtmlDestroyDiv('dhtmlResizeTextarea');
		}
	}
}

Persis.Form.ResizeTextarea.prototype.rescueResizer = function(){
	this.doDestroyResizer = false;
} 
*/
