/*! 
 * Gewusst-Wo Javascript Core Library
 * Copyright (C) 2008 TENSQUARE gmbh
 */

/*=======================================================================*/
/* Library-Definitionen                                                  */
/*=======================================================================*/

if (!__GEWUSSTWO__)
{
   /**
    * __GEWUSSTWO__ definiert den globalen Namespace der Library.
    */
   var __GEWUSSTWO__ = {},
   
   /**
    * Shortcut-Alias von __GEWUSSTWO__.
    */
   $gw$ = __GEWUSSTWO__,
   
   /**
    * Moduldefinition und -version der Core-Library
    */
   __GW_CORE__ =
   {
      version: "1.0.0"
   };
}

/*=======================================================================*/
/* Container-Objekt für globale Variablen einer Seite                    */
/*=======================================================================*/

if (!$gw$.vars)
{
   var $v$ = $gw$.vars = {};
}

/*=======================================================================*/
/* Namespaces                                                            */
/*=======================================================================*/

/**
 * Liefert den angegebenen Namespace. Falls der Namespace noch nicht
 * existiert, wird er neu erzeugt.
 *
 * Der Namespace kann in Dot-Notation angegeben werden, um Hierarchien
 * abzubilden. Alle erzeugten Namespaces werden unterhalb des globalen
 * Namespaces $gw$ platziert.
 *
 * Beachte: Die Namespaces <tt>$gw$.obj</tt> und <tt>$gw$.util
 * </tt> werden standardmäßig erzeugt.
 *
 * @example
 * <pre>
 *   // Namespace "mathlib" erzeugen
 *   var myMathLib = $gw$.getNamespace ("mathlib");
 *   // Namespaces "graph" und "graph.widgets" erzeugen
 *   var graphWidgets = $gw$.getNamespace ("graph.widgets");
 *   // Namespaces "stringutil", "objutil" und "ioutil" erzeugen
 *   $gw$.getNamespace ("stringutil", "objutil", "ioutil");
 * </pre>
 *
 * @param  {String*} name  Namespace. Ein oder mehrere Strings für die
 *         Namespaces, die erzeugt werden sollen. Die Namespaces können
 *         in Dot-Notation angegeben werden, wodurch automatisch eine
 *         entsprechende Hierarchie erzeugt wird.
 * @return {Object} Namespace. Falls mehrere Namespaces erzeugt wurden,
 *         wird der letzte erzeugte Namespace zurückgegeben.
 */
$gw$.getNamespace = function (name)
{
   /* Funktionsargumente holen und Namespace-Referenz initialisieren */
   var args = arguments, argc = args.length, obj = null;

   /* Namespaces erzeugen bzw. ermitteln */
   for (var i = 0; i < argc; i++)
   {
      /* Alle Namespaces sind dem Namespace $gw$ untergeordnet */
      obj = $gw$;

      /* Hierarchische Namespaces in Dot-Notation verarbeiten */
      var components = args[i].split (".");
      for (var j = 0; j < components.length; j++)
      {
         /* Namespace-Komponente holen */
         var n = components[j];
         /* Namespace erzeugen, falls noch nicht definiert */
         obj = obj[n] = obj[n] || { };
      }
   }

   return obj;
};

/*
 * Default-Namespaces erzeugen.
 *
 * NB: Der "obj"-Namespace wird hier noch nicht erzeugt, da das Objekt
 * $gw$.obj unten bedingt erweitert wird, falls es noch nicht existiert.
 * Daher dürfen wir $gw$.obj an dieser Stelle nicht erzeugen.
 */
$gw$.getNamespace ("util", "ui", "pg");

/*=======================================================================*/
/* Logging                                                               */
/*=======================================================================*/

/**
 * Loggt eine Meldung über die Konsole (soweit verfügbar).<p>
 *
 * Die Methode prüft, ob das Objekt <tt>console</tt> mit der Methode
 * <tt>log()</tt> zur Verfügung steht (z.B. über Firebug). Falls ja,
 * wird die angegebene Meldung mittels <tt>console.log()</tt>
 * ausgegeben. Ansonsten kehrt die Methode ohne Aktion zurück.
 *
 * @param  {String} msg  Log-Meldung
 */
$gw$.log = function (msg)
{
  if (typeof(console) == "undefined")
      return;
  
   var c = console;
   if (c && c.log)
      c.log (msg);
};

/*=======================================================================*/
/* Modul-Verwaltung                                                      */
/*=======================================================================*/

/**
 * <tt>Modules</tt> ist das globale Objekt zur Verwaltung der
 * Libraries, Module und Plugins.
 *
 * Ein Modul des Frameworks sollte immer mittels Modules.register()
 * registriert werden. Auf diese Weise kann jederzeit ermittelt werden,
 * welche Module aktuell zur Verfügung stehen.
 *
 * Falls ein Modul Abhängigkeiten zu einem anderen Modul aufweist, kann
 * es über Modules.get() ermitteln, ob das benötigte Modul geladen ist.
 * Außerdem kann man mittels Modules.addListener() eine Listener-Funktion
 * angeben, die immer dann aufgerufen wird, wenn sich ein neues Modul
 * per register() registriert hat. Auf diese Weise lassen sich auch
 * Abhängigkeiten steuern, bei denen ein Modul ein anderes Modul
 * benötigt, das erst später geladen wird. Details hierzu sind in
 * der Beschreibung von {@link $gw$.Modules.addListener()} zu finden.
 *
 * @class Modules
 * @static
 */
$gw$.Modules = function ()
{
   var modules = [], listeners = [];

   /**
    * Registriert ein Modul.
    *
    * Falls Modul-Listener vorhanden sind, werden diese nach erfolgter
    * Registrierung des Moduls mit den Modul-Infos aufgerufen.
    *
    * @param  {String} name  Name des Moduls
    * @param  {Function} mainClass  Referenz auf die Hauptklasse des
    *         Moduls.
    * @param  {Object} info  Meta-Daten des Moduls. Das Objekt sollte
    *         mindestens ein Property namens "version" besitzen, in dem
    *         die Versionsnummer des Moduls angegeben ist.
    *
    * @see #addListener()
    */
   function register (name, mainClass, info)
   {
      var module;

      if (!modules[name])
         modules[name] = { version: null };
      module = modules[name];
      module.name = name;
      module.version = (info) ? info.version : null;
      module.mainClass = mainClass;

      for (var i = 0; i < listeners.length; i++)
         listeners[i] (module);
   }

   /**
    * Liefert das Modul mit dem angegebenen Namen.
    *
    * @param  {String} name  Name des Moduls
    *
    * @return {Object} Modul bzw. <tt>null</tt>, falls es kein Modul
    *         mit diesem Namen gibt. Das zurückgegebene Objekt enthält
    *         die folgenden Properties:
    *         <ul>
    *           <li> name: Name des Moduls
    *           <li> mainClass: Hauptklasse des Moduls
    *           <li> version: Versionsnummer des Moduls
    *         </ul>
    */
   function get (name)
   {
      return modules[name] || null;
   }

   /**
    * Fügt dem Modul-Manager eine Listener-Funktion hinzu, die automatisch
    * aufgerufen wird, sobald sich ein neues Modul mittels {@link
    * #register()} registriert hat.
    *
    * @param  {Function} listener  Listener-Funktion
    */
   function addListener (listener)
   {
      if (listener)
         listeners.push (listener);
   }

   return {
      register: register,
      get: get
   }
}();

/*=======================================================================*/
/* OOP- und Utility-Funktionen in $gw$.obj                               */
/*=======================================================================*/

if (!$gw$.obj)
/**
 * In <tt>$gw$.obj</tt> befinden sich Methoden zu folgenden Themen:
 *
 * <ul>
 *   <li> Implementierung von OOP-Design-Patterns, z.B. zum Erzeugen und
 *        zur Ableitung von Klassen (siehe {@link $gw$.obj.createClass}
 *        und {@link $gw$.obj.inheritClass}) sowie Composition (siehe
 *        {@link $gw$.obj.extend}, {@link $gw$.obj.extendProto} und
 *        {@link $gw$.obj.merge}).
 *   <li> Hilfsfunktionen zum Umgang mit Objekten, z.B. Kopieren,
 *        Umwandeln in JSON- oder Query-Strings, Auflisten der Keys
 *        und Values eines Objekts, etc. Siehe hierzu z.B.
 *        {@link $gw$.obj.clone}
 * </ul>
 *
 * @namespace Namespace für Javascript-Spracherweiterungen, OOP-Features
 *            und sonstige grundlegende Hilfsfunktionen.
 * @class $gw$.obj
 * @static
 */
