/**
 * @class IDU.Core.Web.Enums.Enum
 * @alternateClassName Enum
 * Enum-Typ für JavaScript. Verhält sich ähnlich den C#-Enums und bietet die gängigen Methoden dafür an.
 * 
 * Ein serverseitig serialisierter Enum-Typ bildet clientseitig einen Typ dieser Klasse,
 * der wiederum für jedes Enum-Feld eine Eigenschaft vom Typ {@link IDU.Core.Web.Enums.Enum.EnumField} hat.
 * Die Description-Attribute werden aufgelöst und ebenfalls in den Typ serialisiert. Sie können somit
 * zu Anzeigezwecken herangezogen werden. Fehlt das Description-Attribut, gibt
 * `{@link IDU.Core.Web.Enums.Enum.EnumField#getDescription getDescription()}` den Namen zurück.
 * Flags werden unterstützt.
 * 
 * ## Serialisierung
 * 
 * ### C#
 * 
 *     namespace IDU.Core.Web.FileManagement
 *     {
 *         [Flags]
 *         public enum WellKnownTypeGroup
 *         {
 *             [Description("Unbekannt")]
 *             Unknown = 0,
 *             [Description("Grafikdatei")]
 *             Image = 1,
 *             [Description("Vom Browser unterstützte Grafikdatei")]
 *             WebImage = Image | BrowserSupported,
 *             [Description("Ausführbare Datei")]
 *             Executable = 2,
 *             [Description("Script-Datei")]
 *             ScriptFile = 4,
 *             [Description("Im Borwser darstellbares Datei-Format")]
 *             BrowserSupported = 1024
 *         }
 *     }
 * 
 *     IDU.Core.Web.Enums.EnumHelper.WebEnum<IDU.Core.Web.FileManagement.WellKnownTypeGroup>.Default.RegisterEnumInPage(ResourceCollector);
 * 
 * ### JavaScript
 * 
 *     var typeGroup = IDU.Core.Web.FileManagement.WellKnownTypeGroup.WebImage;
 *     if (typeGroup.hasFlag(IDU.Core.Web.FileManagement.WellKnownTypeGroup))
 *         alert('Dateien vom Typ "' + typeGroup.getDescription() + '" sind im Browser darstellbar.');
 * 
 * ## Parsen
 * 
 * Enums können anhand ihres Zahlenwertes oder Namens mittels {@link IDU.Core.Web.Enums.Enum#static-method-tryParse tryParse}
 * in einen EnumField-Typ konvertiert werden.
 * 
 *     var typeGroup = Enum.tryParse(IDU.Core.Web.FileManagement.WellKnownTypeGroup, 1);
 * 
 *     var typeGroup = Enum.tryParse(IDU.Core.Web.FileManagement.WellKnownTypeGroup, 'Image');
 * 
 * Auch Kombinationen, die nicht als eigenes Feld im Enum-Typ vorhanden sind, werden erkannt. Die Namen und Beschreibungen
 * werden dann wie bei C# durch Komma getrennt aneinander gereiht.
 * 
 *     var typeGroup = Enum.tryParse(IDU.Core.Web.FileManagement.WellKnownTypeGroup, 1028);
 *     var name = typeGroup.getName(); // 'ScriptFile, BrowserSupported'
 *     var description = typeGroup.getDescription(); // 'Script-Datei, Im Borwser darstellbares Datei-Format'
 * 
 *     var typeGroup = Enum.tryParse(IDU.Core.Web.FileManagement.WellKnownTypeGroup, 'Executable, BrowserSupported');
 *     var value = typeGroup.getValue(); // 1026
 * 
 * ## Logische Verknüpfungen
 * 
 * Enums können mit Binäroperatoren wie in C# verknüpft werden. Zu beachten ist jedoch, dass das Ergebnis der Berechnung immer den Zahlenwert liefert.
 * Das ist JavaScript-bedingt nicht anders möglich. Um das Ergebnis wieder in einen Enum-Typ zu konvertieren, muss dieser Wert
 * durch {@link IDU.Core.Web.Enums.Enum#static-method-createCalculated} geschickt werden. Diese Methode ist ähnlich tryParse, außer, dass
 * sie den Enum-Typ schon weiß und definitiv einen Zahlenwert erwartet. Diese Funktion sollte nur zu diesem Zweck verwendet werden.
 * 
 *     var typeGroup = Enum.createCalculated(IDU.Core.Web.FileManagement.WellKnownTypeGroup.WebImage & ~IDU.Core.Web.FileManagement.WellKnownTypeGroup.BrowserSupported);
 *     var name = typeGroup.getName(); // Image
 * 
 * ## Enum-Werte per Ajax verschicken
 * 
 * EnumFields können ohne weiteres Zutun als Parameter für einen Ajax-Request angegeben werden. die
 * {@link IDU.Core.Web.Enums.Enum.EnumField#toJSON toJSON}-Methode wandelt den Wert bei der JSON-Serialisierung in den entsprechenden
 * Zahlenwert um.
 * 
 * ## ExtJS
 * 
 * ### Serialisierung
 * 
 * Enums, die im ExtJS-Umfeld verwendet werden, sollten i.d.R. über das Extensionpack (nicht über die EnumHelper-Klasse) registriert werden.
 * 
 *     this.RegisterExtensionPack(
 *         new IDU.Core.Web.Sencha.Common.ExtensionPacks.Enums(
 *             IDU.Core.Web.Enums.EnumHelper.WebEnum<IDU.Core.Web.FileManagement.WellKnownTypeGroup>.Default));
 * 
 * Es steht dann die Hilfsklasse {@link Ext.ux.Enum} zur Verfügung.
 * 
 * ### Models und Stores
 * 
 * Außerdem ist es dann möglich, Enums als DatenTyp in {@link Ext.data.Model}s zu verwenden.
 * 
 *     Ext4.define('My.Model', {
 *         extend: 'Ext.data.Model',
 *         fields: [
 *             { name: 'typeGroup', type: 'enum', enumType: 'IDU.Core.Web.FileManagement.WellKnownTypeGroup' }
 *             ...
 *         ]
 *     });
 *     
 *     var record = new My.Model({
 *         typeGroup: 1
 *     });
 *     
 *     var typeGroup = record.get('typeGroup'); // Es fällt ein EnumField raus
 *     var name = typeGroup.getName(); // Image
 * 
 * Bei Verwendung des Models in einem Store ist das Laden des Stores über Ajax ebenfalls ohne weiteres Zutun
 * möglich, wenn serverseitig die entsprechende Eigenschaft vom Typ des serialisierten Enums ist.
 * Es fällt am Ende aus den Records immer ein korrekter Typ raus.
 * 
 * ### ComboBox
 * 
 * Wenn bei Registrierung des ExtensionPacks das entsprechende Feature angegeben wird,
 * steht clientseitig die Klasse {@link Ext.ux.form.field.EnumComboBox} zur Verfügung.
 * Mehr Informationen in dieser Klasse.
 * 
 * @extensionpack IDU.Core.Web.Sencha.Common.ExtensionPacks.Enums
 * @registrator IDU.Core.Web.Enums.EnumHelper.RegisterEnumInPage()
 * @author Robin
 * @date 2012-07-04
 */
