Bearbeiten von „OS2.spielplan

Zur Navigation springen Zur Suche springen
Warnung: Du bist nicht angemeldet. Deine IP-Adresse wird bei Bearbeitungen öffentlich sichtbar. Melde dich an oder erstelle ein Benutzerkonto, damit Bearbeitungen deinem Benutzernamen zugeordnet werden. Ein eigenes Benutzerkonto hat eine ganze Reihe von Vorteilen.

Die Bearbeitung kann rückgängig gemacht werden. Bitte prüfe den Vergleich unten, um sicherzustellen, dass du dies tun möchtest, und veröffentliche dann unten deine Änderungen, um die Bearbeitung rückgängig zu machen.

Aktuelle Version Dein Text
Zeile 1: Zeile 1:
[[Kategorie:Greasemonkey]]
[[Kategorie:Greasemonkey]]
[[Kategorie:Greasemonkey WE]]
{| style="background-color:white; font-size:11px; float: right; margin:3px 3px 3px 10px; border:1px solid #999; border-color: #9C1818; border-collapse:collapse;" width=500 cellpadding=3 cellspacing=0
{| style="background-color:white; font-size:11px; float: right; margin:3px 3px 3px 10px; border:1px solid #999; border-color: #9C1818; border-collapse:collapse;" width=500 cellpadding=3 cellspacing=0
| colspan="2" style="padding:0.3em; background-color:#9C1818; font-size: 18px; color:#FFFFFF" align=center| '''OS2.spielplan'''
| colspan="2" style="padding:0.3em; background-color:#9C1818; font-size: 18px; color:#FFFFFF" align=center| '''OS2.spielplan'''
Zeile 8: Zeile 7:
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Version'''
| '''Version'''
| '''0.73 (WebExtensions)'''  
| '''0.70'''  
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Autor'''
| '''Autor'''
Zeile 20: Zeile 19:
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Funktionalität'''
| '''Funktionalität'''
| '''Trennstriche zwischen den Abrechnungsmonaten'''<br> '''Verkürzung von "Vorbericht(e)" und "Kommentar(e)"'''<br> '''Details zu den Spielen'''<br> '''Ergebnisse aufaddieren'''<br> '''Nachträgliche Vorschau: Bilanz'''<br> '''Benutzermenü für Optionen'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Link auf den jeweiligen Spieltag bzw. die jeweilige Runde'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis'''<br> '''Gezielte "hl"-Links auf die eigenen Spiele'''
| '''Trennstriche zwischen den Abrechnungsmonaten'''<br> '''Verkürzung von "Vorbericht(e)" und "Kommentar(e)"'''<br> '''Details zu den Spielen'''<br> '''Ergebnisse aufaddieren'''<br> '''Nachträgliche Vorschau: Bilanz'''<br> '''Benutzermenü für Optionen'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Link auf den jeweiligen Spieltag bzw. die jeweilige Runde'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Letzte Änderung'''
| '''Letzte Änderung'''
Zeile 32: Zeile 31:
// @name        OS2.spielplan
// @name        OS2.spielplan
// @namespace    http://os.ongapo.com/
// @namespace    http://os.ongapo.com/
// @version      0.73
// @version      0.70
// @copyright    2013+
// @copyright    2013+
// @author      Sven Loges (SLC)
// @author      Sven Loges (SLC)
// @description  Spielplan-Abschnitt aus dem Master-Script fuer Online Soccer 2.0
// @description  Spielplan-Abschnitt aus dem Master-Script fuer Online Soccer 2.0
// @include      /^https?://(www\.)?(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/(st|showteam)\.php\?s=6(&\w+=?[+\w]+)*(#\w+)?$/
// @include      http*://os.ongapo.com/st.php?s=*
// @grant        GM.getValue
// @include      http*://os.ongapo.com/showteam.php?s=*
// @grant        GM.setValue
// @include      http*://www.os.ongapo.com/st.php?s=*
// @grant        GM.deleteValue
// @include      http*://www.os.ongapo.com/showteam.php?s=*
// @grant        GM.registerMenuCommand
// @include      http*://online-soccer.eu/st.php?s=*
// @grant        GM.info
// @include      http*://online-soccer.eu/showteam.php?s=*
// @require     https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @include      http*://www.online-soccer.eu/st.php?s=*
// @include     http*://www.online-soccer.eu/showteam.php?s=*
// @grant        GM_getValue
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_setValue
Zeile 50: Zeile 50:
// ==/UserScript==
// ==/UserScript==


// ECMAScript 6:
// ECMAScript 6: Erlaubt 'const', 'let', ...
/* jshint esnext: true */
/* jshint esnext: true */
/* jshint moz: true */
/* jshint moz: true */
Zeile 144: Zeile 144:
                   'Name'      : "sepStyle",
                   'Name'      : "sepStyle",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                   'Choice'    : [ 'solid', 'hidden', 'dotted', 'dashed', 'double', 'groove', 'ridge',
                   'Choice'    : [ "solid", "hidden", "dotted", "dashed", "double", "groove", "ridge",
                                   'inset', 'outset', 'none' ],
                                   "inset", "outset", "none" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Stil: $",
                   'Label'    : "Stil: $",
Zeile 155: Zeile 155:
                   'Name'      : "sepColor",
                   'Name'      : "sepColor",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                   'FreeValue' : true,
                   'FreeValue' : true,
                   'Choice'    : [ 'white', 'yellow', 'black', 'blue', 'cyan', 'gold', 'grey', 'green',
                   'Choice'    : [ "white", "yellow", "black", "blue", "cyan", "gold", "grey", "green",
                                   'lime', 'magenta', 'maroon', 'navy', 'olive', 'orange', 'purple',
                                   "lime", "magenta", "maroon", "navy", "olive", "orange", "purple",
                                   'red', 'teal', 'transparent' ],
                                   "red", "teal", "transparent" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Farbe: $",
                   'Label'    : "Farbe: $",
Zeile 168: Zeile 168:
                   'Name'      : "sepWidth",
                   'Name'      : "sepWidth",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                   'FreeValue' : true,
                   'FreeValue' : true,
                   'Choice'    : [ 'thin', 'medium', 'thick' ],
                   'Choice'    : [ "thin", "medium", "thick" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Dicke: $",
                   'Label'    : "Dicke: $",
Zeile 176: Zeile 176:
                   'FormLabel' : "Dicke:|$"
                   'FormLabel' : "Dicke:|$"
               },
               },
     'saison' : {          // Angezeigte Saison
     'saison' : {          // Laufende Saison
                   'Name'      : "saison",
                   'Name'      : "saison",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                   'FreeValue' : true,
                   'FreeValue' : true,
                   'SelValue'  : false,
                   'SelValue'  : false,
                   'Choice'    : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ],
                   'Choice'    : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
                   'Default'  : 18,
                   'Default'  : 10,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Saison: $",
                   'Label'    : "Saison: $",
                   'Hotkey'    : 's',
                   'Hotkey'    : 'a',
                   'FormLabel' : "Saison:|$"
                   'FormLabel' : "Saison:|$"
               },
               },
Zeile 192: Zeile 192:
                   'Name'      : "ligaSize",
                   'Name'      : "ligaSize",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Choice'    : [ 10, 18, 20 ],
                   'Choice'    : [ 10, 18, 20 ],
Zeile 206: Zeile 206:
                   'Serial'    : true,
                   'Serial'    : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : undefined,  // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'TmNr' : 0, 'LdNr' : 0, 'LgNr' : 0 }
                   'Default'  : undefined,  // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 }
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
Zeile 215: Zeile 215:
               },
               },
     'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
     'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
                  'FormPrio'  : undefined,
                   'Name'      : "reset",
                   'Name'      : "reset",
                   'Type'      : __OPTTYPES.SI,
                   'Type'      : __OPTTYPES.SI,
Zeile 224: Zeile 223:
               },
               },
     'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
     'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                   'Name'      : "storage",
                   'Name'      : "storage",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                   'Choice'    : Object.keys(__OPTMEM),
                   'Choice'    : Object.keys(__OPTMEM),
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
Zeile 235: Zeile 233:
               },
               },
     'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
     'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                   'Name'      : "oldStorage",
                   'Name'      : "oldStorage",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
Zeile 243: Zeile 240:
               },
               },
     'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
     'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'FormPrio'  : undefined,
                   'Name'      : "showForm",
                   'Name'      : "showForm",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
Zeile 249: Zeile 245:
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : false,
                   'Default'  : false,
                  'Title'    : "$V Optionen",
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Optionen anzeigen",
                   'Label'    : "Optionen anzeigen",
                   'Hotkey'    : 'a',
                   'Hotkey'    : 'a',
                  'AltTitle'  : "$V schlie\xDFen",
                   'AltLabel'  : "Optionen verbergen",
                   'AltLabel'  : "Optionen verbergen",
                   'AltHotkey' : 'v',
                   'AltHotkey' : 'v',
Zeile 261: Zeile 255:


// ==================== Invarianter Abschnitt fuer Optionen ====================
// ==================== Invarianter Abschnitt fuer Optionen ====================
// Ein Satz von Logfunktionen, die je nach Loglevel zur Verfuegung stehen. Aufruf: __LOG[level](text)
const __LOG = {
                  'logFun'    : [
                                    console.error,  // [0] Alert
                                    console.error,  // [1] Error
                                    console.log,    // [2] Log: Release
                                    console.log,    // [3] Log: Info
                                    console.log,    // [4] Log: Debug
                                    console.log,    // [5] Log: Verbose
                                    console.log,    // [6] Log: Very verbose
                                    console.log    // [7] Log: Testing
                                ],
                  'init'      : function(win, logLevel = 1) {
                                    for (let level = 0; level < this.logFun.length; level++) {
                                        this[level] = ((level > logLevel) ? function() { } : this.logFun[level]);
                                    }
                                },
                  'stringify' : safeStringify,      // JSON.stringify
                  'changed'  : function(oldVal, newVal) {
                                    const __OLDVAL = this.stringify(oldVal);
                                    const __NEWVAL = this.stringify(newVal);
                                    return ((__OLDVAL !== __NEWVAL) ? __OLDVAL + " => " : "") + __NEWVAL;
                                }
              };
__LOG.init(window, __LOGLEVEL);


// Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist)
// Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist)
Zeile 299: Zeile 265:
}
}


// Ergaenzung fuer Strings: Links oder rechts auffuellen nach Vorlage
// Ein Satz von Logfunktionen, die je nach Loglevel zur Verfuegung stehen. Aufruf: __LOG[level](text)
// padStr: Vorlage, z.B. "00" fuer zweistellige Zahlen
const __LOG = {
// padLeft: true = rechtsbuendig, false = linksbuendig
                  'logFun'  : [
// clip: Abschneiden, falls zu lang
                                  console.error,  // [0] Alert
// return Rechts- oder linksbuendiger String, der so lang ist wie die Vorlage
                                  console.error, // [1] Error
String.prototype.pad = function(padStr, padLeft = true, clip = false) {
                                  console.log,    // [2] Log: Release
    const __LEN = ((clip || (padStr.length > this.length)) ? padStr.length : this.length);
                                  console.log,    // [3] Log: Info
                                  console.log,    // [4] Log: Debug
                                  console.log,   // [5] Log: Verbose
                                  console.log    // [6] Log: Very verbose
                              ],
                  'init'    : function(win, logLevel = 1) {
                                  for (level = 0; level < this.logFun.length; level++) {
                                      this[level] = ((level > logLevel) ? function() { } : this.logFun[level]);
                                  }
                              },
                  'changed'  : function(oldVal, newVal) {
                                  const __OLDVAL = safeStringify(oldVal);
                                  const __NEWVAL = safeStringify(newVal);


    return (padLeft ? String(padStr + this).slice(- __LEN) : String(this + padStr).slice(0, __LEN));
                                  return ((__OLDVAL !== __NEWVAL) ? __OLDVAL + " => " : "") + __NEWVAL;
};
                              }
              };


// Ersetzt in einem String {0}, {1}, ... durch die entsprechenden Parameter
__LOG.init(window, __LOGLEVEL);
// arguments: Parameter, die fuer {0}, {1}, ... eingesetzt werden sollen
// return Resultierender String
String.prototype.format = function() {
    const __ARGS = arguments;
    return this.replace(/{(\d+)}/g, function(match, argIdx) {
                                        const __ARG = __ARGS[argIdx];
                                        return ((__ARG !== undefined) ? __ARG : match);
                                    });
};


// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster mit der Meldung
// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster mit der Meldung
Zeile 325: Zeile 295:
// message: Der Meldungs-Text
// message: Der Meldungs-Text
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben
// return Liefert die Parameter zurueck
function showAlert(label, message, data = undefined) {
function showAlert(label, message, data = undefined) {
     __LOG[0](label + ": " + message);
     __LOG[1](label + ": " + message);


     if (data !== undefined) {
     if (data !== undefined) {
Zeile 334: Zeile 303:


     alert(label + "\n\n" + message);
     alert(label + "\n\n" + message);
    return arguments;
}
// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster
// mit der Meldung zu einer Exception oder einer Fehlermeldung
// label: Eine Ueberschrift
// ex: Exception oder sonstiges Fehlerobjekt
// return Liefert die showAlert()-Parameter zurueck
function showException(label, ex) {
    if (ex && ex.message) {  // Exception
        showAlert(label, ex.message, ex);
    } else {  // sonstiger Fehler
        showAlert(label, ex);
    }
}
// Standard-Callback-Funktion fuer onRejected, also abgefangener Fehler
// in einer Promise bei Exceptions oder Fehler bzw. Rejections
// error: Parameter von reject() im Promise-Objekt, der von Promise.catch() erhalten wurde
// return Liefert die showAlert()-Parameter zurueck
function defaultCatch(error) {
    try {
        const __LABEL = `[${error.lineNumber}] ${__DBMOD.Name}`;
        if (error && error.message) {  // Exception
            return showException(__LABEL, error.message, error);
        } else {
            return showException(__LABEL, error);
        }
    } catch (ex) {
        return showException(`[${ex.lineNumber}] ${__DBMOD.Name}`, ex.message, ex);
    }
}
}


Zeile 400: Zeile 336:


         console.assert((__BASE === null) || ((typeof __BASE) === 'function'), "No function:", __BASE);
         console.assert((__BASE === null) || ((typeof __BASE) === 'function'), "No function:", __BASE);
         console.assert((typeof initFun) === 'function', "Not a function:", initFun);
         console.assert((typeof initFun) === 'function', "No function:", initFun);


         this.init = initFun;
         this.init = initFun;
Zeile 428: Zeile 364:
             const __CREATEPROTO = ((createProto === undefined) ? true : createProto);
             const __CREATEPROTO = ((createProto === undefined) ? true : createProto);


             console.assert((typeof this) === 'function', "Not a function:", this);
             console.assert((typeof this) === 'function');
             console.assert((typeof __MEMBERS) === 'object', "Not an object:", __MEMBERS);
             console.assert((typeof __MEMBERS) === 'object');


             const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
             const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
Zeile 472: Zeile 408:
                                         return this.className;
                                         return this.className;
                                     }
                                     }
                 });
                 } );


// ==================== Ende Abschnitt fuer Klasse Class ====================
// ==================== Ende Abschnitt fuer Klasse Class ====================
Zeile 519: Zeile 455:
                                     this.home = home;
                                     this.home = home;
                                 }
                                 }
           });
           } );


// ==================== Ende Abschnitt fuer Klasse Delims ====================
// ==================== Ende Abschnitt fuer Klasse Delims ====================
Zeile 598: Zeile 534:
                                     this.node = node;
                                     this.node = node;
                                 }
                                 }
           });
           } );


// ==================== Ende Abschnitt fuer Klasse UriDelims ====================
// ==================== Ende Abschnitt fuer Klasse UriDelims ====================
Zeile 697: Zeile 633:
                                         return __DIRS.slice(1);
                                         return __DIRS.slice(1);
                                     }
                                     }
                 });
                 } );


// ==================== Ende Abschnitt fuer Klasse Path ====================
// ==================== Ende Abschnitt fuer Klasse Path ====================
Zeile 756: Zeile 692:
                                         const __NOSCHEME = this.stripSchemePrefix(path);
                                         const __NOSCHEME = this.stripSchemePrefix(path);
                                         const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1);
                                         const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1);
                                         const __PATH = ((~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME);
                                         const __PATH = (~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME;
                                         const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
                                         const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
                                         const __HOSTPORT = ((~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined);
                                         const __HOSTPORT = (~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined;
                                         const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1);
                                         const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1);
                                         const __HOST = ((~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT);
                                         const __HOST = (~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT;
                                         const __PORT = ((~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined);
                                         const __PORT = (~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined;


                                         return {
                                         return {
Zeile 772: Zeile 708:
                                         const __ROOTDELIM = this.delims.root + this.delims.delim;
                                         const __ROOTDELIM = this.delims.root + this.delims.delim;
                                         const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1);
                                         const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1);
                                         const __PATH = ((~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path);
                                         const __PATH = (~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path;
                                         const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
                                         const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);


                                         return ((~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH);
                                         return (~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH;
                                     },
                                     },
               'getSchemePrefix'  : function(path = undefined) {
               'getSchemePrefix'  : function(path = undefined) {
Zeile 781: Zeile 717:
                                         const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);
                                         const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


                                         return ((~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined);
                                         return (~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined;
                                     },
                                     },
               'stripSchemePrefix' : function(path = undefined) {
               'stripSchemePrefix' : function(path = undefined) {
Zeile 787: Zeile 723:
                                         const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);
                                         const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


                                         return ((~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path);
                                         return (~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path;
                                     },
                                     },
               'getNodeSuffix'    : function(path = undefined) {
               'getNodeSuffix'    : function(path = undefined) {
Zeile 793: Zeile 729:
                                         const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                                         const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);


                                         return ((~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined);
                                         return (~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined;
                                     },
                                     },
               'stripNodeSuffix'  : function(path = undefined) {
               'stripNodeSuffix'  : function(path = undefined) {
Zeile 799: Zeile 735:
                                         const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                                         const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);


                                         return ((~ __INDEXNODE) ? path.substring(0, __INDEXNODE) : path);
                                         return (~ __INDEXNODE) ? path.substring(0, __INDEXNODE) : path;
                                     },
                                     },
               'getQueryString'    : function(path = undefined) {
               'getQueryString'    : function(path = undefined) {
Zeile 806: Zeile 742:
                                         const __INDEXQUERY = (__PATH ? __PATH.indexOf(__QUERYDELIM) : -1);
                                         const __INDEXQUERY = (__PATH ? __PATH.indexOf(__QUERYDELIM) : -1);


                                         return ((~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined);
                                         return (~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined;
                                     },
                                     },
               'stripQueryString'  : function(path = undefined) {
               'stripQueryString'  : function(path = undefined) {
Zeile 812: Zeile 748:
                                         const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);
                                         const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);


                                         return ((~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path);
                                         return (~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path;
                                     },
                                     },
               'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
               'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
Zeile 834: Zeile 770:
                                                 if (__PARAM) {
                                                 if (__PARAM) {
                                                     const __INDEX = __PARAM.indexOf(assign);
                                                     const __INDEX = __PARAM.indexOf(assign);
                                                     const __KEY = ((~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM);
                                                     const __KEY = (~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM;
                                                     const __VAL = ((~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true);
                                                     const __VAL = (~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true;


                                                     __RET[__KEY] = __VAL;
                                                     __RET[__KEY] = __VAL;
Zeile 855: Zeile 791:
                                             const __LOWER = (value ? value.toLowerCase() : undefined);
                                             const __LOWER = (value ? value.toLowerCase() : undefined);


                                             if ((__LOWER === 'true') || (__LOWER === 'false')) {
                                             if ((__LOWER === "true") || (__LOWER === "false")) {
                                                 return (value === 'true');
                                                 return (value === "true");
                                             }
                                             }
                                         }
                                         }
Zeile 908: Zeile 844:
                                         return Path.prototype.getDirs.call(this, __PATH);
                                         return Path.prototype.getDirs.call(this, __PATH);
                                     }
                                     }
           });
           } );


// ==================== Ende Abschnitt fuer Klasse URI ====================
// ==================== Ende Abschnitt fuer Klasse URI ====================
Zeile 948: Zeile 884:
                                   return this.getPath();
                                   return this.getPath();
                               }
                               }
                 });
                 } );


// ==================== Ende Abschnitt fuer Klasse Directory ====================
// ==================== Ende Abschnitt fuer Klasse Directory ====================
Zeile 976: Zeile 912:
                                     return ret;
                                     return ret;
                                 }
                                 }
                 });
                 } );


// ==================== Ende Abschnitt fuer Klasse ObjRef ====================
// ==================== Ende Abschnitt fuer Klasse ObjRef ====================
Zeile 1.021: Zeile 957:


// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// valueA: Ein Multipliksnt. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// valueB: Ein Multipliksnt. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
Zeile 1.031: Zeile 967:
     if ((valueA !== undefined) && (valueB !== undefined)) {
     if ((valueA !== undefined) && (valueB !== undefined)) {
         product = parseFloat(valueA) * parseFloat(valueB);
         product = parseFloat(valueA) * parseFloat(valueB);
    }
    if (isNaN(product)) {
        product = defValue;
     }
     }


Zeile 1.040: Zeile 972:
}
}


// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck
// Ueberprueft, ob ein Objekt einer bestimmten Klasse angehoert (ggfs. per Vererbung)
// value: Eine ganze Zahl
// obj: Ein (generisches) Objekt
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben
function getOrdinal(value, defValue = '*') {
    return getValue(value, defValue, value + '.');
}
 
// Hilfsfunktion fuer Array.sort(): Vergleich zweier Zahlen
// valueA: Erster Zahlenwert
// valueB: Zweiter Zahlenwert
// return -1 = kleiner, 0 = gleich, +1 = groesser
function compareNumber(valueA, valueB) {
    return +(valueA > valueB) || (+(valueA === valueB) - 1);
}
 
// Ueberprueft, ob ein Objekt einer bestimmten Klasse angehoert (ggfs. per Vererbung)
// obj: Ein (generisches) Objekt
// base: Eine Objektklasse (Konstruktor-Funktion)
// base: Eine Objektklasse (Konstruktor-Funktion)
// return true, wenn der Prototyp rekursiv gefunden werden konnte
// return true, wenn der Prototyp rekursiv gefunden werden konnte
function instanceOf(obj, base) {
function instanceOf(obj, base) {
     while (obj !== null) {
     while (obj !== null) {
         if (obj === base.prototype) {
         if (obj === base.prototype)
             return true;
             return true;
        }
         if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
         if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
             return (base.prototype === XML.prototype);
             return (base.prototype === XML.prototype);
Zeile 1.114: Zeile 1.029:
     let active = true;
     let active = true;


     if (inList) {
     if (inList !== undefined) {
         active = (inList[item] === true);  // gesetzt und true
         active = (inList[item] === true);  // gesetzt und true
     }
     }
     if (exList) {
     if (exList !== undefined) {
         if (exList[item] === true) {  // gesetzt und true
         if (exList[item] === true) {  // gesetzt und true
             active = false;  // NICHT anzeigen
             active = false;  // NICHT anzeigen
Zeile 1.136: Zeile 1.051:
         if (checkItem(item, addList, ignList)) {
         if (checkItem(item, addList, ignList)) {
             data[item] = addData[item];
             data[item] = addData[item];
        }
    }
    return data;
}
// Entfernt Properties in einem Objekt
// data: Objekt, deren Properties bearbeitet werden
// delList: Checkliste der zu loeschenden Items (true fuer loeschen), falls angegeben
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
// return Das veraenderte Objekt ohne die geloeschten Properties
function delProps(data, delList = undefined, ignList = undefined) {
    for (let item in getValue(data, { })) {
        if (checkItem(item, delList, ignList)) {
            delete data[item];
         }
         }
     }
     }
Zeile 1.232: Zeile 1.132:
}
}


// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// key: Der uebergebene Schluessel
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// value: Der uebergebene Wert
// value: Beliebiger (strukturierter) Wert
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
// return String-Darstellung des Wertes
function replaceArraySimple(key, value) {
function serialize(name, value) {
     if (Array.isArray(value)) {
     const __STREAM = (value !== undefined) ? safeStringify(value) : value;
        return "[ " + value.join(", ") + " ]";
    }
 
    return value;
}


// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
     __LOG[4](name + " >> " + __STREAM);
// key: Der uebergebene Schluessel
// value: Der uebergebene Wert
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function replaceArray(key, value) {
     if (Array.isArray(value)) {
        __RET = value.map(function(element) {
                              return safeStringify(element, replaceArray, 0);
                          });


        return __RET;
    GM_setValue(name, __STREAM);
    }


     return value;
     return __STREAM;
}
}


// Moegliche einfache Ersetzungen mit '$'...
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
let textSubst;
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// return Objekt, das unter dem Namen gespeichert war
function deserialize(name, defValue = undefined) {
    const __STREAM = GM_getValue(name, defValue);


// Substituiert '$'-Parameter in einem Text
     __LOG[4](name + " << " + __STREAM);
// text: Urspruenglicher Text mit '$'-Befehlen
// par1: Der (erste) uebergebene Parameter
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function substParam(text, par1) {
     let ret = getValue(text, "");


     if (! textSubst) {
     if ((__STREAM !== undefined) && __STREAM.length) {
         textSubst  = {
         try {
                'n' : __DBMOD.name,
            return JSON.parse(__STREAM);
                'v' : __DBMOD.version,
        } catch (ex) {
                'V' : __DBMOD.Name
            __LOG[1](name + ": " + ex.message);
            };
        }
     }
     }


     for (let ch in textSubst) {
     return undefined;
        const __SUBST = textSubst[ch];
}


         ret = ret.replace('$' + ch, __SUBST);
// Setzt eine Option dauerhaft und laedt die Seite neu
// name: Name der Option als Speicherort
// value: Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gespeicherter Wert fuer setOptValue()
function setStored(name, value, reload = false, serial = false) {
    if (serial) {
        serialize(name, value);
    } else {
         GM_setValue(name, value);
     }
     }


    return ret.replace('$', par1);
     if (reload) {
}
         window.location.reload();
 
// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
// numberString: Dezimalzahl als String
// return Diese Dezimalzahl als String mit Tausender-Trennpunkten und Komma statt Dezimalpunkt
function getNumberString(numberString) {
     if (numberString.lastIndexOf('.') !== -1) {
        // Zahl enthaelt Dezimalpunkt
        const __VORKOMMA = numberString.substring(0, numberString.lastIndexOf('.'));
        const __NACHKOMMA = numberString.substring(numberString.lastIndexOf('.') + 1, numberString.length);
 
        return getNumberString(__VORKOMMA) + ',' + __NACHKOMMA;
    } else {
         // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
        const __TEMP = reverseString(numberString);
        let result = "";
 
        for (let i = 0; i < __TEMP.length; i++) {
            if ((i > 0) && (i % 3 === 0)) {
                result += '.';
            }
            result += __TEMP.substr(i, 1);
        }
 
        return reverseString(result);
     }
     }
}


// Dreht den uebergebenen String um
     return value;
// string: Eine Zeichenkette
// return Dieselbe Zeichenkette rueckwaerts
function reverseString(string) {
    let result = "";
 
    for (let i = string.length - 1; i >= 0; i--) {
        result += string.substr(i, 1);
    }
 
     return result;
}
}


// Speichert einen String/Integer/Boolean-Wert unter einem Namen ab
// Setzt den naechsten Wert aus einer Array-Liste als Option
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
// arr: Array-Liste mit den moeglichen Optionen
// value: Zu speichernder String/Integer/Boolean-Wert
// name: Name der Option als Speicherort
// return Promise auf ein Objekt, das 'name' und 'value' der Operation enthaelt
// value: Vorher gesetzter Wert
function storeValue(name, value) {
// reload: Seite mit neuem Wert neu laden
    __LOG[4](name + " >> " + value);
// return Gespeicherter Wert fuer setOptValue()
 
function setNextStored(arr, name, value, reload = false, serial = false) {
     return GM.setValue(name, value).then(voidValue => {
     return setStored(name, getNextValue(arr, value), reload, serial);
            __LOG[5]("OK " + name + " >> " + value);
 
            return Promise.resolve({
                    'name'  : name,
                    'value' : value
                });
        }, defaultCatch);
}
}


// Holt einen String/Integer/Boolean-Wert unter einem Namen zurueck
// Fuehrt die in einem Storage gespeicherte Operation aus
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
// return Promise fuer den String/Integer/Boolean-Wert, der unter dem Namen gespeichert war
function getStoredCmds(memory = undefined) {
function summonValue(name, defValue = undefined) {
     const __STORAGE = getMemory(memory);
     return GM.getValue(name, defValue).then(value => {
    const __MEMORY = __STORAGE.Value;
            __LOG[4](name + " << " + value);
    const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];


            return Promise.resolve(value);
    if (__MEMORY !== undefined) {
         }, ex => {
         const __GETITEM = function(item) {
            __LOG[0](name + ": " + ex.message);
                              return __MEMORY.getItem(__RUNPREFIX + item);
 
                          };
            return Promise.reject(ex);
        const __DELITEM = function(item) {
         }, defaultCatch);
                              return __MEMORY.removeItem(__RUNPREFIX + item);
}
                          };
         const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);


// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
        if (__CMD !== undefined) {
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
            const __KEY = __GETITEM('key');
// value: Beliebiger (strukturierter) Wert
            let value = __GETITEM('val');
// return Promise auf ein Objekt, das 'name' und 'value' in der String-Darstellung des Wertes enthaelt
function serialize(name, value) {
    const __STREAM = ((value !== undefined) ? safeStringify(value) : value);


    return storeValue(name, __STREAM);
            try {
}
                value = JSON.parse(value);
            } catch (ex) {
                __LOG[1]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
                // ... meist kann man den String selber aber speichern, daher kein "return"...
            }


// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
            __STOREDCMDS.push({
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
                                'cmd' : __CMD,
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
                                'key' : __KEY,
// return Promise fuer das Objekt, das unter dem Namen gespeichert war
                                'val' : value
function deserialize(name, defValue = undefined) {
                            });
    return summonValue(name).then(stream => {
         }
            if (stream && stream.length) {
                return JSON.parse(stream);
            } else {
                return defValue;
            }
         });
}


// Setzt die Seite gemaess der Aenderungen zurueck...
        __DELITEM('cmd');
// reload: Seite wird ganz neu geladen
         __DELITEM('key');
function refreshPage(reload = true) {
         __DELITEM('val');
    if (reload) {
         __LOG[2]("Seite wird neu geladen...");
         window.location.reload();
     }
     }
    return (__STOREDCMDS.length ? __STOREDCMDS : undefined);
}
}


// Setzt eine Option dauerhaft und laedt die Seite neu
// Fuehrt die in einem Storage gespeicherte Operation aus
// name: Name der Option als Speicherort
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// value: Zu setzender Wert
// optSet: Set mit den Optionen
// reload: Seite mit neuem Wert neu laden
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
// serial: Serialization fuer komplexe Daten
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// return Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined) {
// return Gespeicherter Wert fuer setOptValue()
     const __BEFORELOAD = getValue(beforeLoad, true);
function setStored(name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
    const __STOREDCMDS = getValue(storedCmds, []);
     (serial ? serialize(name, value)
    const __LOADEDCMDS = [];
            : storeValue(name, value))
    let invalidated = false;
                .then(onFulfilled, onRejected)
                .then(() => refreshPage(reload), defaultCatch); // Ende der Kette...


     return value;
     while (__STOREDCMDS.length) {
}
        const __STORED = __STOREDCMDS.shift();
        const __CMD = __STORED.cmd;
        const __KEY = __STORED.key;
        const __VAL = __STORED.val;


// Setzt den naechsten Wert aus einer Array-Liste als Option
        if (__BEFORELOAD) {
// arr: Array-Liste mit den moeglichen Optionen
            if (__STOREDCMDS.length) {
// name: Name der Option als Speicherort
                invalidateOpts(optSet);  // alle Optionen invalidieren
// value: Vorher gesetzter Wert
                invalidated = true;
// reload: Seite mit neuem Wert neu laden
            }
// serial: Serialization fuer komplexe Daten
            switch (__OPTACTION[__CMD]) {
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
            case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
                                  setStored(__KEY, __VAL, false, false);
// return Gespeicherter Wert fuer setOptValue()
                                  break;
function setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
            case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL);
    return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
}
                                  setStored(__KEY, __VAL, false, false);
 
                                  break;
// Fuehrt die in einem Storage gespeicherte Operation aus
            case __OPTACTION.RST : __LOG[4]("RESET (delayed)");
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
                                  __LOADEDCMDS.push(__STORED);
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
                                  break;
function getStoredCmds(memory = undefined) {
            default :              break;
    const __STORAGE = getMemory(memory);
            }
    const __MEMORY = __STORAGE.Value;
        } else {
    const __RUNPREFIX = __STORAGE.Prefix;
            switch (__OPTACTION[__CMD]) {
     const __STOREDCMDS = [];
            case __OPTACTION.SET :
            case __OPTACTION.NXT : __LOG[2]("SET/SETNEXT (undefined)");
                                  break;
            case __OPTACTION.RST : __LOG[4]("RESET");
                                  resetOptions(optSet, false);
                                  loadOptions(optSet); // Reset auf umbenannte Optionen anwenden!
                                  break;
            default :              break;
            }
        }
     }


     if (__MEMORY !== undefined) {
     return (__LOADEDCMDS.length ? __LOADEDCMDS : undefined);
        const __GETITEM = function(item) {
}
                              return __MEMORY.getItem(__RUNPREFIX + item);
                          };
        const __DELITEM = function(item) {
                              return __MEMORY.removeItem(__RUNPREFIX + item);
                          };
        const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);


        if (__CMD !== undefined) {
// Gibt eine Option sicher zurueck
            const __KEY = __GETITEM('key');
// opt: Config und Value der Option, ggfs. undefined
            let value = __GETITEM('val');
// defOpt: Rueckgabewert, falls undefined
// return Daten zur Option (oder defOpt)
function getOpt(opt, defOpt = { }) {
    return getValue(opt, defOpt);
}


            try {
// Gibt eine Option sicher zurueck (Version mit Key)
                value = JSON.parse(value);
// optSet: Platz fuer die gesetzten Optionen (und Config)
            } catch (ex) {
// item: Key der Option
                __LOG[0]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
// defOpt: Rueckgabewert, falls nicht zu finden
                // ... meist kann man den String selber aber speichern, daher kein "return"...
// return Daten zur Option (oder defOpt)
            }
function getOptByName(optSet, item, defOpt = { }) {
 
    if ((optSet !== undefined) && (item !== undefined)) {
            __STOREDCMDS.push({
         return getOpt(optSet[item], defOpt);
                                'cmd' : __CMD,
    } else {
                                'key' : __KEY,
         return defOpt;
                                'val' : value
                            });
        }
 
        __DELITEM('cmd');
         __DELITEM('key');
         __DELITEM('val');
     }
     }
    return (__STOREDCMDS.length ? __STOREDCMDS : undefined);
}
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// Gibt die Konfigurationsdaten einer Option zurueck
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// opt: Config und Value der Option
// optSet: Object mit den Optionen
// defConfig: Rueckgabewert, falls Config nicht zu finden
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
// return Konfigurationsdaten der Option
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
function getOptConfig(opt, defConfig = { }) {
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
    return getValue(getOpt(opt).Config, defConfig);
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
}
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {
 
     const __BEFORELOAD = getValue(beforeLoad, true);
// Setzt den Namen einer Option
     const __STOREDCMDS = getValue(storedCmds, []);
// opt: Config und Value der Option
     const __LOADEDCMDS = [];
// name: Zu setzender Name der Option
    let invalidated = false;
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Name der Option
function setOptName(opt, name) {
     const __CONFIG = getOptConfig(opt);
     const __NAME = getOptName(opt);
 
     if (__NAME !== name) {
        __LOG[4]("RENAME " + __NAME + " => " + name);


    while (__STOREDCMDS.length) {
         __CONFIG.Name = name;
         const __STORED = __STOREDCMDS.shift();
    }
        const __CMD = __STORED.cmd;
        const __KEY = __STORED.key;
        const __VAL = __STORED.val;


        if (__BEFORELOAD) {
    return name;
            if (__STOREDCMDS.length) {
}
                await invalidateOpts(optSet);  // alle Optionen invalidieren
 
                invalidated = true;
// Gibt den Namen einer Option zurueck
            }
// opt: Config und Value der Option
            switch (__OPTACTION[__CMD]) {
// return Name der Option
            case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL);
function getOptName(opt) {
                                  setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);
    const __CONFIG = getOptConfig(opt);
                                  break;
    const __NAME = __CONFIG.Name;
            case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL);
 
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false, onFulfilled, onRejected);
    if (! __NAME) {
                                  setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);
        const __SHARED = __CONFIG.Shared;
                                  break;
 
            case __OPTACTION.RST : __LOG[4]("RESET (delayed)");
        if (__SHARED && ! opt.Loaded) {
                                  __LOADEDCMDS.push(__STORED);
             const __OBJREF = getSharedRef(__SHARED, opt.Item);
                                  break;
 
            default :              break;
             return __OBJREF.getPath();
            }
        } else {
             switch (__OPTACTION[__CMD]) {
            case __OPTACTION.SET :
            case __OPTACTION.NXT : __LOG[2]("SET/SETNEXT (undefined)");
                                  break;
             case __OPTACTION.RST : __LOG[4]("RESET");
                                  await resetOptions(optSet, false);
                                  await loadOptions(optSet);  // Reset auf umbenannte Optionen anwenden!
                                  break;
            default :              break;
            }
         }
         }
        showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
     }
     }


     return (__LOADEDCMDS.length ? __LOADEDCMDS : undefined);
     return __NAME;
}
}


// Gibt eine Option sicher zurueck
// Setzt den Wert einer Option
// opt: Config und Value der Option, ggfs. undefined
// opt: Config und Value der Option
// defOpt: Rueckgabewert, falls undefined
// name: Zu setzender Wert der Option
// return Daten zur Option (oder defOpt)
// return Gesetzter Wert
function getOpt(opt, defOpt = { }) {
function setOptValue(opt, value) {
    return getValue(opt, defOpt);
    if (opt !== undefined) {
}
        if (! opt.ReadOnly) {
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));


// Gibt eine Option sicher zurueck (Version mit Key)
            opt.Value = value;
// optSet: Platz fuer die gesetzten Optionen (und Config)
        }
// item: Key der Option
         return opt.Value;
// defOpt: Rueckgabewert, falls nicht zu finden
// return Daten zur Option (oder defOpt)
function getOptByName(optSet, item, defOpt = { }) {
    if ((optSet !== undefined) && (item !== undefined)) {
         return getOpt(optSet[item], defOpt);
     } else {
     } else {
         return defOpt;
         return undefined;
     }
     }
}
}


// Gibt die Konfigurationsdaten einer Option zurueck
// Gibt den Wert einer Option zurueck
// opt: Config und Value der Option
// opt: Config und Value der Option
// defConfig: Rueckgabewert, falls Config nicht zu finden
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return Konfigurationsdaten der Option
// load: Laedt die Option per loadOption(), falls noetig
function getOptConfig(opt, defConfig = { }) {
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
     return getValue(getOpt(opt).Config, defConfig);
// return Gesetzter Wert
}
function getOptValue(opt, defValue = undefined, load = true, force = false) {
     let value;


// Setzt den Namen einer Option
    if (opt !== undefined) {
// opt: Config und Value der Option
        if (load && ! opt.Loaded) {
// name: Zu setzender Name der Option
            value = loadOption(opt, force);
// reload: Seite mit neuem Wert neu laden
        } else {
// return Gesetzter Name der Option
            value = opt.Value;
function setOptName(opt, name) {
         }
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
 
    if (__NAME !== name) {
        __LOG[4]("RENAME " + __NAME + " => " + name);
 
         __CONFIG.Name = name;
     }
     }


     return name;
     return valueOf(getValue(value, defValue));
}
}


// Gibt den Namen einer Option zurueck
// ==================== Ende Abschnitt fuer diverse Utilities ====================
// opt: Config und Value der Option
// return Name der Option
function getOptName(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = __CONFIG.Name;


    if (! __NAME) {
// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
        const __SHARED = __CONFIG.Shared;


        if (__SHARED && ! opt.Loaded) {
// Namen des Default-, Temporaer- und Null-Memories...
            const __OBJREF = getSharedRef(__SHARED, opt.Item);
const __MEMNORMAL  = 'normal';
const __MEMSESSION  = 'begrenzt';
const __MEMINAKTIVE = 'inaktiv';


            return __OBJREF.getPath();
// Definition des Default-, Dauer- und Null-Memories...
        }
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
const __OPTMEMSESSION  = __OPTMEM[__MEMSESSION];
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];
 
// Medium fuer die Datenbank (Speicher)
let myOptMem = __OPTMEMNORMAL;
let myOptMemSize;


        showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
// Infos ueber dieses Script-Modul
    }
const __DBMOD = new ScriptModule();
 
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
const __DBTOC = { };


    return __NAME;
// Daten zu den Modulen (indiziert durch die Script-Namen)
}
const __DBDATA = { };


// Setzt den Wert einer Option
// ==================== Abschnitt fuer Speicher ====================
// opt: Config und Value der Option
// name: Zu setzender Wert der Option
// return Gesetzter Wert
function setOptValue(opt, value) {
    if (opt !== undefined) {
        if (! opt.ReadOnly) {
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));


            opt.Value = value;
// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
        }
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
        return opt.Value;
// defMemory: Ersatz-Wert, falls memory undefined. Soll nur memory genutzt werden, dann z.B. null uebergeben!
    } else {
// return memory, falls okay, sonst einen Defaultwert
        return undefined;
function getMemory(memory = undefined, defMemory = getValue(myOptMem, __OPTMEMNORMAL)) {
    }
    return getValue(memory, defMemory);
}
}


// Gibt den Wert einer Option zurueck
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// opt: Config und Value der Option
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return true, wenn der Speichertest erfolgreich war
// load: Laedt die Option per loadOption(), falls noetig
function canUseMemory(memory = undefined) {
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
    const __STORAGE = getMemory(memory, { });
// return Gesetzter Wert
    const __MEMORY = __STORAGE.Value;
function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {
    let ret = false;
    let value;
 
    if (__MEMORY !== undefined) {
        const __TESTPREFIX = 'canUseStorageTest';
        const __TESTDATA = Math.random().toString();
        const __TESTITEM = __TESTPREFIX + __TESTDATA;


    if (opt !== undefined) {
         __MEMORY.setItem(__TESTITEM, __TESTDATA);
         if (load && ! opt.Loaded) {
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
            if (! opt.Promise) {
         __MEMORY.removeItem(__TESTITEM);
                loadOption(opt, force);
            }
            if (! asyncLoad) {
                __LOG[0]("Warnung: getOptValue(" + getOptName(opt) + ") fordert zum Nachladen auf, daher nur Default-Wert!");
            }
         } else {
            value = opt.Value;
        }
     }
     }


     return valueOf(getValue(value, defValue));
     __LOG[2]("canUseStorage(" + __STORAGE.Name + ") = " + ret);
 
    return ret;
}
}


// ==================== Ende Abschnitt fuer diverse Utilities ====================
// Ermittelt die Groesse des benutzten Speichers
 
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
// return Groesse des genutzten Speichers in Bytes
function getMemSize(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;


// Namen des Default-, Temporaer- und Null-Memories...
    //getMemUsage(__MEMORY);
const __MEMNORMAL  = 'normal';
const __MEMSESSION  = 'begrenzt';
const __MEMINAKTIVE = 'inaktiv';


// Definition des Default-, Dauer- und Null-Memories...
    if (__MEMORY !== undefined) {
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
        const __SIZE = safeStringify(__MEMORY).length;
const __OPTMEMSESSION  = __OPTMEM[__MEMSESSION];
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];


// Medium fuer die Datenbank (Speicher)
        __LOG[2]("MEM: " + __SIZE + " bytes");
let myOptMem = __OPTMEMNORMAL;
        return __SIZE;
let myOptMemSize;
    } else {
        return 0;
    }
}


// Infos ueber dieses Script-Modul
// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus
const __DBMOD = new ScriptModule();
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird
// out: Logfunktion, etwa console.log
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen)
// name: Name des Objekts
function getMemUsage(value = undefined, out = undefined, depth = -1, name = '$') {
    const __OUT = (out || __LOG[4]);


// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
    if ((typeof value) === 'string') {
const __DBTOC = { };
        const __SIZE = value.length;


// Daten zu den Modulen (indiziert durch die Script-Namen)
        __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.substr(0, 255));
const __DBDATA = { };
    } else if ((typeof value) === 'object') {
        if (depth === 0) {
            const __SIZE = safeStringify(value).length;


// ==================== Abschnitt fuer Speicher ====================
            __OUT("USAGE: " + name + '\t' + __SIZE);
        } else {
            depth--;
            for (let sub in value) {
                getMemUsage(value[sub], __OUT, depth, name + '.' + sub);
            }
            getMemUsage(value, __OUT, 0, name);
        }
    } else {
      const __DATA = (((typeof value) === 'function') ? "" : '\t' + value);


// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
        __OUT("USAGE: " + name + '\t' + (typeof value) + __DATA);
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
     }
// defMemory: Ersatz-Wert, falls memory undefined. Soll nur memory genutzt werden, dann z.B. null uebergeben!
// return memory, falls okay, sonst einen Defaultwert
function getMemory(memory = undefined, defMemory = getValue(myOptMem, __OPTMEMNORMAL)) {
     return getValue(memory, defMemory);
}
}


// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// opt: Option zur Wahl des Speichers
// return true, wenn der Speichertest erfolgreich war
// return Gesuchter Speicher oder Null-Speicher ('inaktiv')
function canUseMemory(memory = undefined) {
function restoreMemoryByOpt(opt) {
     const __STORAGE = getMemory(memory, { });
    // Memory Storage fuer vorherige Speicherung...
    const __MEMORY = __STORAGE.Value;
     const __STORAGE = getOptValue(opt, __MEMNORMAL, true, true);
    let ret = false;


     if (__MEMORY !== undefined) {
     return __OPTMEM[__STORAGE];
        const __TESTPREFIX = 'canUseStorageTest';
}
        const __TESTDATA = Math.random().toString();
        const __TESTITEM = __TESTPREFIX + __TESTDATA;


        __MEMORY.setItem(__TESTITEM, __TESTDATA);
// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
// opt: Option zur Wahl des Speichers
         __MEMORY.removeItem(__TESTITEM);
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
function startMemoryByOpt(opt, saveOpt = undefined) {
    // Memory Storage fuer naechste Speicherung...
    let storage = getOptValue(opt, __MEMNORMAL);
    let optMem = __OPTMEM[storage];
 
    if (! canUseMemory(optMem)) {
        if (storage !== __MEMINAKTIVE) {
            storage = __MEMINAKTIVE;
            optMem = __OPTMEM[storage];
         }
     }
     }


     __LOG[2]("canUseStorage(" + __STORAGE.Name + ") = " + ret);
     if (saveOpt !== undefined) {
        setOpt(saveOpt, storage, false);
    }


     return ret;
     return optMem;
}
}


// Ermittelt die Groesse des benutzten Speichers
// ==================== Ende Abschnitt fuer Speicher ====================
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return Groesse des genutzten Speichers in Bytes
function getMemSize(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;


    //getMemUsage(__MEMORY);
// ==================== Abschnitt fuer die Scriptdatenbank ====================


    if (__MEMORY !== undefined) {
// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
        const __SIZE = safeStringify(__MEMORY).length;
// meta: Metadaten des Scripts (Default: GM_info.script)
// return Beschreibende Daten fuer __DBMOD
function ScriptModule(meta) {
    'use strict';


        __LOG[2]("MEM: " + __SIZE + " bytes");
    const __META = getValue(meta, GM_info.script);
        return __SIZE;
     const __PROPS = {
     } else {
                'name'        : true,
        return 0;
                'version'     : true,
     }
                'namespace'  : true,
}
                'description' : true
            };


// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus
     const __DBMOD = { };
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird
// out: Logfunktion, etwa __LOG[4]
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen)
// name: Name des Objekts
function getMemUsage(value = undefined, out = undefined, depth = -1, name = '$') {
     const __OUT = (out || __LOG[4]);


     if ((typeof value) === 'string') {
     __LOG[5](__META);
        const __SIZE = value.length;


        __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.slice(0, 255));
    // Infos zu diesem Script...
     } else if ((typeof value) === 'object') {
     addProps(__DBMOD, __META, __PROPS);
        if (depth === 0) {
            const __SIZE = safeStringify(value).length;


            __OUT("USAGE: " + name + '\t' + __SIZE);
    // Voller Name fuer die Ausgabe...
        } else {
    Object.defineProperty(__DBMOD, 'Name', {
            depth--;
                    get : function() {
            for (let sub in value) {
                              return this.name + " (" + this.version + ')';
                getMemUsage(value[sub], __OUT, depth, name + '.' + sub);
                          },
            }
                    set : undefined
            getMemUsage(value, __OUT, 0, name);
                });
        }
    } else {
      const __DATA = (((typeof value) === 'function') ? "" : '\t' + value);


        __OUT("USAGE: " + name + '\t' + (typeof value) + __DATA);
    __LOG[2](__DBMOD);
    }
}


// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
     return __DBMOD;
// opt: Option zur Wahl des Speichers
// return Promise auf gesuchten Speicher oder Null-Speicher ('inaktiv')
async function restoreMemoryByOpt(opt) {
    // Memory Storage fuer vorherige Speicherung...
    const __STORAGE = await getOptValue(opt, __MEMNORMAL, true, true, true);
 
     return __OPTMEM[__STORAGE];
}
}


// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
Class.define(ScriptModule, Object);
// opt: Option zur Wahl des Speichers
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
function startMemoryByOpt(opt, saveOpt = undefined, onFulfilled = undefined, onRejected = undefined) {
    // Memory Storage fuer naechste Speicherung...
    let storage = getOptValue(opt, __MEMNORMAL);
    let optMem = __OPTMEM[storage];


    if (! canUseMemory(optMem)) {
// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
        if (storage !== __MEMINAKTIVE) {
// optSet: Gesetzte Optionen (und Config)
            storage = __MEMINAKTIVE;
function initScriptDB(optSet) {
            optMem = __OPTMEM[storage];
    // Speicher fuer die DB-Daten...
        }
    const __DBMEM = myOptMem.Value;
    }


     if (saveOpt !== undefined) {
     __DBTOC.versions = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
        setOpt(saveOpt, storage, false, onFulfilled, onRejected);
     __DBTOC.namespaces = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.namespaces')), { });
     }


     return optMem;
     // Zunaechst den alten Eintrag entfernen...
    delete __DBTOC.versions[__DBMOD.name];
    delete __DBTOC.namespaces[__DBMOD.name];
 
    if (__DBMEM !== undefined) {
        // ... und die Daten der Fremdscripte laden...
        for (let module in __DBTOC.versions) {
            scriptDB(module, getValue(JSON.parse(__DBMEM.getItem('__DBDATA.' + module)), { }));
        }
    }
}
}


// ==================== Ende Abschnitt fuer Speicher ====================
// Setzt die Daten dieses Scriptes in der Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// optSet: Gesetzte Optionen (und Config)
function updateScriptDB(optSet) {
    // Eintrag ins Inhaltsverzeichnis...
    __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
    __DBTOC.namespaces[__DBMOD.name] = __DBMOD.namespace;


// ==================== Abschnitt fuer die Scriptdatenbank ====================
    // Speicher fuer die DB-Daten...
    const __DBMEM = myOptMem.Value;


// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
    if (__DBMEM !== undefined) {
// meta: Metadaten des Scripts (Default: GM.info.script)
        // Permanente Speicherung der Eintraege...
// return Beschreibende Daten fuer __DBMOD
        __DBMEM.setItem('__DBTOC.versions', safeStringify(__DBTOC.versions));
function ScriptModule(meta) {
        __DBMEM.setItem('__DBTOC.namespaces', safeStringify(__DBTOC.namespaces));
    'use strict';
        __DBMEM.setItem('__DBDATA.' + __DBMOD.name, safeStringify(optSet));


    const __META = getValue(meta, GM.info.script);
        // Aktualisierung der Speichergroesse...
     const __PROPS = {
        myOptMemSize = getMemSize(myOptMem);
                'name'        : true,
     }
                'version'    : true,
                'namespace'  : true,
                'description' : true
            };


     const __DBMOD = { };
     // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
    scriptDB(__DBMOD.name, getValue(optSet, { }));


     __LOG[5](__META);
     __LOG[2](__DBDATA);
}


    // Infos zu diesem Script...
// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
     addProps(__DBMOD, __META, __PROPS);
// module: Gesetzte Optionen (und Config)
// initValue: Falls angegeben, zugewiesener Startwert
// return Daten zu diesem Modul
function scriptDB(module, initValue = undefined) {
    const __NAMESPACE = __DBTOC.namespaces[module];
     const __DBMODS = getProp(__DBDATA, __NAMESPACE, { });


     // Voller Name fuer die Ausgabe...
     if (initValue !== undefined) {
    Object.defineProperty(__DBMOD, 'Name', {
        return (__DBMODS[module] = initValue);
                    get : function() {
    } else {
                              return this.name + " (" + this.version + ')';
        return getProp(__DBMODS, module, { });
                          },
    }
                    set : undefined
}
                });


    __LOG[2](__DBMOD);
// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================


    return __DBMOD;
// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
}


Class.define(ScriptModule, Object);
// ==================== Abschnitt fuer das Benutzermenu ====================


// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// Zeigt den Eintrag im Menu einer Option
// optSet: Gesetzte Optionen (und Config)
// val: Derzeitiger Wert der Option
function initScriptDB(optSet) {
// menuOn: Text zum Setzen im Menu
    // Speicher fuer die DB-Daten...
// funOn: Funktion zum Setzen
     const __DBMEM = myOptMem.Value;
// keyOn: Hotkey zum Setzen im Menu
// menuOff: Text zum Ausschalten im Menu
// funOff: Funktion zum Ausschalten
// keyOff: Hotkey zum Ausschalten im Menu
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
    const __ON  = (val ? '*' : "");
     const __OFF = (val ? "" : '*');


     __DBTOC.versions = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
     __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
    __DBTOC.namespaces = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.namespaces')), { });


    // Zunaechst den alten Eintrag entfernen...
     if (val) {
    delete __DBTOC.versions[__DBMOD.name];
         GM_registerMenuCommand(menuOff, funOff, keyOff);
    delete __DBTOC.namespaces[__DBMOD.name];
    } else {
 
        GM_registerMenuCommand(menuOn, funOn, keyOn);
     if (__DBMEM !== undefined) {
         // ... und die Daten der Fremdscripte laden...
        for (let module in __DBTOC.versions) {
            scriptDB(module, getValue(JSON.parse(__DBMEM.getItem('__DBDATA.' + module)), { }));
        }
     }
     }
}
}


// Setzt die Daten dieses Scriptes in der Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// optSet: Gesetzte Optionen (und Config)
// val: Derzeitiger Wert der Option
function updateScriptDB(optSet) {
// arr: Array-Liste mit den moeglichen Optionen
     // Eintrag ins Inhaltsverzeichnis...
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
    __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
// fun: Funktion zum Setzen des naechsten Wertes
     __DBTOC.namespaces[__DBMOD.name] = __DBMOD.namespace;
// key: Hotkey zum Setzen des naechsten Wertes im Menu
function registerNextMenuOption(val, arr, menu, fun, key) {
     const __MENU = menu.replace('$', val);
     let options = "OPTION " + __MENU;


     // Speicher fuer die DB-Daten...
     for (let value of arr) {
     const __DBMEM = myOptMem.Value;
        if (value === val) {
            options += " / *" + value + '*';
        } else {
            options += " / " + value;
        }
    }
     __LOG[3](options);


     if (__DBMEM !== undefined) {
     GM_registerMenuCommand(__MENU, fun, key);
        // Permanente Speicherung der Eintraege...
}
        __DBMEM.setItem('__DBTOC.versions', safeStringify(__DBTOC.versions));
        __DBMEM.setItem('__DBTOC.namespaces', safeStringify(__DBTOC.namespaces));
        __DBMEM.setItem('__DBDATA.' + __DBMOD.name, safeStringify(optSet));


        // Aktualisierung der Speichergroesse...
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
        myOptMemSize = getMemSize(myOptMem);
// val: Derzeitiger Wert der Option
     }
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// fun: Funktion zum Setzen des naechsten Wertes
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
// serial: Serialization fuer komplexe Daten
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
    const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
    const __MENU = getValue(menu, "").replace('$', __VALUE);
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);


     // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
     __LOG[hidden ? 4 : 3](__OPTIONS);
    scriptDB(__DBMOD.name, getValue(optSet, { }));


     __LOG[2](__DBDATA);
     if (! hidden) {
        GM_registerMenuCommand(__MENU, fun, key);
    }
}
}


// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
// Zeigt den Eintrag im Menu einer Option
// module: Gesetzte Optionen (und Config)
// opt: Config und Value der Option
// initValue: Falls angegeben, zugewiesener Startwert
function registerOption(opt) {
// return Daten zu diesem Modul
     const __CONFIG = getOptConfig(opt);
function scriptDB(module, initValue = undefined) {
     const __VALUE = getOptValue(opt);
     const __NAMESPACE = __DBTOC.namespaces[module];
    const __LABEL = __CONFIG.Label;
     const __DBMODS = getProp(__DBDATA, __NAMESPACE, { });
    const __ACTION = opt.Action;
    const __HOTKEY = __CONFIG.Hotkey;
    const __HIDDEN = __CONFIG.HiddenMenu;
    const __SERIAL = __CONFIG.Serial;


     if (initValue !== undefined) {
     if (! __CONFIG.HiddenMenu) {
         return (__DBMODS[module] = initValue);
         switch (__CONFIG.Type) {
        case __OPTTYPES.MC : registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
                            break;
        case __OPTTYPES.SW : registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
                            break;
        case __OPTTYPES.TF : registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                            break;
        case __OPTTYPES.SD : registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
                            break;
        case __OPTTYPES.SI : registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
                            break;
        default :            break;
        }
     } else {
     } else {
         return getProp(__DBMODS, module, { });
         // Nur Anzeige im Log...
        registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
     }
     }
}
}


// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
// ==================== Ende Abschnitt fuer das Benutzermenu ====================


// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
// Initialisiert die gesetzten Option
 
// config: Konfiguration der Option
// ==================== Abschnitt fuer das Benutzermenu ====================
// setValue: Zu uebernehmender Default-Wert (z.B. der jetzt gesetzte)
// return Initialwert der gesetzten Option
function initOptValue(config, setValue = undefined) {
    let value = getValue(setValue, config.Default);  // Standard


// Zeigt den Eintrag im Menu einer Option
    if (config.SharedData !== undefined) {
// val: Derzeitiger Wert der Option
        value = config.SharedData;
// menuOn: Text zum Setzen im Menu
     }
// funOn: Funktion zum Setzen
// keyOn: Hotkey zum Setzen im Menu
// menuOff: Text zum Ausschalten im Menu
// funOff: Funktion zum Ausschalten
// keyOff: Hotkey zum Ausschalten im Menu
// return Promise von GM.registerMenuCommand()
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
    const __ON  = (val ? '*' : "");
     const __OFF = (val ? "" : '*');


     __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
     switch (config.Type) {
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
                            value = config.Choice[0];
                        }
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
    }


     if (val) {
     if (config.Serial || config.Hidden) {
        return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);
         config.HiddenMenu = true;
    } else {
         return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);
     }
     }
    return value;
}
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// Initialisiert die Menue-Funktion einer Option
// val: Derzeitiger Wert der Option
// optAction: Typ der Funktion
// arr: Array-Liste mit den moeglichen Optionen
// item: Key der Option
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// fun: Funktion zum Setzen des naechsten Wertes
// optConfig: Konfiguration der Option
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// return Funktion fuer die Option
// return Promise von GM.registerMenuCommand()
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
function registerNextMenuOption(val, arr, menu, fun, key) {
    let fun;
    const __MENU = substParam(menu, val);
 
    let options = "OPTION " + __MENU;
    if (optAction !== undefined) {
        const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
        const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);


    for (let value of arr) {
        switch (optAction) {
         if (value === val) {
         case __OPTACTION.SET : fun = function() {
            options += " / *" + value + '*';
                                      return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD);
        } else {
                                  };
            options += " / " + value;
                              break;
        case __OPTACTION.NXT : fun = function() {
                                      return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice);
                                  };
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :              break;
         }
         }
     }
     }
    __LOG[3](options);


     return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
     return fun;
}
}


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
// val: Derzeitiger Wert der Option
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// item: Key der Option
// fun: Funktion zum Setzen des naechsten Wertes
// return ObjRef, die das Ziel definiert
// key: Hotkey zum Setzen des naechsten Wertes im Menu
function getSharedRef(shared, item = undefined) {
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
     if (shared === undefined) {
// serial: Serialization fuer komplexe Daten
        return undefined;
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
    }
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
 
     const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
    const __OBJREF = new ObjRef(__DBDATA); // Gemeinsame Daten
     const __MENU = substParam(menu, __VALUE);
     const __PROPS = [ 'namespace', 'module', 'item' ];
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
     const __DEFAULTS = [ __DBMOD.namespace, __DBMOD.name, item ];
                      getValue(__VALUE, "", " = " + __VALUE);
 
    for (let stage in __PROPS) {
        const __DEFAULT = __DEFAULTS[stage];
        const __PROP = __PROPS[stage];
        const __NAME = shared[__PROP];


    __LOG[hidden ? 4 : 3](__OPTIONS);
        if (__NAME === '$') {
            break;
        }


    if (hidden) {
         __OBJREF.chDir(getValue(__NAME, __DEFAULT));
         return Promise.resolve(__VALUE);
    } else {
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
     }
     }
    return __OBJREF;
}
}


// Zeigt den Eintrag im Menu einer Option
// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
// opt: Config und Value der Option
// optConfig: Konfiguration der Option
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
// item: Key der Option
function registerOption(opt) {
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
     const __CONFIG = getOptConfig(opt);
function getSharedConfig(optConfig, item = undefined) {
     const __VALUE = getOptValue(opt);
     let config = getValue(optConfig, { });
     const __LABEL = __CONFIG.Label;
     const __SHARED = config.Shared;
    const __ACTION = opt.Action;
 
    const __HOTKEY = __CONFIG.Hotkey;
     if (__SHARED !== undefined) {
    const __HIDDEN = __CONFIG.HiddenMenu;
        const __OBJREF = getSharedRef(__SHARED, item); // Gemeinsame Daten
    const __SERIAL = __CONFIG.Serial;
 
        if (getValue(__SHARED.item, '$') !== '$') {  // __REF ist ein Item
            const __REF = valueOf(__OBJREF);


    if (! __CONFIG.HiddenMenu) {
            config = { };  // Neu aufbauen...
        switch (__CONFIG.Type) {
            addProps(config, getOptConfig(__REF));
        case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
            addProps(config, optConfig);
        case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
            config.setConst('SharedData', getOptValue(__REF));
                                                      __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
         } else {  // __REF enthaelt die Daten selbst
         case __OPTTYPES.TF : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
            if (! config.Name) {
                                                      __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                config.Name = __OBJREF.getPath();
        case __OPTTYPES.SD : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
            }
        case __OPTTYPES.SI : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
            config.setConst('SharedData', __OBJREF); // Achtung: Ggfs. zirkulaer!
        default :           return Promise.resolve(__VALUE);
         }
         }
    } else {
        // Nur Anzeige im Log...
        return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
     }
     }
    return config;
}
}


// ==================== Ende Abschnitt fuer das Benutzermenu ====================
// Initialisiert die gesetzten Optionen
 
// optConfig: Konfiguration der Optionen
// Initialisiert die gesetzten Option
// optSet: Platz fuer die gesetzten Optionen
// config: Konfiguration der Option
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
// setValue: Zu uebernehmender Default-Wert (z.B. der jetzt gesetzte)
// return Gefuelltes Objekt mit den gesetzten Optionen
// return Initialwert der gesetzten Option
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
function initOptValue(config, setValue = undefined) {
     let value;
     let value = getValue(setValue, config.Default); // Standard


     if (config.SharedData !== undefined) {
     if (optSet === undefined) {
         value = config.SharedData;
         optSet = { };
     }
     }


     switch (config.Type) {
     for (let opt in optConfig) {
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
        const __OPTCONFIG = optConfig[opt];
                            value = config.Choice[0];
        const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
                        }
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
    }


    if (config.Serial || config.Hidden) {
        if ((preInit === undefined) || (__PREINIT === preInit)) {
        config.HiddenMenu = true;
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
    }
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat? Wurde ggfs. bereits geaendert...
            const __USESTUB = ((preInit === false) && __PREINIT);
            const __LOADED = (__USESTUB && optSet[opt].Loaded);
            const __VALUE = (__USESTUB ? optSet[opt].Value : undefined);


     return value;
            optSet[opt] = {
                'Item'      : opt,
                'Config'    : __CONFIG,
                'Loaded'    : (__ISSHARED || __LOADED),
                'Value'    : initOptValue(__CONFIG, __VALUE),
                'SetValue'  : __CONFIG.SetValue,
                'ReadOnly'  : (__ISSHARED || __CONFIG.ReadOnly),
                'Action'    : initOptAction(__CONFIG.Action, opt, optSet, __CONFIG),
                'AltAction' : initOptAction(__ALTACTION, opt, optSet, __CONFIG)
            };
        } else if (preInit) {  // erstmal nur Stub
            optSet[opt] = {
                'Item'      : opt,
                'Config'    : __OPTCONFIG,
                'Loaded'    : false,
                'Value'    : initOptValue(__OPTCONFIG),
                'ReadOnly'  : (__ISSHARED || __OPTCONFIG.ReadOnly)
            };
        }
    }
 
     return optSet;
}
}


// Initialisiert die Menue-Funktion einer Option
    // Abhaengigkeiten:
// optAction: Typ der Funktion
    // ================
// item: Key der Option
    // initOptions (PreInit):
// optSet: Platz fuer die gesetzten Optionen (und Config)
    // restoreMemoryByOpt: PreInit oldStorage
// optConfig: Konfiguration der Option
    // getStoredCmds: restoreMemoryByOpt
// return Funktion fuer die Option
    // runStoredCmds (beforeLoad): getStoredCmds
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
    // loadOptions (PreInit): PreInit
     let fun;
     // startMemoryByOpt: storage oldStorage
 
    // initScriptDB: startMemoryByOpt
     if (optAction !== undefined) {
     // initOptions (Rest): PreInit
        const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
    // getMyTeam callback (getOptPrefix): initTeam
        const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);
    // __MYTEAM (initTeam): initOptions
    // renameOptions: getOptPrefix
    // runStoredCmds (afterLoad): getStoredCmds, renameOptions
    // loadOptions (Rest): PreInit/Rest runStoredCmds
    // updateScriptDB: startMemoryByOpt
    // showOptions: startMemoryByOpt renameOptions
    // buildMenu: showOptions
    // buildForm: showOptions


        switch (optAction) {
// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
        case __OPTACTION.SET : fun = function() {
// optConfig: Konfiguration der Optionen
                                      return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);
// optSet: Platz fuer die gesetzten Optionen
                                  };
// return Gefuelltes Objekt mit den gesetzten Optionen
                              break;
function startOptions(optConfig, optSet = undefined, classification = undefined) {
        case __OPTACTION.NXT : fun = function() {
    optSet = initOptions(optConfig, optSet, true); // PreInit
                                      return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice).catch(defaultCatch);
                                  };
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD).then(
                                              result => __LOG[3]("RESETTING (" + result + ")..."),
                                              defaultCatch);
                                  };
                              break;
        default :              break;
        }
    }


     return fun;
     // Memory Storage fuer vorherige Speicherung...
}
    myOptMemSize = getMemSize(myOptMem = restoreMemoryByOpt(optSet.oldStorage));


// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
    // Zwischengespeicherte Befehle auslesen...
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
    const __STOREDCMDS = getStoredCmds(myOptMem);
// item: Key der Option
 
// return ObjRef, die das Ziel definiert
    // ... ermittelte Befehle ausführen...
function getSharedRef(shared, item = undefined) {
    const __LOADEDCMDS = runStoredCmds(__STOREDCMDS, optSet, true);  // BeforeLoad
     if (shared === undefined) {
 
        return undefined;
    // Bisher noch nicht geladenene Optionen laden...
    }
     loadOptions(optSet);


     const __OBJREF = new ObjRef(__DBDATA);  // Gemeinsame Daten
     // Memory Storage fuer naechste Speicherung...
     const __PROPS = [ 'namespace', 'module', 'item' ];
     myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));
    const __DEFAULTS = [ __DBMOD.namespace, __DBMOD.name, item ];


     for (let stage in __PROPS) {
     // Globale Daten ermitteln...
        const __DEFAULT = __DEFAULTS[stage];
    initScriptDB(optSet);
        const __PROP = __PROPS[stage];
        const __NAME = shared[__PROP];


        if (__NAME === '$') {
    optSet = initOptions(optConfig, optSet, false); // Rest
            break;
        }


         __OBJREF.chDir(getValue(__NAME, __DEFAULT));
    if (classification !== undefined) {
        // Umbenennungen durchfuehren...
         classification.renameOptions();
     }
     }


     return __OBJREF;
    // ... ermittelte Befehle ausführen...
    runStoredCmds(__LOADEDCMDS, optSet, false);  // Rest
 
    // Als globale Daten speichern...
    updateScriptDB(optSet);
 
     return optSet;
}
}


// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
// Installiert die Visualisierung und Steuerung der Optionen
// optConfig: Konfiguration der Option
// optSet: Platz fuer die gesetzten Optionen
// item: Key der Option
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function getSharedConfig(optConfig, item = undefined) {
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
     let config = getValue(optConfig, { });
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     const __SHARED = config.Shared;
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     if (! optParams.hideMenu) {
        buildMenu(optSet);
     }


     if (__SHARED !== undefined) {
     if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
        const __OBJREF = getSharedRef(__SHARED, item);  // Gemeinsame Daten
        buildForm(optParams.menuAnchor, optSet, optParams);
 
        if (getValue(__SHARED.item, '$') !== '$') {  // __OBJREF ist ein Item
            const __REF = valueOf(__OBJREF);
 
            config = { };  // Neu aufbauen...
            addProps(config, getOptConfig(__REF));
            addProps(config, optConfig);
            config.setConst('SharedData', getOptValue(__REF), false);   // Wert muss schon da sein, NICHT nachladen, sonst ggfs. Promise
        } else {  // __OBJREF enthaelt die Daten selbst
            if (! config.Name) {
                config.Name = __OBJREF.getPath();
            }
            config.setConst('SharedData', __OBJREF);  // Achtung: Ggfs. zirkulaer!
        }
     }
     }
}


     return config;
// Setzt eine Option auf einen vorgegebenen Wert
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// opt: Config und vorheriger Value der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setOpt(opt, value, reload = false) {
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
}
}


// Initialisiert die gesetzten Optionen
// Ermittelt die naechste moegliche Option
// optConfig: Konfiguration der Optionen
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen
// value: Ggfs. zu setzender Wert
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
// return Zu setzender Wert
// return Gefuelltes Objekt mit den gesetzten Optionen
function getNextOpt(opt, value = undefined) {
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
    const __CONFIG = getOptConfig(opt);
     let value;
     const __VALUE = getOptValue(opt, value);


     if (optSet === undefined) {
     switch (__CONFIG.Type) {
        optSet = { };
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
     }
     }


     for (let opt in optConfig) {
     return __VALUE;
        const __OPTCONFIG = optConfig[opt];
}
        const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);


        if ((preInit === undefined) || (__PREINIT === preInit)) {
// Setzt die naechste moegliche Option
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
// opt: Config und Value der Option
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
// value: Default fuer ggfs. zu setzenden Wert
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?
// reload: Seite mit neuem Wert neu laden
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);
// return Gesetzter Wert
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);
function setNextOpt(opt, value = undefined, reload = false) {
            const __VALUE = (__LOADED ? optSet[opt].Value : undefined);
    return setOpt(opt, getNextOpt(opt, value), reload);
}


            optSet[opt] = {
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab
                'Item'      : opt,
// opt: Config und Value der Option
                'Config'    : __CONFIG,
// value: Default fuer ggfs. zu setzenden Wert
                'Loaded'    : (__ISSHARED || __LOADED),
// reload: Seite mit neuem Wert neu laden
                'Promise'  : __PROMISE,
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
                'Value'    : initOptValue(__CONFIG, __VALUE),
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
                'SetValue'  : __CONFIG.SetValue,
// return Gesetzter Wert
                'ReadOnly'  : (__ISSHARED || __CONFIG.ReadOnly),
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3) {
                'Action'    : initOptAction(__CONFIG.Action, opt, optSet, __CONFIG),
    const __CONFIG = getOptConfig(opt);
                'AltAction' : initOptAction(__ALTACTION, opt, optSet, __CONFIG)
    const __CHOICE = __CONFIG.Choice;
            };
 
        } else if (preInit) {  // erstmal nur Stub
     if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {
            optSet[opt] = {
        return setNextOpt(opt, value, reload);
                'Item'      : opt,
                'Config'    : __OPTCONFIG,
                'Loaded'    : false,
                'Promise'  : undefined,
                'Value'     : initOptValue(__OPTCONFIG),
                'ReadOnly'  : (__ISSHARED || __OPTCONFIG.ReadOnly)
            };
        }
     }
     }


     return optSet;
     const __VALUE = getOptValue(opt, value);
}


     // Abhaengigkeiten:
     try {
    // ================
        const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
    // initOptions (PreInit):
        let message = "";
    // restoreMemoryByOpt: PreInit oldStorage
    // getStoredCmds: restoreMemoryByOpt
    // runStoredCmds (beforeLoad): getStoredCmds
    // loadOptions (PreInit): PreInit
    // startMemoryByOpt: storage oldStorage
    // initScriptDB: startMemoryByOpt
    // initOptions (Rest): PreInit
    // getMyTeam callback (getOptPrefix): initTeam
    // __MYTEAM (initTeam): initOptions
    // renameOptions: getOptPrefix
    // runStoredCmds (afterLoad): getStoredCmds, renameOptions
    // loadOptions (Rest): PreInit/Rest runStoredCmds
    // updateScriptDB: startMemoryByOpt
    // showOptions: startMemoryByOpt renameOptions
    // buildMenu: showOptions
    // buildForm: showOptions


// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
        if (selValue) {
// optConfig: Konfiguration der Optionen
            for (let index = 0; index < __CHOICE.length; index++) {
// optSet: Platz fuer die gesetzten Optionen
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
// return Promise auf gefuelltes Objekt mit den gesetzten Optionen
            }
async function startOptions(optConfig, optSet = undefined, classification = undefined) {
            message += "\nNummer eingeben:";
    optSet = initOptions(optConfig, optSet, true); // PreInit
        } else {
            message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
        }


    // Memory Storage fuer vorherige Speicherung...
        const __ANSWER = prompt(message, __NEXTVAL);
    myOptMemSize = getMemSize(myOptMem = await restoreMemoryByOpt(optSet.oldStorage));


    // Zwischengespeicherte Befehle auslesen...
        if (__ANSWER) {
    const __STOREDCMDS = getStoredCmds(myOptMem);
            const __INDEX = parseInt(__ANSWER, 10) - 1;
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);


    // ... ermittelte Befehle ausfuehren...
            if (nextVal === undefined) {
    const __LOADEDCMDS = await runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
                const __CASTVAL = this[__VALTYPE](__ANSWER);


    // Bisher noch nicht geladenene Optionen laden...
                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
    await loadOptions(optSet);
                    nextVal = __CASTVAL;
                }
            }


    // Memory Storage fuer naechste Speicherung...
            if (nextVal !== __VALUE) {
    myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));
                if (nextVal) {
                    return setOpt(opt, nextVal, reload);
                }


    // Globale Daten ermitteln...
                const __LABEL = __CONFIG.Label.replace('$', __VALUE);
    initScriptDB(optSet);


    optSet = initOptions(optConfig, optSet, false); // Rest
                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
 
            }
     if (classification !== undefined) {
        }
         // Umbenennungen durchfuehren...
     } catch (ex) {
        await classification.renameOptions();
         __LOG[1]("promptNextOpt: " + ex.message);
     }
     }


     // ... ermittelte Befehle ausfuehren...
     return __VALUE;
    await runStoredCmds(__LOADEDCMDS, optSet, false);  // Rest
}


    // Als globale Daten speichern...
// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
     updateScriptDB(optSet);
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setOptByName(optSet, item, value, reload = false) {
     const __OPT = getOptByName(optSet, item);


     return optSet;
     return setOpt(__OPT, value, reload);
}
}


// Installiert die Visualisierung und Steuerung der Optionen
// Ermittelt die naechste moegliche Option (Version mit Key)
// optSet: Platz fuer die gesetzten Optionen
// optSet: Platz fuer die gesetzten Optionen (und Config)
// optParams: Eventuell notwendige Parameter zur Initialisierung
// item: Key der Option
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// value: Default fuer ggfs. zu setzenden Wert
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// return Zu setzender Wert
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
function getNextOptByName(optSet, item, value = undefined) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __OPT = getOptByName(optSet, item);
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return Liefert die gesetzten Optionen zurueck
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     if (! optParams.hideMenu) {
        buildMenu(optSet).then(() => __LOG[3]("Menu OK"));
    }


     if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
     return getNextOpt(__OPT, value);
        buildForm(optParams.menuAnchor, optSet, optParams);
    }
 
    return optSet;
}
}


// Setzt eine Option auf einen vorgegebenen Wert
// Setzt die naechste moegliche Option (Version mit Key)
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// optSet: Platz fuer die gesetzten Optionen (und Config)
// opt: Config und vorheriger Value der Option
// item: Key der Option
// value: (Bei allen Typen) Zu setzender Wert
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// reload: Seite mit neuem Wert neu laden
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
// return Gesetzter Wert
function setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
function setNextOptByName(optSet, item, value = undefined, reload = false) {
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));
    const __OPT = getOptByName(optSet, item);
 
     return setNextOpt(__OPT, value, reload);
}
}


// Ermittelt die naechste moegliche Option
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// value: Ggfs. zu setzender Wert
// item: Key der Option
// return Zu setzender Wert
// value: Default fuer ggfs. zu setzenden Wert
function getNextOpt(opt, value = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);
 
    switch (__CONFIG.Type) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
    }
 
    return __VALUE;
}
 
// Setzt die naechste moegliche Option
// opt: Config und Value der Option
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
function setNextOpt(opt, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
    return setOpt(opt, getNextOpt(opt, value), reload, onFulfilled, onRejected);
}
 
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab
// opt: Config und Value der Option
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// reload: Seite mit neuem Wert neu laden
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
// return Gesetzter Wert
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
function promptNextOptByName(optSet, item, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3) {
     const __CONFIG = getOptConfig(opt);
     const __OPT = getOptByName(optSet, item);
    const __CHOICE = __CONFIG.Choice;


     if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {
     return promptNextOpt(__OPT, value, reload, freeValue, selValue, minChoice);
        return setNextOpt(opt, value, reload, onFulfilled, onRejected);
}
    }


     const __VALUE = getOptValue(opt, value);
// Baut das Benutzermenu auf
// optSet: Gesetzte Optionen
function buildMenu(optSet) {
     __LOG[3]("buildMenu()");


     try {
     for (let opt in optSet) {
         const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
        registerOption(optSet[opt]);
         let message = "";
    }
}
 
// Invalidiert eine (ueber Menu) gesetzte Option
// opt: Zu invalidierende Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
function invalidateOpt(opt, force = false) {
    if (! opt.ReadOnly) {
         const __CONFIG = getOptConfig(opt);
 
         // Wert "ungeladen"...
        opt.Loaded = (force || ! __CONFIG.AutoReset);


         if (selValue) {
         if (opt.Loaded && __CONFIG.AutoReset) {
             for (let index = 0; index < __CHOICE.length; index++) {
             // Nur zuruecksetzen, gilt als geladen...
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
            setOptValue(opt, initOptValue(__CONFIG));
            }
            message += "\nNummer oder Wert eingeben:";
        } else {
            message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
         }
         }
    }
}


         const __ANSWER = prompt(message, __NEXTVAL);
// Invalidiert die (ueber Menu) gesetzten Optionen
// optSet: Set mit den Optionen
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return Set mit den geladenen Optionen
function invalidateOpts(optSet, force = false) {
    for (let opt in optSet) {
         const __OPT = optSet[opt];


         if (__ANSWER) {
         if (__OPT.Loaded) {
             const __INDEX = parseInt(__ANSWER, 10) - 1;
             invalidateOpt(__OPT, force);
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);
        }
    }


            if (nextVal === undefined) {
    return optSet;
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
}
                const __CASTVAL = this[__VALTYPE](__ANSWER);


                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
// Laedt eine (ueber Menu) gesetzte Option
                    nextVal = __CASTVAL;
// opt: Zu ladende Option
                }
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
            }
// return Gesetzter Wert der gelandenen Option
function loadOption(opt, force = false) {
    const __CONFIG = getOptConfig(opt);
    const __ISSHARED = getValue(__CONFIG.Shared, false, true);
    const __NAME = getOptName(opt);
    const __DEFAULT = getOptValue(opt, undefined, false, false);
    let value;


            if (nextVal !== __VALUE) {
    if (opt.Loaded && ! __ISSHARED) {
                if (nextVal) {
        __LOG[1]("Error: Oprion '" + __NAME + "' bereits geladen!");
                    return setOpt(opt, nextVal, reload, onFulfilled, onRejected);
    }
                }
 
    if (opt.ReadOnly || __ISSHARED) {
        value = __DEFAULT;
    } else if (! force && __CONFIG.AutoReset) {
        value = initOptValue(__CONFIG);
    } else {
        value = (__CONFIG.Serial ?
                        deserialize(__NAME, __DEFAULT) :
                        GM_getValue(__NAME, __DEFAULT));
    }


                const __LABEL = substParam(__CONFIG.Label, __VALUE);
    __LOG[5]("LOAD " + __NAME + ": " + __LOG.changed(__DEFAULT, value));


                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
    // Wert als geladen markieren...
            }
     opt.Loaded = true;
        }
     } catch (ex) {
        __LOG[0]("promptNextOpt: " + ex.message);
    }


     return __VALUE;
    // Wert intern setzen...
     return setOptValue(opt, value);
}
}


// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// Laedt die (ueber Menu) gesetzten Optionen
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// optSet: Set mit den Optionen
// optSet: Platz fuer die gesetzten Optionen (und Config)
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// item: Key der Option
// return Set mit den geladenen Optionen
// value: (Bei allen Typen) Zu setzender Wert
function loadOptions(optSet, force = false) {
// reload: Seite mit neuem Wert neu laden
     for (let opt in optSet) {
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
        const __OPT = optSet[opt];
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
 
// return Gesetzter Wert
        if (! __OPT.Loaded) {
function setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
            loadOption(__OPT, force);
     const __OPT = getOptByName(optSet, item);
        }
    }


     return setOpt(__OPT, value, reload, onFulfilled, onRejected);
     return optSet;
}
}


// Ermittelt die naechste moegliche Option (Version mit Key)
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// optSet: Platz fuer die gesetzten Optionen (und Config)
// opt: Gesetzte Option
// item: Key der Option
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// value: Default fuer ggfs. zu setzenden Wert
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Zu setzender Wert
function deleteOption(opt, force = false, reset = true) {
function getNextOptByName(optSet, item, value = undefined) {
     const __CONFIG = getOptConfig(opt);
     const __OPT = getOptByName(optSet, item);


     return getNextOpt(__OPT, value);
     if (force || ! __CONFIG.Permanent) {
}
        const __NAME = getOptName(opt);


// Setzt die naechste moegliche Option (Version mit Key)
        __LOG[4]("DELETE " + __NAME);
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
function setNextOptByName(optSet, item, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
    const __OPT = getOptByName(optSet, item);


    return setNextOpt(__OPT, value, reload, onFulfilled, onRejected);
        GM_deleteValue(__NAME);
}


// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
        if (reset) {
// optSet: Platz fuer die gesetzten Optionen (und Config)
            setOptValue(opt, initOptValue(__CONFIG));
// item: Key der Option
        }
// value: Default fuer ggfs. zu setzenden Wert
     }
// reload: Seite mit neuem Wert neu laden
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
function promptNextOptByName(optSet, item, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
    const __OPT = getOptByName(optSet, item);
 
     return promptNextOpt(__OPT, value, reload, freeValue, selValue, minChoice, onFulfilled, onRejected);
}
}


// Baut das Benutzermenu auf (asynchron im Hintergrund)
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// optSet: Gesetzte Optionen
// optSet: Gesetzte Optionen
// return Promise auf void
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
async function buildMenu(optSet) {
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
     __LOG[3]("buildMenu()");
// reset: Setzt bei Erfolg auf Initialwert der Option
function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
     const __DELETEALL = (optSelect === undefined) || (optSelect === true);
    const __OPTSELECT = getValue(optSelect, { });


     for (let opt in optSet) {
     for (let opt in optSet) {
         await registerOption(optSet[opt]).then(
         if (getValue(__OPTSELECT[opt], __DELETEALL)) {
                result => __LOG[6](`REGISTEROPTION[${opt}] = ${result}`),
            deleteOption(optSet[opt], force, reset);
                defaultCatch);
        }
     }
     }
}
}


// Invalidiert eine (ueber Menu) gesetzte Option
// Benennt eine Option um und laedt sie ggfs. nach
// opt: Zu invalidierende Option
// opt: Gesetzte Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// name: Neu zu setzender Name (Speicheradresse)
// return Promise auf resultierenden Wert
// reload: Wert nachladen statt beizubehalten
function invalidateOpt(opt, force = false) {
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
     return Promise.resolve(opt.Promise).then(value => {
// return Umbenannte Option
            if (opt.Loaded && ! opt.ReadOnly) {
function renameOption(opt, name, reload = false, force = false) {
                const __CONFIG = getOptConfig(opt);
     const __NAME = getOptName(opt);


                // Wert "ungeladen"...
    if (__NAME !== name) {
                opt.Loaded = (force || ! __CONFIG.AutoReset);
        deleteOption(opt, true, ! reload);


                if (opt.Loaded && __CONFIG.AutoReset) {
        setOptName(opt, name);
                    // Nur zuruecksetzen, gilt als geladen...
                    setOptValue(opt, initOptValue(__CONFIG));
                }
            }


            return getOptValue(opt);
         if (reload) {
         }, defaultCatch);
            loadOption(opt, force);
}
         }
 
// Invalidiert die (ueber Menu) gesetzten Optionen
// optSet: Object mit den Optionen
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return Promise auf Object mit den geladenen Optionen
async function invalidateOpts(optSet, force = false) {
    for (let opt in optSet) {
        const __OPT = optSet[opt];
 
         await invalidateOpt(__OPT, force);
     }
     }


     return optSet;
     return opt;
}
}


// Laedt eine (ueber Menu) gesetzte Option
// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
// opt: Zu ladende Option
// name: Gesetzter Name (Speicheradresse)
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// prefix: Prefix, das vorangestellt werden soll
// return Promise auf gesetzten Wert der gelandenen Option
// return Neu zu setzender Name (Speicheradresse)
function loadOption(opt, force = false) {
function prefixName(name, prefix) {
     if (! opt.Promise) {
     return (prefix + name);
        const __CONFIG = getOptConfig(opt);
}
        const __ISSHARED = getValue(__CONFIG.Shared, false, true);
        const __NAME = getOptName(opt);
        const __DEFAULT = getOptValue(opt, undefined, false, false, false);
        let value;


        if (opt.Loaded && ! __ISSHARED) {
// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
            const __ERROR = "Error: Oprion '" + __NAME + "' bereits geladen!";
// name: Gesetzter Name (Speicheradresse)
// postfix: Postfix, das angehaengt werden soll
// return Neu zu setzender Name (Speicheradresse)
function postfixName(name, postfix) {
    return (name + postfix);
}


            __LOG[0](__MESSAGE);
// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// 'reload': Option nachladen?
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// renameParam: Wird an renameFun uebergeen
// renameFun: function(name, param) zur Ermittlung des neuen Namens
// - name: Neu zu setzender Name (Speicheradresse)
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix
function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
    if (renameFun === undefined) {
        __LOG[1]("RENAME: Illegale Funktion!");
    }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];


             return Promise.reject(__MESSAGE);
        if (__OPT === undefined) {
         }
             __LOG[1]("RENAME: Option '" + opt + "' nicht gefunden!");
         } else {
            const __NAME = getOptName(__OPT);
            const __NEWNAME = renameFun(__NAME, renameParam);
            const __ISSCALAR = ((typeof __OPTPARAMS) === 'boolean');
            // Laedt die unter dem neuen Namen gespeicherten Daten nach?
            const __RELOAD = (__ISSCALAR ? __OPTPARAMS : __OPTPARAMS.reload);
            // Laedt auch Optionen mit 'AutoReset'-Attribut?
            const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force);


        if (opt.ReadOnly || __ISSHARED) {
             renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE);
             value = __DEFAULT;
        } else if (! force && __CONFIG.AutoReset) {
            value = initOptValue(__CONFIG);
        } else {
            value = (__CONFIG.Serial ?
                            deserialize(__NAME, __DEFAULT) :
                            GM.getValue(__NAME, __DEFAULT));
         }
         }
    }
}


        opt.Promise = Promise.resolve(value).then(value => {
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
                // Paranoide Sicherheitsabfrage (das sollte nie passieren!)...
// optSet: Gesetzte Optionen
                if (opt.Loaded || ! opt.Promise) {
// reload: Seite mit "Werkseinstellungen" neu laden
                    showAlert("Error", "Unerwarteter Widerspruch zwischen opt.Loaded und opt.Promise", safeStringify(opt));
function resetOptions(optSet, reload = true) {
                }
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
                __LOG[5]("LOAD " + __NAME + ": " + __LOG.changed(__DEFAULT, value));
    deleteOptions(optSet, true, false, ! reload);


                // Wert intern setzen...
    if (reload) {
                const __VAL = setOptValue(opt, value);
        // ... und Seite neu laden (mit "Werkseinstellungen")...
 
        window.location.reload();
                // Wert als geladen markieren...
                opt.Promise = undefined;
                opt.Loaded = true;
 
                return __VAL;
            }, defaultCatch);
     }
     }
    return opt.Promise;
}
}


// Laedt die (ueber Menu) gesetzten Optionen
// ==================== Abschnitt fuer diverse Utilities ====================
// optSet: Object mit den Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Array mit Promises neuer Ladevorgaenge (fuer Objekte mit 'name' und 'value')
function loadOptions(optSet, force = false) {
    const __PROMISES = [];


     for (let opt in optSet) {
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
         const __OPT = optSet[opt];
// form: <form>...</form>
 
// props: Map von name:value-Paaren
         if (! __OPT.Loaded) {
// type: Typ der Input-Felder (Default: unsichtbare Daten)
             const __PROMISE = loadOption(__OPT, force).then(value => {
// return Ergaenztes Form-Konstrukt
                    __LOG[5]("LOADED " + opt + " << " + value);
function addInputField(form, props, type = "hidden") {
 
     for (let fieldName in props) {
                    return Promise.resolve({
         let field = form[fieldName];
                            'name'  : opt,
         if (! field) {
                            'value' : value
             field = document.createElement("input");
                        });
            field.type = type;
                }, defaultCatch);
            field.name = fieldName;
 
             form.appendChild(field);
             __PROMISES.push(__PROMISE);
         }
         }
        field.value = props[fieldName];
     }
     }


     return Promise.all(__PROMISES);
     return form;
}
}


// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// opt: Gesetzte Option
// form: <form>...</form>
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// props: Map von name:value-Paaren
// reset: Setzt bei Erfolg auf Initialwert der Option (auch fuer nicht 'AutoReset')
// return Ergaenztes Form-Konstrukt
// return Promise von GM.deleteValue() (oder void)
function addHiddenField(form, props) {
function deleteOption(opt, force = false, reset = true) {
     return addInputField(form, props, "hidden");
     const __CONFIG = getOptConfig(opt);
}


     if (force || ! __CONFIG.Permanent) {
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
         const __NAME = getOptName(opt);
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function addEvent(obj, type, callback, capture = false) {
    if (obj.addEventListener) {
        return obj.addEventListener(type, callback, capture);
     } else if (obj.attachEvent) {
        return obj.attachEvent("on" + type, callback);
    } else {
         __LOG[1]("Could not add " + type + " event:");
        __LOG[2](callback);


        __LOG[4]("DELETE " + __NAME);
         return false;
 
         return GM.deleteValue(__NAME).then(voidValue => {
                if (reset || __CONFIG.AutoReset) {
                    setOptValue(opt, initOptValue(__CONFIG));
                }
            }, defaultCatch);
     }
     }
    return Promise.resolve();
}
}


// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// optSet: Gesetzte Optionen
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// type: Name des Events, z.B. "click"
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// callback: Funktion als Reaktion
// reset: Setzt bei Erfolg auf Initialwert der Option
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return Promise auf diesen Vorgang
// return false bei Misserfolg
async function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
function removeEvent(obj, type, callback, capture = false) {
     const __DELETEALL = ((optSelect === undefined) || (optSelect === true));
     if (obj.removeEventListener) {
     const __OPTSELECT = getValue(optSelect, { });
        return obj.removeEventListener(type, callback, capture);
    } else if (obj.detachEvent) {
        return obj.detachEvent("on" + type, callback);
     } else {
        __LOG[1]("Could not remove " + type + " event:");
        __LOG[2](callback);


    for (let opt in optSet) {
         return false;
         if (getValue(__OPTSELECT[opt], __DELETEALL)) {
            await deleteOption(optSet[opt], force, reset);
        }
     }
     }
    return Promise.resolve();
}
}


// Benennt eine Option um und laedt sie ggfs. nach
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// opt: Gesetzte Option
// id: ID des betroffenen Eingabeelements
// name: Neu zu setzender Name (Speicheradresse)
// type: Name des Events, z.B. "click"
// reload: Wert nachladen statt beizubehalten
// callback: Funktion als Reaktion
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return Promise auf umbenannte Option
// return false bei Misserfolg
async function renameOption(opt, name, reload = false, force = false) {
function addDocEvent(id, type, callback, capture = false) {
     const __NAME = getOptName(opt);
     const __OBJ = document.getElementById(id);


     if (__NAME !== name) {
     return addEvent(__OBJ, type, callback, capture);
        await deleteOption(opt, true, ! reload);
 
        setOptName(opt, name);
 
        await invalidateOpt(opt, opt.Loaded);
 
        if (reload) {
            opt.Loaded = false;
 
            await loadOption(opt, force);
        }
    }
 
    return Promise.resolve(opt);
}
}


// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// name: Gesetzter Name (Speicheradresse)
// id: ID des betroffenen Eingabeelements
// prefix: Prefix, das vorangestellt werden soll
// type: Name des Events, z.B. "click"
// return Neu zu setzender Name (Speicheradresse)
// callback: Funktion als Reaktion
function prefixName(name, prefix) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     return (prefix + name);
// return false bei Misserfolg
function removeDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);
 
     return removeEvent(__OBJ, type, callback, capture);
}
}


// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// name: Gesetzter Name (Speicheradresse)
// name: Name des Elements (siehe "name=")
// postfix: Postfix, das angehaengt werden soll
// index: Laufende Nummer des Elements (0-based), Default: 0
// return Neu zu setzender Name (Speicheradresse)
// doc: Dokument (document)
function postfixName(name, postfix) {
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
     return (name + postfix);
function getElement(name, index = 0, doc = document) {
     const __TAGS = document.getElementsByName(name);
    const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
 
    return __TABLE;
}
}


// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// optSet: Gesetzte Optionen
// index: Laufende Nummer des Elements (0-based)
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// tag: Tag des Elements ("table")
// 'reload': Option nachladen?
// doc: Dokument (document)
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// return Gesuchtes Element oder undefined (falls nicht gefunden)
// renameParam: Wird an renameFun uebergeen
function getTable(index, tag = "table", doc = document) {
// renameFun: function(name, param) zur Ermittlung des neuen Namens
     const __TAGS = document.getElementsByTagName(tag);
// - name: Neu zu setzender Name (Speicheradresse)
     const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix
// return Promise auf diesen Vorgang
async function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
     if (renameFun === undefined) {
        __LOG[0]("RENAME: Illegale Funktion!");
     }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];


        if (__OPT === undefined) {
    return __TABLE;
            __LOG[0]("RENAME: Option '" + opt + "' nicht gefunden!");
        } else {
            const __NAME = getOptName(__OPT);
            const __NEWNAME = renameFun(__NAME, renameParam);
            const __ISSCALAR = ((typeof __OPTPARAMS) === 'boolean');
            // Laedt die unter dem neuen Namen gespeicherten Daten nach?
            const __RELOAD = (__ISSCALAR ? __OPTPARAMS : __OPTPARAMS.reload);
            // Laedt auch Optionen mit 'AutoReset'-Attribut?
            const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force);
 
            await renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE);
        }
    }
}
}


// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// optSet: Gesetzte Optionen
// index: Laufende Nummer des Elements (0-based)
// reload: Seite mit "Werkseinstellungen" neu laden
// doc: Dokument (document)
// return Promise auf diesen Vorgang
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
async function resetOptions(optSet, reload = true) {
function getRows(index, doc = document) {
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
     const __TABLE = getTable(index, "table", doc);
     await deleteOptions(optSet, true, false, ! reload);
     const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;


     // ... und ggfs. Seite neu laden (mit "Werkseinstellungen")...
     return __ROWS;
    refreshPage(reload);
}
}


// ==================== Abschnitt fuer diverse Utilities ====================
// ==================== Abschnitt fuer Optionen auf der Seite ====================


// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// Liefert den Funktionsaufruf zur Option als String
// form: <form>...</form>
// opt: Auszufuehrende Option
// props: Map von name:value-Paaren
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// value: Ggfs. zu setzender Wert
// return Ergaenztes Form-Konstrukt
// serial: Serialization fuer String-Werte (Select, Textarea)
function addInputField(form, props, type = 'hidden') {
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
     for (let fieldName in props) {
// return String mit dem (reinen) Funktionsaufruf
        let field = form[fieldName];
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
        if (! field) {
     const __STORAGE = getMemory(memory);
            field = document.createElement('input');
    const __MEMORY = __STORAGE.Value;
            field.type = type;
    const __MEMSTR = __STORAGE.Display;
            field.name = fieldName;
    const __RUNPREFIX = __STORAGE.Prefix;
            form.appendChild(field);
        }
        field.value = props[fieldName];
    }


     return form;
     if (__MEMORY !== undefined) {
}
        const __RELOAD = "window.location.reload()";
 
        const __SETITEM = function(item, val, quotes = true) {
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
// form: <form>...</form>
                          };
// props: Map von name:value-Paaren
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
// return Ergaenztes Form-Konstrukt
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
function addHiddenField(form, props) {
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
    return addInputField(form, props, 'hidden');
                          };
}
        const __CONFIG = getOptConfig(opt);
        const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
        const __THISVAL = ((__CONFIG.ValType === "String") ? "'\\x22' + this.value + '\\x22'" : "this.value");
        const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
        const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
        if (__ACTION !== undefined) {
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
            switch (__ACTION) {
// type: Name des Events, z.B. "click"
            case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
// callback: Funktion als Reaktion
                                  return __SETITEMS('SET', getOptName(opt), __VALSTR);
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
            case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
// return false bei Misserfolg
                                  return __SETITEMS('NXT', getOptName(opt), __VALSTR);
function addEvent(obj, type, callback, capture = false) {
            case __OPTACTION.RST : //return "doActionRst()";
    if (obj.addEventListener) {
                                  return __SETITEMS('RST');
        return obj.addEventListener(type, callback, capture);
            default :              break;
    } else if (obj.attachEvent) {
            }
        return obj.attachEvent('on' + type, callback);
         }
    } else {
    }
         __LOG[0]("Could not add " + type + " event:");
        __LOG[2](callback);


        return false;
    return undefined;
    }
}
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Liefert die Funktionsaufruf zur Option als String
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// opt: Auszufuehrende Option
// type: Name des Events, z.B. "click"
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// callback: Funktion als Reaktion
// value: Ggfs. zu setzender Wert
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// return false bei Misserfolg
// serial: Serialization fuer String-Werte (Select, Textarea)
function removeEvent(obj, type, callback, capture = false) {
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
     if (obj.removeEventListener) {
// return String mit dem (reinen) Funktionsaufruf
        return obj.removeEventListener(type, callback, capture);
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {
    } else if (obj.detachEvent) {
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
        return obj.detachEvent('on' + type, callback);
    } else {
        __LOG[0]("Could not remove " + type + " event:");
        __LOG[2](callback);


        return false;
    return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
    }
}
}


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// Zeigt eine Option auf der Seite als Auswahlbox an
// id: ID des betroffenen Eingabeelements
// opt: Anzuzeigende Option
// type: Name des Events, z.B. "click"
// return String mit dem HTML-Code
// callback: Funktion als Reaktion
function getOptionSelect(opt) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
    const __CONFIG = getOptConfig(opt);
// return false bei Misserfolg
    const __NAME = getOptName(opt);
function addDocEvent(id, type, callback, capture = false) {
    const __VALUE = getOptValue(opt);
     const __OBJ = document.getElementById(id);
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


     return addEvent(__OBJ, type, callback, capture);
    if (__CONFIG.FreeValue && ! (~ __CONFIG.Choice.indexOf(__VALUE))) {
        element += '\n<option value="' + __VALUE + '" SELECTED>' + __VALUE + '</option>';
    }
    for (let value of __CONFIG.Choice) {
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';
 
     return __LABEL.replace('$', element);
}
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Zeigt eine Option auf der Seite als Radiobutton an
// id: ID des betroffenen Eingabeelements
// opt: Anzuzeigende Option
// type: Name des Events, z.B. "click"
// return String mit dem HTML-Code
// callback: Funktion als Reaktion
function getOptionRadio(opt) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
    const __CONFIG = getOptConfig(opt);
// return false bei Misserfolg
    const __NAME = getOptName(opt);
function removeDocEvent(id, type, callback, capture = false) {
    const __VALUE = getOptValue(opt, false);
     const __OBJ = document.getElementById(id);
    const __ACTION = getFormActionEvent(opt, false, true, "click", false);
    const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
     const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'ON" value="1"' +
                        (__VALUE ? ' CHECKED' : __ACTION) +
                        ' /><label for="' + __NAME + 'ON">' +
                        __CONFIG.Label + '</label>';
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';


     return removeEvent(__OBJ, type, callback, capture);
     return [ __ELEMENTON, __ELEMENTOFF ];
}
}


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// Zeigt eine Option auf der Seite als Checkbox an
// name: Name des Elements (siehe "name=")
// opt: Anzuzeigende Option
// index: Laufende Nummer des Elements (0-based), Default: 0
// return String mit dem HTML-Code
// doc: Dokument (document)
function getOptionCheckbox(opt) {
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
    const __CONFIG = getOptConfig(opt);
function getElement(name, index = 0, doc = document) {
    const __NAME = getOptName(opt);
     const __TAGS = doc.getElementsByName(name);
    const __VALUE = getOptValue(opt, false);
     const __TABLE = (__TAGS ? __TAGS[index] : undefined);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     return __TABLE;
     return '<input type="checkbox" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __VALUE + '"' +
          (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
          __NAME + '">' + __FORMLABEL + '</label>';
}
}


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// index: Laufende Nummer des Elements (0-based)
// opt: Anzuzeigende Option
// tag: Tag des Elements ("table")
// return String mit dem HTML-Code
// doc: Dokument (document)
function getOptionTextarea(opt) {
// return Gesuchtes Element oder undefined (falls nicht gefunden)
    const __CONFIG = getOptConfig(opt);
function getTable(index, tag = 'table', doc = document) {
    const __NAME = getOptName(opt);
     const __TAGS = doc.getElementsByTagName(tag);
    const __VALUE = getOptValue(opt);
     const __TABLE = (__TAGS ? __TAGS[index] : undefined);
    const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
    const __SUBMIT = getValue(__CONFIG.Submit, "");
    //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                          safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


     return __TABLE;
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
}
}


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// Zeigt eine Option auf der Seite als Button an
// name: Name des Tabellen-Elements (siehe "name=")
// opt: Anzuzeigende Option
// index: Laufende Nummer des Tabellen-Elements (0-based), Default: 0
// return String mit dem HTML-Code
// doc: Dokument (document)
function getOptionButton(opt) {
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
    const __CONFIG = getOptConfig(opt);
function getElementRows(name, index = 0, doc = document) {
    const __NAME = getOptName(opt);
     const __TABLE = getElement(name, index, doc);
    const __VALUE = getOptValue(opt, false);
     const __ROWS = (__TABLE ? __TABLE.rows : undefined);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
     const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     return __ROWS;
     return '<label for="' + __NAME + '">' + __FORMLABEL +
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';
}
}


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// Zeigt eine Option auf der Seite an (je nach Typ)
// index: Laufende Nummer des Elements (0-based)
// opt: Anzuzeigende Option
// doc: Dokument (document)
// return String mit dem HTML-Code
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getOptionElement(opt) {
function getRows(index, doc = document) {
     const __CONFIG = getOptConfig(opt);
     const __TABLE = getTable(index, 'table', doc);
     const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
     const __ROWS = (__TABLE ? __TABLE.rows : undefined);
    let element = "";


     return __ROWS;
     if (! __CONFIG.Hidden) {
}
        switch (__TYPE) {
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                            break;
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                                element = getOptionCheckbox(opt);
                            } else {
                                element = getOptionRadio(opt);
                            }
                            break;
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                            break;
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
                            break;
        case __OPTTYPES.SI : element = getOptionButton(opt);
                            break;
        default :            break;
        }


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
        if (element.length === 2) {
// id: ID des Tabellen-Elements
            element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
// doc: Dokument (document)
        }
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
     }
function getRowsById(id, doc = document) {
    const __TABLE = doc.getElementById(id);
     const __ROWS = (__TABLE ? __TABLE.rows : undefined);


     return __ROWS;
     return element;
}
}


// ==================== Abschnitt fuer Optionen auf der Seite ====================
// Baut das Benutzermenu auf der Seite auf
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return String mit dem HTML-Code
function getForm(optSet, optParams = { }) {
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    let form = __FORM;
    let count = 0;  // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)


// Liefert den Funktionsaufruf zur Option als String
    for (let opt in optSet) {
// opt: Auszufuehrende Option
        if (checkItem(opt, __SHOWFORM, optParams.hideForm)) {
// isAlt: Angabe, ob AltAction statt Action gemeint ist
            const __ELEMENT = getOptionElement(optSet[opt]);
// value: Ggfs. zu setzender Wert
            const __TDOPT = (~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"';
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __MEMSTR = __STORAGE.Display;
    const __RUNPREFIX = __STORAGE.Prefix;


    if (__MEMORY !== undefined) {
            if (__ELEMENT) {
        const __RELOAD = "window.location.reload()";
                if (++count > __FORMBREAK) {
        const __SETITEM = function(item, val, quotes = true) {
                    if (++column > __FORMWIDTH) {
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                        column = 1;
                          };
                    }
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                }
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                if (column === 1) {
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                    form += '</tr><tr>';
                          };
                }
        const __CONFIG = getOptConfig(opt);
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
        const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
        const __THISVAL = ((__CONFIG.ValType === 'String') ? "'\\x22' + this.value + '\\x22'" : "this.value");
        const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
        const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);
 
        if (__ACTION !== undefined) {
            switch (__ACTION) {
            case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                  return __SETITEMS('SET', getOptName(opt), __VALSTR);
            case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                  return __SETITEMS('NXT', getOptName(opt), __VALSTR);
            case __OPTACTION.RST : //return "doActionRst()";
                                  return __SETITEMS('RST');
            default :              break;
             }
             }
         }
         }
     }
     }
    form += '\n' + __FORMEND;


     return undefined;
     return form;
}
}


// Liefert die Funktionsaufruf zur Option als String
// Fuegt das Script in die Seite ein
// opt: Auszufuehrende Option
// optSet: Gesetzte Optionen
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// optParams: Eventuell notwendige Parameter
// value: Ggfs. zu setzender Wert
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// serial: Serialization fuer String-Werte (Select, Textarea)
// return String mit dem HTML-Code fuer das Script
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
function getScript(optSet, optParams = { }) {
// return String mit dem (reinen) Funktionsaufruf
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
function getFormActionEvent(opt, isAlt = false, value = undefined, type = 'click', serial = undefined, memory = undefined) {
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
    //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
     const __SCRIPT = "";
 
    //window.eval('function activateMenu() { console.log("TADAAA!"); }');


     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
     return __SCRIPT;
}
}


// Hilfsfunktion: Wendet eine Konvertierung auf jede "Zeile" innerhalb eines Textes an
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// text: Urspruenglicher Text
// anchor: Element, das als Anker fuer die Anzeige dient
// convFun: function(line, index, arr): Konvertiert line in "Zeile" line des Arrays arr
// optSet: Gesetzte Optionen
// separator: Zeilentrenner im Text (Default: '\n')
// optParams: Eventuell notwendige Parameter
// thisArg: optionaler this-Parameter fuer die Konvertierung
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// limit: optionale Begrenzung der Zeilen
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// return String mit dem neuen Text
// 'formWidth': Anzahl der Elemente pro Zeile
function eachLine(text, convFun, separator = '\n', thisArg = undefined, limit = undefined) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     const __ARR = text.split(separator, limit);
function buildForm(anchor, optSet, optParams = { }) {
    const __RES = __ARR.map(convFun, thisArg);
     __LOG[3]("buildForm()");


     return __RES.join(separator);
     const __FORM = getForm(optSet, optParams);
}
    const __SCRIPT = getScript(optSet, optParams);


// Hilfsfunktion: Ergaenzt einen HTML-Code um einen Titel (ToolTip)
     addForm(anchor, __FORM, __SCRIPT);
// html: Urspruenglicher HTML-Code (z.B. ein HTML-Element oder Text)
// title: Im ToolTip angezeigter Text
// separator: Zeilentrenner im Text (Default: '|')
// limit: optionale Begrenzung der Zeilen
// return String mit dem neuen HTML-Code
function withTitle(html, title, separator = '|', limit = undefined) {
     if (title && title.length) {
        return eachLine(html, line => '<abbr title="' + title + '">' + line + '</abbr>', separator, undefined, limit);
    } else {
        return html;
    }
}
}


// Hilfsfunktion: Ermittelt einen Label- oder FormLabel-Eintrag (Default)
// Informationen zu hinzugefuegten Forms
// label: Config-Eintrag fuer Label oder FormLabel
const __FORMS = { };
// defLabel: Ersatzwert, falls label nicht angegeben
// isSelect: Angabe, ob ein Parameter angezeigt wird (Default: false)
// isForm: Angabe, ob ein FormLabel gesucht ist (Default: true)
// return Vollstaendiger Label- oder FormLabel-Eintrag
function formatLabel(label, defLabel = undefined, isSelect = false, isForm = true) {
    const __LABEL = getValue(label, defLabel);


    if (isSelect && __LABEL && (substParam(__LABEL, '_') === __LABEL)) {
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        return __LABEL + (isForm ? "|$" : " $");
// anchor: Element, das als Anker fuer die Anzeige dient
     } else {
// form: HTML-Form des Optionsmenu (hinten angefuegt)
        return __LABEL;
// script: Script mit Reaktionen
     }
function addForm(anchor, form = "", script = "") {
}
     const __OLDFORM = __FORMS[anchor];
     const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);


// Zeigt eine Option auf der Seite als Auswahlbox an
     __FORMS[anchor] = {
// opt: Anzuzeigende Option
                          'Script' : script,
// return String mit dem HTML-Code
                          'Form'   : form
function getOptionSelect(opt) {
                      };
     const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, 'change', undefined);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label, true);
    const __TITLE = substParam(getValue(__CONFIG.Title, __CONFIG.Label), __VALUE);
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


     if (__CONFIG.FreeValue && ! (~ __CONFIG.Choice.indexOf(__VALUE))) {
     anchor.innerHTML = __REST + script + form;
        element += '\n<option value="' + __VALUE + '" SELECTED>' + __VALUE + '</option>';
}
    }
    for (let value of __CONFIG.Choice) {
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';


    return withTitle(substParam(__LABEL, element), __TITLE);
// ==================== Abschnitt fuer Klasse Classification ====================
}


// Zeigt eine Option auf der Seite als Radiobutton an
// Basisklasse fuer eine Klassifikation der Optionen nach Kriterium (z.B. Erst- und Zweitteam oder Fremdteam)
// opt: Anzuzeigende Option
function Classification() {
// return String mit dem HTML-Code
     'use strict';
function getOptionRadio(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, false, true, 'click', false);
    const __ALTACTION = getFormActionEvent(opt, true, false, 'click', false);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel); // nur nutzen, falls angegeben
    const __TITLE = getValue(__CONFIG.Title, '$');
    const __TITLEON = substParam(__TITLE, __CONFIG.Label);
     const __TITLEOFF = substParam(getValue(__CONFIG.AltTitle, __TITLE), __CONFIG.AltLabel);
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'ON" value="1"' +
                        (__VALUE ? ' CHECKED' : __ACTION) +
                        ' /><label for="' + __NAME + 'ON">' +
                        __CONFIG.Label + '</label>';
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';
    const __ELEMENT = [
                          withTitle(__FORMLABEL, __VALUE ? __TITLEON : __TITLEOFF),
                          withTitle(__ELEMENTON, __TITLEON),
                          withTitle(__ELEMENTOFF, __TITLEOFF)
                      ];


     return ((__FORMLABEL && __FORMLABEL.length) ? __ELEMENT : __ELEMENT.slice(1, 3));
     this.renameFun = prefixName;
    //this.renameParamFun = undefined;
    this.optSet = undefined;
    this.optSelect = { };
}
}


// Zeigt eine Option auf der Seite als Checkbox an
Class.define(Classification, Object, {
// opt: Anzuzeigende Option
                    'renameOptions' : function() {
// return String mit dem HTML-Code
                                          const __PARAM = this.renameParamFun();
function getOptionCheckbox(opt) {
 
    const __CONFIG = getOptConfig(opt);
                                          if (__PARAM !== undefined) {
    const __NAME = getOptName(opt);
                                              // Klassifizierte Optionen umbenennen...
    const __VALUE = getOptValue(opt, false);
                                              renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
                                          }
    const __VALUELABEL = (__VALUE ? __CONFIG.Label : getValue(__CONFIG.AltLabel, __CONFIG.Label));
                                      },
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
                    'deleteOptions' : function() {
    const __TITLE = substParam(getValue(__VALUE ? __CONFIG.Title : getValue(__CONFIG.AltTitle, __CONFIG.Title), '$'), __VALUELABEL);
                                          return deleteOptions(this.optSet, this.optSelect, true, true);
                                      }
                } );


    return withTitle('<input type="checkbox" name="' + __NAME +
// ==================== Ende Abschnitt fuer Klasse Classification ====================
                    '" id="' + __NAME + '" value="' + __VALUE + '"' +
                    (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
                    __NAME + '">' + __FORMLABEL + '</label>', __TITLE);
}


// Zeigt eine Option auf der Seite als Daten-Textfeld an
// ==================== Abschnitt fuer Klasse TeamClassification ====================
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionTextarea(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, 'submit', undefined);
    const __SUBMIT = getValue(__CONFIG.Submit, "");
    //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
    const __TITLE = substParam(getValue(__CONFIG.Title, '$'), __FORMLABEL);
    const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                          safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


    return [ withTitle(__ELEMENTLABEL, __TITLE), __ELEMENTTEXT ];
// Klasse fuer die Klassifikation der Optionen nach Team (Erst- und Zweitteam oder Fremdteam)
}
function TeamClassification() {
    'use strict';


// Zeigt eine Option auf der Seite als Button an
     Classification.call(this);
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionButton(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
    const __BUTTONLABEL = (__VALUE ? getValue(__CONFIG.AltLabel, __CONFIG.Label) : __CONFIG.Label);
     const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __BUTTONLABEL);
    const __BUTTONTITLE = substParam(getValue(__VALUE ? getValue(__CONFIG.AltTitle, __CONFIG.Title) : __CONFIG.Title, '$'), __BUTTONLABEL);


     return '<label for="' + __NAME + '">' + __FORMLABEL + '</label>' +
     this.team = undefined;
          withTitle('<input type="button" name="" + ' + __NAME +
    this.teamParams = undefined;
                    '" id="' + __NAME + '" value="' + __BUTTONLABEL +
                    '"' + __ACTION + '/>', __BUTTONTITLE);
}
}


// Zeigt eine Option auf der Seite an (je nach Typ)
Class.define(TeamClassification, Classification, {
// opt: Anzuzeigende Option
                    'renameParamFun' : function() {
// return String mit dem HTML-Code
                                          const __MYTEAM = (this.team = getMyTeam(this.optSet, this.teamParams, this.team));
function getOptionElement(opt) {
    const __CONFIG = getOptConfig(opt);
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
    let element = "";


    if (! __CONFIG.Hidden) {
                                          if (__MYTEAM.LdNr) {
        switch (__TYPE) {
                                              // Prefix fuer die Optionen mit gesonderten Behandlung...
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                                              return __MYTEAM.LdNr.toString() + '.' + __MYTEAM.LgNr.toString() + ':';
                            break;
                                          } else {
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                                              return undefined;
                                element = getOptionCheckbox(opt);
                                          }
                            } else {
                                      }
                                element = getOptionRadio(opt);
                } );
                            }
                            break;
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                            break;
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
                            break;
        case __OPTTYPES.SI : element = getOptionButton(opt);
                            break;
        default :            break;
        }


        if ((typeof element) !== 'string') {
// ==================== Ende Abschnitt fuer Klasse TeamClassification ====================
            element = '<div>' + Array.from(element).join('<br />') + '</div>';
        }
    }


    return element;
// ==================== Abschnitt fuer Klasse Team ====================
}


// Gruppiert die Daten eines Objects nach einem Kriterium
// Klasse fuer Teamdaten
// data: Object mit Daten
function Team(team, land, liga) {
// byFun: function(val), die das Kriterium ermittelt. Default: value
     'use strict';
// filterFun: function(key, index, arr), die das Kriterium key im Array arr an der Stelle index vergleicht. Default: Wert identisch
// sortFun: function(a, b), nach der die Kriterien sortiert werden. Default: Array.sort()
// return Neues Object mit Eintraegen der Form <Kriterium> : [ <alle Keys zu diesem Kriterium> ]
function groupData(data, byFun, filterFun, sortFun) {
     const __BYFUN = (byFun || (val => val));
    const __FILTERFUN = (filterFun || ((key, index, arr) => (arr[index] === key)));
    const __KEYS = Object.keys(data);
    const __VALS = Object.values(data);
    const __BYKEYS = __VALS.map(__BYFUN);
    const __BYKEYSET = new Set(__BYKEYS);
    const __BYKEYARRAY = [...__BYKEYSET];
    const __SORTEDKEYS = __BYKEYARRAY.sort(sortFun);
    const __GROUPEDKEYS = __SORTEDKEYS.map(byVal => __KEYS.filter((key, index, arr) => __FILTERFUN(byVal, index, __BYKEYS)));
    const __ASSIGN = ((keyArr, valArr) => Object.assign({ }, ...keyArr.map((key, index) => ({ [key] : valArr[index] }))));


     return __ASSIGN(__SORTEDKEYS, __GROUPEDKEYS);
     this.Team = team;
    this.Land = land;
    this.Liga = liga;
    this.LdNr = getLandNr(land);
    this.LgNr = getLigaNr(liga);
}
}


// Baut das Benutzermenu auf der Seite auf
Class.define(Team, Object, {
// optSet: Gesetzte Optionen
                    '__TEAMITEMS' : {  // Items, die in Team als Teamdaten gesetzt werden...
// optParams: Eventuell notwendige Parameter
                                        'Team' : true,
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
                                        'Liga' : true,
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
                                        'Land' : true,
// 'formWidth': Anzahl der Elemente pro Zeile
                                        'LdNr' : true,
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
                                        'LgNr' : true
// return String mit dem HTML-Code
                                    }
function getForm(optSet, optParams = { }) {
                } );
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
 
    const __FORMEND = '</tr></tbody></table></form>';
// ==================== Ende Abschnitt fuer Klasse Team ====================
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    const __PRIOOPTS = groupData(optSet, opt => getOptConfig(opt).FormPrio);
    let form = __FORM;
    let count = 0;  // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)


    for (let optKeys of Object.values(__PRIOOPTS)) {
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
        for (let optKey of optKeys) {
            if (checkItem(optKey, __SHOWFORM, optParams.hideForm)) {
                const __ELEMENT = getOptionElement(optSet[optKey]);
                const __TDOPT = ((~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"');


                if (__ELEMENT) {
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
                    if (++count > __FORMBREAK) {
const __OPTSET = { };
                        if (++column > __FORMWIDTH) {
                            column = 1;
                        }
                    }
                    if (column === 1) {
                        form += '</tr><tr>';
                    }
                    form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
                }
            }
        }
    }
    form += '\n' + __FORMEND;


    return form;
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
}
const __TEAMCLASS = new TeamClassification();


// Fuegt das Script in die Seite ein
// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
// optSet: Gesetzte Optionen
__TEAMCLASS.optSelect = {
// optParams: Eventuell notwendige Parameter
                      'ligaSize'  : true
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
                  };
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
 
// return String mit dem HTML-Code fuer das Script
// Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option
function getScript(optSet, optParams = { }) {
// optSet: Platz fuer die gesetzten Optionen
     //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
// teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'LdNr' und 'LgNr')
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
// myTeam: Objekt fuer die Teamdaten
    //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
// return Die Teamdaten oder undefined bei Fehler
    const __SCRIPT = "";
function getMyTeam(optSet = undefined, teamParams = undefined, myTeam = new Team()) {
     if (teamParams !== undefined) {
        addProps(myTeam, teamParams, myTeam.__TEAMITEMS);
        __LOG[2]("Ermittelt: " + safeStringify(myTeam));
        // ... und abspeichern...
        setOpt(optSet.team, myTeam, false);
    } else {
        const __TEAM = getOptValue(optSet.team)// Gespeicherte Parameter


    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
            addProps(myTeam, __TEAM, myTeam.__TEAMITEMS);
            __LOG[2]("Gespeichert: " + safeStringify(myTeam));
        } else {
            __LOG[1]("Unbekannt: " + safeStringify(__TEAM));
        }
    }


     return __SCRIPT;
     //return ((myTeam.length > 0) ? myTeam : undefined);
    return myTeam;
}
}


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// Behandelt die Optionen und laedt das Benutzermenu
// anchor: Element, das als Anker fuer die Anzeige dient
// optConfig: Konfiguration der Optionen
// optSet: Gesetzte Optionen
// optSet: Platz fuer die gesetzten Optionen
// optParams: Eventuell notwendige Parameter
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// 'Tab': Tabelle mit dem Spielplan
// 'Zei': Startzeile des Spielplans mit dem ersten ZAT
// 'Spa': Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
// 'teamParams': Getrennte Daten-Option wird genutzt, hier: Team() mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function buildForm(anchor, optSet, optParams = { }) {
// return Gefuelltes Objekt mit den gesetzten Optionen
     __LOG[3]("buildForm()");
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
     // Klassifikation ueber Land und Liga des Teams...
    __TEAMCLASS.optSet = optSet;  // Classification mit optSet verknuepfen
    __TEAMCLASS.teamParams = optParams.teamParams; // Ermittelte Parameter


     const __FORM = getForm(optSet, optParams);
     optSet = startOptions(optConfig, optSet, __TEAMCLASS);
    const __SCRIPT = getScript(optSet, optParams);
 
    addForm(anchor, __FORM, __SCRIPT);
}


// Informationen zu hinzugefuegten Forms
    // Werte aus der HTML-Seite ermitteln...
const __FORMS = { };
    const __BOXSAISONS = document.getElementsByTagName("option");
    const __SAISON = getSaisonFromComboBox(__BOXSAISONS);
    const __LIGASIZE = getLigaSizeFromSpielplan(optParams.Tab.rows, optParams.Zei, optParams.Spa, getOptValue(optSet.saison));


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
    // ... und abspeichern...
// anchor: Element, das als Anker fuer die Anzeige dient
    setOpt(optSet.saison, __SAISON, false);
// form: HTML-Form des Optionsmenu (hinten angefuegt)
     setOpt(optSet.ligaSize, __LIGASIZE, false);
// script: Script mit Reaktionen
function addForm(anchor, form = "", script = "") {
    const __OLDFORM = __FORMS[anchor];
     const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);


     __FORMS[anchor] = {
     showOptions(optSet, optParams);
                          'Script' : script,
                          'Form'  : form
                      };


     anchor.innerHTML = __REST + script + form;
     return optSet;
}
}


// ==================== Abschnitt fuer Klasse Classification ====================
// ==================== Ende Abschnitt fuer Optionen ====================


// Basisklasse fuer eine Klassifikation der Optionen nach Kriterium (z.B. Erst- und Zweitteam oder Fremdteam)
// ==================== Abschnitt fuer Spielplan und ZATs ====================
function Classification() {
    'use strict';


    this.renameFun = prefixName;
// Beschreibungstexte aller Runden
    //this.renameParamFun = undefined;
const __POKALRUNDEN = [ "", "1. Runde", "2. Runde", "3. Runde", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
    this.optSet = undefined;
const __QUALIRUNDEN = [ "", "Quali 1", "Quali 2", "Quali 3" ];
    this.optSelect = { };
const __OSCRUNDEN  = [ "", "Viertelfinale", "Halbfinale", "Finale" ];
}
const __OSERUNDEN  = [ "", "Runde 1", "Runde 2", "Runde 3", "Runde 4", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
const __HINRUECK    = [ " Hin", " R\xFCck", "" ];


Class.define(Classification, Object, {
// ==================== Abschnitt fuer Klasse RundenLink ====================
                    'renameOptions' : function() {
                                          const __PARAM = this.renameParamFun();


                                          if (__PARAM !== undefined) {
function RundenLink(saison, team) {
                                              // Klassifizierte Optionen umbenennen...
    'use strict';
                                              return renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun);
                                          } else {
                                              return Promise.resolve();
                                          }
                                      },
                    'deleteOptions' : function(ignList) {
                                          const __OPTSELECT = addProps([], this.optSelect, null, ignList);


                                          return deleteOptions(this.optSet, __OPTSELECT, true, true);
    this.uri = new URI("http://os.ongapo.com/?erganzeigen=1&stataktion=Statistik+ausgeben");
                                      }
    this.runde = 0;
                });
    this.prop = "";
    this.label = "";


// ==================== Ende Abschnitt fuer Klasse Classification ====================
    if (saison) {
 
        this.setSaison(saison);
// ==================== Abschnitt fuer Klasse TeamClassification ====================
     }
 
     if (team) {
// Klasse fuer die Klassifikation der Optionen nach Team (Erst- und Zweitteam oder Fremdteam)
        this.setTeam(team);
function TeamClassification() {
     }
     'use strict';
 
     Classification.call(this);
 
    this.team = undefined;
     this.teamParams = undefined;
}
}


Class.define(TeamClassification, Classification, {
Class.define(RundenLink, Object, {
                    'renameParamFun' : function() {
        'setSaison'   : function(saison) {
                                          const __MYTEAM = (this.team = getMyTeam(this.optSet, this.teamParams, this.team));
                            this.uri.setQueryPar('saauswahl', saison);
 
                        },
                                          if (__MYTEAM.LdNr) {
        'setTeam'      : function(team) {
                                              // Prefix fuer die Optionen mit gesonderten Behandlung...
                            this.uri.setQueryPar('landauswahl', team.LdNr);
                                              return __MYTEAM.LdNr.toString() + '.' + __MYTEAM.LgNr.toString() + ':';
                            this.uri.setQueryPar('ligaauswahl', team.LgNr);
                                          } else {
                        },
                                              return undefined;
        'setPage'      : function(page, label) {
                                          }
                            this.uri.home();
                                      }
                            this.uri.down(page + ".php");
                });
                            this.setLabel(label);
 
                        },
// ==================== Ende Abschnitt fuer Klasse TeamClassification ====================
        'setRunde'     : function(prop, runde) {
                            this.prop = prop;
                            this.runde = runde;
                        },
        'setLabel'     : function(label) {
                            this.label = (label || "");
                        },
        'setAnzeigen' : function(show = true) {
                            this.uri.setQueryPar('erganzeigen', (show ? 1 : 0));
                        },
        'getLabel'    : function() {
                            return (this.label || "Link");
                        },
        'getHTML'      : function(target = undefined) {
                            if ((this.runde <= 0) || (! this.uri.getLeaf())) {
                                return this.label;
                            } else {
                                if (this.prop) {
                                    this.uri.setQueryPar(this.prop, this.runde);
                                }


// ==================== Abschnitt fuer Klasse Team ====================
                                return "<a " + URI.prototype.formatParams({
                                                                      'href'  : this.uri.getPath(),
                                                                      'target' : (target ? target : '_blank')
                                                                  }, function(value) {
                                                                        return '"' + value + '"';
                                                                    }, ' ', '=') + '>' + this.getLabel() + "</a>";
                            }
                        }
    });


// Klasse fuer Teamdaten
// ==================== Ende Abschnitt fuer Klasse RundenLink ====================


/*class*/ function Team /*{
// Liefert einen vor den ersten ZAT zurueckgesetzten Spielplanzeiger
     constructor*/(team, land, liga, teamId) {
// saison: Enthaelt die Nummer der laufenden Saison
         'use strict';
// ligaSize: Anzahl der Teams in dieser Liga (Gegner + 1)
 
// - ZATs pro Abrechnungsmonat
         this.Team = team;
// - Saison
         this.Land = land;
// - ZAT
         this.Liga = liga;
// - GameType
         this.TmNr = (teamId || 0);
// - Heim/Auswaerts
         this.LdNr = getLandNr(land);
// - Gegner
         this.LgNr = getLigaNr(liga);
// - Tore
     }
// - Gegentore
//}
// - Ligengroesse
// - Ligaspieltag
// - Pokalrunde
// - Eurorunde
// - Hin/Rueck
// - ZAT Rueck
// - ZAT Korr
function firstZAT(saison, ligaSize) {
     return {
        'anzZATpMonth' : ((saison < 2) ? 7 : 6),    // Erste Saison 7 ZAT, danach 6 ZAT...
        'saison'      : saison,
        'ZAT'          : 0,
        'gameType'    : "spielfrei",
        'heim'        : true,
        'gegner'      : "",
        'gFor'        : 0,
         'gAga'         : 0,
        'ligaSize'    : ligaSize,
         'ligaSpieltag' : 0,
         'pokalRunde'  : 1,
         'euroRunde'    : 0,
         'hinRueck'    : 2,    // 0: Hin, 1: Rueck, 2: unbekannt
         'ZATrueck'    : 0,
         'ZATkorr'      : 0
     };
}


Class.define(Team, Object, {
// Liefert den ZAT als String
                    '__TEAMITEMS' : {  // Items, die in Team als Teamdaten gesetzt werden...
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
                                        'Team' : true,
// longStats: Formatiert die Langversion des Textes
                                        'Liga' : true,
function getZAT(currZAT, longStats) {
                                        'Land' : true,
    return (longStats ? currZAT.gameType + ' ' + (currZAT.heim ? "Heim" : "Ausw\xE4rts") + ' ' : "") +
                                        'TmNr' : true,
          (longStats ? '[' + currZAT.ligaSpieltag + ' ' + currZAT.pokalRunde + ' ' + currZAT.euroRunde + "] " : "") +
                                        'LdNr' : true,
          (longStats ? '[' + currZAT.ZATrueck + __HINRUECK[currZAT.hinRueck] +
                                        'LgNr' : true
                        ' ' + ((currZAT.ZATkorr < 0) ? "" : '+') + currZAT.ZATkorr + "] " : "") +
                                    }
          (longStats ? currZAT.gegner + ((currZAT.gFor > -1) ? " (" + currZAT.gFor + " : " + currZAT.gAga + ')' : "") + ' ' : "") +
                });
          'S' + currZAT.saison + "-Z" + ((currZAT.ZAT < 10) ? '0' : "") + currZAT.ZAT;
}


// ==================== Ende Abschnitt fuer Klasse Team ====================
// Liefert die ZATs der Sonderspieltage fuer 10er- (2) und 20er-Ligen (4)
 
// saison: Enthaelt die Nummer der laufenden Saison
// ==================== Abschnitt fuer Klasse Verein ====================
// return [ 10erHin, 10erRueck, 20erHin, 20erRueck ], ZAT-Nummern der Zusatzspieltage
 
function getLigaExtra(saison) {
// Klasse fuer Vereinsdaten
     if (saison < 3) {
 
         return [ 8, 64, 32, 46 ];
/*class*/ function Verein /*extends Team {
    } else {
     constructor*/(team, land, liga, teamId, manager, flags) {
         return [ 9, 65, 33, 57 ];
         'use strict';
 
        Team.call(this, team, land, liga, teamId);
 
         this.Manager = manager;
        this.Flags = (flags || []);
     }
     }
//}
}


Class.define(Verein, Team, {
// Spult die Daten um anzZAT ZATs vor und schreibt Parameter
                    '__TEAMITEMS' : {  // Items, die in Verein als Teamdaten gesetzt werden...
// anhand des Spielplans fort. Also Spieltag, Runde, etc.
                                        'Team'    : true,
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
                                        'Liga'    : true,
// anzZAT: Anzahl der ZAT, um die vorgespult wird
                                        'Land'    : true,
function incZAT(currZAT, anzZAT = 1) {
                                        'TmNr'    : true,
    const __LIGAEXTRA = getLigaExtra(currZAT.saison);
                                        'LdNr'    : true,
    const __LIGAFIRST = 3 - (__LIGAEXTRA[0] % 2);
                                        'LgNr'    : true,
                                        'Manager' : true,
                                        'Flags'  : true
                                    }
                });


// ==================== Ende Abschnitt fuer Klasse Verein ====================
    for (let i = anzZAT; i > 0; i--) {
        currZAT.ZAT++;
        if ((currZAT.ZAT - __LIGAFIRST + 1) % 2 === 1) {
            currZAT.ligaSpieltag++;
        } else {
            const __POS = __LIGAEXTRA.indexOf(currZAT.ZAT);


// ==================== Spezialisierter Abschnitt fuer Optionen ====================
            if (~ __POS) {
 
                if (__POS < 2 * (currZAT.ligaSize % 9)) {
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
                    currZAT.ligaSpieltag++;
const __OPTSET = { };
                }
 
             }
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
const __TEAMCLASS = new TeamClassification();
 
// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
__TEAMCLASS.optSelect = {
                      'ligaSize'  : true
                  };
 
// Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option
// optSet: Platz fuer die gesetzten Optionen
// teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'TmNr', 'LdNr' und 'LgNr')
// myTeam: Objekt fuer die Teamdaten
// return Die Teamdaten oder undefined bei Fehler
function getMyTeam(optSet = undefined, teamParams = undefined, myTeam = new Team()) {
    if (teamParams !== undefined) {
        addProps(myTeam, teamParams, myTeam.__TEAMITEMS);
        __LOG[2]("Ermittelt: " + safeStringify(myTeam));
        // ... und abspeichern, falls erweunscht...
        if (optSet && optSet.team) {
             setOpt(optSet.team, myTeam, false);
         }
         }
    } else {
         if ((currZAT.ZAT > 12) && ((currZAT.ZAT % 10) === 5)) {    // passt fuer alle Saisons: 12, 20, 30, 40, 48, 58, 68 / 3, 15, 27, 39, 51, 63, 69
         const __TEAM = ((optSet && optSet.team) ? getOptValue(optSet.team) : undefined)// Gespeicherte Parameter
            currZAT.pokalRunde++;
 
        }
         if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
         if (((currZAT.ZAT + currZAT.ZATkorr) % 6) === 4) {
             addProps(myTeam, __TEAM, myTeam.__TEAMITEMS);
            if (currZAT.ZAT < 63) {
            __LOG[2]("Gespeichert: " + safeStringify(myTeam));
                currZAT.ZATrueck = currZAT.ZAT + 2;
        } else {
                currZAT.euroRunde++;
            __LOG[6]("Team nicht ermittelt: " + safeStringify(__TEAM));
                currZAT.hinRueck = 0;
            } else {
                currZAT.euroRunde = 11;    // Finale
                currZAT.hinRueck = 2;
            }
        }
        if (currZAT.ZAT === currZAT.ZATrueck) {
            currZAT.hinRueck = 1;        // 5, 7; 11, 13;  (17, 19) / 23,  25; 29, 31; 35,  37; 41,  43; 47, 49; 53,  55; 59,  61; 69
             if (currZAT.saison < 3) {    // 4, 6; 10, 14*; (16, 22*) / 24**, 26; 34, 36; 38*, 42; 44*, 50; 52, 54; 56*, 60; 62*, 66; 70
                if (currZAT.ZAT === 22) {
                    currZAT.ZATkorr = 4;
                } else if ((currZAT.ZAT - 6) % 20 > 6) {
                    currZAT.ZATkorr = 2;
                } else {
                    currZAT.ZATkorr = 0;
                }
                if ((currZAT.ZAT === 22) || (currZAT.ZAT === 30)) {
                    currZAT.euroRunde--;   // Frueher: 3. Quali-Rueckspiel erst knapp vor 1. Hauptrunde
                }
            }
         }
         }
     }
     }
    return myTeam;
}
}


// Behandelt die Optionen und laedt das Benutzermenu
// Liefert die Beschreibung des Spiels am aktuellen ZAT
// optConfig: Konfiguration der Optionen
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// optSet: Platz fuer die gesetzten Optionen
// showLink: Angabe, ob ein Link eingefuegt werden soll
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Beschreibung des Spiels
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function getZusatz(currZAT, showLink = true) {
// 'Tab': Tabelle mit dem Spielplan
     const __LINK = new RundenLink(currZAT.saison, __TEAMCLASS.team);
// 'Zei': Startzeile des Spielplans mit dem ersten ZAT
// 'Spa': Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
// 'teamParams': Getrennte Daten-Option wird genutzt, hier: Team() mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return Promise auf gefuelltes Objekt mit den gesetzten Optionen
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
     // Klassifikation ueber Land und Liga des Teams...
    __TEAMCLASS.optSet = optSet; // Classification mit optSet verknuepfen
    __TEAMCLASS.teamParams = optParams.teamParams;  // Ermittelte Parameter


     return startOptions(optConfig, optSet, __TEAMCLASS).then(optSet => {
     if (currZAT.gameType === "Liga") {
                    // Werte aus der HTML-Seite ermitteln...
        if (currZAT.ZAT < 70) {
                    const __BOXSAISONS = document.getElementsByTagName('option');
            __LINK.setRunde("stauswahl", currZAT.ligaSpieltag);
                    const __SAISON = getSelectionFromComboBox(__BOXSAISONS, 0, 'Number');
            __LINK.setPage("ls", __LINK.runde + ". Spieltag");
                    const __LIGASIZE = (optParams.Tab ? getLigaSizeFromSpielplan(optParams.Tab.rows, optParams.Zei, optParams.Spa, getOptValue(optSet.saison)) : undefined);
        } else {
            __LINK.setLabel("Relegation");
        }
    } else if (currZAT.gameType === "LP") {
        __LINK.setRunde("stauswahl", currZAT.pokalRunde);
        __LINK.setPage("lp", __POKALRUNDEN[__LINK.runde]);
    } else if ((currZAT.gameType === "OSCQ") || (currZAT.gameType === "OSEQ")) {
        __LINK.setRunde("runde", currZAT.euroRunde);
        __LINK.setPage(((currZAT.gameType === "OSCQ") ? "oscq" : "oseq"), __QUALIRUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]);
    } else if (currZAT.gameType === "OSC") {
        if (currZAT.euroRunde < 9) {
            const __GRUPPENPHASE = ((currZAT.euroRunde < 6) ? "HR-Grp. " : "ZR-Grp. ");


                    // ... und abspeichern...
            __LINK.setRunde("", (currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck);
                    setOpt(optSet.saison, __SAISON, false);
            __LINK.setPage(((currZAT.euroRunde < 6) ? "oschr" : "osczr"), __GRUPPENPHASE + "Spiel " + __LINK.runde);
                    setOpt(optSet.ligaSize, __LIGASIZE, false);
        } else {
            __LINK.setPage("oscfr", __OSCRUNDEN[currZAT.euroRunde - 8] + __HINRUECK[currZAT.hinRueck]);
        }
    } else if (currZAT.gameType === "OSE") {
        __LINK.setRunde("runde", currZAT.euroRunde - 3);
        __LINK.setPage("ose", __OSERUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]);
    } else {
        __LINK.setLabel();  // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert")
    }


                    return showOptions(optSet, optParams);
    return (showLink ? __LINK.getHTML() : "");
                }, defaultCatch);
}
}


// ==================== Ende Abschnitt fuer Optionen ====================
// ==================== Abschnitt fuer Statistiken des Spielplans ====================


// ==================== Abschnitt fuer Spielplan und ZATs ====================
// Liefert eine auf 0 zurueckgesetzte Ergebnissumme
 
// - Siege
// Beschreibungstexte aller Runden
// - Unentschieden
const __POKALRUNDEN = [ "", "1. Runde", "2. Runde", "3. Runde", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
// - Niederlagen
const __QUALIRUNDEN = [ "", "Quali 1", "Quali 2", "Quali 3" ];
// - Tore
const __OSCRUNDEN  = [ "", "Viertelfinale", "Halbfinale", "Finale" ];
// - Gegentore
const __OSERUNDEN  = [ "", "Runde 1", "Runde 2", "Runde 3", "Runde 4", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
// - Siegpunkte
const __HINRUECK   = [ " Hin", " R\xFCck", "" ];
function emptyStats() {
    return {
        'S'    : 0,
        'U'    : 0,
        'N'    : 0,
        'gFor' : 0,
        'gAga' : 0,
        'P'   : 0
    };
}


// ==================== Abschnitt fuer Klasse RundenLink ====================
// Liefert die Stats als String
// stats: Enthaelt die summierten Stats
// longStats: Formatiert die Langversion des Textes
function getStats(stats, longStats) {
    return (longStats ? '[' + stats.S + ' ' + stats.U + ' ' + stats.N + "] " : "/ ") +
          stats.gFor + ':' + stats.gAga + ' ' + ((stats.gFor < stats.gAga) ? "" : (stats.gFor > stats.gAga) ? '+' : "") +
          (stats.gFor - stats.gAga) + " (" + stats.P + ')';
}


function RundenLink(saison, team) {
// Summiert ein Ergebnis auf die Stats und liefert den neuen Text zurueck
     'use strict';
// stats: Enthaelt die summierten Stats
// longStats: Formatiert die Langversion des Textes
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT (mit dem Ergebnis)
function addResultToStats(stats, longStats, currZAT) {
     let ret = "";


     this.uri = new URI("http://os.ongapo.com/?erganzeigen=1&stataktion=Statistik+ausgeben");
     if (currZAT.gFor > -1) {
    this.runde = 0;
        let p = 0;
    this.prop = "";
 
    this.label = "";
        if (currZAT.gFor > currZAT.gAga) {
            stats.S++;
            p = 3;
        } else if (currZAT.gFor === currZAT.gAga) {
            stats.U++;
            p = 1;
        } else {
            stats.N++;
        }
        stats.P += p;
        stats.gFor += currZAT.gFor;
        stats.gAga += currZAT.gAga;


    if (saison) {
         ret = getStats(stats, longStats);
        this.setSaison(saison);
    }
    if (team) {
         this.setTeam(team);
     }
     }
    return ret;
}
}


Class.define(RundenLink, Object, {
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
         'setSaison'   : function(saison) {
 
                            this.uri.setQueryPar('saauswahl', saison);
const __GAMETYPES = {   // "Blind FSS gesucht!"
                        },
         'unbekannt' : -1,
         'setTeam'      : function(team) {
        "reserviert" :  0,
                            this.uri.setQueryPar('landauswahl', team.LdNr);
        "Frei"      :  0,
                            this.uri.setQueryPar('ligaauswahl', team.LgNr);
         "spielfrei"  : 0,
                            this.uri.setQueryPar('hl',         team.TmNr);                        },
        "Friendly"  :  1,
         'setPage'      : function(page, label) {
        "Liga"      :  2,
                            this.uri.home();
        "LP"        :  3,
                            this.uri.down(page + ".php");
        "OSEQ"      :  4,
                            this.setLabel(label);
         "OSE"        : 5,
                        },
        "OSCQ"      :  6,
         'setRunde'     : function(prop, runde) {
        "OSC"       :  7
                            this.prop = prop;
    };
                            this.runde = runde;
 
                        },
const __LIGANRN = {
         'setLabel'     : function(label) {
         'unbekannt' : 0,
                            this.label = (label || "");
        '1. Liga'    :  1,
                        },
        '2. Liga A'  :  2,
         'setAnzeigen'  : function(show = true) {
         '2. Liga B' : 3,
                            this.uri.setQueryPar('erganzeigen', (show ? 1 : 0));
         '3. Liga A'  : 4,
                        },
        '3. Liga B' : 5,
         'getLabel'     : function() {
         '3. Liga C' : 6,
                            return (this.label || "Link");
         '3. Liga D' : 7
                        },
    };
         'getHTML'     : function(target = undefined) {
                            if ((this.runde <= 0) || (! this.uri.getLeaf())) {
                                return this.label;
                            } else {
                                if (this.prop) {
                                    this.uri.setQueryPar(this.prop, this.runde);
                                }


                                return "<a " + URI.prototype.formatParams({
const __LANDNRN = {
                                                                      'href'  : this.uri.getPath(),
        'unbekannt'             :   0,
                                                                      'target' : (target ? target : '_blank')
        'Albanien'              : 45,
                                                                  }, function(value) {
        'Andorra'               : 95,
                                                                        return '"' + value + '"';
        'Armenien'              : 83,
                                                                    }, ' ', '=') + '>' + this.getLabel() + "</a>";
        'Aserbaidschan'         : 104,
                            }
        'Belgien'                :  12,
                        }
        'Bosnien-Herzegowina'   :  66,
    });
        'Bulgarien'             :  42,
 
        'D\xE4nemark'            :  8,
// ==================== Ende Abschnitt fuer Klasse RundenLink ====================
        'Deutschland'           :  6,
 
        'England'               :  1,
// Liefert einen vor den ersten ZAT zurueckgesetzten Spielplanzeiger
        'Estland'               :  57,
// saison: Enthaelt die Nummer der laufenden Saison
        'Far\xF6er'              :  68,
// ligaSize: Anzahl der Teams in dieser Liga (Gegner + 1)
        'Finnland'              :  40,
// - ZATs pro Abrechnungsmonat
        'Frankreich'            :  32,
// - Saison
        'Georgien'              :  49,
// - ZAT
        'Griechenland'          :  30,
// - GameType
        'Irland'                :  5,
// - Heim/Auswaerts
        'Island'                :  29,
// - Gegner
        'Israel'                : 23,
// - Tore
        'Italien'                : 10,
// - Gegentore
        'Kasachstan'            : 105,
// - Ligengroesse
        'Kroatien'              :  24,
// - Ligaspieltag
        'Lettland'              :  97,
// - Pokalrunde
        'Liechtenstein'          :  92,
// - Eurorunde
        'Litauen'                :  72,
// - Hin/Rueck
        'Luxemburg'              :  93,
// - ZAT Rueck
        'Malta'                  :  69,
// - ZAT Korr
        'Mazedonien'            :  86,
function firstZAT(saison, ligaSize) {
        'Moldawien'              :  87,
    return {
        'Niederlande'            :  11,
         'anzZATpMonth' : ((saison < 2) ? 7 : 6),   // Erste Saison 7 ZAT, danach 6 ZAT...
         'Nordirland'             :   4,
         'saison'       : saison,
         'Norwegen'               :   9,
         'ZAT'          : 0,
         '\xD6sterreich'          : 14,
         'gameType'     : 'spielfrei',
         'Polen'                 : 25,
         'heim'        : true,
        'Portugal'               :  17,
         'gegner'       : "",
         'Rum\xE4nien'           :  28,
         'gFor'        : 0,
         'Russland'              : 19,
         'gAga'        : 0,
         'San Marino'             : 98,
         'ligaSize'     : ligaSize,
         'Schottland'             :  2,
         'ligaSpieltag' : 0,
         'Schweden'              : 27,
         'pokalRunde'   : 1,
         'Schweiz'               :  37,
         'euroRunde'   : 0,
         'Serbien und Montenegro' : 41,
         'hinRueck'     : 2,   // 0: Hin, 1: Rueck, 2: unbekannt
         'Slowakei'               : 70,
         'ZATrueck'     : 0,
         'Slowenien'             : 21,
         'ZATkorr'     : 0
         'Spanien'               : 13,
         'Tschechien'             : 18,
         'T\xFCrkei'             : 39,
        'Ukraine'                : 20,
        'Ungarn'                : 26,
        'Wales'                  :   3,
         'Weissrussland'         : 71,
         'Zypern'                 : 38
     };
     };
// ==================== Abschnitt fuer Daten des Spielplans ====================
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// gameType: Name des Wettbewerbs eines Spiels
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
function getGameTypeID(gameType) {
    return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);
}
}


// Liefert den ZAT als String
// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// land: Name des Landes
// longStats: Formatiert die Langversion des Textes
// return OS2-ID des Landes, 0 fuer ungueltig
function getZAT(currZAT, longStats) {
function getLandNr(land) {
     return (longStats ? currZAT.gameType + ' ' + (currZAT.heim ? "Heim" : "Ausw\xE4rts") + ' ' : "") +
     return getValue(__LANDNRN[land], __LANDNRN.unbekannt);
          (longStats ? '[' + currZAT.ligaSpieltag + ' ' + currZAT.pokalRunde + ' ' + currZAT.euroRunde + "] " : "") +
          (longStats ? '[' + currZAT.ZATrueck + __HINRUECK[currZAT.hinRueck] +
                        ' ' + ((currZAT.ZATkorr < 0) ? "" : '+') + currZAT.ZATkorr + "] " : "") +
          (longStats ? currZAT.gegner + ((currZAT.gFor > -1) ? " (" + currZAT.gFor + " : " + currZAT.gAga + ')' : "") + ' ' : "") +
          'S' + currZAT.saison + "-Z" + ((currZAT.ZAT < 10) ? '0' : "") + currZAT.ZAT;
}
}


// Liefert die ZATs der Sonderspieltage fuer 10er- (2) und 20er-Ligen (4)
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
// saison: Enthaelt die Nummer der laufenden Saison
// land: Name der Liga
// return [ 10erHin, 10erRueck, 20erHin, 20erRueck ], ZAT-Nummern der Zusatzspieltage
// return OS2-ID der Liga, 0 fuer ungueltig
function getLigaExtra(saison) {
function getLigaNr(liga) {
     if (saison < 3) {
    return getValue(__LIGANRN[liga], __LIGANRN.unbekannt);
         return [ 8, 64, 32, 46 ];
}
 
// Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck
// cell: Tabellenzelle mit dem Namen des Gegners
// return Der Name des Gegners
function getGegnerFromCell(cell) {
    const __GEGNER = cell.textContent;
    const __POS = __GEGNER.indexOf(" (");
 
     if (~ __POS) {
         return __GEGNER.substr(0, __POS);
     } else {
     } else {
         return [ 9, 65, 33, 57 ];
         return __GEGNER;
     }
     }
}
}


// Spult die Daten um anzZAT ZATs vor und schreibt Parameter
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle, etwa "2 : 1", und liefert zwei Werte zurueck
// anhand des Spielplans fort. Also Spieltag, Runde, etc.
// cell: Tabellenzelle mit Eintrag "2 : 1"
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// return { '2', '1' } im Beispiel
// anzZAT: Anzahl der ZAT, um die vorgespult wird
function getErgebnisFromCell(cell) {
function incZAT(currZAT, anzZAT = 1) {
     const __ERGEBNIS = cell.textContent.split(" : ", 2);
     const __LIGAEXTRA = getLigaExtra(currZAT.saison);
    const __LIGAFIRST = 3 - (__LIGAEXTRA[0] % 2);


     for (let i = anzZAT; i > 0; i--) {
     return __ERGEBNIS;
        currZAT.ZAT++;
}
        if ((currZAT.ZAT - __LIGAFIRST + 1) % 2 === 1) {
 
            currZAT.ligaSpieltag++;
// Ermittelt die Spielart aus einer Tabellenzelle, etwa "Liga : Heim", und liefert zwei Werte zurueck
        } else {
// cell: Tabellenzelle mit Eintrag "Liga : Heim" (Spielplan) oder "Liga  Heim: " (Managerbuero)
            const __POS = __LIGAEXTRA.indexOf(currZAT.ZAT);
// return { "Liga", "Heim" } im Beispiel
function getSpielArtFromCell(cell) {
    const __TEXT = cell.textContent.replace('\xA0', "").replace(':', "").replace("  ", ' ');
    const __SPIELART = __TEXT.split(' ', 2);
 
    return __SPIELART;
}
 
// Ermittelt den Namen des Spielgegners aus einer Tabellenzelle und setzt gegner im Spielplanzeiger
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// cell: Tabellenzelle mit dem Namen des Gegners
function setGegnerFromCell(currZAT, cell) {
    const __GEGNER = getGegnerFromCell(cell);


            if (~ __POS) {
    currZAT.gegner = __GEGNER;
                if (__POS < 2 * (currZAT.ligaSize % 9)) {
                    currZAT.ligaSpieltag++;
                }
            }
        }
        if ((currZAT.ZAT > 12) && ((currZAT.ZAT % 10) === 5)) {    // passt fuer alle Saisons: 12, 20, 30, 40, 48, 58, 68 / 3, 15, 27, 39, 51, 63, 69
            currZAT.pokalRunde++;
        }
        if (((currZAT.ZAT + currZAT.ZATkorr) % 6) === 4) {
            if (currZAT.ZAT < 63) {
                currZAT.ZATrueck = currZAT.ZAT + 2;
                currZAT.euroRunde++;
                currZAT.hinRueck = 0;
            } else {
                currZAT.euroRunde = 11;    // Finale
                currZAT.hinRueck = 2;
            }
        }
        if (currZAT.ZAT === currZAT.ZATrueck) {
            currZAT.hinRueck = 1;        // 5, 7; 11, 13;  (17, 19)  / 23,  25; 29, 31; 35,  37; 41,  43; 47, 49; 53,  55; 59,  61; 69
            if (currZAT.saison < 3) {    // 4, 6; 10, 14*; (16, 22*) / 24**, 26; 34, 36; 38*, 42; 44*, 50; 52, 54; 56*, 60; 62*, 66; 70
                if (currZAT.ZAT === 22) {
                    currZAT.ZATkorr = 4;
                } else if ((currZAT.ZAT - 6) % 20 > 6) {
                    currZAT.ZATkorr = 2;
                } else {
                    currZAT.ZATkorr = 0;
                }
                if ((currZAT.ZAT === 22) || (currZAT.ZAT === 30)) {
                    currZAT.euroRunde--;    // Frueher: 3. Quali-Rueckspiel erst knapp vor 1. Hauptrunde
                }
            }
        }
    }
}
}


// Liefert die Beschreibung des Spiels am aktuellen ZAT
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle und setzt tore/gtore im Spielplanzeiger
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// showLink: Angabe, ob ein Link eingefuegt werden soll
// cell: Tabellenzelle mit Eintrag "2 : 1"
// return Beschreibung des Spiels
function setErgebnisFromCell(currZAT, cell) {
function getZusatz(currZAT, showLink = true) {
     const __ERGEBNIS = getErgebnisFromCell(cell);
     const __LINK = new RundenLink(currZAT.saison, __TEAMCLASS.team);


     if (currZAT.gameType === 'Liga') {
     if (__ERGEBNIS.length === 2) {
         if (currZAT.ZAT < 70) {
         currZAT.gFor = parseInt(__ERGEBNIS[0], 10);
            __LINK.setRunde('stauswahl', currZAT.ligaSpieltag);
        currZAT.gAga = parseInt(__ERGEBNIS[1], 10);
            __LINK.setPage('ls', __LINK.runde + ". Spieltag");
    } else {
        } else {
        currZAT.gFor = -1;
            __LINK.setLabel("Relegation");
         currZAT.gAga = -1;
         }
    }
    } else if (currZAT.gameType === 'LP') {
}
        __LINK.setRunde('stauswahl', currZAT.pokalRunde);
 
        __LINK.setPage('lp', __POKALRUNDEN[__LINK.runde]);
// Ermittelt die Spielart aus einer Tabellenzelle und setzt gameType/heim im Spielplanzeiger
     } else if ((currZAT.gameType === 'OSCQ') || (currZAT.gameType === 'OSEQ')) {
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
        __LINK.setRunde('runde', currZAT.euroRunde);
// cell: Tabellenzelle mit Eintrag "Liga : Heim" oder "Liga Heim"
        __LINK.setPage(((currZAT.gameType === 'OSCQ') ? 'oscq' : 'oseq'), __QUALIRUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]);
function setSpielArtFromCell(currZAT, cell) {
     } else if (currZAT.gameType === 'OSC') {
    const __SPIELART = getSpielArtFromCell(cell);
         if (currZAT.euroRunde < 9) {
 
             const __GRUPPENPHASE = ((currZAT.euroRunde < 6) ? "HR-Grp. " : "ZR-Grp. ");
    currZAT.gameType = __SPIELART[0];
     currZAT.heim    = (__SPIELART.length < 2) || (__SPIELART[1] === "Heim");
}
 
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// gameType: Name des Wettbewerbs eines Spiels
// label: Anzuklickender Text des neuen Links
// return HTML-Link auf die Preview-Seite fuer diesen Spielbericht
function getBilanzLinkFromCell(cell, gameType, label) {
    const __GAMETYPEID = getGameTypeID(gameType);
    let ret = "";
 
     if (cell.textContent !== "Vorschau") {   // Nur falls Link nicht bereits vorhanden
         if (__GAMETYPEID > 1) {             // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert"
             const __SEARCHFUN = ":os_bericht(";
            let paarung = cell.innerHTML.substr(cell.innerHTML.indexOf(__SEARCHFUN) + __SEARCHFUN.length);


             __LINK.setRunde("", (currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck);
             paarung = paarung.substr(0, paarung.indexOf(')'));
             __LINK.setPage(((currZAT.euroRunde < 6) ? 'oschr' : 'osczr'), __GRUPPENPHASE + "Spiel " + __LINK.runde);
             paarung = paarung.substr(0, paarung.lastIndexOf(','));
        } else {
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
             __LINK.setPage('oscfr', __OSCRUNDEN[currZAT.euroRunde - 8] + __HINRUECK[currZAT.hinRueck]);
             ret = ' <a href="javascript:spielpreview(' + paarung + ',' + __GAMETYPEID + ')">' + label + "</a>";
         }
         }
    } else if (currZAT.gameType === 'OSE') {
        __LINK.setRunde('runde', currZAT.euroRunde - 3);
        __LINK.setPage('ose', __OSERUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]);
    } else if (currZAT.gameType === 'Supercup') {
        __LINK.setRunde("", 1);
        __LINK.setPage('supercup', currZAT.gameType);
    } else {
        __LINK.setLabel();  // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert")
     }
     }


     return (showLink ? __LINK.getHTML() : "");
     return ret;
}
}


// ==================== Abschnitt fuer Statistiken des Spielplans ====================
// Addiert einen Link auf die Bilanz hinter den Spielberichts-Link
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// gameType: Name des Wettbewerbs eines Spiels
// label: Anzuklickender Text des neuen Links
function addBilanzLinkToCell(cell, gameType, label) {
    const __BILANZLINK = getBilanzLinkFromCell(cell, gameType, label);


// Liefert eine auf 0 zurueckgesetzte Ergebnissumme
    if (__BILANZLINK !== "") {
// - Siege
         cell.innerHTML += __BILANZLINK;
// - Unentschieden
     }
// - Niederlagen
// - Tore
// - Gegentore
// - Siegpunkte
function emptyStats() {
    return {
         'S'    : 0,
        'U'    : 0,
        'N'    : 0,
        'gFor' : 0,
        'gAga' : 0,
        'P'    : 0
     };
}
}


// Liefert die Stats als String
// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
// stats: Enthaelt die summierten Stats
 
// longStats: Formatiert die Langversion des Textes
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
function getStats(stats, longStats) {
        'Zeile'  : 0,
    return (longStats ? '[' + stats.S + ' ' + stats.U + ' ' + stats.N + "] " : "/ ") +
        'Spalte' : 1,
          stats.gFor + ':' + stats.gAga + ' ' + ((stats.gFor < stats.gAga) ? "" : (stats.gFor > stats.gAga) ? '+' : "") +
        'start' : " von ",
          (stats.gFor - stats.gAga) + " (" + stats.P + ')';
        'middle' : "</b><br>",
}
        'liga'   : ". Liga",
        'land'   : ' ',
        'end'    : "<a href="
    };


// Summiert ein Ergebnis auf die Stats und liefert den neuen Text zurueck
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
// stats: Enthaelt die summierten Stats
        'Zeile'  : 0,
// longStats: Formatiert die Langversion des Textes
        'Spalte' : 0,
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT (mit dem Ergebnis)
        'start'  : "<b>",
function addResultToStats(stats, longStats, currZAT) {
        'middle' : " - ",
    let ret = "";
        'liga'  : ". Liga",
        'land'  : 'target="_blank">',
        'end'    : "</a></b>"
    };


    if (currZAT.gFor > -1) {
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
        let p = 0;
// cell: Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
// teamSeach: Muster fuer die Suche, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
function getTeamParamsFromTable(table, teamSearch = undefined) {
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
    const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
    const __TEAMCELLSTR  = (table === undefined) ? "" : table.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML;
    const __SEARCHSTART  = __TEAMSEARCH.start;
    const __SEARCHMIDDLE = __TEAMSEARCH.middle;
    const __SEARCHLIGA  = __TEAMSEARCH.liga;
    const __SEARCHLAND  = __TEAMSEARCH.land;
    const __SEARCHEND    = __TEAMSEARCH.end;
    const __INDEXSTART  = __TEAMCELLSTR.indexOf(__SEARCHSTART);
    const __INDEXEND    = __TEAMCELLSTR.indexOf(__SEARCHEND);


        if (currZAT.gFor > currZAT.gAga) {
    let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
            stats.S++;
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
            p = 3;
    const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
        } else if (currZAT.gFor === currZAT.gAga) {
            stats.U++;
            p = 1;
        } else {
            stats.N++;
        }
        stats.P += p;
        stats.gFor += currZAT.gFor;
        stats.gAga += currZAT.gAga;


        ret = getStats(stats, longStats);
    let land = (~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
     }
    const __TEAMNAME = (~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
     let liga = ((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;


     return ret;
    if (land !== undefined) {
        if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
            land = land.substr(2);
        }
        if (liga !== undefined) {
            liga = liga.substring(0, liga.length - land.length);
        }
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
        if (~ __INDEXLAND) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
        }
    }
 
    const __TEAM = new Team(__TEAMNAME, land, liga);
 
     return __TEAM;
}
}


// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// url: Adresse der Seite
// leafs: Liste von Filenamen mit der Default-Seitennummer (falls Query-Parameter nicht gefunden)
// item: Query-Parameter, der die Nummer der Unterseite angibt
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url, leafs, item = "page") {
    const __URI = new URI(url);
    const __LEAF = __URI.getLeaf();
 
    for (let leaf in leafs) {
        if (__LEAF === leaf) {
            const __DEFAULT = leafs[leaf];


const __GAMETYPENRN = {    // "Blind FSS gesucht!"
            return getValue(__URI.getQueryPar(item), __DEFAULT);
        'unbekannt'  : -1,
         }
         'reserviert' :  0,
     }
        'Frei'      :  0,
        'spielfrei'  :  0,
        'Friendly'  :  1,
        'Liga'      :  2,
        'LP'        :  3,
        'OSEQ'      :  4,
        'OSE'        :  5,
        'OSCQ'      :  6,
        'OSC'        :  7,
        'Supercup'  : 10
     };


const __GAMETYPEALIASES = {
     return -1;
        'unbekannt'  :  "unbekannt",
}
        'reserviert' :  undefined,
        'Frei'      :  undefined,
        'spielfrei'  :  "",
        'Friendly'  :  "FSS",
        'Liga'      :  undefined,
        'LP'        :  "Pokal",
        'OSEQ'      :  undefined,
        'OSE'        :  undefined,
        'OSCQ'      :  undefined,
        'OSC'        :  undefined,
        'Supercup'  : "Super"
     };
const __GAMETYPES = reverseMapping(__GAMETYPENRN);


const __LIGANRN = {
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
        'unbekannt'  : 0,
// saisons: Alle "option"-Eintraege der Combo-Box
        '1. Liga'    :  1,
// return Nummer der ausgewaehlten Saison (Default: 0)
         '2. Liga A'  :  2,
function getSaisonFromComboBox(saisons) {
        '2. Liga B'  :  3,
    let saison = 0;
         '3. Liga A'  :  4,
 
        '3. Liga B'  :  5,
    for (let i = 0; i < saisons.length; i++) {
        '3. Liga C'  :  6,
         const __SAISON = saisons[i];
         '3. Liga D'  :  7
 
     };
         if (__SAISON.outerHTML.match(/selected/)) {
const __LIGATYPES = reverseMapping(__LIGANRN);
            saison = __SAISON.textContent;
         }
     }
 
    return saison;
}


const __LANDNRN = {
// Ermittelt aus dem Spielplan die Ligengroesse ueber die Sonderspieltage
        'unbekannt'              :   0,
// rows: Tabellenzeilen mit dem Spielplan
        'Albanien'              : 45,
// startIdx: Index der Zeile mit dem ersten ZAT
        'Andorra'                : 95,
// colArtIdx: Index der Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
        'Armenien'              : 83,
// saison: Enthaelt die Nummer der laufenden Saison
        'Aserbaidschan'          : 104,
// return 10 bei 36 Spielen, 18 bei 34 Spielen, 20 bei 38 Spielen
        'Belgien'                :  12,
function getLigaSizeFromSpielplan(rows, startIdx, colArtIdx, saison) {
        'Bosnien-Herzegowina'    :  66,
    const __LIGAEXTRA = getLigaExtra(saison);
        'Bulgarien'              :  42,
    const __TEST10ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[0] - 1].cells[colArtIdx]);
        'D\xE4nemark'            :  8,
    const __TEST20ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[2] - 1].cells[colArtIdx]);
        'Deutschland'            :  6,
 
        'England'                :  1,
    if (__TEST20ER[0] === "Liga") {
        'Estland'                :  57,
         return 20;
        'Far\xF6er'              :  68,
    } else if (__TEST10ER[0] === "Liga") {
        'Finnland'              :  40,
         return 10;
         'Frankreich'            :  32,
    } else {
        'Georgien'              :  49,
         return 18;
        'Griechenland'          :  30,
    }
        'Irland'                :  5,
}
        'Island'                :  29,
 
        'Israel'                :  23,
// ==================== Ende Abschnitt fuer Spielplan und ZATs ====================
         'Italien'                :  10,
 
        'Kasachstan'            : 105,
// ==================== Hauptprogramm ====================
         'Kroatien'              :  24,
 
        'Lettland'              :  97,
// Verarbeitet Ansicht "Saisonplan"
        'Liechtenstein'          :  92,
function procSpielplan() {
        'Litauen'                :  72,
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
        'Luxemburg'              :  93,
    const __ROWOFFSETLOWER = 0;
        'Malta'                  :  69,
 
        'Mazedonien'            :  86,
    const __COLUMNINDEX = {
        'Moldawien'              :  87,
         'Art' : 1,
        'Niederlande'            :  11,
         'Geg' : 2,
        'Nordirland'            :  4,
         'Erg' : 3,
        'Norwegen'              :  9,
         'Ber' : 4,
        '\xD6sterreich'          :  14,
         'Zus' : 5,
        'Polen'                  :  25,
         'Kom' : 6
        'Portugal'              :  17,
         'Rum\xE4nien'           : 28,
        'Russland'              :  19,
        'San Marino'            :  98,
         'Schottland'             :   2,
         'Schweden'               : 27,
         'Schweiz'               : 37,
         'Serbien und Montenegro' : 41,
         'Slowakei'               : 70,
        'Slowenien'              :  21,
        'Spanien'                :  13,
        'Tschechien'            :  18,
        'T\xFCrkei'              :  39,
        'Ukraine'                :  20,
        'Ungarn'                :  26,
        'Wales'                  :  3,
        'Weissrussland'          :  71,
        'Zypern'                :  38
     };
     };
const __LAENDER = reverseMapping(__LANDNRN);


const __TLALAND = {
    const __TEAMPARAMS = getTeamParamsFromTable(getTable(1), __TEAMSEARCHTEAM);  // Link mit Team, Liga, Land...
        undefined : 'unbekannt',
 
        'ALB'    : 'Albanien',
     buildOptions(__OPTCONFIG, __OPTSET, {
        'AND'    : 'Andorra',
                    'Tab'       : getTable(2),
        'ARM'    : 'Armenien',
                    'Zei'       : __ROWOFFSETUPPER,
        'AZE'     : 'Aserbaidschan',
                    'Spa'       : __COLUMNINDEX.Art,
        'BEL'    : 'Belgien',
                    'teamParams' : __TEAMPARAMS,
        'BIH'     : 'Bosnien-Herzegowina',
                    'menuAnchor' : getTable(0, "div"),
        'BUL'     : 'Bulgarien',
                    'hideForm'   : {
        'DEN'     : 'D\xE4nemark',
                                        'team'        : true
        'GER'     : 'Deutschland',
                                    },
        'ENG'     : 'England',
                    'formWidth' : 3,
        'EST'    : 'Estland',
                    'formBreak' : 4
        'FRO'    : 'Far\xF6er',
                });
        'FIN'     : 'Finnland',
        'FRA'     : 'Frankreich',
         'GEO'    : 'Georgien',
        'GRE'    : 'Griechenland',
        'IRL'    : 'Irland',
        'ISL'    : 'Island',
        'ISR'    : 'Israel',
        'ITA'    : 'Italien',
        'KAZ'    : 'Kasachstan',
        'CRO'    : 'Kroatien',
        'LVA'    : 'Lettland',
        'LIE'    : 'Liechtenstein',
        'LTU'    : 'Litauen',
        'LUX'    : 'Luxemburg',
        'MLT'    : 'Malta',
        'MKD'    : 'Mazedonien',
        'MDA'    : 'Moldawien',
        'NED'    : 'Niederlande',
        'NIR'    : 'Nordirland',
        'NOR'    : 'Norwegen',
        'AUT'    : '\xD6sterreich',
        'POL'    : 'Polen',
        'POR'    : 'Portugal',
        'ROM'    : 'Rum\xE4nien',
        'RUS'    : 'Russland',
        'SMR'    : 'San Marino',
        'SCO'    : 'Schottland',
        'SWE'    : 'Schweden',
        'SUI'    : 'Schweiz',
        'SCG'     : 'Serbien und Montenegro',
        'SVK'     : 'Slowakei',
        'SVN'    : 'Slowenien',
        'ESP'    : 'Spanien',
        'CZE'    : 'Tschechien',
        'TUR'    : 'T\xFCrkei',
        'UKR'    : 'Ukraine',
        'HUN'    : 'Ungarn',
        'WAL'    : 'Wales',
        'BLR'    : 'Weissrussland',
        'CYP'    : 'Zypern'
    };
const __LANDTLAS = reverseMapping(__TLALAND);


// ==================== Abschnitt fuer Daten des Spielplans ====================
    const __ZAT = firstZAT(getOptValue(__OPTSET.saison), getOptValue(__OPTSET.ligaSize));


// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
    const __ROWS = getRows(2);
// gameType: Name des Wettbewerbs eines Spiels
// defValue: Default-Wert
// return OS2-ID fuer den Spieltyp (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig
function getGameTypeID(gameType, defValue = __GAMETYPENRN.unbekannt) {
    return getValue(__GAMETYPENRN[gameType], defValue);
}


// Gibt den Namen eines Wettbewerbs zurueck
    let ligaStats = emptyStats();
// id: OS2-ID des Wettbewerbs eines Spiels (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig
     let euroStats = emptyStats();
// defValue: Default-Wert
// return Spieltyp fuer die uebergebene OS2-ID
function getGameType(id, defValue) {
     return getValue(__GAMETYPES[id], defValue);
}


// Gibt den alternativen (Kurznamen) fuer den Namen eines Wettbewerbs zurueck
    for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
// gameType: Name des Wettbewerbs eines Spiels
        const __CELLS = __ROWS[i].cells;   // Aktuelle Eintraege
// return Normalerweise den uebergebenen Parameter, in Einzelfaellen eine Kurzversion
function getGameTypeAlias(gameType) {
    return getValue(__GAMETYPEALIASES[gameType], getValue(gameType, __GAMETYPEALIASES.unbekannt));
}


// Gibt den Namen des Landes mit dem uebergebenen Kuerzel (TLA) zurueck.
        incZAT(__ZAT);
// tla: Kuerzel (TLA) des Landes
// defValue: Default-Wert
// return Name des Landes, 'unbekannt' fuer undefined
function getLandName(tla, defValue = __TLALAND[undefined]) {
    return getValue(__TLALAND[tla], defValue);
}


// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
        setGegnerFromCell(__ZAT, __CELLS[__COLUMNINDEX.Geg]);
// land: Name des Landes
        setSpielArtFromCell(__ZAT, __CELLS[__COLUMNINDEX.Art]);
// defValue: Default-Wert
        setErgebnisFromCell(__ZAT, __CELLS[__COLUMNINDEX.Erg]);
// return OS2-ID des Landes, 0 fuer ungueltig
function getLandNr(land, defValue = __LANDNRN.unbekannt) {
    return getValue(__LANDNRN[land], defValue);
}


// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
        if (getOptValue(__OPTSET.shortKom)) {
// land: Name der Liga
            const __CELLKOM = __CELLS[__COLUMNINDEX.Kom];
// defValue: Default-Wert
            const __CELLART = __CELLS[__COLUMNINDEX.Art];
// return OS2-ID der Liga, 0 fuer ungueltig
function getLigaNr(liga, defValue = __LIGANRN.unbekannt) {
    return getValue(__LIGANRN[liga], defValue);
}
 
// Kehrt das Mapping eines Objekts um und liefert ein neues Objekt zurueck.
// obj: Objekt mit key => value
// convFun: Konvertierfunktion fuer die Werte
// return Neues Objekt mit value => key (doppelte value-Werte fallen heraus!)
function reverseMapping(obj, convFun) {
    if (! obj) {
        return obj;
    }


    const __RET = { };
            __CELLKOM.innerHTML = __CELLKOM.innerHTML.replace("Vorbericht(e)", 'V').replace("Kommentar(e)", 'K').replace("&amp;amp;", '/').replace('&', '/');
 
            __CELLART.innerHTML = __CELLART.innerHTML.replace(": Heim", "(H)").replace(": Ausw\xE4rts", "(A)").replace("Friendly", "FSS");
    for (let key in obj) {
        const __VALUE = obj[key];
 
        __RET[__VALUE] = (convFun ? convFun(key) : key);
    }
 
    return __RET;
}
 
// ==================== Abschnitt fuer sonstige Parameter ====================
 
// Formatiert eine Zelle um (mit einfachen Parametern)
// cell: Zu formatierende Zelle
// bold: Inhalt fett darstellen (true = ja, false = nein)
// color: Falls angegeben, die Schriftfarbe
// bgColor: Falls angegeben, die Hintergrundfarbe
// return Die formatierte Zelle
function formatCell(cell, bold = true, color = undefined, bgColor = undefined) {
    if (cell) {
        if (bold) {
            cell.style.fontWeight = 'bold';
         }
         }
        if (color) {
            cell.style.color = color;
        }
        if (bgColor) {
            cell.style.backgroundColor = bgColor;
        }
    }


    return cell;
        __CELLS[__COLUMNINDEX.Zus].className = __CELLS[__COLUMNINDEX.Art].className;
}


// Ermittelt die auszugewaehlenden Werte eines Selects (Combo-Box) als Array zurueck
        if (__CELLS[__COLUMNINDEX.Zus].textContent === "") {
// element: 'select'-Element oder dessen Name auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
            const __CELLBER = __CELLS[__COLUMNINDEX.Ber];
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
            let stats = "";
// valFun: Funktion zur Ermittlung des Wertes eines 'option'-Eintrags (getSelectedOptionText, getSelectedValue, ...)
// defValue: Default-Wert, falls nichts selektiert ist
// return Array mit den Options-Werten
function getSelectionArray(element, valType = 'String', valFun = getSelectedValue, defValue = undefined) {
    const __SELECT = ((typeof element) === 'string' ? getValue(document.getElementsByName(element), [])[0] : element);


    return (__SELECT ? [].map.call(__SELECT.options, function(option) {
            addBilanzLinkToCell(__CELLBER, __ZAT.gameType, "Bilanz");
                                                        return this[valType](getValue(valFun(option), defValue));
                                                    }) : undefined);
}


// Ermittelt den ausgewaehlten Wert eines Selects (Combo-Box) und gibt diesen zurueck
            if (getOptValue(__OPTSET.shortKom)) {
// element: 'select'-Element oder dessen Name auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
                __CELLBER.innerHTML = __CELLBER.innerHTML.replace("Klick", "(*)").replace("Bilanz", 'V').replace("Vorschau", 'V');
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
            }
// valFun: Funktion zur Ermittlung des Wertes eines 'option'-Eintrags (getSelectedOptionText, getSelectedValue, ...)
// defValue: Default-Wert, falls nichts selektiert ist
// return Ausgewaehlter Wert
function getSelection(element, valType = 'String', valFun = getSelectedOptionText, defValue = undefined) {
    const __SELECT = ((typeof element) === 'string' ? getValue(document.getElementsByName(element), [])[0] : element);


    return this[valType](getValue(valFun(__SELECT), defValue));
            if (__ZAT.gameType === "Liga") {
}
                if (__ZAT.ZAT < 70) {
                    stats = addResultToStats(ligaStats, getOptValue(__OPTSET.longStats), __ZAT);
                }
            } else if ((__ZAT.gameType === "OSCQ") || (__ZAT.gameType === "OSEQ") || (__ZAT.gameType === "OSE")) {
                if (__ZAT.hinRueck !== 1) {
                    euroStats = emptyStats();
                }
                stats = addResultToStats(euroStats, getOptValue(__OPTSET.longStats), __ZAT);
            } else if (__ZAT.gameType === "OSC") {
                if ((__ZAT.hinRueck !== 1) && ((__ZAT.euroRunde >= 9) || ((__ZAT.euroRunde % 3) === 0))) {
                    euroStats = emptyStats();
                }
                stats = addResultToStats(euroStats, getOptValue(__OPTSET.longStats), __ZAT);
            }


// Ermittelt den ausgewaehlten Wert einer Combo-Box und gibt diesen zurueck
            if (getOptValue(__OPTSET.showStats)) {
// comboBox: Alle 'option'-Eintraege der Combo-Box
                if (stats !== "") {
// defValue: Default-Wert, falls nichts selektiert ist
                    stats = ' ' + stats;
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
                }
// return Ausgewaehlter Wert
            } else {
function getSelectionFromComboBox(comboBox, defValue = undefined, valType = 'String') {
                stats = "";
    let selection;
            }
 
            __CELLS[__COLUMNINDEX.Zus].innerHTML = getZusatz(__ZAT, true) + stats;
    for (let i = 0; i < comboBox.length; i++) {
        const __ENTRY = comboBox[i];
 
        if (__ENTRY.outerHTML.match(/selected/)) {
            selection = __ENTRY.textContent;
         }
         }
    }


    return this[valType](getValue(selection, defValue));
        if (getOptValue(__OPTSET.sepMonths) && (__ZAT.ZAT % __ZAT.anzZATpMonth === 0) && (i < __ROWS.length - __ROWOFFSETLOWER - 1)) {
}
            // Format der Trennlinie zwischen den Monaten...
            const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);


// Liefert den Text (textContent) einer selektierten Option
/*
// element: 'select'-Element auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
            for (let entry of __CELLS) {
// return Wert der Selektion (textContent)
                entry.style.borderBottom = __BORDERSTRING;
function getSelectedOptionText(element) {
            }
    const __SELECTEDOPTIONS = getValue(element, { }).selectedOptions;
*/
    const __OPTION = getValue(__SELECTEDOPTIONS, { })[0];
             for (let j = 0; j < __CELLS.length; j++) {
 
                __CELLS[j].style.borderBottom = __BORDERSTRING;
    return (__OPTION ? __OPTION.textContent : undefined);
            }
}
 
// Liefert den 'value' einer selektierten Option
// element: 'select'-Element auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// return Wert der Selektion ('value')
function getSelectedValue(element) {
    return getValue(element, { }).value;
}
 
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
 
// Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck
// cell: Tabellenzelle mit dem Namen des Gegners
// return Der Name des Gegners
function getGegnerFromCell(cell) {
    const __GEGNER = cell.textContent;
    const __POS = __GEGNER.indexOf(" (");
 
    if (~ __POS) {
        return __GEGNER.substr(0, __POS);
    } else {
        return __GEGNER;
    }
}
 
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle, etwa "2 : 1", und liefert zwei Werte zurueck
// cell: Tabellenzelle mit Eintrag "2 : 1"
// return { '2', '1' } im Beispiel
function getErgebnisFromCell(cell) {
    const __ERGEBNIS = cell.textContent.split(" : ", 2);
 
    return __ERGEBNIS;
}
 
// Ermittelt die Spielart aus einer Tabellenzelle, etwa "Liga : Heim", und liefert zwei Werte zurueck
// cell: Tabellenzelle mit Eintrag "Liga : Heim" (Spielplan) oder "Liga  Heim: " (Managerbuero)
// return { "Liga", "Heim" } im Beispiel
function getSpielArtFromCell(cell) {
    const __TEXT = cell.textContent.replace('\xA0', "").replace(':', "").replace("  ", ' ');
    const __SPIELART = __TEXT.split(' ', 2);
 
    return __SPIELART;
}
 
// Ermittelt den Namen des Spielgegners aus einer Tabellenzelle und setzt gegner im Spielplanzeiger
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// cell: Tabellenzelle mit dem Namen des Gegners
function setGegnerFromCell(currZAT, cell) {
    const __GEGNER = getGegnerFromCell(cell);
 
    currZAT.gegner = __GEGNER;
}
 
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle und setzt tore/gtore im Spielplanzeiger
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// cell: Tabellenzelle mit Eintrag "2 : 1"
// return Modifizierter Spielplanzeiger
function setErgebnisFromCell(currZAT, cell) {
    const __ERGEBNIS = getErgebnisFromCell(cell);
 
    if (__ERGEBNIS.length === 2) {
        currZAT.gFor = parseInt(__ERGEBNIS[0], 10);
        currZAT.gAga = parseInt(__ERGEBNIS[1], 10);
    } else {
        currZAT.gFor = -1;
        currZAT.gAga = -1;
    }
 
    return currZAT;
}
 
// Ermittelt die Spielart aus einer Tabellenzelle und setzt gameType/heim im Spielplanzeiger
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// cell: Tabellenzelle mit Eintrag "Liga : Heim" oder "Liga Heim"
function setSpielArtFromCell(currZAT, cell) {
    const __SPIELART = getSpielArtFromCell(cell);
 
    currZAT.gameType = __SPIELART[0];
    currZAT.heim    = (__SPIELART.length < 2) || (__SPIELART[1] === 'Heim');
}
 
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// gameType: Name des Wettbewerbs eines Spiels
// label: Anzuklickender Text des neuen Links
// return HTML-Link auf die Preview-Seite fuer diesen Spielbericht
function getBilanzLinkFromCell(cell, gameType, label) {
    const __GAMETYPEID = getGameTypeID(gameType);
    let ret = "";
 
    if (cell.textContent !== 'Vorschau') {  // Nur falls Link nicht bereits vorhanden
        if (__GAMETYPEID > 1) {              // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert"
             const __SEARCHFUN = ":os_bericht(";
            let paarung = cell.innerHTML.substr(cell.innerHTML.indexOf(__SEARCHFUN) + __SEARCHFUN.length);
 
            paarung = paarung.substr(0, paarung.indexOf(')'));
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
            ret = ' <a href="javascript:spielpreview(' + paarung + ',' + __GAMETYPEID + ')">' + label + "</a>";
        }
    }
 
    return ret;
}
 
// Addiert einen Link auf die Bilanz hinter den Spielberichts-Link
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// gameType: Name des Wettbewerbs eines Spiels
// label: Anzuklickender Text des neuen Links
function addBilanzLinkToCell(cell, gameType, label) {
    const __BILANZLINK = getBilanzLinkFromCell(cell, gameType, label);
 
    if (__BILANZLINK !== "") {
        cell.innerHTML += __BILANZLINK;
    }
}
 
// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
 
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
        'Tabelle'  : 1,
        'Zeile'    : 0,
        'Spalte'    : 1,
        'start'    : " von ",
        'middle'    : "</b><br>",
        'liga'      : ". Liga",
        'land'      : ' ',
        'end'      : "<a href="
    };
 
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
        'Tabelle'  : 1,
        'Zeile'    : 0,
        'Spalte'    : 0,
        'start'    : "<b>",
        'middle'    : " - ",
        'liga'      : ". Liga",
        'land'      : 'target="_blank">',
        'end'      : "</a></b>"
    };
 
const __TEAMIDSEARCHHAUPT = {  // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>"
        'Tabelle'  : 0,
        'Zeile'    : 6,
        'Spalte'    : 0,
        'start'    : '<a href="livegame/index.php?spiele=',
        'end'      : ',0">LIVEGAME</a>'
    };
 
const __TEAMIDSEARCHTEAM = {  // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>"
        'Tabelle'  : 0,
        'Zeile'    : 1,
        'Spalte'    : 1,
        'start'    : '<a hspace="20" href="javascript:tabellenplatz(',
        'end'      : ')">Tabellenpl\xE4tze</a>'
    };
 
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
// teamSearch: Muster fuer die Suche nach Team, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt, ausserdem die
//              Adresse der Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
// teamIdSearch: Muster fuer die Suche nach Team-ID, die Eintraege fuer 'start' und 'end' enthaelt, ausserdem die
//              Adresse der Tabellenzelle mit den Parametern zur Team-ID "startTEAMIDend"
// doc: Optionale Angabe des Dokuments, in dem die Tabelle gesucht wird  (Default: document)
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'TmNr' : TEAMID, 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'TmNr' : 930, 'LdNr' : 20, 'LgNr' : 1 }
function getTeamParamsFromTable(teamSearch, teamIdSearch, doc = document) {
    // Ermittlung von Team, Liga und Land...
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
    const __TEAMTABLE    = getTable(getValue(__TEAMSEARCH.Tabelle, 1), 'table', doc);
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
    const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
    const __TEAMCELLSTR  = (__TEAMTABLE === undefined) ? "" : __TEAMTABLE.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML;
    const __SEARCHSTART  = __TEAMSEARCH.start;
    const __SEARCHMIDDLE = __TEAMSEARCH.middle;
    const __SEARCHLIGA  = __TEAMSEARCH.liga;
    const __SEARCHLAND  = __TEAMSEARCH.land;
    const __SEARCHEND    = __TEAMSEARCH.end;
    const __INDEXSTART  = __TEAMCELLSTR.indexOf(__SEARCHSTART);
    const __INDEXEND    = __TEAMCELLSTR.indexOf(__SEARCHEND);
 
    let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
    const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
 
    let land = ((~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined);
    const __TEAMNAME = ((~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined);
    let liga = (((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined);
 
    if (land !== undefined) {
        if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
            land = land.substr(2);
        }
        if (liga !== undefined) {
            liga = liga.substring(0, liga.length - land.length);
        }
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
        if (~ __INDEXLAND) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
        }
    }
 
    // Ermittlung der Team-ID (indirekt ueber den Livegame- bzw. Tabellenplatz-Link)...
    const __TEAMIDSEARCH  = getValue(teamIdSearch, __TEAMIDSEARCHHAUPT);
    const __TEAMIDTABLE    = getTable(getValue(__TEAMIDSEARCH.Tabelle, 0), 'table', doc);
    const __TEAMIDCELLROW  = getValue(__TEAMIDSEARCH.Zeile, 6);
    const __TEAMIDCELLCOL  = getValue(__TEAMIDSEARCH.Spalte, 0);
    const __TEAMIDCELLSTR  = (__TEAMIDTABLE === undefined) ? "" : __TEAMIDTABLE.rows[__TEAMIDCELLROW].cells[__TEAMIDCELLCOL].innerHTML;
    const __SEARCHIDSTART  = __TEAMIDSEARCH.start;
    const __SEARCHIDEND    = __TEAMIDSEARCH.end;
    const __INDEXIDSTART  = __TEAMIDCELLSTR.indexOf(__SEARCHIDSTART);
    const __INDEXIDEND    = __TEAMIDCELLSTR.indexOf(__SEARCHIDEND);
    const __TEAMIDSTR      = __TEAMIDCELLSTR.substring(__INDEXIDSTART + __SEARCHIDSTART.length, __INDEXIDEND);
    const __TEAMID        = Number.parseInt(__TEAMIDSTR, 10);
 
    const __TEAM = new Team(__TEAMNAME, land, liga, __TEAMID);
 
    return __TEAM;
}
 
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// url: Adresse der Seite
// leafs: Liste von Filenamen mit der Default-Seitennummer (falls Query-Parameter nicht gefunden)
// item: Query-Parameter, der die Nummer der Unterseite angibt
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url, leafs, item = 'page') {
    const __URI = new URI(url);
    const __LEAF = __URI.getLeaf();
 
    for (let leaf in leafs) {
        if (__LEAF === leaf) {
            const __DEFAULT = leafs[leaf];
 
            return getValue(__URI.getQueryPar(item), __DEFAULT);
         }
         }
     }
     }
    return -1;
}
}


// Ermittelt aus dem Spielplan die Ligengroesse ueber die Sonderspieltage
try {
// rows: Tabellenzeilen mit dem Spielplan
    // URL-Legende:
// startIdx: Index der Zeile mit dem ersten ZAT
    // s=0: Teamuebersicht
// colArtIdx: Index der Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
    // s=1: Vertragsdaten
// saison: Enthaelt die Nummer der laufenden Saison
    // s=2: Einzelwerte
// return 10 bei 36 Spielen, 18 bei 34 Spielen, 20 bei 38 Spielen
    // s=3: Statistik Saison
function getLigaSizeFromSpielplan(rows, startIdx, colArtIdx, saison) {
    // s=4: Statistik Gesamt
     const __LIGAEXTRA = getLigaExtra(saison);
    // s=5: Teaminfo
     const __TEST10ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[0] - 1].cells[colArtIdx]);
     // s=6: Saisonplan
     const __TEST20ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[2] - 1].cells[colArtIdx]);
     // s=7: Vereinshistorie
    // s=8: Transferhistorie
     // s=9: Leihhistorie


     if (__TEST20ER[0] === 'Liga') {
     // Verzweige in unterschiedliche Verarbeitungen je nach Wert von s:
        return 20;
    switch (getPageIdFromURL(window.location.href, {
    } else if (__TEST10ER[0] === 'Liga') {
                                                      'showteam.php' : 0,  // Teamansicht Hauptfenster
         return 10;
                                                      'st.php'       : 0  // Teamansicht Popupfenster
    } else {
                                                  }, 's')) {
         return 18;
         case 6  : procSpielplan(); break;
         default : break;
     }
     }
} catch (ex) {
    showAlert('[' + ex.lineNumber + "] " + __DBMOD.Name, ex.message, ex);
} finally {
    __LOG[2]("SCRIPT END");
}
}
// ==================== Ende Abschnitt fuer sonstige Parameter des Spielplans ====================
// ==================== Ende Abschnitt fuer Spielplan und ZATs ====================
// ==================== Hauptprogramm ====================
// Verarbeitet Ansicht "Saisonplan"
function procSpielplan() {
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
    const __ROWOFFSETLOWER = 0;
    const __CLASSFREI = 'DMI';
    const __COLUMNINDEX = {
        'ZAT' : 0,
        'Art' : 1,
        'Geg' : 2,
        'Erg' : 3,
        'Ber' : 4,
        'Zus' : 5,
        'Kom' : 6
    };
    const __TEAMPARAMS = getTeamParamsFromTable(__TEAMSEARCHTEAM, __TEAMIDSEARCHTEAM);
    return buildOptions(__OPTCONFIG, __OPTSET, {
                            'Tab'        : getTable(2),
                            'Zei'        : __ROWOFFSETUPPER,
                            'Spa'        : __COLUMNINDEX.Art,
                            'teamParams' : __TEAMPARAMS,
                            'menuAnchor' : getTable(0, 'div'),
                            'hideForm'  : {
                                              'team'        : true
                                          },
                            'formWidth'  : 3,
                            'formBreak'  : 4
                        }).then(optSet => {
            const __ZAT = firstZAT(getOptValue(__OPTSET.saison), getOptValue(__OPTSET.ligaSize));
            const __ROWS = getRows(2);
            if (! __ROWS) {
                __LOG[0]("Kein Spielplan vorhanden!");
                return;
            }
            let ligaStats = emptyStats();
            let euroStats = emptyStats();
            for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
                const __CELLS = __ROWS[i].cells;    // Aktuelle Eintraege
                const __ARTCLASS = __CELLS[__COLUMNINDEX.Art].className;
                incZAT(__ZAT);
                setGegnerFromCell(__ZAT, __CELLS[__COLUMNINDEX.Geg]);
                setSpielArtFromCell(__ZAT, __CELLS[__COLUMNINDEX.Art]);
                setErgebnisFromCell(__ZAT, __CELLS[__COLUMNINDEX.Erg]);
                if (getOptValue(__OPTSET.shortKom)) {
                    const __CELLKOM = __CELLS[__COLUMNINDEX.Kom];
                    const __CELLART = __CELLS[__COLUMNINDEX.Art];
                    __CELLKOM.innerHTML = __CELLKOM.innerHTML.replace("Vorbericht(e)", 'V').replace("Kommentar(e)", 'K').replace("&amp;", '/').replace('&', '/');
                    __CELLART.innerHTML = __CELLART.innerHTML.replace(": Heim", "(H)").replace(": Ausw\xE4rts", "(A)").replace(__ZAT.gameType, getGameTypeAlias(__ZAT.gameType));
                }
                __CELLS[__COLUMNINDEX.Erg].className = __ARTCLASS;
                __CELLS[__COLUMNINDEX.Zus].className = __ARTCLASS;
                if (__ZAT.gameType === 'spielfrei') {
                    __CELLS[__COLUMNINDEX.ZAT].className = __CLASSFREI;
                }
                if (__CELLS[__COLUMNINDEX.Zus].textContent === "") {
                    const __CELLBER = __CELLS[__COLUMNINDEX.Ber];
                    let stats = "";
                    addBilanzLinkToCell(__CELLBER, __ZAT.gameType, "Bilanz");
                    if (getOptValue(__OPTSET.shortKom)) {
                        __CELLBER.innerHTML = __CELLBER.innerHTML.replace("Klick", "(*)").replace("Bilanz", 'V').replace("Vorschau", 'V');
                    }
                    if (__ZAT.gameType === 'Liga') {
                        if (__ZAT.ZAT < 70) {
                            stats = addResultToStats(ligaStats, getOptValue(__OPTSET.longStats), __ZAT);
                        }
                    } else if ((__ZAT.gameType === 'OSCQ') || (__ZAT.gameType === 'OSEQ') || (__ZAT.gameType === 'OSE')) {
                        if (__ZAT.hinRueck !== 1) {
                            euroStats = emptyStats();
                        }
                        stats = addResultToStats(euroStats, getOptValue(__OPTSET.longStats), __ZAT);
                    } else if (__ZAT.gameType === 'OSC') {
                        if ((__ZAT.hinRueck !== 1) && ((__ZAT.euroRunde >= 9) || ((__ZAT.euroRunde % 3) === 0))) {
                            euroStats = emptyStats();
                        }
                        stats = addResultToStats(euroStats, getOptValue(__OPTSET.longStats), __ZAT);
                    }
                    if (getOptValue(__OPTSET.showStats)) {
                        if (stats !== "") {
                            stats = ' ' + stats;
                        }
                    } else {
                        stats = "";
                    }
                    __CELLS[__COLUMNINDEX.Zus].innerHTML = getZusatz(__ZAT, true) + stats;
                }
                if (getOptValue(__OPTSET.sepMonths) && (__ZAT.ZAT % __ZAT.anzZATpMonth === 0) && (i < __ROWS.length - __ROWOFFSETLOWER - 1)) {
                    // Format der Trennlinie zwischen den Monaten...
                    const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
/*
                    for (let entry of __CELLS) {
                        entry.style.borderBottom = __BORDERSTRING;
                    }
*/
                    for (let j = 0; j < __CELLS.length; j++) {
                        __CELLS[j].style.borderBottom = __BORDERSTRING;
                    }
                }
            }
        });
}
(() => {
    (async () => {
        try {
            // URL-Legende:
            // s=0: Teamuebersicht
            // s=1: Vertragsdaten
            // s=2: Einzelwerte
            // s=3: Statistik Saison
            // s=4: Statistik Gesamt
            // s=5: Teaminfo
            // s=6: Saisonplan
            // s=7: Vereinshistorie
            // s=8: Transferhistorie
            // s=9: Leihhistorie
            // Verzweige in unterschiedliche Verarbeitungen je nach Wert von s:
            switch (getPageIdFromURL(window.location.href, {
                                                              'showteam.php' : 0,  // Teamansicht Hauptfenster
                                                              'st.php'      : 0  // Teamansicht Popupfenster
                                                          }, 's')) {
                case 6  : await procSpielplan().catch(defaultCatch); break;
                default : break;
            }
            return 'OK';
        } catch (ex) {
            return defaultCatch(ex);
        }
    })().then(rc => {
            __LOG[1]('SCRIPT END', __DBMOD.Name, '(' + rc + ')');
        })
})();


// *** EOF ***
// *** EOF ***
</pre>
</pre>

Bitte beachte, dass alle Beiträge zu Online-Soccer-Wiki von anderen Mitwirkenden bearbeitet, geändert oder gelöscht werden können. Reiche hier keine Texte ein, falls du nicht willst, dass diese ohne Einschränkung geändert werden können.

Du bestätigst hiermit auch, dass du diese Texte selbst geschrieben hast oder diese von einer gemeinfreien Quelle kopiert hast (weitere Einzelheiten unter Online-Soccer-Wiki:Urheberrechte). ÜBERTRAGE OHNE GENEHMIGUNG KEINE URHEBERRECHTLICH GESCHÜTZTEN INHALTE!

Abbrechen Bearbeitungshilfe (wird in einem neuen Fenster geöffnet)