$gw$.obj =
{
   /**
    * Erweitert ein Objekt mit den Methoden und sonstigen Properties eines
    * anderen Objekts.<p>
    *
    * Dabei kann gesteuert werden, ob Methoden/Properties, die im Zielobjekt
    * bereits vorhanden sind, überschrieben werden oder erhalten bleiben.<p>
    *
    * Außerdem lassen sich optional die zu übernehmenden Properties explizit
    * angeben, so dass die Elemente des Quellobjekts nur teilweise in das
    * Zielobjekt übernommen werden.<p>
    *
    * Beispiel:
    * @example
    * <pre>
    *   var A = {
    *      name: "Object A",
    *      toString: function ()
    *      {
    *         return this.name;
    *      }
    *   };
    *
    *   var B = {
    *      name: "Object B",
    *
    *      logMessage: function (msg)
    *      {
    *         console.log (msg);
    *      },
    *
    *      toString: function ()
    *      {
    *         return "Foo";
    *      }
    *   };
    *
    *   // Objekt A mit Methoden/Properties aus B erweitern
    *   $gw$.obj.extend (A, B);
    *
    *   // Neue Methode logMessage() von A aufrufen.
    *   // Ausgabe: "Object A", weil die bereits in A vorhandenen Members
    *   // name und toString() nicht überschrieben wurden.
    *   A.logMessage (A.toString ());
    *
    *   // A mit Methoden/Properties aus B erweitern und diesmal
    *   // bereits vorhandene Members überschreiben
    *   $gw$.obj.extend (A, B, true);
    *
    *   // Ausgabe: "Object B"
    *   A.logMessage (A.name);
    *   // Ausgabe: "Foo"
    *   A.logMessage (A.toString ());
    * </pre>
    *
    * @method extend
    *
    * @param  {Object} dest  Ziel-Objekt, das erweitert werden soll.
    * @param  {Object} source  Objekt, dessen Eigenschaften nach dest
    *         übernommen werden sollen.
    * @param  {boolean} [override]  Optionaler Parameter. Falls true, werden
    *         bereits vorhandene, gleichnamige Methoden und Properties in dest
    *         durch diejenigen aus source ersetzt. Andernfalls werden nur
    *         solche Methoden und Properties übernommen, die noch nicht in
    *         dest vorhanden  sind. Der Defaultwert für override ist false.
    * @param  {String[]} [propertiesFilter]  Optionaler Parameter. Hier können
    *         die Methoden/Properties angegeben werden, die von source nach
    *         dest übertragen werden sollen. Falls properties nicht angegeben
    *         ist, werden sämtliche Properties nach dest übernommen.
    *
    * @return {Object} dest
    *
    * @see #extendProto()
    */
   extend: function (dest, source, override, propertiesFilter)
   {
      if (propertiesFilter && (propertiesFilter.constructor !== Array))
         propertiesFilter = [propertiesFilter];

      for (var member in source)
      {
         if ((override || ((!dest[member]) && (dest[member] !== false))) && (!propertiesFilter || propertiesFilter.contains (member)))
              dest[member] = source[member];
      }

      if (override)
         $gw$.obj._inheritNativeMethods (dest, source);

      return dest;
   },

   /**
    * Überträgt die Methoden toString() und valueOf() aus einem Objekt
    * in ein anderes.<p>
    *
    * Die Methode behebt ein Problem im Internet Explorer. Dort werden
    * bei einer Enumeration der Members eines Objekts, z.B. mittels
    *
    * <pre>
    *   for (var prop in myObject)
    *     ...
    * </pre>
    *
    * die nativen Methoden (u.a. toString() und valueOf()) eines Objekts
    * nicht aufgelistet, selbst wenn es in der Klasse/dem Objekt eigene
    * Implementierungen dieser Methoden gibt. Das führt dazu, dass diese
    * Methoden bei der Ableitung einer Klasse mittels inheritClass()
    * oder beim Anreichern eines Objekts per extend() nicht in die
    * Zielklasse bzw. das Zielobjekt übernommen werden.<p>
    *
    * Die vorliegende Methode löst dieses Problem durch explizite
    * Zuweisung der Methoden toString() und valueOf(), sofern diese
    * in source eine eigene Implementierung haben.
    *
    * @param  {Object} dest  Zielobjekt, in das die nativen Methoden
    *         aus source übertragen werden sollen.
    * @param  {Object} source  Quellobjekt, aus dem die nativen
    *         Methoden nach dest übertragen werden sollen.
    */
   _inheritNativeMethods: function (dest, source)
   {
      /* Uns interessieren nur die Methoden toString() und valueOf() */
      var nativeMethods = ["toString", "valueOf"];

      /* Methoden aus source nach dest übertragen */
      for (var i = 0; i < nativeMethods.length; i++)
      {
         /* Namen der Methode und Funktionszeiger aus source holen */
         var name = nativeMethods[i], func = source[name];

         /* Ist Methode in source vorhanden, und ist es eine
            eigene Implementierung (also nicht die native Methode
            aus Object)? Falls ja, übernehmen wir die Methode. */
         if ($gw$.val.isFunction (func) && (func != Object.prototype[name]))
            dest[name] = func;
      }
   },

   /**
    * Erzeugt eine Kopie eines Objekts.
    *
    * Die erzeugte Kopie ist eine "flache" Kopie, d.h. die Properties des
    * Objekts werden per einfacher Zuweisung kopiert.
    *
    * @param  {Object} obj  Objekt, das kopiert werden soll.
    *
    * @return {Object} Kopie des Objekts
    */
   clone: function (obj)
   {
      return $gw$.obj.extend ({ }, obj);
   },

   /**
    * Fügt die Methoden und Properties mehrerer Objekte zu einem einzigen,
    * neuen Objekt zusammen.
    *
    * Falls es gleichnamige Methoden/Properties in den Objekten gibt, werden
    * diejenigen des jeweils zuletzt angegebenen Objekts übernommen.
    *
    * @param  {Object*} objs  Ein oder mehrere Objekte
    *
    * @return Neues Objekt mit den zusammengefassten Methoden und
    *         Eigenschaften der angegebenen Objekte.
    */
   merge: function (objs)
   {
      var result = {}, args = arguments;
      for (var i = 0, len = args.length; i < len; i++)
         $gw$.obj.extend (result, args[i], true);
      return result;
   },

   /**
    * Liefert die Keys (Namen der Members/Properties) des angegebenen Objekts.
    *
    * @param  {Object} obj  Objekt
    *
    * @return {Mixed[]} Array der Keys des Objekts
    *
    * @see #values()
    */
   keys: function (obj)
   {
      var keys = [];
      for (var member in obj)
         keys.push (member);
      return keys;
   },

   /**
    * Liefert die Values (Werte der Members/Properties) des angegebenen
    * Objekts.
    *
    * @param  {Object} obj  Objekt
    *
    * @return {Mixed[]} Array der Values des Objekts
    *
    * @see #keys()
    */
   values: function (obj)
   {
      var values = [];
      for (var member in obj)
         values.push (obj[member]);
      return values;
   },

   /**
    * Wandelt ein array-ähnliches Objekt in ein Array um.
    *
    * Falls das angegebene Objekt eine Methode toArray() besitzt, wird
    * diese aufgerufen und deren Ergebnis zurückgeliefert.
    *
    * Andernfalls muss das Objekt ein Property namens length mit der
    * Anzahl der Elemente besitzen sowie den Zugriff auf die Elemente
    * über nummerische Indizes erlauben.
    *
    * Die Methode kann z.B. verwendet werden, um das arguments-Objekt
    * einer Funktion in ein Array umzuwandeln.
    *
    * @param  {Object} obj  Objekt. Das Objekt muss entweder eine
    *         Methode namens toArray() besitzen, oder ein length-Property
    *         haben und den Zugriff auf die Elemente über nummerische
    *         Indizes ermöglichen.
    *
    * @return {Mixed[]} Array mit den Elementen aus obj
    */
   toArray: function (obj)
   {
      if (!obj)
         return [];
      if (obj.toArray) return obj.toArray();
      var len = obj.length, result = new Array (len);
      while (len--)
         result[len] = obj[len];
      return result;
   },

   /**
    * Wandelt ein Objekt in seine JSON-Notation um.
    *
    * @param  {Object} obj  Objekt
    *
    * @return {String} JSON-Repräsentierung des Objekts.
    */
   toJSON: function (obj)
   {
      return $gw$.util.JSON.toJSON (obj);
   },

   /**
    * Erzeugt einen URL-Query-String aus den Properties eines Objekts.
    *
    * @param  {Object} obj  Objekt, dessen Properties in einen Query-String
    *         gewandelt werden sollen.
    * @param  {boolean} [includeObjectMembers]  Optionaler Parameter. Falls
    *         true, werden auch Objekt-Properties in den Query-String
    *         übertragen (über obj.toString()). Der Defaultwert ist false.
    *
    * @return {String} Query-String
    *
    * @see String#toQueryParams()
    */
   toQueryString: function (obj, includeObjectMembers, enc)
   {
      var fields = [];
      if (!enc)
         enc = encodeURIComponent || escape;
      for (var key in obj)
      {
         if (!isString (key))
            continue;

         var val = obj[key];

         if (!isArray (val))
            val = [val];
         for (var i = 0; i < val.length; i++)
         {
            var v = val[i];
            if (isFunction (v) || (!includeObjectMembers && isObject (v)))
               continue;
            fields.push (key + (isDefined (v) ? "=" + enc (String (v)) : ""));
         }
      }
      return fields.join ("&");
   }
};

/*=======================================================================*/
/* Funktionen zum Prüfen von Javascript-Werten                           */
/*=======================================================================*/

if (!$gw$.val)
/**
 * Hilfsfunktionen zum Prüfen von Javascript-Werten.
 */
$gw$.val =
{
   /**
    * Prüft, ob der angegebene Wert ein Array ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x ein Array ist, sonst false.
    */
   isArray: function (x) { return x && x.constructor === Array; },

   /**
    * Prüft, ob der angegebene Wert ein Boolean ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x ein Boolean ist, sonst false.
    */
   isBoolean: function (x) { return typeof x === "boolean"; },

   /**
    * Prüft, ob der angegebene Wert ein Date-Objekt ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x ein Date-Objekt ist, sonst false.
    */
   isDate: function (x) { return x && x.constructor === Date; },

   /**
    * Prüft, ob der angegebene Wert eine Funktion ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x eine Funktion ist, sonst false.
    */
   isFunction: function (x) { return typeof x === "function"; },

   /**
    * Prüft, ob der angegebene Wert den Typ number besitzt.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x eine number-Instanz ist, sonst false.
    */
   isNumber: function (x) { return typeof x === "number" && isFinite (x); },

   /**
    * Prüft, ob der angegebene Wert ein Objekt oder eine Funktion ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x ein Objekt oder eine Funktion ist,
    *         sonst false.
    */
   isObject: function (x) { return (x && ((typeof x === "object") || $gw$.val.isFunction (x))); },

   /**
    * Prüft, ob der angegebene Wert ein String ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x ein String ist, sonst false.
    */
   isString: function (x) { return typeof x === "string"; },

   /**
    * Prüft, ob der angegebene Wert definiert ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x definiert ist, sonst false.
    */
   isDefined: function (x) { return typeof x !== "undefined"; },

   /**
    * Prüft, ob der angegebene Wert undefiniert ist.
    * @param  {Mixed} x  Javascript-Wert
    * @return {boolean} true, wenn x undefiniert ist, sonst false.
    */
   isUndefined: function (x) { return typeof x === "undefined"; }
};