(function(global) {
	var _combinedValueType,
		_isCombined = function(v)
		{
			if (isNaN(v) || v === null)
				return false;
			// schauen, ob im Binärstring mehr als eine 1 vorkommt
			return /1.*1/.test(v.toString(2));
		},
		_combinedStringRx = /\s*,\s*/g,
		_comma = ',',
		_registeredTypes = {};
//////////////////////////////////////////////////
// #region Enum-Member
//////////////////////////////////////////////////
// #region CTOR
//////////////////////////////////////////////////
/**
 * @class EnumField
 * @ignore
 */
/**
 * @class IDU.Core.Web.Enums.Enum.EnumField
 * Feld eines Enums.
 * Dieses kann nicht direkt erstellt werden, sondern wird von {@link Enum#static-method-registerType}
 * am zugehörigen Enum registriert.
 * @author Robin
 * @date 2012-07-04
 */
/**
 * @constructor
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Erstellt einen neuen Enum-Member.
 * Alternativ können beliebig viele EnumField als Parameter übergeben werden.
 * In diesem Fall wird ein kombinierter Enum-Wert (Flags) erstellt.
 * @param {Enum} type Enum-Typ
 * @param {String} name Name
 * @param {Number} value Wert
 * @param {String} description Beschreibung
 * @private
 */
var EnumField = function(type, name, value, description)
{
	if (!(type instanceof Enum))
		throw new Error("type muss ein Enum sein");
	if (typeof name !== 'string')
		throw new Error("name muss ein String sein");
	if (typeof value !== 'number' || parseInt(value, 10) != value)
		throw new Error("value muss ein Integer sein");

	var me = this;

	me.type = type;
	me.name = name;
	me.value = value;
	me.description = description || name;

	// Zur Prüfung statt "instanceof" wg. frameübergreifenden Vergleichen
	me.___isEnumField = true;
};
//////////////////////////////////////////////////
// #endregion
//////////////////////////////////////////////////
// #region Prototyp-Sachen
//////////////////////////////////////////////////
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt an, ob der EnumField gleich diesem ist.
 * Da in JS keine Operator-Überladungen möglich sind, ist diese Methode
 * vor === bevorzugt zu verwenden, da sie die Enum-Typen vergleicht und nicht nur die numerischen Werte.
 * @param {IDU.Core.Web.Enums.Enum.EnumField} value
 * @return {Boolean}
 */
EnumField.prototype.equals = function(value)
{
	// zurücksetzen, da hier definitiv was anderes gemacht wird als eine Berechnung
	_combinedValueType = undefined;
	///if (!(value instanceof EnumField && value.type === this.type))
	if (value === undefined || value === null || !(value.___isEnumField && value.type.getTypeName() === this.type.getTypeName()))
		throw new Error("value ist nicht vom Typ " + this.type.getTypeName());
	return this.value === value.value;
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt an, ob der Enum ein Flag enthält
 * @param {IDU.Core.Web.Enums.Enum.EnumField} flag Flag, muss vom gleichen Typ wie dieser Enum sein
 * @return {Boolean}
 */
EnumField.prototype.hasFlag = function(flag)
{
	// zurücksetzen, da hier definitiv was anderes gemacht wird als eine Berechnung
	_combinedValueType = undefined;
	///if (!(flag instanceof EnumField && flag.type === this.type))
	if (flag === undefined || flag === null || !(flag.___isEnumField && flag.type.getTypeName() === this.type.getTypeName()))
		throw new Error("flag ist nicht vom Typ " + this.type.getTypeName());
	return (this.value & flag.value) === flag.value;
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt den Wert für JSON zurück (native JSON.stringify nutzt toJSON, wenn es diese Methode gibt).
 * Das muss hier hin, da wir aufgrund der cirular Reference (mit type) sonst in eine Endlosschleife kommen würden.
 * ExtJS4 JSON-Encoder prüft diese Methode ebenfalls ab.
 * @private
 */
EnumField.prototype.toJSON = function()
{
	return this.getValue();
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt den Namen des Enums aus.
 * @return {String}
 */
EnumField.prototype.toString = function()
{
	return this.getName();
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Wert des Enums für Operationen -> binäre Verknüpfungen
 * Wichtig! Um einen Enum-Member und nicht nur die Zahl zu erhalten, muss das
 * Ergebnis in Enum.createCalculated übergeben werden!
 * @return {Number}
 * @private
 */
EnumField.prototype.valueOf = function()
{
	//if (_combinedValueType && _combinedValueType !== this.type)
	if (_combinedValueType && _combinedValueType.getTypeName() !== this.type.getTypeName())
		throw new Error("Operatoren können nur zwischen Enums gleichen Typs verwendet werden");

	_combinedValueType = this.type;

	return this.value;
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt den Namen des Enums aus.
 * @return {String}
 */
EnumField.prototype.getName = function()
{
	// zurücksetzen, da hier definitiv was anderes gemacht wird als eine Berechnung
	_combinedValueType = undefined;
	return this.name;
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt den Enum-Typs aus.
 * @return {Enum}
 */
EnumField.prototype.getType = function()
{
	// zurücksetzen, da hier definitiv was anderes gemacht wird als eine Berechnung
	_combinedValueType = undefined;
	return this.type;
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt den Wert aus.
 * @return {Number}
 */
EnumField.prototype.getValue = function()
{
	// zurücksetzen, da hier definitiv was anderes gemacht wird als eine Berechnung
	_combinedValueType = undefined;
	return this.value;
};
/**
 * @member IDU.Core.Web.Enums.Enum.EnumField
 * Gibt den Anzeigetext des Enums aus.
 * @return {String}
 */
EnumField.prototype.getDescription = function()
{
	// zurücksetzen, da hier definitiv was anderes gemacht wird als eine Berechnung
	_combinedValueType = undefined;
	return this.description || this.name;
};
//////////////////////////////////////////////////
// #endregion
//////////////////////////////////////////////////
// #endregion
//////////////////////////////////////////////////
// #region CTOR
//////////////////////////////////////////////////
/**
 * @constructor
 * @member IDU.Core.Web.Enums.Enum
 * Erstellt einen neuen Enum.
 * Ein Enum sollte nicht clientseitig erstellt, sondern immer über
 * IDU.Core.Web.Enums.EnumHelper.RegisterEnumInPage in der Seite registriert werden.
 * @param {String} typeName Name des Typs
 * @param {Object} values Enum-Werte (Key = Name, Value = Wert (int))
 * @param {Object} descriptions Anzeigenamen (optional) (Key = Name, Value = Anzeigetext)
 * @param {Boolean} hasFlags Gibt an, ob es ein Flags-Enum ist
 * @private
 */
var Enum = function(typeName, values, descriptions, hasFlags)
{
	if (typeof typeName !== 'string')
		throw new Error("typeName muss ein String sein");
	if (!values || typeof values !== 'object')
		throw new Error("values muss ein Objekt sein");

	var me = this,
		parts = typeName.split('.'),
		i = 0,
		len = parts.length - 1,
		leaf = parts[len],
		current = global,
		part,
		o;

	me.___typeName = typeName;
	me.___hasFlags = !!hasFlags;
	me.___isEnum = true;

	descriptions = descriptions || {};
	// Enum-Member zuweisen
	for (o in values)
		if (values.hasOwnProperty(o))
			me[o] = new EnumField(me, o, values[o], descriptions[o]);

	// Enum noch unter dem Typ-Namen global verfügbar machen
	for (; i < len; i++)
	{
		part = parts[i];
		if (!current[part])
			current[part] = {};
		current = current[part];
	}
	_registeredTypes[typeName] = current[leaf] = me;
};
//////////////////////////////////////////////////
// #endregion
//////////////////////////////////////////////////
// #region Prototyp-Sachen
//////////////////////////////////////////////////
/**
 * @member IDU.Core.Web.Enums.Enum
 * Gibt den Namen des Typs aus.
 * @return {String}
 */
Enum.prototype.getTypeName = function()
{
	return this.___typeName;
};
/**
 * @member IDU.Core.Web.Enums.Enum
 * Gibt an, ob es ein Flag-Enum ist
 * @return {Boolean}
 */
Enum.prototype.hasFlags = function()
{
	return !!this.___hasFlags;
};
//////////////////////////////////////////////////
// #endregion
//////////////////////////////////////////////////
// #region Statische Sachen
//////////////////////////////////////////////////
/**
 * @member IDU.Core.Web.Enums.Enum
 * Gibt die Namen eines Enums zurück
 * @param {IDU.Core.Web.Enums.Enum} enumType Enum
 * @return {String[]} Namen
 * @static
 */
Enum.getNames = function()
{
	var _cache = {};
	return function(enumType)
	{
		//if (!(enumType instanceof Enum))
		if (!enumType || !enumType.___isEnum)
			throw new Error("enumType ist nicht vom Typ Enum");
		var typeName = enumType.getTypeName(),
			names,
			i;
		if (!_cache[typeName])
		{
			names = [];
			for (i in enumType)
				//if (enumType.hasOwnProperty(i) && enumType[i] instanceof EnumField)
				if (enumType.hasOwnProperty(i) && enumType[i].___isEnumField)
					names.push(enumType[i].getName());
			_cache[typeName] = names;
		}
		return _cache[typeName];
	};
}();
/**
 * @member IDU.Core.Web.Enums.Enum
 * Gibt die Werte eines Enums zurück
 * @param {IDU.Core.Web.Enums.Enum} enumType Enum
 * @return {Number[]} Werte
 * @static
 */
Enum.getValues = function()
{
	var _cache = {};
	return function(enumType)
	{
		//if (!(enumType instanceof Enum))
		if (!enumType || !enumType.___isEnum)
			throw new Error("enumType ist nicht vom Typ Enum");
		var typeName = enumType.getTypeName(),
			names,
			i;
		if (!_cache[typeName])
		{
			names = [];
			for (i in enumType)
				//if (enumType.hasOwnProperty(i) && enumType[i] instanceof EnumField)
				if (enumType.hasOwnProperty(i) && enumType[i].___isEnumField)
					names.push(enumType[i].getValue());
			_cache[typeName] = names;
		}
		return _cache[typeName];
	};
}();
/**
 * @member IDU.Core.Web.Enums.Enum
 * Versucht, anhand des Typs und des Wertes oder Namen ein den entsprechenden Enum-Member zu finden.
 * Gibt im Fehlerfall null zurück
 * @param {IDU.Core.Web.Enums.Enum} enumType Enum
 * @param {String/Number} value Wert oder Name
 * @param {Boolean} ignoreCase true, um den Vergleich case-insensitiv durchzuführen (wenn value der Name ist)
 * @return {IDU.Core.Web.Enums.Enum.EnumField} Enum-Member oder null im Fehlerfall
 * @static
 */
Enum.tryParse = function(enumType, value, ignoreCase)
{
	if (!(enumType instanceof Enum))
		throw new Error("enumType ist nicht vom Typ Enum");

	// zurücksetzen, hier geht die Berechnung ja erst los
	_combinedValueType = undefined;

	if (value && value.___isEnumField && Enum.isMemberOf(enumType, value))
		return value;

	var i,
		e,
		n,
		v,
		isString = typeof value === 'string',
		isCombined = isString ? value.indexOf(_comma) !== -1 : _isCombined(value);

	// kombinierten String vorbereiten
	if (isString && isCombined)
	{
		value = _comma + value.replace(_combinedStringRx, _comma) + _comma;
		if (ignoreCase === true)
			value = value.toUpperCase();
	}

	for (i in enumType)
	{
		//if (enumType.hasOwnProperty(i) && enumType[i] instanceof EnumField)
		if (enumType.hasOwnProperty(i) && enumType[i].___isEnumField)
		{
			e = enumType[i];
			n = e.value;

			// wird ein vordefinierter Enum-Member gefunden, dann wird dieser auch zurückgegeben
			if (e === value || n === value || e.name === value || (ignoreCase === true && e.name.toUpperCase() === String(value).toUpperCase()))
			{
				_combinedValueType = undefined;
				return e;
			}
			// sonst kombinieren wir
			else if (isCombined && ((typeof value === 'number' && (n & value) === n) || (isString && value.indexOf(_comma + (ignoreCase === true ? e.name.toUpperCase() : e.name) + _comma) !== -1)))
			{
				if (typeof v === 'undefined')
					v = e.valueOf();
				else
					v |= e;
			}
		}
	}
	return v ? Enum.createCalculated(v) : null;
};
/**
 * @member IDU.Core.Web.Enums.Enum
 * Erstellt einen Enum-Member aus einem (kombinierten) Wert
 * @param {Number} value Wert, der über Binäroperationen mehrerer EnumField erstellt wurde
 * @return {IDU.Core.Web.Enums.Enum.EnumField} EnumField
 * @static
 */
Enum.createCalculated = function()
{
	var _cache = {};
	return function(value)
	{
		if (!(typeof value === 'number' && _combinedValueType instanceof Enum))
			throw new Error("Der Wert muss über Binäroperationen mehrerer EnumField erstellt worden sein. Andernfalls Enum.tryParse verwenden.");

		var type = _combinedValueType,
			typeName = type.getTypeName();

		_combinedValueType = undefined;

		// schon mal gemacht -> zurückgeben
		if (_cache[typeName] && _cache[typeName][value])
			return _cache[typeName][value];

		if (!_cache[typeName])
			_cache[typeName] = {};

		var names = [],
			nullName,
			descriptions = [],
			nullDescription,
			v = 0,
			val,
			i,
			map = {},
			values = [],
			hasFlags = type.hasFlags();

		for (i in type)
		{
			//if (type.hasOwnProperty(i) && type[i] instanceof EnumField)
			if (type.hasOwnProperty(i) && type[i].___isEnumField)
			{
				val = type[i].getValue();
				// wenn es bereits einen vordefinierten (kombinierten) Member gibt, dann nehmen wir diesen auch
				if (val === value)
				{
					_cache[typeName][value] = type[i];
					return type[i];
				}

				if ((value & val) === val)
				{
					if (val > 0)
					{
						// merken -> muss nachher rückwärts nach Größe der Werte abgearbeitet werden
						map[val] = type[i];
						values.push(val);

						names.push(type[i].getName());
						descriptions.push(type[i].getDescription());
					}
					// 0-Wert nur nutzen, wenn kein anderer dabei ist
					else
					{
						nullName = type[i].getName();
						nullDescription = type[i].getDescription();
					}
					v |= val;
				}
			}
		}
		// jetzt nochmal rückwärts ablaufen wg. evtl. Doppelungen
		// z.B.
		// Enum (None = 0, One = 1, Two = 2, Five = One | 4, Six = Two | 4)
		// v == 7 -> One, Six
		if (hasFlags && _isCombined(v) && values.length > 0)
		{
			values.sort();
			names.length = 0;
			descriptions.length = 0;
			val = 0;

			for (i = values.length - 1; i >= 0; i--)
			{
				if ((values[i] & val) > 0)
					continue;

				names.unshift(map[values[i]].getName());
				descriptions.unshift(map[values[i]].getDescription());

				val |= values[i];
			}
		}
		// Namen und Beschreibung werden nur kombiniert verwendet, wenn es ein Flag-Enum ist und überhaupt
		// Einzelwerte gefunden wurden, sonst den Wert als String nehmen (wie in C#)
		_cache[typeName][value] = new EnumField(
			type,
			hasFlags && names.length > 0 ? names.join(", ") : (nullName || v.toString()),
			v,
			hasFlags && descriptions.length > 0 ? descriptions.join(", ") : (nullDescription || v.toString()));

		return _cache[typeName][value];
	};
}();
/**
 * @member IDU.Core.Web.Enums.Enum
 * Gibt an, ob der angegebene Wert als Enum definiert ist
 * @param {IDU.Core.Web.Enums.Enum} enumType Typ des Enums
 * @param {Object} value der zu prüfende Wert (Zahl, Name (case-sensitiv) oder EnumField)
 * @return {Boolean} isDefined
 * @static
 */
Enum.isDefined = function(enumType, value)
{
	if (value === undefined || value === null)
		return false;
	if (Enum.isMemberOf(enumType, value) && !enumType.hasFlags())
		return true;
	//value = value instanceof EnumField ? value : Enum.tryParse(enumType, value, false);
	value = value.___isEnumField ? value : Enum.tryParse(enumType, value, false);
	return value && enumType[value.getName()] instanceof EnumField;
};
/**
 * @member IDU.Core.Web.Enums.Enum
 * Gibt an, ob der angegebene Wert ein Member des angegebenen Typs ist
 * @param {IDU.Core.Web.Enums.Enum} enumType Typ des Enums
 * @param {Object} value der zu prüfende Wert
 * @return {Boolean} isMemberOf
 * @static
 */
Enum.isMemberOf = function(enumType, value)
{
	if (value === undefined || value === null)
		return false;
	if (!(enumType instanceof Enum))
		throw new Error("enumType ist nicht vom Typ Enum");
	//if (!(value instanceof EnumField) || value.getType() != enumType)
	if (!value || !value.___isEnumField || value.getType().getTypeName() !== enumType.getTypeName())
		return false;
	return enumType.hasFlags() || enumType[value.getName()].getName() === value.getName();
};
/**
 * @member IDU.Core.Web.Enums.Enum
 * Registriert einen Typ in der Seite
 * Ein Enum sollte nicht clientseitig erstellt, sondern immer über
 * IDU.Core.Web.Enums.EnumHelper.RegisterEnumInPage in der Seite registriert werden.
 * @param {String} typeName Name des Typs
 * @param {Object} values Enum-Werte (Key = Name, Value = Wert (int))
 * @param {Object} descriptions Anzeigenamen (optional) (Key = Name, Value = Anzeigetext)
 * @param {Boolean} hasFlags Gibt an, ob es ein Flags-Enum ist
 * @return {IDU.Core.Web.Enums.Enum} Enum
 * @private
 * @static
 */
Enum.registerType = function(typeName, values, descriptions, hasFlags)
{
	// nicht doppelt registrieren (z.B. bei dynamischen Nachladen von ClientScripts),
	// sonst passen Vergleiche u.U. nicht mehr, da type != type

	// CTOR kümmert sich um Registrierung
	return _registeredTypes[typeName] || new Enum(typeName, values, descriptions, hasFlags);
};
//////////////////////////////////////////////////
// #endregion
//////////////////////////////////////////////////
// Enum öffentlich machen
global.Enum = IDU.Core.Web.namespace('IDU.Core.Web.Enums.Enum', Enum);
})(this);