/*=======================================================================*/
/* Erweiterungen der String-Klasse                                       */
/*=======================================================================*/

/**
 * Die <tt>String</tt>-Klasse wird um eine Reihe nützlicher Methoden
 * erweitert. Da die Erweiterung auf Prototype-Ebene stattfindet,
 * stehen die hier implementierten Methoden automatisch in allen
 * Strings zur Verfügung.
 *
 * @class String
 */
$gw$.obj.extend (String.prototype,
/** @lends String.prototype */
{
   /**
    * Liefert diesen String ohne führende und abschließende Leerzeichen
    * und sonstige Whitespace-Zeichen (CR, LF, TAB, etc.).
    *
    * @return {String} String ohne führende/abschließende Whitespace-Zeichen.
    */
   trim: function ()
   {
      return this.replace(/^\s+/, "").replace(/\s+$/, "");
   },

   /**
    * Prüft, ob der String mit dem angegebenen Teilstring beginnt.
    *
    * @param  {String} s  Teilstring
    *
    * @return {Boolean} true, falls der String mit s beginnt, sonst false.
    *
    * @see #endsWith()
    * @see #contains()
    */
   startsWith: function (s)
   {
      return this.indexOf (s) === 0;
   },

   /**
    * Prüft, ob der String mit dem angegebenen Teilstring endet.
    *
    * @param  {String} s  Teilstring
    *
    * @return {Boolean} true, falls der String mit s endet, sonst false.
    *
    * @see #startsWith()
    * @see #contains()
    */
   endsWith: function (s)
   {
      var i = this.lastIndexOf (s);
      return (i >= 0) && (i === (this.length - s.length));
   },

   /**
    * Prüft, ob der String den angegebenen Teilstring enthält.
    *
    * @param  {String} s  Teilstring
    *
    * @return {Boolean} true, falls der String den Teilstring s enthält, sonst false.
    *
    * @see #startsWith()
    * @see #endsWith()
    */
   contains: function (s)
   {
      return (this.indexOf (s) >= 0);
   },

   /**
    * Wandelt den String in einen Integer-Wert.
    *
    * @param  {Number} [defaultValue=0]  Defaultwert, der zurückgegeben
    *         wird, falls der String keine gültige ganze Zahl repräsentiert.
    *         Wird der Parameter nicht angegeben, gilt defaultValue == 0.
    *
    * @return {Number} Integer-Wert.
    *
    * @see #toFloatDef()
    */
   toIntDef: function (defaultValue)
   {
      var result = parseInt (this, 10);
      return (isNaN (result) ? ((defaultValue) ? defaultValue : 0) : result);
   },

   /**
    * Wandelt den String in einen Fließkomma-Wert.
    *
    * @param  {Number} [defaultValue=0]  Defaultwert, der zurückgegeben
    *         wird, falls der String keine gültige Fließkomma-Zahl
    *         repräsentiert. Wird der Parameter nicht angegeben, gilt
    *         defaultValue == 0.
    *
    * @return {Number} Float-Wert.
    *
    * @see #toIntDef()
    */
   toFloatDef: function (defaultValue)
   {
      var result = parseFloat (this);
      return (isNaN (result) ? ((defaultValue) ? defaultValue : 0) : result);
   },

   /**
    * Ersetzt die in diesem String enthaltenen HTML-Sonderzeichen '<', '>', '"'
    * und '&' durch die entsprechenden HTML-Entity-Sequenzen ("&lt;", "&gt;",
    * "&quot;" und "&amp;") und liefert den entsprechend konvertierten String
    * zurück.<p>
    *
    * Die Methode eignet sich insbesondere dazu, den Inhalt eines Strings
    * "sicher" in HTML-Dokumente zu integrieren.
    *
    * @return {String} Inhalt dieses Strings mit enkodierten HTML-Sonderzeichen.
    *
    * @see #htmlDecode()
    */
   htmlEncode: function ()
   {
      return this.replace (/&/g, "&amp;").
                  replace (/"/g, "&quot;").
                  replace (/</g, "&lt;").
                  replace (/>/g, "&gt;");
   },

   /**
    * Ersetzt die HTML-Entities &lt;", "&gt;", "&quot;" und "&amp;" dieses
    * Strings durch ihre Originalform.
    *
    * @return {String} Inhalt dieses Strings mit dekodierten HTML-Entities.
    *
    * @see #htmlEncode()
    */
   htmlDecode: function ()
   {
      return this.replace (/&amp;/g, "&").
                  replace (/&quot;/g, '"').
                  replace (/&lt;/g, "<").
                  replace (/&gt;/g, ">");
   },

   /**
    * urlDecode dekodiert diesen URLEncodierten String. Da decodeURIComponent
    * Probleme ("malformed URI sequence" Fehler) mit dem +-Zeichen in einem
    * URLEncodierten String hat, werden diese durch ein Leerzeichen ersetzte.
    * Dieses ist möglich, da ein dekodiertes Leerzeichen wieder eine Leerzeichen
    * ergibt.
    * 
    * Da decodeURIComponent ein UTF-8 enkodierten String erwartet, wird für
    * urlDecode auch eine UTF-8 Enkodierung vorausgesetzt. Sollte dies nicht
    * der Fall sein, könnte es zu Darstellungs-/Dekodierungs-Fehlern kommen.
    *
    * Sollte (Beispielweise bei älteren Browsern) kein decodeURIComponent
    * definiert sein, wird unescape anstelle von decodeURIComponent benutzt.
    * In diesem Fall würde es allerdings mit der erwarteten UTF-8-Kodierung 
    * definitiv zu Darstellungs-/Dekodierungs-Fehlern kommen.
    *
    * @return {String} Inhalt dieses Strings mit dekodierten HTML-Entities.
    *
    * @see #htmlEncode()
    */
   urlDecode: function()   
   {
      if ($gw$.val.isFunction(decodeURIComponent))
      {
         try
         {
            return decodeURIComponent(this.replace (/\+/g," "));
         }
         catch( ex ) 
         {
             $gw$.log(ex.toString() + " - value: "+ this);
             return unescape(this);
         }         
      }
      else
         return unescape(this);
   },

   /**
    * Interpretiert diesen String als HTTP-Query-String ("Name=Wert"-Paare,
    * getrennt durch "&") und liefert die enthaltenen Parameter als Objekt
    * mit Key/Value-Paaren zurück.<p>
    *
    * Falls der String mit einem Fragezeichen "?" beginnt, wird das
    * erste Zeichen ignoriert.
    *
    * @param  {String} [separator="&"]  Trennzeichen, mit dem die
    *         Name/Wert-Paare im String getrennt sind. Der Parameter ist
    *         optional. Wird er nicht angegeben, gilt separator == "&".
    *
    * @return {Object} Objekt mit den Parametern des Query-Strings.
    */
   toQueryParams: function (separator)
   {
      /* Return-Objekt erzeugen */
      var result = { },
      /* Führendes Fragezeichen am Anfang des Query-Strings ignorieren */
      s = this.startsWith ("?") ? this.substr (1) : this,
      /* Array der Name/Wert-Paare erzeugen */
      par = s.split (separator || "&"),
      /* Anzahl der Query-Parameter merken */
      count = par.length,
      /* Sonstige lokale Variablen */
      i, c, field, name, value, currentValue;

      /* Query-Parameter in Result-Objekt übertragen */
      for (i = 0; i < count; i++)
      {
         /* Name und Wert trennen */
         field = par[i].split ("=");
         /* Parameter-Name vorhanden? */
         if (field[0])
         {
            /* Namen des Parameters und Anzahl der verbleibenden
               Array-Elemente in field[] merken. */
            name = field.shift (), c = field.length;

            /* Parameter-Wert holen. Dabei müssen wir den Fall behandeln,
               dass im Wert selbst das Zeichen "=" vorkommt. Falls ja,
               müssen wir die in field[] getrennten Teile des Werts
               wieder korrekt zusammenfügen. */
            value = (c == 1) ? field[0] : ((c > 0) ? field.join ("=") : null);

            /* Wert definiert? Falls ja, den Wert dekodieren */
            if (value)
               value = value.urlDecode(); 

            /* Existiert bereits ein Wert für diesen Key? */
            currentValue = result[name];
            if ($gw$.val.isDefined (currentValue))
            {
               /* Ja, dann wandeln wir den Wert bei Bedarf in ein Array
                  um. Auf diese Weise werden mehrere Werte für denselben
                  Parameter-Namen automatisch in ein Array umgewandelt.
                  Diese Funktionalität gewährleistet eine Kompatibilität
                  zur umgekehrten Methode obj.toQueryParams(). */
               if ($gw$.val.isDefined (value))
               {
                  /* Nur übernehmen, wenn neuer Wert definiert ist */
                  if (!$gw$.val.isArray (currentValue))
                     currentValue = [currentValue];
                  currentValue.push (value);
               }
               value = currentValue;
            }
            
            /* Name/Wert in Result-Objekt speichern */
            result[name] = value;
         }
      }
      return result;
   },

   /**
    * Ersetzt Makros (benannte Platzhalter-Strings) in einem String durch
    * die angegebenen Werte. Die Makros müssen dem in macroPattern angegeben
    * Muster entsprechen, das als regulärer Ausdruck anzugeben ist.
    *
    * In der Praxis empfiehlt sich die Verwendung der Klasse Template
    * (siehe dort), die als Container für ein Template mit Makros dient
    * und die Expansion dieses Templates über die Methode Template.expand()
    * ermöglicht.
    *
    * @param  {String|RegExp} [macroPattern]  Format der Makros, definiert
    *         als regulärer Ausdruck.<p>
    *
    *         Der reguläre Ausdruck muss zwingend drei Teilausdrücke
    *         enthalten. Dabei muss der erste Teilausdruck alle Zeichen bis
    *         zum Beginn eines Makros selektieren. Der zweite Teilausdruck
    *         muss das gesamte Makro inklusive Präfix und Suffix umfassem,
    *         und der dritte Teilausdruck muss den Namen des Makros
    *         selektieren.<p>
    *
    *         Beispiele für verschiedene Makro-Notationen:
    *         <ul>
    *           <li> Notation <tt>"#{Name}": /(^|.|\r|\n)(#\{(.*?)\})/</tt>
    *           <li> Notation <tt>"${Name}": /(^|.|\r|\n)(\$\{(.*?)\})/</tt>
    *           <li> Notation <tt>"[#Name]": /(^|.|\r|\n)(\[#(.*?)\])/</tt>
    *         </ul>
    *
    * @param  {Object} values  Object mit den Namen und Werten der zu
    *         ersetzenden Makros.<p>
    *
    *         Beispiel: <tt>{ name: "Smith", firstname: "John" }</tt>
    *
    * @see $gw$.util.Template
    */
   expandMacros: function (macroPattern, values)
   {
      var result = "", s = this, m;

      /* Template verarbeiten */
      while (s.length > 0)
      {
         /* Nächstes Makro suchen */
         if ((m = s.match (macroPattern)) != null)
         {
            /* Text bis zum Beginn des Makros übernehmen */
            result += s.slice (0, m.index);
            /* Escape-Sequenz, d.h. keine Makro-Expansion (1:1-Übernahme)?
               Falls ja, dann einfach Platzhalter ohne Übersetzung übernehmen. */
            if (m[1] == "\\")
               result += m[2];
            else
            {
               /* Makro ersetzen. Alles bis zum Makro übernehmen. */
               result += m[1];
               /* Wert aus values-Objekt anhand des Makronamens holen */
               var v = values[m[3]];
               if (v != null)
                  result += String (v);
            }
            s = s.slice (m.index + m[0].length);
         }
         else
         {
            result += s;
            break;
         }
      }

      return result;
   },

   /**
    * Liefert die JSON-Darstellung dieses Strings.
    *
    * @return {String} JSON-Repräsentierung dieses Strings
    */
   toJSON: function ()
   {
      /* Zeichen, die übersetzt werden müssen */
      var ctrlChars = /["\\\x00-\x1f\x7f-\x9f]/g;

      /* Zeichen, die sich in JS-Strings als Escape-Sequenz darstellen lassen */
      var specialCharTrans =
      {

         "\b": "\\b",
         "\t": "\\t",
         "\n": "\\n",
         "\f": "\\f",
         "\r": "\\r",
         '"' : '\\"',
         "\\": "\\\\"
      };

      /* Wenn der String keinerlei Zeichen aus ctrlChars enthält, können
         wir ihn einfach in doppelte Anführungszeichen einschließen und
         zurückgeben. Ansonsten müssen wir Steuer- und Sonderzeichen in
         Escape-Sequenzen oder Zeichencode-Notation übersetzen. */
      if (!ctrlChars.test (this))
         return '"' + this + '"';
      else
         return '"' + this.replace (ctrlChars, function (ch) {
            var c = specialCharTrans[ch];
            if (c)
               return c;
            c = ch.charCodeAt ();
            return "\\u00" + Math.floor (c / 16).toString (16) +
                                        (c % 16).toString (16);
         }) + '"';
   }
});

/*=======================================================================*/
/* Erweiterungen der Array-Klasse                                        */
/*=======================================================================*/

$gw$.obj.extend (Array.prototype,
/** @lends Array.prototype */
{
   /**
    * Lieferte eine Kopie dieses Arrays.
    *
    * Die zurückgegebene Kopie ist ein eigenständiges Array. Die kopierten
    * Array-Elemente sind jedoch lediglich eine "flache" Kopie, d.h. falls
    * die Elemente Arrays oder Objekte enthalten, werden lediglich deren
    * Referenzen kopiert.
    *
    * @return Kopie des Arrays
    */
   clone: function ()
   {
      return [].concat (this);
   },

   /**
    * Liefert die Inhalte dieses Arrays als "flaches" (eindimensionales)
    * Array. Dabei werden in dem Array enthaltene verschachtelte Arrays
    * expandiert und deren Elemente sequenziell in das zurückgegebene
    * flache Array übertragen.<p>
    *
    * Diese Methode ist z.B. nützlich, wenn eine Funktion wahlweise einen
    * einzelnen Array-Parameter oder eine variable Parameterliste
    * unterstützt.<p>
    *
    * Beispiele:
    * <pre>
    *   var a;
    *   a = [1, 2, 3].flatten ();       // a == [1, 2, 3];
    *   a = [[1, 2], 3].flatten ();     // a == [1, 2, 3];
    *   a = [[[1], 2], 3].flatten ();   // a == [1, 2, 3];
    *
    *   // Funktion mit variabler Parameterliste
    *   function test ()
    *   {
    *      var args = $gw$.obj.toArray (arguments).flatten ();
    *      console.log (args);
    *   }
    *
    *   // Dieser Aufruf...
    *   test ("A", "B", "C");
    *   // ...erzielt dasselbe Ergebnis wie...
    *   test (["A", "B", "C"]);
    *   // oder...
    *   test ([["A"], ["B"]], "C");
    * </pre>
    */
   flatten: function ()
   {
      var result = [];

      this.forEach (function (item)
         {
            if ($gw$.val.isArray (item))
               result = result.concat (item.flatten ());
            else
               result.push (item);
         });

      return result;
   },

   /**
    * Liefert den Index eines Elements in diesem Array.
    *
    * @param  item  Gesuchtes Element
    *
    * @return Index des (ersten) Elements, das mit item übereinstimmt
    *         bzw. -1, falls das Element nicht gefunden wurde.
    *
    * @see #lastIndexOf()
    * @see #contains()
    */
   indexOf: function (item)
   {
      for (var i = 0, len = this.length; i < len; i++)
         if (this[i] == item) return i;
      return -1;
   },

   /**
    * Liefert den letzten Index eines Elements in diesem Array.
    *
    * @param  item  Gesuchtes Element
    *
    * @return Index des (letzten) Elements, das mit item übereinstimmt
    *         bzw. -1, falls das Element nicht gefunden wurde.
    *
    * @see #indexOf()
    * @see #contains()
    */
   lastIndexOf: function (item)
   {
      for (var i = this.length - 1; i >= 0; i--)
         if (this[i] == item) return i;
      return -1;
   },

   /**
    * Prüft, ob das Array ein bestimmtes Element enthält.
    *
    * @param  item  Gesuchtes Element
    *
    * @return true, falls das Array das Element enthält, sonst false.
    *
    * @see #indexOf()
    * @see #lastIndexOf()
    */
   contains: function (item)
   {
       return this.indexOf (item) >= 0;
   },

   /**
    * Ruft die angegebene Funktion für jedes Element in diesem Array auf.
    *
    * Optional lässt sich ein Kontext (für "this") angeben, in dem der
    * Aufruf der angegebenen Callback-Funktion erfolgt.
    *
    * @param  callback  Funktion, die für die Array-Elemente aufgerufen
    *         wird. Die Funktion wird mit jeweils drei Parametern
    *         aufgerufen: 1) dem jeweiligen Array-Element, 2) dem Index
    *         des Elements und 3) dem Array-Objekt.
    * @param  context  Optionaler Kontext für "this", der beim Aufruf
    *         der Callback-Funktion gelten soll.
    */
   forEach: function (callback, context)
   {
      for (var i = 0, len = this.length; i < len; i++)
         callback.call (context, this[i], i, this);
   }
});

/*=======================================================================*/
/* Erweiterungen der Function-Klasse                                     */
/*=======================================================================*/

$gw$.obj.extend (Function.prototype,
/** @lends Function.prototype */
{
   /**
    * Liefert ein Binding der Funktion für einen bestimmten Objekt-Kontext
    * und ermöglicht die Angabe von Default-Aufrufparametern.<p>
    *
    * Die Methode liefert einen Wrapper für diese Funktion, der dafür sorgt,
    * dass die Funktion im Kontext eines vorgegebenen Objekts aufgerufen
    * wird. Der Kontext (auch: Execution Scope) bestimmt den Inhalt von
    * "this" während des Aufrufs der Funktion.<p>
    *
    * Dadurch wird es z.B. möglich, die Referenz auf eine Objektmethode
    * zu speichern und die Methode später (aus einem anderen Kontext
    * heraus) aufzurufen, wobei "this" auf den ursprünglichen Scope
    * verweist. Ein Beispiel hierfür ist der verzögerte Aufruf einer
    * Objektmethode mittels window.setTimeout(). Ohne ein explizites
    * Binding würde die Methode immer im Kontext des window-Objekts
    * aufgerufen, d.h. innerhalb der aufgerufenen Methode wäre
    * this == window.<p>
    *
    * Darüber hinaus ermöglicht getBinding() die Angabe von vorgegebenen
    * Funktionsargumenten, die beim Aufruf der Wrapper-Funktion automatisch
    * mit übergeben werden.
    *
    * @example
    *   // Globale Variable im Scope des window-Objekts
    *   var counter = 1;
    *
    *   // MyObj-Objekt
    *   var MyObj = {
    *     // Lokale Variable im Scope von MyObj
    *     counter: 2,
    *
    *     test: function ()
    *     {
    *        alert (this.counter);
    *     }
    *   };
    *
    *   // Beliebiges anderes Objekt mit einem Property "counter"
    *   var Data = { counter: 3 };
    *
    *   // Hier wird MyObj.test() im Scope von window ausgeführt.
    *   // Dies führt zur Ausgabe von "1".
    *   setTimeout (MyObj.test, 1000);
    *
    *   // Durch das Binding an "MyObj" wird test() im Kontext von
    *   // MyObj ausgeführt. Die Ausgabe lautet hier "2".
    *   setTimeout (MyObj.test.getBinding (MyObj), 1000);
    *
    *   // Das Binding kann für ein beliebiges Objekt erfolgen, hier
    *   // für Data. Folge: Ausgabe von "3".
    *   var fn = MyObj.test.getBinding (Data);
    *   fn ();
    *
    *   // Auch kann man das Binding gut für anonyme Funktionen (Closures)
    *   // verwenden. Das u.g. Beispiel demonstriert außerdem die Vorgabe
    *   // von Funktionsargumenten, die bei Aufruf des Bindings automatisch
    *   // mit übergeben werden. Ausgabe hier: "1003"
    *   fn = function (offset) { alert (this.counter + offset); }.getBinding (Data, 1000);
    *   fn ();
    *
    *   // getBinding() kann auch verwendet, um beim Aufruf einer Funktion
    *   // bestimmte Parameter automatisch zu übergeben.
    *   function test (a, b, c)
    *   {
    *      alert (a + "," + b + "," + c);
    *   }
    *   // Binding für die test()-Funktion holen. Kontext hier "null", da
    *   // nicht benötigt. Bei Aufruf des Bindings werden die Parameter "1"
    *   // und "2" automatisch an test() übergeben.
    *   fn = test.getBinding (null, "1", "2");
    *   // Binding aufrufen. Ausgabe ist: "1,2,3".
    *   fn ("3");
    *
    * @param  {Object} context  Kontext (Scope), in dem die Funktion beim
    *         Aufruf des Bindings ausgeführt wird.
    * @param  {Mixed*} args  Zusätzlich zu context kann eine beliebige Anzahl
    *         von Funktionsargumenten angegeben werden. Diese Parameter werden
    *         beim Aufruf der zurückgegebenen Wrapper-Funktion automatisch
    *         als Parameter an die Funktion übergeben. Die hier definierten
    *         Parameter werden als erste Parameter (noch vor den explizit
    *         beim Aufruf der Wrapper-Funktion angegebenen Parametern) an
    *         die Funktion übergeben.
    *
    * @return {Function} Referenz auf eine Wrapper-Funktion dieser Funktion,
    *         die bei Aufruf im angegebenen Kontext ausgeführt wird.
    */
   getBinding: function (context, arg0)
   {
      /* Funktions-Argumente holen */
      var args = $gw$.obj.toArray (arguments);

      /* Falls context nicht definiert ist und auch keine zusätzlichen
         Aufruf-Argumente angegeben wurden, liefern wir eine unveränderte
         Referenz auf die Funktion zurück. */
      if ((args.length <= 1) && (isUndefined (context)))
         return this;

      /* Funktions-Referenz in lokale Variable übertragen. Dies ist
         nötig, damit die Funktion in dem Closure (s.u.) referenziert
         werden kann. */
      var fn = this;

      /* context-Parameter aus args-Array entfernen */
      args.shift ();

      /* Binding zurückliefern */
      return function () { return fn.apply (context, args.concat ($gw$.obj.toArray (arguments))); };
   },

   /**
    * Liefert einen Wrapper dieser Funktion, bei dem ein oder mehrere
    * Parameter vordefiniert sind.
    *
    * Die Funktionsweise entspricht getBinding(), jedoch ohne den dort
    * verwendeten Parameter context.
    *
    * @example
    *   function test (msg)
    *   {
    *      alert (msg);
    *   }
    *
    *   // Wrapper für test() mit vordefiniertem msg-Parameter
    *   var fn = test.getPrefilled ("Hello world");
    *   // Ausgabe: "Hello world"
    *   fn ();
    *
    * @param  arg0  Ein oder mehrere Parameter, die beim Aufruf der
    *         hier zurückgegebenen Wrapper-Funktion automatisch an
    *         diese Funktion weitergegeben werden.
    *
    * @return Referenz auf einen Wrapper dieser Funktion.
    */
   getPrefilled: function (arg0)
   {
      /* Funktions-Argumente holen */
      var args = $gw$.obj.toArray (arguments);

      /* Original-Funktion zurückgeben, falls keine Parameter angegeben sind */
      if (!args.length)
         return this;

       /* Funktions-Referenz in lokale Variable übertragen. Dies ist
         nötig, damit die Funktion in dem Closure (s.u.) referenziert
         werden kann. */
      var fn = this;

      /* Wrapper zurückliefern */
      return function () { return fn.apply (this, args.concat ($gw$.obj.toArray (arguments))); };
   },

   /**
    * Ruft die Funktion nach der angegebenen Zeitspanne auf.
    *
    * Die Methode verwendet window.setTimeout(), um die Funktion nach der
    * in delay angegebenen Zeit aufzurufen. Die nach delay angegebenen
    * Parameter werden dabei weitergereicht.
    *
    * @example
    *   function test (msg)
    *   {
    *      alert (msg);
    *   }
    *
    *   // Funktion test() nach 3 Sekunden mit Parameter "Hello world" aufrufen
    *   test.callDelayed (3000, "Hello world");
    *
    * @param  delay  Wartezeit in Millisekunden
    * @param  arg0,...  Ein oder mehrere Parameter, die beim Aufruf der
    *         Funktion verwendet werden sollen.
    *
    * @return Timer-ID (von window.setTimeout()). Diese ID kann verwendet
    *         werden, um die Ausführung der Funktion mittels clearTimeout()
    *         zu verhindern.
    *
    * @see #callWhenIdle()
    */
   callDelayed: function (delay, arg0)
   {
      /* Funktions-Argumente holen */
      var args = $gw$.obj.toArray (arguments);

      /* Verzögerung in Millisekunden */
      delay = (delay || 1);

      /* Funktions-Referenz in lokale Variable übertragen. Dies ist
         nötig, damit die Funktion in dem Closure (s.u.) referenziert
         werden kann. */
      var fn = this;

      /* delay-Parameter aus args-Array entfernen */
      args.shift ();

      /* Verzögerte Ausführung über setTimeout() auslösen und die
         Timer-ID zurückgeben. */
      return window.setTimeout ( function () { return fn.apply (fn, args); }, delay);
   },

   /**
    * Ruft die Funktion auf, sobald der Javascript-Interpreter alle
    * wartenden Anweisungen verarbeitet hat.
    *
    * Die Methode verwendet intern callDelayed() mit einer sehr kurzen
    * Zeitspanne.
    *
    * @example
    *   function test (msg)
    *   {
    *      alert (msg);
    *   }
    *
    *   // Funktion test() mit "Hello world" aufrufen, sobald der Interpreter
    *   // alle aktuell ausstehenden Anweisungen verarbeitet hat
    *   test.callWhenIdle ("Hello world");
    *
    * @param  arg0,...  Ein oder mehrere Parameter, die beim Aufruf der
    *         Funktion verwendet werden sollen.
    *
    * @see #callDelayed()
    */
   callWhenIdle: function (arg0)
   {
      this.callDelayed.getPrefilled (1).apply (this, arguments);
   }
});

/*=======================================================================*/
/* Erweiterungen der Date-Klasse                                         */
/*=======================================================================*/

$gw$.obj.extend (Date.prototype,
/** @lends Date.prototype */
{

   /**
    * Liefert die JSON-Darstellung dieses Date-Objekts.
    *
    * @return Datum/Uhrzeit-String im ISO-Format.
    */
   toJSON: function ()
   {
      function f2 (n)
      {
         return n < 10 ? '0' + n : n;
      }

      return this.getUTCFullYear ()       + '-' +
             f2 (this.getUTCMonth () + 1) + '-' +
             f2 (this.getUTCDate ())      + 'T' +
             f2 (this.getUTCHours ())     + ':' +
             f2 (this.getUTCMinutes ())   + ':' +
             f2 (this.getUTCSeconds ())   + 'Z';
   }
});

/*=======================================================================*/
/* Dynamisches Laden von Scripts und CSS-Dateien                         */
/*=======================================================================*/

/**
 * Die <tt>Loader</tt>-Klasse ermöglicht das dynamische Laden von
 * Javascript- und CSS-Dateien und stellt Hilfsfunktionen bereit,
 * damit ein Javascript-Modul den eigenen URL (Pfad) sowie die
 * im URL enthaltenen Query-Parameter ermitteln kann.
 *
 * @class $gw$.util.Loader
 */
$gw$.util.Loader = function ()
{
   /**
    * Sucht das Script-Element, über das die angegebene Javascript-Datei
    * in das aktuelle Dokument eingebunden ist.<p>
    *
    * Beispiel:
    * @example
    * <pre>
    *   // Referenz auf Loader holen
    *   var loader = $gw$.util.Loader;
    *   // Script "globals.js" suchen
    *   var scriptEl = loader.findScript ("globals.js");
    *
    * </pre>
    *
    * @public
    *
    * @param  {String} fileName  Dateiname der Javascript-Datei. Ein
    *         Host oder Pfad muss hier nicht angegeben werden. Der
    *         Dateiname darf die Wildcards "?" und "*" enthalten.
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         Element gesucht werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn sich das Element nicht in
    *         dem aktuellen Window-Objekt befindet.
    *
    * @return DOM-Element des Scripts bzw. <tt>null</tt>, falls kein
    *         Script mit dem angegebenen Dateinamen existiert.
    */
   function findScript (fileName, win)
   {
      return findElementByFileName ("script", "src", fileName, win);
   }

   /**
    * Sucht das CSS-Link-Element, über das die angegebene CSS-Datei
    * in das aktuelle Dokument eingebunden ist.<p>
    *
    * @public
    *
    * @param  {String} fileName  Dateiname der CSS-Datei. Ein Host oder
    *         Pfad muss hier nicht angegeben werden. Der Dateiname darf
    *         die Wildcards "?" und "*" enthalten.
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         Element gesucht werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn sich das Element nicht in
    *         dem aktuellen Window-Objekt befindet.
    *
    * @return DOM-Element des Scripts bzw. <tt>null</tt>, falls kein
    *         Script mit dem angegebenen Dateinamen existiert.
    */
   function findCSS (fileName, win)
   {
      return findElementByFileName ("link", "href", fileName, win);
   }

   /**
    * Sucht ein DOM-Element eines bestimmten Typs, das ein Attribut mit
    * dem angegebenen Dateinamen besitzt.<p>
    *
    * Über diese Methode kann man z.B. das Script-Element finden, über
    * das eine bestimmte Javascript-Datei geladen wurde.<p>
    *
    * Die Suche findet den Dateinamen unabhängig davon, unter welchem
    * Pfad das Element die Datei referenziert.
    *
    * @param  {String} type  Element-Typ (z.B. "script" oder "link").
    * @param  {String} urlAttr  Name des Attributs des angegebenen
    *         Element-Typs, in dem sich der URL befindet. Bei
    *         script-Elementen ist dies z.B. "src", bei link-Elementen
    *         ist es "href".
    * @param  {String} fileName  Dateiname
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         Element gesucht werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn sich das Element nicht in
    *         dem aktuellen Window-Objekt befindet.
    *
    * @return Gefundenes DOM-Element bzw. <tt>null</tt>, falls kein
    *         Element mit dem angegebenen Dateinamen existiert.
    */
   function findElementByFileName (type, urlAttr, fileName, win)
   {
      var w = win || window, doc = w.document,
          items = doc.getElementsByTagName (type),
          matcher = getURLMatcher (fileName);

      for (var i = 0; (items) && (i < items.length); i++)
      {
         var item = items[i], url = item[urlAttr];
         if (url && matcher.test (url))
            return item;
      }
      return null;
   }

   /**
    * Liefert eine Regular Expression zum Matching eines URLs für
    * einen bestimmten Dateinamen.<p>
    *
    * Das zurückgegebene RegExp-Objekt enthält drei Teilausdrücke:
    * <ol>
    *   <li> Pfad (inklusive Protokoll und Host, soweit vorhanden)
    *   <li> Dateiname
    *   <li> Query-Parameter (soweit vorhanden)
    * </ol>
    *
    * Damit ist die RE auch zum Splitting eines URLs in die o.g.
    * drei Komponenten geeignet.
    *
    * @param  {String} fileName  Dateiname. Der Dateiname darf die
    *         Wildcards "?" und "*" enthalten, die in der Methode
    *         automatisch in die RE-Entsprechungen "." und ".*"
    *         übersetzt werden.
    *
    * @return {RegExp} Regulärer Ausdruck
    */
   function getURLMatcher (fileName)
   {
      /*
       * Zunächst den Dateinamen RegExp-kompatibel machen
       */
      fileName = fileName.
                   /* "." durch "\." ersetzen */
                   replace (/\./g,   "\\.").
                   /* "?" durch "[^?]" ersetzen */
                   replace (/\?/g,   "[^?]").
                   /* Alleinstehendes "*" durch "[^\/?]+" ersetzen, damit
                      mindestens ein Zeichen für den Dateinamen selektiert
                      wird */
                   replace (/^\*$/,  "[^\/?]+").
                   /* Sonstiges "*" durch "[^?]*" ersetzen */
                   replace (/\*/g,   "[^?]*");

      /*
       * RegExp mit drei Teilausdrücken erzeugen.
       *
       * Der erste Teilausdruck (^|.*\/) selektiert den Pfad, soweit
       * vorhanden, inklusive des abschließenden Slash-Zeichens "/".
       *
       * Der zweite Teilausdruck selektiert den angegebenen Dateinamen.
       *
       * Der dritte Teilausdruck (?:$|[?](.*)) selektiert die durch
       * ein Fragezeichen "?" angehängten Query-Parameter. Dieser
       * Teilausdruck besteht aus einem äußeren (inklusive "?") und
       * einem inneren Teilausdruck (.*), der nur die Query-Parameter
       * ohne führendes "?" selektiert. Da der äußere Teilausdruck
       * als non-capturing deklariert ist (mittels "(?:...)"), wird
       * dieser beim Ausführen der RegExp nicht zurückgegeben, sondern
       * nur der innere Teilausdruck.
       */
      return new RegExp ("(^|.*\/)(" + fileName + ")(?:$|[?](.*))");
   }

   /**
    * Interne Funktion. Ermittelt den URL eines Elements (Script, CSS,
    * u.a.) und liefert dessen Bestandteile als Pfad, Dateiname und
    * Query-Parameter.
    *
    * @param  {String|Element} el  DOM-Element oder Dateiname
    *         des Elements.
    * @param  {String} [type]  Typ des Elements. Mögliche Werte sind
    *         z.B. "script" oder "link". Der Parameter ist optional.
    *         Wird er nicht angegeben, gilt <tt>type == "script"</tt>.
    * @param  {String} urlAttr  Name des Attributs des angegebenen
    *         Elements, in dem sich der URL befindet. Bei script-Elementen
    *         ist dies z.B. "src", bei link-Elementen ist es "href".
    *         Der Parameter ist optional. Wird er nicht angegeben,
    *         verwendet die Funktion das für den Typ passende Attribut.
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         Element gesucht werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn sich das Element nicht in
    *         dem aktuellen Window-Objekt befindet.
    *
    * @return {Object} Objekt mit folgenden String-Properties:<br />
    *         <pre>
    *           url: Vollständiger URL inkl. Parameter
    *           path: Pfad des URLs (ggf. inkl. Protokoll und Host)
    *           fileName: Dateiname
    *           params: Parameter
    *         </pre>
    *         Falls das Element nicht gefunden wurde oder keinen URL
    *         besitzt, wird <tt>null</tt> zurückgegeben.
    *
    * @private
    */
   function getElementURLItems (el, type, urlAttr, win)
   {
      type = type || "script";
      if (!urlAttr)
         urlAttr = (type == "script") ? "src" : "href";

      if (typeof el === "string")
         el = findElementByFileName (type, urlAttr, el, win);

      if (el)
      {
         var matcher = getURLMatcher ("*"),
             tok = matcher.exec (el[urlAttr]);
         if (tok && (tok.length >= 4))
         {
            return { url: tok[0], path: tok[1], fileName: tok[2], params: tok[3] };
         }
      }
      return null;
   }

   /**
    * Ermittelt den URL eines Script-Elements und liefert dessen Bestandteile
    * (Pfad, Dateiname und Query-Parameter).
    *
    * @param  {String|Element} script  DOM-Element oder Dateiname
    *         des Scripts.
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         Element gesucht werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn sich das Element nicht in
    *         dem aktuellen Window-Objekt befindet.
    *
    * @return {Object} Objekt mit folgenden String-Properties:<br />
    *         <pre>
    *           path: Pfad des URLs (ggf. inkl. Protokoll und Host)
    *           fileName: Dateiname
    *           params: Parameter
    *         </pre>
    *         Falls das Element nicht gefunden wurde oder keinen URL
    *         besitzt, wird <tt>null</tt> zurückgegeben.
    */
   function getScriptURL (script, win)
   {
      return getElementURLItems (script, "script");
   }

   /**
    * Liefert die Query-Parameter des angegebenen Scripts.
    *
    * @example
    *   <script type="text/javascript" src="http://www.abc.com/js/main.js?id=4711">
    *   ...
    *   var params = $gw$.util.Loader.getScriptParams ("main.js");
    *   $gw$.log (params); // -> { id: "4711" }
    *
    * @param  {String|Element} script  DOM-Script-Element oder Dateiname
    *         des Scripts.
    *
    * @return {Object} Query-Parameter bzw. <tt>null</tt>, falls das
    *         Script undefiniert ist oder nicht gefunden wurde.
    *
    * @see #getScriptPath()
    * @see #getScriptURL()
    */
   function getScriptParams (script)
   {
      var url = getScriptURL (script);
      if (url)
         return (url.params) ? url.params.toQueryParams () : { };
      return null;
   }

   /**
    * Liefert den Pfad des angegebenen Scripts.<p>
    *
    * @example
    *   <script type="text/javascript" src="http://www.abc.com/js/main.js">
    *   ...
    *   var path = $gw$.util.Loader.getScriptPath ("main.js");
    *   $gw$.log (path); // -> "http://www.abc.com/js/"
    * 
    *
    * @param  {String|Element} script  DOM-Script-Element oder Dateiname
    *         des Scripts.
    *
    * @return {Object} Pfad bzw. <tt>null</tt>, falls das Script undefiniert
    *         ist oder nicht gefunden wurde.
    *
    * @see #getScriptParams()
    * @see #getScriptURL()
    */
   function getScriptPath (script)
   {
      var url = getScriptURL (script);
      if (url)
         return (url.path) ? url.path : "";
      return null;
   }

   /**
    * Lädt das angegebene Script.
    *
    * @param  {String} url  URL des Scripts
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         Script geladen werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn das Script in ein anderes
    *         als das aktuelle Window-Objekt eingefügt werden soll.
    * @param  {String} [charset]  Zeichensatz der Script-Datei, z.B.
    *         "UTF-8". Der Parameter ist optional.
    *
    * @see #loadCSS()
    */
   function loadScript (url, win, charset)
   {              
		var doc = (win) ? win.document : document;
		var script = doc.createElement ("script");
		script.src = url;
		if (charset)
		   script.charset = charset;
		doc.getElementsByTagName ("head")[0].appendChild (script);
   }

   /**
    * Lädt die angegebene CSS-Datei.
    *
    * @param  {String} url  URL der CSS-Datei
    * @param  {Object} [win]  Window-Objekt, in dessen Document-Objekt das
    *         CSS geladen werden soll. Dieser Parameter ist optional
    *         und kann verwendet werden, wenn die Styles auf ein anderes
    *         als das aktuelle Window-Objekt angewendet werden sollen.
    * @param  {String} [charset]  Zeichensatz der CSS-Datei, z.B.
    *         "UTF-8". Der Parameter ist optional.
    *
    * @see #loadScript()
    */
   function loadCSS (url, win, charset)
   {
		var doc = (win) ? win.document : document;
		var type = "text/css";
		if (charset)
		   type += ";" + charset;
		var css = doc.createElement ("link");
		css.type = type;
		css.rel = "stylesheet";
		css.href = url;
		css.media = "screen";
		doc.getElementsByTagName ("head")[0].appendChild (css);
   }

   return {
      findCSS: findCSS,
      findScript: findScript,
      getScriptURL: getScriptURL,
      getScriptParams: getScriptParams,
      getScriptPath: getScriptPath,
      loadScript: loadScript,
      loadCSS: loadCSS
   };
}();

/*=======================================================================*/
/* Templates                                                             */
/*=======================================================================*/

/**
 * Ein Template ist ein Text mit Makros (benannte Platzhalter-Strings),
 * die durch beliebige Werte ersetzt werden können.<p>
 *
 * Standardmäßig haben die Makros in der Template-Klasse das folgende
 * Format: ${name}.<p>
 *
 * Optional kann ein beliebiges anderes Platzhalter-Format verwendet
 * werden, das als regulärer Ausdruck anzugeben ist (siehe unten).
 *
 * @class Template
 *
 * @param  template  Template-Text mit Makros
 * @param  macroPattern  Format der Makros als regulärer Ausdruck. Der
 *         Parameter ist optional. Wird er nicht angegeben, dann wird das
 *         Format "${name}" verwendet. Ansonsten muss der hier angegebene
 *         reguläre Ausdruck zwingend drei Teilausdrücke enthalten. Dabei
 *         muss der erste Teilausdruck alle Zeichen bis zum Beginn eines
 *         Makros selektieren. Der zweite Teilausdruck muss das gesamte
 *         Makro inklusive Präfix und Suffix umfassen, und der dritte
 *         Teilausdruck muss den Namen des Makros selektieren.<p>
 *
 *         Beispiele für verschiedene Makro-Notationen:
 *         <ul>
 *           <li> Notation "#{Name}": <tt>/(^|.|\r|\n)(#\{(.*?)\})/</tt>
 *           <li> Notation "${Name}": <tt>/(^|.|\r|\n)(\$\{(.*?)\})/</tt>
 *           <li> Notation "[#Name]": <tt>/(^|.|\r|\n)(\[#(.*?)\])/</tt>
 *         </ul>
 *
 * @see String#expandMacros()
 */
$gw$.util.Template = function (template, macroPattern)
{
   /**
    * Expandiert die angegebenen Werte in diesem Template und liefert
    * den expandierten Template-Text zurück.<p>
    *
    * Beispiel:
    * @example
    *   // Template erzeugen
    *   var t = new $gw$.util.Template ("Hello ${firstname} ${lastname}!");
    *   // Template expandieren. Ausgabe: "Hello Karl Meier!".
    *   console.log (t.expand ({ firstname: "Karl", lastname: "Meier" }));
    *
    * @public
    *
    * @param  {Object} values  Makro-Werte
    *
    * @return Expandiertes Template
    */
   function expand (values)
   {
      return this.template.expandMacros (this.macroPattern, values);
   }

   this.template = String (template);
   this.macroPattern = (macroPattern) ? macroPattern : /(^|.|\r|\n)(\$\{(.*?)\})/;
   this.expand = expand;
};

/*=======================================================================*/
/* Timer                                                                 */
/*=======================================================================*/

/**
 * Die Klasse Timer ermöglicht das periodische Aufrufen einer Funktion
 * in einem vorgegebenen Zeit-Intervall.
 *
 * Intern verwendet Timer die nativen Methoden window.setInterval() und
 * window.clearInterval, besitzt jedoch eine Reihe von Vorteilen, u.a.:
 *
 * <ul>
 *   <li> Es wird sichergestellt, dass die angegebene Callback-Funktion
 *        niemals mehrfach parallel aufgerufen wird, falls die Ausführung
 *        des Codes in der Callback-Funktion länger als der angegebene
 *        Wiederholungs-Intervall dauert.
 *
 *   <li> Es ist möglich, einen Kontext anzugeben (this), in dem die
 *        Callback-Funktion aufgerufen wird. Dies ist insbesondere
 *        nützlich, wenn die Callback-Funktion eine Objekt-Methode ist.
 *        Wenn als Kontext die Objekt-Instanz der Callback-Methode
 *        angegeben wird, dann verweist <tt>this</tt> innerhalb der
 *        Callback-Methode auf die Objekt-Instanz der Methode.
 *
 *   <li> Optional kann die Anzahl der Ausführungen festgelegt werden.
 *        Siehe hierzu den Parameter callCount des Konstruktors.
 * </ul>
 *
 * @class Timer
 *
 * @example
 *   // Alle 10 Sekunden eine Alert-Message anzeigen
 *   var ti1 = new Timer (function () { alert("Timer event"); }, 10000);
 *   // Timer starten
 *   ti1.start ();
 *
 *   // Objekt mit Callback-Methode
 *   var Obj = {
 *      counter: 0,
 *
 *      incCounter: function (timer)
 *      {
 *         // Counter inkrementieren ("this" verweist auf "Obj")
 *         this.counter++;
 *         // Timer nach 3 Aufrufen anhalten
 *         if (this.counter == 3)
 *            timer.stop ();
 *      }
 *   };
 *
 *   // Timer erzeugen: Aufruf von Obj.incCounter() alle 2 Sekunden.
 *   // Beim Aufruf von Obj.incCounter() ist this == Obj.
 *   // Timer sofort starten.
 *   var ti2 = new Timer (Obj.incCounter, 2000, Obj).start ();
 *
 * @param  callback  Callback-Funktion, die periodisch aufgerufen werden soll.
 * @param  interval  Intervall in Millisekunden
 * @param  context  Objekt-Kontext für den Aufruf der Callback-Funktion (this).
 *         Falls der Parameter nicht angegeben ist, wird die Callback-Funktion
 *         im Kontext der Timer-Instanz aufgerufen.
 * @param  callCount  Anzahl der Aufrufe der Callback-Funktion (optional).
 *         Hierüber kann die Anzahl der
 */
$gw$.util.Timer = function (callback, interval, context, callCount)
{
   this.callback = callback;
   this.interval = interval;
   this.context = context;
   this.timer = null;

   var busy = false;
   var counter = (callCount || -1);

   /**
    * Startet den Timer.<p>
    *
    * Falls der Timer bereits gestartet ist, kehrt die Methode ohne
    * Aktion zurück.
    *
    * @return Referenz auf diesen Timer
    *
    * @see #stop()
    */
   this.start = function ()
   {
      var context = this;
      if (!this.timer)
         this.timer = setInterval (function () { onTimer.call (context); }, this.interval);
      return this;
   };

   /**
    * Stoppt den Timer.<p>
    *
    * Falls der Timer momentan nicht läuft, kehrt die Methode ohne
    * Aktion zurück.
    *
    * @return Referenz auf diesen Timer
    *
    * @see #start()
    */
   this.stop = function ()
   {
      if (this.timer)
      {
         clearInterval (this.timer);
         this.timer = null;
      }
      return this;
   };

   this.execute = function ()
   {
      var context = this.context;
      if (!context)
         context = this;
      if ((counter > 0) && (--counter == 0))
         this.stop ();
      this.callback.call (context, this);
      return this;
   };

   function onTimer ()
   {
      if (busy)
         return;
      busy = true;
      try
      {
         this.execute ();
      }
      finally
      {
         busy = false;
      }
   }
};

/*=======================================================================*/
/* JSON                                                                  */
/*=======================================================================*/

/**
 * JSON ist ein globales Objekt, das die Umwandlung von JSON-Strings
 * in Objekte und umgekehrt unterstützt.<p>
 *
 * JSON enthält zwei Methoden:<p>
 *
 *   <tt>{@link #toJSON} (value, whitelist)</tt><br />
 *     Diese Methode wandelt einen Javascript-Wert (i.d.R. Objekt oder
 *     Array) in einen JSON-String um. Über den optionalen Parameter
 *     whitelist kann ein Array von Bezeichnern angegeben werden, um
 *     die aus value nach JSON umgewandelten Objekt-Members zu
 *     beschränken.<p>
 *
 *   <tt>{@link #parseJSON} (text, filter)</tt><br />
 *     Wandelt einen JSON-String in ein Javascript-Objekt oder -Array.
 *     Diese Methode ist als sichere Alternative zu eval() gedacht,
 *     insbesondere wenn der JSON-Content aus einer externen, nicht
 *     vertrauenswürdigen Quelle stammt. In solchen Fällen birgt der
 *     Aufruf von eval() u.a. die Gefahr von XSS-Attacken, da der
 *     übergebene JS-Code ohne jede Prüfung ausgeführt wird. In der
 *     Methode parseJSON() wird sichergestellt, dass nur JS-Literale
 *     in dem Parameter "text" enthalten sind. Dabei wird insbesondere
 *     kontrolliert, ob unzulässige Elemente wie Funktions-Aufrufe "()",
 *     das Schlüsselwort "new" oder Zuweisungen "=" in text enthalten
 *     sind. Falls ja, wird eine SyntaxError-Exception ausgelöst.
 *     Nur wenn der JS-Code in text ein gültiger JSON-String und damit
 *     "sicher" ist, wird eval() aufgerufen und das Ergebnis zurückgegeben.<p>
 *
 * Die Klasse basiert teilweise auf dem Public-Domain-Quelltext "json2.js"
 * von Douglas Crockford (siehe http://www.JSON.org/js.html).<p>
 *
 * Beachte: JSON ist ein Singleton-Objekt, d.h. es kann nicht
 * instanziiert werden.
 *
 * @class $gw$.util.JSON
 */
$gw$.util.JSON = function ()
{
   /**
    * Wandelt einen Javascript-Wert in seine JSON-Notation um.
    *
    * Werte, die in JSON nicht dargestellt werden können (z.B. Funktionen
    * oder undefined-Werte), werden nicht serialisiert. Wenn es sich dabei
    * um Objekt-Member handelt, werden sie nicht übertragen. In Arrays
    * werden nicht darstellbare Werte durch null ersetzt.
    *
    * @public
    *
    * @param  value  Wert, normalerweise ein Objekt oder Array.
    * @param  whitelist  Optionales Array mit den Namen der Elemente
    *         (Properties) aus value, die in das JSON-Format ünertragen
    *         werden sollen. Falls keine Whitelist angegeben ist, werden
    *         sämtliche Properties aus value übertragen.
    *
    * @return JSON-Repräsentierung von value
    */
   function toJSON (value, whitelist)
   {
      var i, len, key, val;
      
      switch (typeof value)
      {
         /*
            Strings haben immer eine toJSON()-Methode. Falls nicht nativ,
            wird der String-Prototyp entsprechend erweitert (siehe oben).
         */
         case "string":
            return value.toJSON ();

         case "number":
            return isFinite (value) ? String (value) : "null";

         case "boolean":
         case "null":
            return String (value);

         /*
            Objekte, Arrays, null
         */
         case "object":
         {
            /* Sonderbehandlung nötig, weil typeof(null) == "object" */
            if (!value)
               return "null";

            /* Wenn Objekt eine toJSON()-Methode hat, rufen wir diese auf */
            if ((typeof value.toJSON === "function") && (value.toJSON != toJSON))
               return value.toJSON ();

            /* Lokales Array der Objekt-/Array-Werte als JSON-Strings */
            var valueStrings = [];

            /* Arrays */
            if (isArray (value))
            {
               len = value.length;
               for (i = 0; i < len; i++)
                  valueStrings.push (toJSON (value[i]) || "null");
               return "[" + valueStrings.join (",") + "]";
            }

            /*
               Ab hier: Sonstige Objekte
            */
            /* Whitelist der zu exportierenden Members angegeben? */
            if (whitelist)
            {
               /* Ja, dann nur die in der Whitelist enthaltenen Properties
                  in den JSON-String übertragen */
               len = whitelist.length;
               for (i = 0; i < len; i++)
               {
                  key = whitelist[i];
                  if (typeof key === "string")
                  {
                     val = toJSON (value[key], whitelist);
                     if (val)
                        valueStrings.push (key.toJSON () + ":" + val);
                  }
               }
            }
            else
            {
               /* Nein, dann sämtliche Properties des Objekts übertragen */
               for (key in value)
               {
                  if (typeof key === "string")
                  {
                     val = toJSON (value[key]);
                     if (val)
                        valueStrings.push (key.toJSON () + ":" + val);
                  }
               }
            }

            return "{" + valueStrings.join (",") + "}";
         }
      }
   }

   /**
    * Wandelt einen JSON-String in ein Objekt oder Array um.
    *
    * Diese Methode ist als sichere Alternative zu eval() gedacht, v.a. wenn
    * der JSON-Content aus einer externen, nicht vertrauenswürdigen Quelle
    * stammt. In solchen Fällen birgt der Aufruf von eval() u.a. die Gefahr
    * von XSS-Attacken, da der an eval() übergebene JS-Code ohne jede Prüfung
    * ausgeführt wird.
    *
    * In der hier vorliegenden Methode wird sichergestellt, dass nur JS-Literale
    * in dem Parameter "text" enthalten sind. Dabei wird insbesondere geprüft,
    * ob unzulässige Elemente wie Funktions-Aufrufe "()", das Schlüsselwort
    * "new" oder Zuweisungen "=" in text enthalten sind. Falls ja, wird eine
    * SyntaxError-Exception ausgelöst.
    *
    * Nur wenn der JS-Code in text ein gültiger JSON-String und damit "sicher"
    * ist, wird eval() aufgerufen und das Ergebnis zurückgegeben.
    *
    * Darüber hinaus erlaubt die Methode die Verwendung einer Filterfunktion,
    * die für jedes Key/Value-Paar des erzeugten Objekts aufgerufen wird und
    * z.B. die automatische Umwandlung von Werten ermöglicht. Ein Beispiel
    * für das automatische Erzeugen von Date-Objekten ist unten aufgeführt.
    *
    * @public
    *
    * @param  text  JSON-Notation eines Objekts oder Arrays
    * @param  filter  Optionale Filterfunktion zum Filtern des Ergebnisses.
    *         Die Funktion wird für jedes Key/Value-Paar aufgerufen, das
    *         in dem erzeugten Objekt enthalten ist. Der Returnwert der
    *         Funktion wird anstelle des im JSON-String enthaltenen Werts
    *         verwendet. Die Filterfunktion kann z.B. verwendet werden, um
    *         Stringwerte automatisch in entsprechende Objekte umzuwandeln.
    *
    *         Beispiel (aus json2.js von D. Crockford):
    *
    *         @example
    *         // JSON-String parsen. Falls ein Key den String "date"
    *         // enthält, wird der Wert automatisch in ein Date-Objekt
    *         // umgewandelt.
    *
    *         myData = JSONUtils.parseJSON (text, function (key, value) {
    *                     return key.indexOf('date') >= 0 ? new Date(value) : value;
    *                  });
    *
    *
    */
   function parseJSON (text, filter)
   {
      /*
         Nachfolgende Implementierung entspricht der Methode JSON.parse()
         aus json2.js von D. Crockford.
      */

      /*
         Lokale Funktion zum rekursiven Iterieren über die Member des
         neu erzeugten Objekts und Aufruf der Filterfunktion, die im
         Parameter "filter" angegeben wurde.
      */
      function walk (k, v)
      {
         var i, n;
         if (v && typeof v === 'object')
         {
            for (i in v)
            {
               if (Object.prototype.hasOwnProperty.apply (v, [i]))
               {
                  n = walk(i, v[i]);
                  if (n !== undefined)
                     v[i] = n;
                  else
                     delete v[i];
               }
            }
         }
         return filter (k, v);
      }

      if (/^[\],:{}\s]*$/.test (text.replace (/\\["\\\/bfnrtu]/g, "@").
          replace (/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").
          replace (/(?:^|:|,)(?:\s*\[)+/g, "")))
      {
         var result = eval ("(" + text + ")");
         return typeof filter === "function" ? walk ("", result) : result;
      }

      throw new SyntaxError ("parseJSON");
   }

   return {
      toJSON: toJSON,
      parseJSON: parseJSON
   };
}();

/*=======================================================================*/
/* Cookies                                                               */
/*=======================================================================*/

$gw$.util.Cookie = function ()
{
   function get (name)
   {
      var cookies = document.cookie;
      if (cookies && (cookies != ""))
      {
         cookies = cookies.split (";");
         for (var i = 0, n = name + "="; i < cookies.length; i++)
         {
            var cookie = cookies[i].trim ();
            if (cookie.startsWith (n))
               return decodeURIComponent (cookie.substr (n.length));
         }
      }

      return null;
   }

   function getValues (name)
   {
      var cookie = get (name);
      if (isString (cookie))
         return cookie.toQueryParams ();
      return null;
   }

   function _createCookieString (name, value, options)
   {
      /* Leere Optionen, falls nicht definiert */
      options = options || {};

      /* Wert definiert? */
      if (value == null)
      {
         /* Bei value == null wird der Cookie gelöscht. */
         value = "";
         options.expires = -1;
      }
      else if (isObject (value) && !isArray (value))
         value = $gw$.obj.toQueryString (value);
      else
         value = encodeURIComponent (value);

      var cookie = name + "=" + value;

      /* Expires-Option holen. Dies darf wahlweise eine Zahl sein,
         die die Anzahl von Tagen angibt, oder ein Datum. */
      var expires = options.expires;
      if (expires && (isNumber (expires) || expires.toUTCString))
      {
         var ti;
         if (isNumber (expires))
         {
            ti = new Date ();
            ti.setTime (ti.getTime () + (expires * 86400000));
         }
         else
            ti = expires;

         cookie += "; expires=" + ti.toUTCString ();
      }

      /* Path */
      if (options.path && (options.path != ""))
         cookie += "; path=" + options.path;

      /* Domain */
      if (options.domain && (options.domain != ""))
         cookie += "; domain=" + options.domain;

      /* Secure */
      if (options.secure)
         cookie += "; secure";

      /* Cookie-String zurückgeben */
      return cookie;
   }

   function set (name, value, options)
   {
      var cookie = _createCookieString (name, value, options);
      document.cookie = cookie;
      return cookie;
   }

   function remove (name)
   {
      set (name, null);
   }

   return {
      get: get,
      getValues: getValues,
      set: set,
      remove: remove
   };
}();

/*=======================================================================*/
/* Hilfsfunktionen                                                       */
/*=======================================================================*/

/*
 * Die nachfolgenden Hilfsfunktionen aus dem Namespace $gw$.obj werden zur
 * Vereinfachung zusätzlich im globalen Namespace (window) deklariert,
 * so dass sie direkt über ihren Namen (ohne Objekt-Präfix) aufgerufen
 * werden können, also z.B. isArray(x) anstelle von $gw$.val.isArray(x).
 *
 * Erweiterungen in diesem Bereich sollten sorgfältig ausgewählt werden,
 * da im window-Namespace die größte Gefahr von Namenskollisionen mit
 * anderen JS-Libraries besteht. Daher sollten hier nur solche Funktionen
 * deklariert werden, bei denen entweder aufgrund ihres Namens eine extrem
 * geringe Kollisionswahrscheinlichkeit besteht, oder es sehr sicher ist,
 * dass gleichnamige Funktionen in anderen JS-Libraries exakt dieselbe
 * Funktionalität besitzen.
 */
$gw$.obj.extend (this, {
   isArray: $gw$.val.isArray,
   isBoolean: $gw$.val.isBoolean,
   isDate: $gw$.val.isDate,
   isFunction: $gw$.val.isFunction,
   isNumber: $gw$.val.isNumber,
   isObject: $gw$.val.isObject,
   isString: $gw$.val.isString,
   isDefined: $gw$.val.isDefined,
   isUndefined: $gw$.val.isUndefined
});

/*=======================================================================*/
/* Library/Modul registrieren                                            */
/*=======================================================================*/

$gw$.Modules.register ("core", $gw$, __GW_CORE__);
