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'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Dateiname'''
| '''Dateiname'''
| '''OS2.spielplan.user.js'''
| '''os2.spielplan.user.js'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Version'''
| '''Version'''
| '''0.73 (WebExtensions)'''  
| '''0.60'''  
|- 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'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Letzte Änderung'''
| '''Letzte Änderung'''
Zeile 26: Zeile 25:
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
|}
|}
== [https://github.com/Eselce/OS2.scripts/blob/master/OS2.spielplan.user.js Quellcode] [https://eselce.github.io/OS2.scripts/OS2.spielplan.user.js INSTALLATION] ==
<pre>
<pre>
// ==UserScript==
// ==UserScript==
// @name         OS2.spielplan
// @name       OS2.spielplan
// @namespace   http://os.ongapo.com/
// @namespace   http://os.ongapo.com/
// @version     0.73
// @version     0.60
// @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=*
// @grant       GM_getValue
// @include    http*://www.online-soccer.eu/showteam.php?s=*
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_deleteValue
// @grant       GM_setValue
// @grant       GM_registerMenuCommand
// @grant       GM_deleteValue
// @grant        GM_info
// @grant       GM_registerMenuCommand
// ==/UserScript==
// ==/UserScript==


// ECMAScript 6:
// ECMAScript 6: Erlaubt 'const', 'let', ...
/* jshint esnext: true */
/* jshint esnext: true */
/* jshint moz: true */
/* jshint moz: true */


// ==================== Konfigurations-Abschnitt fuer Optionen ====================
// ==================== Konfigurations-Abschnitt fuer Optionen ====================
const __LOGLEVEL = 3;


// Options-Typen
// Options-Typen
Zeile 72: Zeile 67:
     'NXT' : "set next option value",
     'NXT' : "set next option value",
     'RST' : "reset options"
     'RST' : "reset options"
};
const __OPTMEM = {
    'normal' : {
                  'Name'      : "Browser",
                  'Value'    : localStorage,
                  'Display'  : "localStorage",
                  'Prefix'    : 'run'
              },
    'begrenzt' : {
                  'Name'      : "Session",
                  'Value'    : sessionStorage,
                  'Display'  : "sessionStorage",
                  'Prefix'    : 'run'
              },
    'inaktiv' : {
                  'Name'      : "inaktiv",
                  'Value'    : undefined,
                  'Display'  : "",
                  'Prefix'    : ""
              }
};
};


Zeile 144: Zeile 118:
                   '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 129:
                   'Name'      : "sepColor",
                   'Name'      : "sepColor",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                  '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 141:
                   'Name'      : "sepWidth",
                   'Name'      : "sepWidth",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                  'FreeValue' : true,
                   'Choice'    : [ "thin", "medium", "thick" ],
                   'Choice'    : [ 'thin', 'medium', 'thick' ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Dicke: $",
                   'Label'    : "Dicke: $",
Zeile 176: Zeile 148:
                   '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,
                   'Choice'    : [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ],
                  '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 ],
                  'Default'  : 18,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Saison: $",
                   'Label'    : "Saison: $",
                   'Hotkey'    : 's',
                   'Hotkey'    : 'a',
                   'FormLabel' : "Saison:|$"
                   'FormLabel' : "Saison:|$"
               },
               },
Zeile 192: Zeile 161:
                   'Name'      : "ligaSize",
                   'Name'      : "ligaSize",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                  'AutoReset' : true,
                   'Choice'    : [ 10, 18, 20 ],
                   'Choice'    : [ 10, 18, 20 ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
Zeile 199: Zeile 167:
                   'Hotkey'    : 'i',
                   'Hotkey'    : 'i',
                   'FormLabel' : "Liga:|$er"
                   'FormLabel' : "Liga:|$er"
              },
    'team' : {            // Datenspeicher fuer Daten des Erst- bzw. Zweitteams
                  'Name'      : "team",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'Permanent' : true,
                  'Default'  : undefined,  // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'TmNr' : 0, 'LdNr' : 0, 'LgNr' : 0 }
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 6,
                  'Replace'  : null,
                  'Space'    : 1,
                  'Label'    : "Verein:"
               },
               },
     '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 222: Zeile 175:
                   'Hotkey'    : 'O',
                   'Hotkey'    : 'O',
                   'FormLabel' : ""
                   'FormLabel' : ""
              },
    'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                  'Name'      : "storage",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'String',
                  'Choice'    : Object.keys(__OPTMEM),
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Speicher: $",
                  'Hotkey'    : 'c',
                  'FormLabel' : "Speicher:|$"
              },
    'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                  'Name'      : "oldStorage",
                  'Type'      : __OPTTYPES.SD,
                  'PreInit'  : true,
                  'AutoReset' : true,
                  'Hidden'    : true
               },
               },
     '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 182:
                   '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 262: Zeile 193:
// ==================== Invarianter Abschnitt fuer Optionen ====================
// ==================== Invarianter Abschnitt fuer Optionen ====================


// Ein Satz von Logfunktionen, die je nach Loglevel zur Verfuegung stehen. Aufruf: __LOG[level](text)
// ==================== Abschnitt fuer diverse Utilities ====================
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;
// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert
                                }
// value: Ein Wert. Ist dieser nicht undefined, wird er zurueckgeliefert
              };
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
 
// retValue: Falls definiert, Rueckgabe-Wert fuer den Fall, dass value nicht undefined ist
__LOG.init(window, __LOGLEVEL);
// return Der Wert. Sind weder value noch defValue definiert, dann undefined
 
function getValue(value, defValue = undefined, retValue = undefined) {
// Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist)
    return (value === undefined) ? defValue : (retValue === undefined) ? value : retValue;
if (Function.prototype.name === undefined) {
    Object.defineProperty(Function.prototype, 'name', {
            get : function() {
                      return /function ([^(\s]*)/.exec(this.toString())[1];
                  }
        });
}
}


// Ergaenzung fuer Strings: Links oder rechts auffuellen nach Vorlage
// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert
// padStr: Vorlage, z.B. "00" fuer zweistellige Zahlen
// value: Ein Wert. Ist dieser definiet und in den Grenzen, wird er zurueckgeliefert
// padLeft: true = rechtsbuendig, false = linksbuendig
// minValue: Untere Grenze fuer den Wert, falls angegeben
// clip: Abschneiden, falls zu lang
// minValue: Obere Grenze fuer den Wert, falls angegeben
// return Rechts- oder linksbuendiger String, der so lang ist wie die Vorlage
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist oder der Wert ausserhalb liegt
String.prototype.pad = function(padStr, padLeft = true, clip = false) {
// return Der Wert. Sind weder value (in den Grenzen) noch defValue definiert, dann undefined
    const __LEN = ((clip || (padStr.length > this.length)) ? padStr.length : this.length);
function getValueIn(value, minValue = undefined, maxValue = undefined, defValue = undefined) {
 
     const __VALUE = getValue(value, defValue);
    return (padLeft ? String(padStr + this).slice(- __LEN) : String(this + padStr).slice(0, __LEN));
};
 
// Ersetzt in einem String {0}, {1}, ... durch die entsprechenden Parameter
// 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
    if ((minValue !== undefined) && (__VALUE < minValue)) {
// label: Eine Ueberschrift
        return defValue;
// message: Der Meldungs-Text
    }
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
// return Liefert die Parameter zurueck
        return defValue;
function showAlert(label, message, data = undefined) {
     }
     __LOG[0](label + ": " + message);


     if (data !== undefined) {
     return __VALUE;
        __LOG[2](data);
}
    }


     alert(label + "\n\n" + message);
// Ermittelt den naechsten Wert aus einer Array-Liste
// arr: Array-Liste mit den moeglichen Werte
// value: Vorher gesetzter Wert
// return Naechster Wert in der Array-Liste
function getNextValue(arr, value) {
     const __POS = arr.indexOf(value) + 1;


     return arguments;
     return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
}
}


// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// mit der Meldung zu einer Exception oder einer Fehlermeldung
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// label: Eine Ueberschrift
// value: Beliebiger (strukturierter) Wert
// ex: Exception oder sonstiges Fehlerobjekt
// return String-Darstellung des Wertes
// return Liefert die showAlert()-Parameter zurueck
function serialize(name, value) {
function showException(label, ex) {
     const __STREAM = (value !== undefined) ? JSON.stringify(value) : value;
     if (ex && ex.message) {  // Exception
        showAlert(label, ex.message, ex);
    } else {  // sonstiger Fehler
        showAlert(label, ex);
    }
}


// Standard-Callback-Funktion fuer onRejected, also abgefangener Fehler
    console.log(name + " >> " + __STREAM);
// 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
    GM_setValue(name, __STREAM);
            return showException(__LABEL, error.message, error);
 
        } else {
     return __STREAM;
            return showException(__LABEL, error);
        }
     } catch (ex) {
        return showException(`[${ex.lineNumber}] ${__DBMOD.Name}`, ex.message, ex);
    }
}
}


// ==================== Abschnitt fuer Klasse Class ====================
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
// 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);


function Class(className, baseClass, initFun) {
    console.log(name + " << " + __STREAM);
    'use strict';


     try {
     if ((__STREAM !== undefined) && (__STREAM.length !== 0)) {
        const __BASE = ((baseClass !== undefined) ? baseClass : Object);
         try {
        const __BASEPROTO = (__BASE ? __BASE.prototype : undefined);
             return JSON.parse(__STREAM);
        const __BASECLASS = (__BASEPROTO ? __BASEPROTO.__class : undefined);
        } catch (ex) {
 
            console.error(name + ": " + ex.message);
        this.className = (className || '?');
        this.baseClass = __BASECLASS;
        Object.setConst(this, 'baseProto', __BASEPROTO, false);
 
         if (! initFun) {
             const __BASEINIT = (__BASECLASS || { }).init;
 
            if (__BASEINIT) {
                initFun = function() {
                              // Basisklassen-Init aufrufen...
                              return __BASEINIT.call(this, arguments);
                          };
            } else {
                initFun = function() {
                              // Basisklassen-Init fehlt (und Basisklasse ist nicht Object)...
                              return false;
                          };
            }
         }
         }
    }


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


        this.init = initFun;
// Setzt eine Option dauerhaft und laedt die Seite neu
     } catch (ex) {
// name: Name der Option als Speicherort
         showAlert('[' + ex.lineNumber + "] Error in Class " + className, ex.message, ex);
// value: Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gespeicherter Wert fuer setOptValue()
function setStored(name, value, reload = true, serial = false) {
     if (serial) {
         serialize(name, value);
    } else {
        GM_setValue(name, value);
     }
     }
}


Class.define = function(subClass, baseClass, members = undefined, initFun = undefined, createProto = true) {
    if (reload) {
         return (subClass.prototype = subClass.subclass(baseClass, members, initFun, createProto));
         window.location.reload();
     };
     }


Object.setConst = function(obj, item, value, config) {
    return value;
        return Object.defineProperty(obj, item, {
}
                        enumerable  : false,
                        configurable : (config || true),
                        writable    : false,
                        value       : value
                    });
    };


Object.setConst(Object.prototype, 'subclass', function(baseClass, members, initFun, createProto) {
// Setzt den naechsten Wert aus einer Array-Liste als Option
        'use strict';
// arr: Array-Liste mit den moeglichen Optionen
// name: Name der Option als Speicherort
// value: Vorher gesetzter Wert
// reload: Seite mit neuem Wert neu laden
// return Gespeicherter Wert fuer setOptValue()
function setNextStored(arr, name, value, reload = true, serial = false) {
    return setStored(name, getNextValue(arr, value), reload, serial);
}


        try {
// Fuehrt die in einem Storage gespeicherte Operation aus
            const __MEMBERS = (members || { });
// optSet: Set mit den Optionen
            const __CREATEPROTO = ((createProto === undefined) ? true : createProto);
// session: true = bis Browserende gespeichert (sessionStorage), false = unbegrenzt gespeichert (localStorage)
function runStored(optSet, session = true) {
    const __STORAGE = (session ? sessionStorage : localStorage);
    const __CMD = ((__STORAGE !== undefined) ? __STORAGE.getItem('runcmd') : undefined);


            console.assert((typeof this) === 'function', "Not a function:", this);
    if (__CMD !== undefined) {
            console.assert((typeof __MEMBERS) === 'object', "Not an object:", __MEMBERS);
        const __KEY = __STORAGE.getItem('runkey');
        let value = __STORAGE.getItem('runval');


            const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
        try {
             const __PROTO = (__CREATEPROTO ? Object.create(__CLASS.baseProto) : this.prototype);
             value = JSON.parse(value);
 
            for (let item in __MEMBERS) {
                if ((item !== '__name') && (item !== '__init')) {
                    Object.setConst(__PROTO, item, __MEMBERS[item]);
                }
            }
 
            Object.setConst(__PROTO, '__class', __CLASS, ! __CREATEPROTO);
 
            return __PROTO;
         } catch (ex) {
         } catch (ex) {
             showAlert('[' + ex.lineNumber + "] Error in subclassing", ex.message, ex);
             console.error("runStored(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
            // ... meist kann man den String selber aber speichern, daher kein "return"...
         }
         }
    }, false);


Class.define(Object, null, {
        const __VAL = value;
                    '__init'      : function() {
                                        // Oberstes Basisklassen-Init...
                                        return true;
                                    },
                    'getClass'    : function() {
                                        return this.__class;
                                    },
                    'getClassName' : function() {
                                        const __CLASS = this.getClass();


                                        return (__CLASS ? __CLASS.getName() : undefined);
        switch (__OPTACTION[__CMD]) {
                                    },
        case __OPTACTION.SET : console.log("SET '" + __KEY + "' " + __VAL);
                    'setConst'     : function(item, value, config = undefined) {
                              setStored(__KEY, __VAL, false, false);
                                        return Object.setConst(this, item, value, config);
                              break;
                                    }
        case __OPTACTION.NXT : console.log("SETNEXT '" + __KEY + "' " + __VAL);
                }, undefined, false);
                              //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
 
                              setStored(__KEY, __VAL, false, false);
Class.define(Function, Object);
                              break;
 
        case __OPTACTION.RST : console.log("RESET");
Class.define(Class, Object, {
                              resetOptions(optSet, false);
                    'getName'     : function() {
                              break;
                                        return this.className;
         default :              break;
                                    }
                });
 
// ==================== Ende Abschnitt fuer Klasse Class ====================
 
// ==================== Abschnitt fuer Klasse Delims ====================
 
// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von Pfaden
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
// back: (Optional) Name des relativen Vaterverzeichnisses
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function Delims(delim, back, root, home) {
    'use strict';
 
    if ((typeof delim) === 'object') {
         // Erster Parameter ist Objekt mit den Properties...
        if (back === undefined) {
            back = delim.back;
         }
         }
        if (root === undefined) {
            root = delim.root;
        }
        if (home === undefined) {
            home = delim.home;
        }
        delim = delim.delim;
     }
     }


     this.setDelim(delim);
     __STORAGE.removeItem('runcmd');
     this.setBack(back);
     __STORAGE.removeItem('runkey');
     this.setRoot(root);
     __STORAGE.removeItem('runval');
    this.setHome(home);
}
}


Class.define(Delims, Object, {
// Gibt eine Option sicher zurueck
              'setDelim'      : function(delim = undefined) {
// opt: Config und Value der Option, ggfs. undefined
                                    this.delim = delim;
// defOpt: Rueckgabewert, falls undefined
                                },
// return Daten zur Option (oder defOpt)
              'setBack'        : function(back = undefined) {
function getOpt(opt, defOpt = { }) {
                                    this.back = back;
    return getValue(opt, defOpt);
                                },
}
              'setRoot'        : function(root = undefined) {
                                    this.root = root;
                                },
              'setHome'        : function(home = undefined) {
                                    this.home = home;
                                }
          });


// ==================== Ende Abschnitt fuer Klasse Delims ====================
// Gibt eine Option sicher zurueck (Version mit Key)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// 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 {
        return defOpt;
    }
}


// ==================== Abschnitt fuer Klasse UriDelims ====================
// Gibt die Konfigurationsdaten einer Option zurueck
// opt: Config und Value der Option
// defConfig: Rueckgabewert, falls Config nicht zu finden
// return Konfigurationsdaten der Option
function getOptConfig(opt, defConfig = { }) {
    return getValue(getOpt(opt).Config, defConfig);
}


// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von URIs
// Setzt den Namen einer Option
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
// opt: Config und Value der Option
// back: (Optional) Name des relativen Vaterverzeichnisses
// name: Zu setzender Name der Option
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// reload: Seite mit neuem Wert neu laden
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
// return Gesetzter Name der Option
// scheme: (Optional) Trennzeichen fuer den Schema-/Protokollnamen vorne
function setOptName(opt, name) {
// host: (Optional) Prefix fuer Hostnamen hinter dem Scheme-Trenner
     return (getOptConfig(opt).Name = name);
// port: (Optional) Trennzeichen vor der Portangabe, falls vorhanden
}
// query: (Optional) Trennzeichen fuer die Query-Parameter hinter dem Pfad
// parSep: (Optional) Trennzeichen zwischen je zwei Parametern
// parAss: (Optional) Trennzwischen zwischen Key und Value
// node: (Optional) Trennzeichen fuer den Knotennamen hinten (Fragment, Kapitel)
function UriDelims(delim, back, root, home, scheme, host, port, query, qrySep, qryAss, node) {
     'use strict';
 
    if ((typeof delim) === 'object') {
        // Erster Parameter ist Objekt mit den Properties...
        if (scheme === undefined) {
            scheme = delim.scheme;
        }
        if (host === undefined) {
            host = delim.host;
        }
        if (port === undefined) {
            port = delim.port;
        }
        if (query === undefined) {
            query = delim.query;
        }
        if (qrySep === undefined) {
            qrySep = delim.qrySep;
        }
        if (qryAss === undefined) {
            qryAss = delim.qryAss;
        }
        if (node === undefined) {
            node = delim.node;
        }
    }


     Delims.call(this, delim, back, root, home);
// Gibt den Namen einer Option zurueck
// opt: Config und Value der Option
// return Name der Option
function getOptName(opt) {
     return getOptConfig(opt).Name;
}


    this.setScheme(scheme);
// Setzt den Wert einer Option
    this.setHost(host);
// opt: Config und Value der Option
    this.setPort(port);
// name: Zu setzender Wert der Option
    this.setQuery(query);
// return Gesetzter Wert
     this.setQrySep(qrySep);
function setOptValue(opt, value) {
    this.setQryAss(qryAss);
     return (opt !== undefined) ? (opt.Value = value) : undefined;
    this.setNode(node);
}
}


Class.define(UriDelims, Delims, {
// Gibt den Wert einer Option zurueck
              'setScheme'      : function(scheme = undefined) {
// opt: Config und Value der Option
                                    this.scheme = scheme;
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
                                },
// return Gesetzter Wert
              'setHost'        : function(host = undefined) {
function getOptValue(opt, defValue = undefined) {
                                    this.host = host;
     return getValue((opt !== undefined) ? opt.Value : undefined, defValue);
                                },
              'setPort'        : function(port = undefined) {
                                    this.port = port;
                                },
              'setQuery'      : function(query = undefined) {
                                    this.query = query;
                                },
              'setQrySep'      : function(qrySep = undefined) {
                                    this.qrySep = qrySep;
                                },
              'setQryAss'      : function(qryAss = undefined) {
                                    this.qryAss = qryAss;
                                },
              'setNode'        : function(node = undefined) {
                                    this.node = node;
                                }
          });
 
// ==================== Ende Abschnitt fuer Klasse UriDelims ====================
 
// ==================== Abschnitt fuer Klasse Path ====================
 
// Basisklasse fuer die Verwaltung eines Pfades
// homePath: Absoluter Startpfad als String
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
// 'delim': Trennzeichen zwischen zwei Ebenen
// 'back': Name des relativen Vaterverzeichnisses
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function Path(homePath, delims) {
     'use strict';
 
    this.dirs = [];
    this.setDelims(delims);
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });
 
    this.home();
}
}


Class.define(Path, Object, {
// ==================== Ende Abschnitt fuer diverse Utilities ====================
                  'root'          : function() {
                                        this.dirs.splice(0, this.dirs.length);
                                    },
                  'home'          : function() {
                                        this.dirs = this.homeDirs.slice();
                                    },
                  'up'            : function() {
                                        this.dirs.pop();
                                    },
                  'down'          : function(subDir) {
                                        this.dirs.push(subDir);
                                    },
                  'setDelims'      : function(delims = undefined) {
                                        this.setConst('delims', new Delims(delims));
                                    },
                  'setDelim'      : function(delim = undefined) {
                                        this.delims.setDelim(delim || '/');
                                    },
                  'setBackDelim'  : function(backDelim = undefined) {
                                        this.delims.setBack(backDelim || "..");
                                    },
                  'setRootDelim'  : function(rootDelim = undefined) {
                                        this.delims.setRoot(rootDelim || "");
                                    },
                  'setHomeDelim'  : function(homeDelim = undefined) {
                                        this.delims.setHome(homeDelim || '~');
                                    },
                  'setSchemeDelim' : function(schemeDelim = undefined) {
                                        this.delims.setScheme(schemeDelim || ':');
                                    },
                  'setHostDelim'  : function(hostDelim = undefined) {
                                        this.delims.setHost(hostDelim || '//');
                                    },
                  'setPortDelim'  : function(portDelim = undefined) {
                                        this.delims.setHost(portDelim || ':');
                                    },
                  'setQueryDelim'  : function(queryDelim = undefined) {
                                        this.delims.setQuery(queryDelim || '?');
                                    },
                  'setParSepDelim' : function(parSepDelim = undefined) {
                                        this.delims.setParSep(parSepDelim || '&');
                                    },
                  'setParAssDelim' : function(parAssDelim = undefined) {
                                        this.delims.setParAss(parAssDelim || '=');
                                    },
                  'setNodeDelim'  : function(nodeDelim = undefined) {
                                        this.delims.setNode(nodeDelim || '#');
                                    },
                  'getLeaf'        : function(dirs = undefined) {
                                        const __DIRS = (dirs || this.dirs);


                                        return ((__DIRS && __DIRS.length) ? __DIRS.slice(-1)[0] : "");
// ==================== Abschnitt fuer das Benutzermenu ====================
                                    },
                  'getPath'        : function(dirs = undefined, delims = undefined) {
                                        const __DELIMS = new Delims(delims);
                                        const __DELIM = (__DELIMS.delim || this.delims.delim);
                                        const __ROOTDELIM = ((__DELIMS.root !== undefined) ? __DELIMS.root : this.delims.root);
                                        const __DIRS = (dirs || this.dirs);


                                        return __ROOTDELIM + __DELIM + __DIRS.join(__DELIM);
// Zeigt den Eintrag im Menu einer Option
                                    },
// opt: Derzeitiger Wert der Option
                  'getDirs'        : function(path = undefined, delims = undefined) {
// menuOn: Text zum Setzen im Menu
                                        const __DELIMS = new Delims(delims);
// funOn: Funktion zum Setzen
                                        const __DELIM = (__DELIMS.delim || this.delims.delim);
// keyOn: Hotkey zum Setzen im Menu
                                        const __ROOTDELIM = ((__DELIMS.root !== undefined) ? __DELIMS.root : this.delims.root);
// menuOff: Text zum Ausschalten im Menu
                                        const __HOMEDELIM = ((__DELIMS.home !== undefined) ? __DELIMS.home : this.delims.home);
// funOff: Funktion zum Ausschalten
                                        const __DIRS = (path ? path.split(__DELIM) : []);
// keyOff: Hotkey zum Ausschalten im Menu
                                        const __FIRST = __DIRS[0];
function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
    const __ON  = (opt ? '*' : "");
    const __OFF = (opt ? "" : '*');


                                        if (__FIRST && (__FIRST !== __ROOTDELIM) && (__FIRST !== __HOMEDELIM)) {
    console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
                                            showAlert("Kein absoluter Pfad", this.getPath(__DIRS), this);
                                        }


                                        return __DIRS.slice(1);
    if (opt) {
                                    }
        GM_registerMenuCommand(menuOff, funOff, keyOff);
                });
    } else {
        GM_registerMenuCommand(menuOn, funOn, keyOn);
    }
}


// ==================== Ende Abschnitt fuer Klasse Path ====================
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
 
// opt: Derzeitiger Wert der Option
// ==================== Abschnitt fuer Klasse URI ====================
// arr: Array-Liste mit den moeglichen Optionen
// 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
function registerNextMenuOption(opt, arr, menu, fun, key) {
    const __MENU = menu.replace('$', opt);
    let options = "OPTION " + __MENU;


// Basisklasse fuer die Verwaltung einer URI/URL
    for (let value of arr) {
// homePath: Absoluter Startpfad als String
        if (value === opt) {
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
            options += " / *" + value + '*';
// 'delim': Trennzeichen zwischen zwei Ebenen
        } else {
// 'back': Name des relativen Vaterverzeichnisses
            options += " / " + value;
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
        }
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
     }
function URI(homePath, delims) {
     console.log(options);
    'use strict';
 
    Path.call(this);
 
    const __HOSTPORT = this.getHostPort(homePath);
 
     this.scheme = this.getSchemePrefix(homePath);
     this.host = __HOSTPORT.host;
    this.port = this.parseValue(__HOSTPORT.port);
    this.query = this.parseQuery(this.getQueryString(homePath));
    this.node = this.getNodeSuffix(homePath);
 
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });


     this.home();
     GM_registerMenuCommand(__MENU, fun, key);
}
}


Class.define(URI, Path, {
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
              'setDelims'        : function() {
// opt: Derzeitiger Wert der Option
                                        this.setConst('delims', new UriDelims('/', "..", "", '~', ':', "//", ':', '?', '&', '=', '#'));
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
                                    },
// fun: Funktion zum Setzen des naechsten Wertes
              'setSchemeDelim'    : function(schemeDelim = undefined) {
// key: Hotkey zum Setzen des naechsten Wertes im Menu
                                        this.delims.setScheme(schemeDelim || ':');
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar)
                                    },
// serial: Serialization fuer komplexe Daten
              'setQueryDelim'    : function(queryDelim = undefined) {
function registerDataOption(opt, menu, fun, key, hidden = false, serial = true) {
                                        this.delims.setQuery(queryDelim || '?');
    const __VALUE = ((serial && (opt !== undefined)) ? JSON.stringify(opt) : opt);
                                    },
    const __MENU = getValue(menu, "").replace('$', __VALUE);
              'setParSepDelim'    : function(parSepDelim = undefined) {
    const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                                        this.delims.setParSep(parSepDelim || '&');
                      getValue(__VALUE, "", " = " + __VALUE);
                                    },
              'setParAssDelim'    : function(parAssDelim = undefined) {
                                        this.delims.setParAss(parAssDelim || '=');
                                    },
              'setNodeDelim'      : function(nodeDelim = undefined) {
                                        this.delims.setNode(nodeDelim || '#');
                                    },
              'getServerPath'    : function(path = undefined) {
                                        return this.stripHostPort(this.stripQueryString(this.stripNodeSuffix(this.stripSchemePrefix(path))));
                                    },
              'getHostPort'      : function(path = undefined) {
                                        const __HOSTDELIM = this.delims.host;
                                        const __PORTDELIM = this.delims.port;
                                        const __ROOTDELIM = this.delims.root + this.delims.delim;
                                        const __NOSCHEME = this.stripSchemePrefix(path);
                                        const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1);
                                        const __PATH = ((~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME);
                                        const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
                                        const __HOSTPORT = ((~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined);
                                        const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1);
                                        const __HOST = ((~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT);
                                        const __PORT = ((~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined);


                                        return {
     console.log(__OPTIONS);
                                                    'host' : __HOST,
                                                    'port' : __PORT
                                                };
                                    },
              'stripHostPort'     : function(path = undefined) {
                                        const __HOSTDELIM = this.delims.host;
                                        const __ROOTDELIM = this.delims.root + this.delims.delim;
                                        const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1);
                                        const __PATH = ((~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path);
                                        const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);


                                        return ((~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH);
    if (! hidden) {
                                    },
        GM_registerMenuCommand(__MENU, fun, key);
              'getSchemePrefix'  : function(path = undefined) {
    }
                                        const __SCHEMEDELIM = this.delims.scheme;
}
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


                                        return ((~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined);
// Zeigt den Eintrag im Menu einer Option
                                    },
// opt: Config und Value der Option
              'stripSchemePrefix' : function(path = undefined) {
function registerOption(opt) {
                                        const __SCHEMEDELIM = this.delims.scheme;
    const __CONFIG = getOptConfig(opt);
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


                                        return ((~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path);
    if (! __CONFIG.HiddenMenu) {
                                    },
        switch (__CONFIG.Type) {
              'getNodeSuffix'    : function(path = undefined) {
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
                                        const __NODEDELIM = this.delims.node;
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                            break;
 
        case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                        return ((~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined);
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
                                    },
                            break;
              'stripNodeSuffix'  : function(path = undefined) {
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                        const __NODEDELIM = this.delims.node;
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                            break;
 
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                        return ((~ __INDEXNODE) ? path.substring(0, __INDEXNODE) : path);
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
                                    },
                            break;
              'getQueryString'    : function(path = undefined) {
        case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                        const __QUERYDELIM = this.delims.query;
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
                                        const __PATH = this.stripNodeSuffix(path);
                            break;
                                        const __INDEXQUERY = (__PATH ? __PATH.indexOf(__QUERYDELIM) : -1);
        default :           break;
 
        }
                                        return ((~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined);
    } else {
                                    },
        // Nur Anzeige im Log...
              'stripQueryString'  : function(path = undefined) {
        registerDataOption(getOptValue(opt), getOptName(opt), opt.Action, __CONFIG.Hotkey, __CONFIG.HiddenMenu, __CONFIG.Serial);
                                        const __QUERYDELIM = this.delims.query;
    }
                                        const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);
}
 
                                        return ((~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path);
                                    },
              'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
                                        const __PARAMS = [];


                                        for (let param in params) {
// ==================== Ende Abschnitt fuer das Benutzermenu ====================
                                            __PARAMS.push(param + assign + formatFun(params[param]));
                                        }


                                        return __PARAMS.join(delim);
// Initialisiert die gesetzten Option
                                    },
// config: Konfiguration der Option
              'parseParams'      : function(params, parseFun, delim = ' ', assign = '=') {
// return Initialwert der gesetzten Option
                                        const __RET = { };
function initOptValue(config) {
    let value = config.Default; // Standard


                                        if (params) {
    switch (config.Type) {
                                            const __PARAMS = params.split(delim);
    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;
    }


                                            for (let index = 0; index < __PARAMS.length; index++) {
    if (config.Serial || config.Hidden) {
                                                const __PARAM = __PARAMS[index];
        config.HiddenMenu = true;
    }


                                                if (__PARAM) {
    return value;
                                                    const __INDEX = __PARAM.indexOf(assign);
}
                                                    const __KEY = ((~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM);
                                                    const __VAL = ((~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true);


                                                    __RET[__KEY] = __VAL;
// Initialisiert die Menue-Funktion einer Option
                                                }
// optAction: Typ der Funktion
                                            }
// item: Key der Option
                                        }
// optSet: Platz fuer die gesetzten Optionen (und Config)
// return Funktion fuer die Option
function initOptAction(optAction, item = undefined, optSet = undefined) {
    var fun;


                                        return __RET;
    if (optAction !== undefined) {
                                    },
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
              'rawValue'          : function(value) {
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);
                                        return value;
                                    },
              'parseValue'        : function(value) {
                                        const __VALUE = Number(value);


                                        if (__VALUE == value) { // schwacher Vergleich true, also Number
        switch (optAction) {
                                            return __VALUE;
        case __OPTACTION.SET : fun = function() {
                                        } else {
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                            const __LOWER = (value ? value.toLowerCase() : undefined);
                                  };
                              break;
        case __OPTACTION.NXT : fun = function() {
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :             break;
        }
    }


                                            if ((__LOWER === 'true') || (__LOWER === 'false')) {
    return fun;
                                                return (value === 'true');
}
                                            }
                                        }


                                        return value;
// Initialisiert die gesetzten Optionen
                                    },
// optConfig: Konfiguration der Optionen
              'getQuery'          : function(delims = { }) {
// optSet: Platz fuer die gesetzten Optionen
                                        const __QRYSEP = ((delims.qrySep !== undefined) ? delims.qrySep : this.delims.qrySep);
// return Gefuelltes Objekt mit den gesetzten Optionen
                                        const __QRYASS = ((delims.qryAss !== undefined) ? delims.qryAss : this.delims.qryAss);
function initOptions(optConfig, optSet = undefined) {
    var value;


                                        return this.formatParams(this.query, this.rawValue, __QRYSEP, __QRYASS);
    if (optSet === undefined) {
                                    },
        optSet = { };
              'parseQuery'        : function(path = undefined, delims = { }) {
    }
                                        const __QRYSEP = ((delims.qrySep !== undefined) ? delims.qrySep : this.delims.qrySep);
                                        const __QRYASS = ((delims.qryAss !== undefined) ? delims.qryAss : this.delims.qryAss);
 
                                        return this.parseParams(path, this.parseValue, __QRYSEP, __QRYASS);
                                    },
              'setQuery'          : function(query) {
                                        this.query = query;
                                    },
              'setQueryPar'      : function(key, value) {
                                        this.query[key] = value;
                                    },
              'getQueryPar'      : function(key) {
                                        return this.query[key];
                                    },
              'getPath'          : function(dirs = undefined, delims = undefined) {
                                        const __DELIMS = new UriDelims(delims);
                                        const __SCHEMEDELIM = ((__DELIMS.scheme !== undefined) ? __DELIMS.scheme : this.delims.scheme);
                                        const __HOSTDELIM = ((__DELIMS.host !== undefined) ? __DELIMS.host : this.delims.host);
                                        const __PORTDELIM = ((__DELIMS.port !== undefined) ? __DELIMS.port : this.delims.port);
                                        const __QUERYDELIM = ((__DELIMS.query !== undefined) ? __DELIMS.query : this.delims.query);
                                        const __NODEDELIM = ((__DELIMS.node !== undefined) ? __DELIMS.node : this.delims.node);
                                        const __SCHEMENAME = this.scheme;
                                        const __SCHEME = (__SCHEMENAME ? __SCHEMENAME + __SCHEMEDELIM : "");
                                        const __HOSTNAME = this.host;
                                        const __HOST = (__HOSTNAME ? __HOSTDELIM + __HOSTNAME : "");
                                        const __PORTNR = this.port;
                                        const __PORT = ((__HOSTNAME && __PORTNR) ? __PORTDELIM + __PORTNR : "");
                                        const __QUERYSTR = this.getQuery();
                                        const __QUERY = (__QUERYSTR ? __QUERYDELIM + __QUERYSTR : "");
                                        const __NODENAME = this.node;
                                        const __NODE = (__NODENAME ? __NODEDELIM + __NODENAME : "");


                                        return __SCHEME + __HOST + __PORT + Path.prototype.getPath.call(this, dirs, delims) + __QUERY + __NODE;
    for (let opt in optConfig) {
                                    },
        const __CONFIG = optConfig[opt];
              'getDirs'          : function(path = undefined, delims = undefined) {
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
                                        const __PATH = this.getServerPath(path);


                                        return Path.prototype.getDirs.call(this, __PATH);
        optSet[opt] = {
                                    }
            'Config'    : __CONFIG,
          });
            'Value'    : initOptValue(__CONFIG),
            'SetValue'  : undefined,
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
        };
    }


// ==================== Ende Abschnitt fuer Klasse URI ====================
     return optSet;
 
// ==================== Abschnitt fuer Klasse Directory ====================
 
// Basisklasse fuer eine Verzeichnisstruktur
// homePath: Absoluter Startpfad als String
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
// 'delim': Trennzeichen zwischen zwei Ebenen
// 'back': Name des relativen Vaterverzeichnisses
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function Directory(homePath, delims) {
     'use strict';
 
    Path.call(this, homePath, delims);
}
}


Class.define(Directory, Path, {
// Setzt eine Option auf einen vorgegebenen Wert
                    'chDir' : function(subDir = undefined) {
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
                                  if (subDir === undefined) {
// opt: Config und vorheriger Value der Option
                                      this.root();
// value: (Bei allen Typen) Zu setzender Wert
                                  } else if ((typeof subDir) === 'object') {
// reload: Seite mit neuem Wert neu laden
                                      for (let sub of subDir) {
// return Gesetzter Wert
                                          this.chDir(sub);
function setOpt(opt, value, reload = false) {
                                      }
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
                                  } else {
                                      if (subDir === this.delims.home) {
                                          this.home();
                                      } else if (subDir === this.delims.back) {
                                          this.up();
                                      } else {
                                          this.down(subDir);
                                      }
                                  }
                              },
                    'pwd'  : function() {
                                  return this.getPath();
                              }
                });
 
// ==================== Ende Abschnitt fuer Klasse Directory ====================
 
// ==================== Abschnitt fuer Klasse ObjRef ====================
 
// Basisklasse fuer eine Objekt-Referenz
function ObjRef(rootObj) {
     'use strict';
 
    Directory.call(this, undefined, new Delims('/', "..", '/', '~'));
 
    this.setConst('rootObj', rootObj); // Wichtig: Verweis nicht verfolgen! Gefahr durch Zyklen!
}
}


Class.define(ObjRef, Directory, {
// Ermittelt die naechste moegliche Option
                    'valueOf' : function() {
// opt: Config und Value der Option
                                    let ret = this.rootObj;
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
function getNextOpt(opt, value = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);


                                    for (let name of this.dirs) {
    switch (__CONFIG.Type) {
                                        if (ret === undefined) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
                                            break;
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
                                        }
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
                                        ret = ret[name];
    case __OPTTYPES.SD : return getValue(value, __VALUE);
                                    }
    case __OPTTYPES.SI : break;
    default :            break;
    }


                                    return ret;
    return __VALUE;
                                }
}
                });


// ==================== Ende Abschnitt fuer Klasse ObjRef ====================
// Setzt die naechste moegliche Option
 
// opt: Config und Value der Option
// ==================== Abschnitt fuer diverse Utilities ====================
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setNextOpt(opt, value = undefined, reload = true) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);


// Gibt einen Wert zurueck. Ist dieser nicht definiert oder null, wird ein Alternativwert geliefert
    return setOpt(opt, getNextOpt(opt, __VALUE), reload);
// value: Ein Wert. Ist dieser nicht undefined oder null, wird er zurueckgeliefert (oder retValue)
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// retValue: Falls definiert, Rueckgabe-Wert fuer den Fall, dass value nicht undefined oder null ist
// return Der Wert. Sind weder value noch defValue definiert, dann undefined
function getValue(value, defValue = undefined, retValue = undefined) {
    return ((value === undefined) || (value === null)) ? defValue : (retValue === undefined) ? value : retValue;
}
}


// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert
// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// value: Ein Wert. Ist dieser definiet und in den Grenzen, wird er zurueckgeliefert
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// minValue: Untere Grenze fuer den Wert, falls angegeben
// optSet: Platz fuer die gesetzten Optionen (und Config)
// minValue: Obere Grenze fuer den Wert, falls angegeben
// item: Key der Option
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist oder der Wert ausserhalb liegt
// value: (Bei allen Typen) Zu setzender Wert
// return Der Wert. Sind weder value (in den Grenzen) noch defValue definiert, dann undefined
// reload: Seite mit neuem Wert neu laden
function getValueIn(value, minValue = undefined, maxValue = undefined, defValue = undefined) {
// return Gesetzter Wert
     const __VALUE = getValue(value, defValue);
function setOptByName(optSet, item, value, reload = false) {
     const __OPT = getOptByName(optSet, item);
 
    return setOpt(__OPT, value, reload);
}


    if ((minValue !== undefined) && (__VALUE < minValue)) {
// Ermittelt die naechste moegliche Option (Version mit Key)
        return defValue;
// opt: Config und Value der Option
    }
// optSet: Platz fuer die gesetzten Optionen (und Config)
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
// item: Key der Option
        return defValue;
// value: Ggfs. zu setzender Wert
    }
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
    const __OPT = getOptByName(optSet, item);


     return __VALUE;
     return getNextOpt(__OPT, value);
}
}


// Ermittelt den naechsten Wert aus einer Array-Liste
// Setzt die naechste moegliche Option (Version mit Key)
// arr: Array-Liste mit den moeglichen Werte
// opt: Config und Value der Option
// value: Vorher gesetzter Wert
// optSet: Platz fuer die gesetzten Optionen (und Config)
// return Naechster Wert in der Array-Liste
// item: Key der Option
function getNextValue(arr, value) {
// value: Ggfs. zu setzender Wert
     const __POS = arr.indexOf(value) + 1;
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setNextOptByName(optSet, item, value = undefined, reload = true) {
     const __OPT = getOptByName(optSet, item);


     return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
     return setNextOpt(__OPT, value, reload);
}
}


// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// Baut das Benutzermenu auf
// valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// optSet: Gesetzte Optionen
// valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
function buildMenu(optSet) {
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
     console.log("buildMenu()");
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {
     let product = defValue;


     if ((valueA !== undefined) && (valueB !== undefined)) {
     for (let opt in optSet) {
         product = parseFloat(valueA) * parseFloat(valueB);
         registerOption(optSet[opt]);
    }
 
    if (isNaN(product)) {
        product = defValue;
     }
     }
    return parseFloat(product.toFixed(digits));
}
}


// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck
// Laedt eine (ueber Menu) gesetzte Option
// value: Eine ganze Zahl
// opt: Zu ladende Option
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben
// return Gesetzter Wert der gelandenen Option
function getOrdinal(value, defValue = '*') {
function loadOption(opt, force = false) {
     return getValue(value, defValue, value + '.');
     const __CONFIG = getOptConfig(opt);
}


// Hilfsfunktion fuer Array.sort(): Vergleich zweier Zahlen
    if (! force && __CONFIG.AutoReset) {
// valueA: Erster Zahlenwert
        return setOptValue(opt, initOptValue(__CONFIG));
// valueB: Zweiter Zahlenwert
    } else if (__CONFIG.Serial) {
// return -1 = kleiner, 0 = gleich, +1 = groesser
        return setOptValue(opt, deserialize(getOptName(opt), getOptValue(opt)));
function compareNumber(valueA, valueB) {
    } else {
    return +(valueA > valueB) || (+(valueA === valueB) - 1);
        return setOptValue(opt, GM_getValue(getOptName(opt), getOptValue(opt)));
    }
}
}


// Ueberprueft, ob ein Objekt einer bestimmten Klasse angehoert (ggfs. per Vererbung)
// Laedt die (ueber Menu) gesetzten Optionen
// obj: Ein (generisches) Objekt
// optSet: Set mit den Optionen
// base: Eine Objektklasse (Konstruktor-Funktion)
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return true, wenn der Prototyp rekursiv gefunden werden konnte
// return Set mit den geladenen Optionen
function instanceOf(obj, base) {
function loadOptions(optSet, force = false) {
     while (obj !== null) {
     for (let opt in optSet) {
         if (obj === base.prototype) {
         loadOption(optSet[opt], force);
            return true;
        }
        if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
            return (base.prototype === XML.prototype);
        }
        obj = Object.getPrototypeOf(obj);
     }
     }


     return false;
     return optSet;
}
}


// Liefert alle Basisklassen des Objekts (inkl. Vererbung)
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// obj: Ein (generisches) Objekt
// opt: Gesetzte Option
// return true, wenn der Prototyp rekursiv gefunden werden konnte
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
function getPrototypes(obj) {
// reset: Setzt bei Erfolg auf Initialwert der Option
     let ret = [];
function deleteOption(opt, force = false, reset = true) {
     const __CONFIG = getOptConfig(opt);


     while (obj !== null) {
     if (force || ! __CONFIG.Permanent) {
         const __PROTO = Object.getPrototypeOf(obj);
         GM_deleteValue(getOptName(opt));


         ret.push(__PROTO);
         if (reset) {
        if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
            setOptValue(opt, initOptValue(__CONFIG));
            break;
         }
         }
        obj = __PROTO;
     }
     }
    return ret;
}
}


// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// obj: Ein (generisches) Objekt
// optSet: Gesetzte Optionen
// return Array von Items (Property-Namen)
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
function getAllProperties(obj) {
// reset: Setzt bei Erfolg auf Initialwert der Option
    let ret = [];
function deleteOptions(optSet, force = false, reset = true) {
 
     for (let opt in optSet) {
     for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
        deleteOption(optSet[opt], force, reset);
      ret = ret.concat(Object.getOwnPropertyNames(o));
     }
     }
    return ret;
}
}


// Ueberpruefung, ob ein Item aktiv ist oder nicht
// Entfernt eine (ueber Menu) gesetzte Option
// item: Name des betroffenen Items
// opt: Gesetzte Option
// inList: Checkliste der inkludierten Items (Positivliste, true fuer aktiv)
// name: Neu zu setzender Name (Speicheradresse)
// exList: Checkliste der exkludierten Items (Negativliste, true fuer inaktiv)
// reload: Wert nachladen statt beizubehalten
// return Angabe, ob das Item aktiv ist
// return Umbenannte Option
function checkItem(item, inList = undefined, exList = undefined) {
function renameOption(opt, name, reload = false) {
     let active = true;
     const __NAME = getOptName(opt);


     if (inList) {
     if (__NAME !== name) {
        active = (inList[item] === true);  // gesetzt und true
         deleteOption(opt, true, ! reload);
    }
    if (exList) {
         if (exList[item] === true) {  // gesetzt und true
            active = false; // NICHT anzeigen
        }
    }


    return active;
        setOptName(opt, name);
}


// Fuegt Properties zu einem Objekt hinzu, die in einem zweiten stehen. Doppelte Werte werden ueberschrieben
         if (reload) {
// data: Objekt, dem Daten hinzugefuegt werden
             loadOption(opt);
// addData: Objekt, das zusaetzliche Properties enthaelt
// addList: Checkliste der zu setzenden Items (true fuer kopieren), falls angegeben
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
// return Das gemergete Objekt mit allen Properties
function addProps(data, addData, addList = undefined, ignList = undefined) {
    for (let item in getValue(addData, { })) {
         if (checkItem(item, addList, ignList)) {
             data[item] = addData[item];
         }
         }
     }
     }


     return data;
     return opt;
}
}


// Entfernt Properties in einem Objekt
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// data: Objekt, deren Properties bearbeitet werden
// reload: Seite mit "Werkseinstellungen" neu laden
// delList: Checkliste der zu loeschenden Items (true fuer loeschen), falls angegeben
// optSet: Gesetzte Optionen
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
function resetOptions(optSet, reload = true) {
// return Das veraenderte Objekt ohne die geloeschten Properties
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
function delProps(data, delList = undefined, ignList = undefined) {
    deleteOptions(optSet, false, true);
     for (let item in getValue(data, { })) {
 
         if (checkItem(item, delList, ignList)) {
     if (reload) {
            delete data[item];
         // ... und Seite neu laden (mit "Werkseinstellungen")...
        }
        window.location.reload();
     }
     }
    return data;
}
}


// Gibt den Wert einer Property zurueck. Ist dieser nicht definiert oder null, wird er vorher gesetzt
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// obj: Ein Objekt. Ist dieses undefined oder null, wird defValue zurueckgeliefert
// item: Key des Properties
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return Der Wert des Properties. Sind das obj oder das Property und defValue undefined oder null, dann undefined
function getProp(obj, item, defValue = undefined) {
    if ((obj === undefined) || (obj === null)) {
        return defValue;
    }


    const __PROP = obj[item];
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
const __OPTSET = { };


    if ((__PROP !== undefined) && (__PROP !== null)) {
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
        return __PROP;
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined };
    }


    return (obj[item] = defValue);
// Behandelt die Optionen und laedt das Benutzermenu
}
// optConfig: Konfiguration der Optionen
 
// optSet: Platz fuer die gesetzten Optionen
// Sicheres obj.valueOf() fuer alle Daten
// optParams: Eventuell notwendige Parameter zur Initialisierung
// data: Objekt oder Wert
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// return Bei Objekten valueOf() oder das Objekt selber, bei Werten der Wert
// 'Tab': Tabelle mit dem Spielplan
function valueOf(data) {
// 'Zei': Startzeile des Spielplans mit dem ersten ZAT
    return (((typeof data) === 'object') ? data.valueOf() : data);
// 'Spa': Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
}
// '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 Gefuelltes Objekt mit den gesetzten Optionen
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
    const __BOXSAISONS = document.getElementsByTagName("option");


// Sicheres JSON.stringify(), das auch mit Zyklen umgehen kann
    optSet = initOptions(optConfig, optSet);
// value: Auszugebene Daten. Siehe JSON.stringify()
// replacer: Elementersetzer. Siehe JSON.stringify()
// space: Verschoenerung. Siehe JSON.stringify()
// cycleReplacer: Ersetzer im Falle von Zyklen
// return String mit Ausgabe der Objektdaten
function safeStringify(value, replacer = undefined, space = undefined, cycleReplacer = undefined) {
    return JSON.stringify(value, serializer(replacer, cycleReplacer), space);
}


// Hilfsfunktion fuer safeStringify(): Kapselt replacer und einen cycleReplacer fuer Zyklen
    runStored(optSet, true);
// replacer: Elementersetzer. Siehe JSON.stringify()
     loadOptions(optSet);
// cycleReplacer: Ersetzer im Falle von Zyklen
// return Ersetzer-Funktion fuer JSON.stringify(), die beide Ersetzer vereint
function serializer(replacer = undefined, cycleReplacer = undefined) {
    const __STACK = [];
     const __KEYS = [];


     if (! cycleReplacer) {
     // Werte aus der HTML-Seite ermitteln...
        cycleReplacer = function(key, value) {
    const __SAISON = getSaisonFromComboBox(__BOXSAISONS);
                if (__STACK[0] === value) {
    const __LIGASIZE = getLigaSizeFromSpielplan(optParams.Tab.rows, optParams.Zei, optParams.Spa, getOptValue(optSet.saison));
                    return "[~]";
                }
                return "[~." + __KEYS.slice(0, __STACK.indexOf(value)).join('.') + ']';
            };
    }


     return function(key, value) {
     // ... und abspeichern...
            if (__STACK.length) {
    setOpt(optSet.saison, __SAISON, false);
                const __THISPOS = __STACK.indexOf(this);
    setOpt(optSet.ligaSize, __LIGASIZE, false);


                if (~ __THISPOS) {
    if (! optParams.hideMenu) {
                    __STACK.splice(__THISPOS + 1);
        buildMenu(optSet);
                    __KEYS.splice(__THISPOS, Infinity, key);
    }
                } else {
                    __STACK.push(this);
                    __KEYS.push(key);
                }
                if (~ __STACK.indexOf(value)) {
                    value = cycleReplacer.call(this, key, value);
                }
            } else {
                __STACK.push(value);
            }


            return ((! replacer) ? value : replacer.call(this, key, value));
     if (optParams.menuAnchor !== undefined) {
        };
         buildForm(optParams.menuAnchor, optSet, optParams);
}
 
// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// key: Der uebergebene Schluessel
// value: Der uebergebene Wert
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function replaceArraySimple(key, value) {
     if (Array.isArray(value)) {
         return "[ " + value.join(", ") + " ]";
     }
     }


     return value;
     return optSet;
}
}


// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// ==================== Abschnitt fuer diverse Utilities ====================
// 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;
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// form: <form>...</form>
// props: Map von name:value-Paaren
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = "hidden") {
    for (let fieldName in props) {
         let field = form[fieldName];
        if (! field) {
            field = document.createElement("input");
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
        }
        field.value = props[fieldName];
     }
     }


     return value;
     return form;
}
}


// Moegliche einfache Ersetzungen mit '$'...
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
let textSubst;
// form: <form>...</form>
// props: Map von name:value-Paaren
// return Ergaenztes Form-Konstrukt
function addHiddenField(form, props) {
    return addInputField(form, props, "hidden");
}


// Substituiert '$'-Parameter in einem Text
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// text: Urspruenglicher Text mit '$'-Befehlen
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// par1: Der (erste) uebergebene Parameter
// type: Name des Events, z.B. "click"
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
// callback: Funktion als Reaktion
function substParam(text, par1) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     let ret = getValue(text, "");
// return false bei Misserfolg
 
function addEvent(obj, type, callback, capture = false) {
     if (! textSubst) {
     if (obj.addEventListener) {
         textSubst  = {
        return obj.addEventListener(type, callback, capture);
                'n' : __DBMOD.name,
     } else if (obj.attachEvent) {
                'v' : __DBMOD.version,
         return obj.attachEvent("on" + type, callback);
                'V' : __DBMOD.Name
    } else {
            };
        console.log("Could not add " + type + " event:");
    }
        console.log(callback);


    for (let ch in textSubst) {
         return false;
         const __SUBST = textSubst[ch];
 
        ret = ret.replace('$' + ch, __SUBST);
     }
     }
    return ret.replace('$', par1);
}
}


// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// numberString: Dezimalzahl als String
// type: Name des Events, z.B. "click"
// return Diese Dezimalzahl als String mit Tausender-Trennpunkten und Komma statt Dezimalpunkt
// callback: Funktion als Reaktion
function getNumberString(numberString) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     if (numberString.lastIndexOf('.') !== -1) {
// return false bei Misserfolg
         // Zahl enthaelt Dezimalpunkt
function removeEvent(obj, type, callback, capture = false) {
        const __VORKOMMA = numberString.substring(0, numberString.lastIndexOf('.'));
     if (obj.removeEventListener) {
        const __NACHKOMMA = numberString.substring(numberString.lastIndexOf('.') + 1, numberString.length);
         return obj.removeEventListener(type, callback, capture);
 
    } else if (obj.detachEvent) {
         return getNumberString(__VORKOMMA) + ',' + __NACHKOMMA;
         return obj.detachEvent("on" + type, callback);
     } else {
     } else {
         // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
         console.log("Could not remove " + type + " event:");
         // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
         console.log(callback);
        const __TEMP = reverseString(numberString);
        let result = "";


        for (let i = 0; i < __TEMP.length; i++) {
         return false;
            if ((i > 0) && (i % 3 === 0)) {
                result += '.';
            }
            result += __TEMP.substr(i, 1);
        }
 
         return reverseString(result);
     }
     }
}
}


// Dreht den uebergebenen String um
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// string: Eine Zeichenkette
// id: ID des betroffenen Eingabeelements
// return Dieselbe Zeichenkette rueckwaerts
// type: Name des Events, z.B. "click"
function reverseString(string) {
// callback: Funktion als Reaktion
    let result = "";
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
 
// return false bei Misserfolg
    for (let i = string.length - 1; i >= 0; i--) {
function addDocEvent(id, type, callback, capture = false) {
        result += string.substr(i, 1);
    const __OBJ = document.getElementById(id);
    }


     return result;
     return addEvent(__OBJ, type, callback, capture);
}
}


// Speichert einen String/Integer/Boolean-Wert unter einem Namen ab
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
// id: ID des betroffenen Eingabeelements
// value: Zu speichernder String/Integer/Boolean-Wert
// type: Name des Events, z.B. "click"
// return Promise auf ein Objekt, das 'name' und 'value' der Operation enthaelt
// callback: Funktion als Reaktion
function storeValue(name, value) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     __LOG[4](name + " >> " + value);
// return false bei Misserfolg
function removeDocEvent(id, type, callback, capture = false) {
     const __OBJ = document.getElementById(id);


     return GM.setValue(name, value).then(voidValue => {
     return removeEvent(__OBJ, type, callback, capture);
            __LOG[5]("OK " + name + " >> " + value);
 
            return Promise.resolve({
                    'name'  : name,
                    'value' : value
                });
        }, defaultCatch);
}
}


// Holt einen String/Integer/Boolean-Wert unter einem Namen zurueck
// Helferfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
// item: Name des betroffenen Items
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// return Promise fuer den String/Integer/Boolean-Wert, der unter dem Namen gespeichert war
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
function summonValue(name, defValue = undefined) {
// return Angabe, ob das Item sichtbar sein soll
     return GM.getValue(name, defValue).then(value => {
function checkVisible(item, showList, hideList = undefined) {
            __LOG[4](name + " << " + value);
     let show = true;


            return Promise.resolve(value);
    if (showList !== undefined) {
        }, ex => {
        show = (showList[item] === true); // gesetzt und true
            __LOG[0](name + ": " + ex.message);
    }
    if (hideList !== undefined) {
        if (hideList[item] === true) {  // gesetzt und true
            show = false; // NICHT anzeigen
        }
    }


            return Promise.reject(ex);
    return show;
        }, defaultCatch);
}
}


// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// Helferfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
// index: Laufende Nummer des Elements (0-based)
// value: Beliebiger (strukturierter) Wert
// tag: Tag des Elements ("table")
// return Promise auf ein Objekt, das 'name' und 'value' in der String-Darstellung des Wertes enthaelt
// doc: Dokument (document)
function serialize(name, value) {
function getTable(index, tag = "table", doc = document) {
     const __STREAM = ((value !== undefined) ? safeStringify(value) : value);
     const __TAGS = document.getElementsByTagName(tag);
    const __TABLE = __TAGS[index];


     return storeValue(name, __STREAM);
     return __TABLE;
}
}


// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
// index: Laufende Nummer des Elements (0-based)
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// doc: Dokument (document)
// return Promise fuer das Objekt, das unter dem Namen gespeichert war
function getRows(index, doc = document) {
function deserialize(name, defValue = undefined) {
     const __TABLE = getTable(index, "table", doc);
     return summonValue(name).then(stream => {
    const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
            if (stream && stream.length) {
 
                return JSON.parse(stream);
    return __ROWS;
            } else {
                return defValue;
            }
        });
}
}


// Setzt die Seite gemaess der Aenderungen zurueck...
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// reload: Seite wird ganz neu geladen
 
function refreshPage(reload = true) {
// Liefert den Funktionsaufruf zur Option als String
     if (reload) {
// opt: Auszufuehrende Option
         __LOG[2]("Seite wird neu geladen...");
// isAlt: Angabe, ob AltAction statt Action gemeint ist
         window.location.reload();
// value: Ggfs. zu setzender Wert
// serial: Serialization fuer String-Werte (Select, Textarea)
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
    const __NAMSTR = "'" + getOptName(opt) + "'";
    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) ? JSON.stringify(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 "(sessionStorage.setItem('runcmd', 'SET'), sessionStorage.setItem('runkey', " + __NAMSTR + "), sessionStorage.setItem('runval', " + __VALSTR + "), window.location.reload())";
         case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                              return "(sessionStorage.setItem('runcmd', 'NXT'), sessionStorage.setItem('runkey', " + __NAMSTR + "), sessionStorage.setItem('runval', " + __VALSTR + "), window.location.reload())";
         case __OPTACTION.RST : //return "doActionRst()";
                              return "(sessionStorage.setItem('runcmd', 'RST'), window.location.reload())";
        default :              break;
        }
     }
     }
    return undefined;
}
}


// Setzt eine Option dauerhaft und laedt die Seite neu
// Liefert die Funktionsaufruf zur Option als String
// name: Name der Option als Speicherort
// opt: Auszufuehrende Option
// value: Zu setzender Wert
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// reload: Seite mit neuem Wert neu laden
// value: Ggfs. zu setzender Wert
// serial: Serialization fuer komplexe Daten
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// serial: Serialization fuer String-Werte (Select, Textarea)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return String mit dem (reinen) Funktionsaufruf
// return Gespeicherter Wert fuer setOptValue()
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined) {
function setStored(name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
     const __ACTION = getFormAction(opt, isAlt, value, serial);
     (serial ? serialize(name, value)
            : storeValue(name, value))
                .then(onFulfilled, onRejected)
                .then(() => refreshPage(reload), defaultCatch); // Ende der Kette...


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


// Setzt den naechsten Wert aus einer Array-Liste als Option
// Zeigt eine Option auf der Seite als Auswahlbox an
// arr: Array-Liste mit den moeglichen Optionen
// opt: Anzuzeigende Option
// name: Name der Option als Speicherort
// return String mit dem HTML-Code
// value: Vorher gesetzter Wert
function getOptionSelect(opt) {
// reload: Seite mit neuem Wert neu laden
    const __CONFIG = getOptConfig(opt);
// serial: Serialization fuer komplexe Daten
    const __NAME = getOptName(opt);
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
    const __VALUE = getOptValue(opt);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
// return Gespeicherter Wert fuer setOptValue()
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
function setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';
}


// Fuehrt die in einem Storage gespeicherte Operation aus
     for (let value of __CONFIG.Choice) {
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
         element += '\n<option value="' + value + '"' +
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
                  ((value === __VALUE) ? ' SELECTED' : "") +
function getStoredCmds(memory = undefined) {
                  '>' + value + '</option>';
     const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];
 
    if (__MEMORY !== 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) {
            const __KEY = __GETITEM('key');
            let value = __GETITEM('val');
 
            try {
                value = JSON.parse(value);
            } catch (ex) {
                __LOG[0]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
                // ... meist kann man den String selber aber speichern, daher kein "return"...
            }
 
            __STOREDCMDS.push({
                                'cmd' : __CMD,
                                'key' : __KEY,
                                'val' : value
                            });
        }
 
        __DELITEM('cmd');
        __DELITEM('key');
        __DELITEM('val');
     }
     }
    element += '\n</select>';


     return (__STOREDCMDS.length ? __STOREDCMDS : undefined);
     return __LABEL.replace('$', element);
}
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// Zeigt eine Option auf der Seite als Radiobutton an
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// opt: Anzuzeigende Option
// optSet: Object mit den Optionen
// return String mit dem HTML-Code
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
function getOptionRadio(opt) {
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
     const __CONFIG = getOptConfig(opt);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
     const __NAME = getOptName(opt);
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
     const __VALUE = getOptValue(opt, false);
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {
     const __ACTION = getFormActionEvent(opt, false, true, "click", false);
     const __BEFORELOAD = getValue(beforeLoad, true);
     const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
     const __STOREDCMDS = getValue(storedCmds, []);
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
     const __LOADEDCMDS = [];
                        '" id="' + __NAME + 'ON" value="1"' +
     let invalidated = false;
                        (__VALUE ? ' CHECKED' : __ACTION) +
 
                        ' /><label for="' + __NAME + 'ON">' +
     while (__STOREDCMDS.length) {
                        __CONFIG.Label + '</label>';
        const __STORED = __STOREDCMDS.shift();
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
        const __CMD = __STORED.cmd;
                        '" id="' + __NAME + 'OFF" value="0"' +
        const __KEY = __STORED.key;
                        (__VALUE ? __ALTACTION : ' CHECKED') +
        const __VAL = __STORED.val;
                        ' /><label for="' + __NAME + 'OFF">' +
 
                        __CONFIG.AltLabel + '</label>';
        if (__BEFORELOAD) {
            if (__STOREDCMDS.length) {
                await invalidateOpts(optSet);  // alle Optionen invalidieren
                invalidated = true;
            }
            switch (__OPTACTION[__CMD]) {
            case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL);
                                  setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);
                                  break;
            case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false, onFulfilled, onRejected);
                                  setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);
                                  break;
            case __OPTACTION.RST : __LOG[4]("RESET (delayed)");
                                  __LOADEDCMDS.push(__STORED);
                                  break;
            default :             break;
            }
        } 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;
            }
        }
    }


     return (__LOADEDCMDS.length ? __LOADEDCMDS : undefined);
     return [ __ELEMENTON, __ELEMENTOFF ];
}
}


// Gibt eine Option sicher zurueck
// Zeigt eine Option auf der Seite als Checkbox an
// opt: Config und Value der Option, ggfs. undefined
// opt: Anzuzeigende Option
// defOpt: Rueckgabewert, falls undefined
// return String mit dem HTML-Code
// return Daten zur Option (oder defOpt)
function getOptionCheckbox(opt) {
function getOpt(opt, defOpt = { }) {
    const __CONFIG = getOptConfig(opt);
     return getValue(opt, defOpt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
 
    return '<input type="checkbox" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __VALUE + '"' +
          (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
          __NAME + '">' + __FORMLABEL + '</label>';
}
}


// Gibt eine Option sicher zurueck (Version mit Key)
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// optSet: Platz fuer die gesetzten Optionen (und Config)
// opt: Anzuzeigende Option
// item: Key der Option
// return String mit dem HTML-Code
// defOpt: Rueckgabewert, falls nicht zu finden
function getOptionTextarea(opt) {
// return Daten zur Option (oder defOpt)
     const __CONFIG = getOptConfig(opt);
function getOptByName(optSet, item, defOpt = { }) {
    const __NAME = getOptName(opt);
     if ((optSet !== undefined) && (item !== undefined)) {
    const __VALUE = getOptValue(opt);
        return getOpt(optSet[item], defOpt);
    const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
     } else {
    const __SUBMIT = getValue(__CONFIG.Submit, "");
        return defOpt;
    const __ONSUBMIT = ((__SUBMIT.length > 0) ? ' 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 + '>' +
                          JSON.stringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


// Gibt die Konfigurationsdaten einer Option zurueck
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
// opt: Config und Value der Option
// defConfig: Rueckgabewert, falls Config nicht zu finden
// return Konfigurationsdaten der Option
function getOptConfig(opt, defConfig = { }) {
     return getValue(getOpt(opt).Config, defConfig);
}
}


// Setzt den Namen einer Option
// Zeigt eine Option auf der Seite als Button an
// opt: Config und Value der Option
// opt: Anzuzeigende Option
// name: Zu setzender Name der Option
// return String mit dem HTML-Code
// reload: Seite mit neuem Wert neu laden
function getOptionButton(opt) {
// return Gesetzter Name der Option
function setOptName(opt, name) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
    const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     if (__NAME !== name) {
     return '<label for="' + __NAME + '">' + __FORMLABEL +
        __LOG[4]("RENAME " + __NAME + " => " + name);
          '</label><input type="button" name="' + __NAME +
 
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
        __CONFIG.Name = name;
          __ACTION + '/>';
    }
 
    return name;
}
}


// Gibt den Namen einer Option zurueck
// Zeigt eine Option auf der Seite an (je nach Typ)
// opt: Config und Value der Option
// opt: Anzuzeigende Option
// return Name der Option
// return String mit dem HTML-Code
function getOptName(opt) {
function getOptionElement(opt) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);
     const __NAME = __CONFIG.Name;
     const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
    let element = "";


     if (! __NAME) {
     if (! __CONFIG.Hidden) {
         const __SHARED = __CONFIG.Shared;
        switch (__TYPE) {
 
         case __OPTTYPES.MC : element = getOptionSelect(opt);
         if (__SHARED && ! opt.Loaded) {
                            break;
            const __OBJREF = getSharedRef(__SHARED, opt.Item);
         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;
        }


            return __OBJREF.getPath();
        if (element.length === 2) {
            element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
         }
         }
        showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
     }
     }


     return __NAME;
     return element;
}
}


// Setzt den Wert einer Option
// Baut das Benutzermenu auf der Seite auf
// opt: Config und Value der Option
// optSet: Gesetzte Optionen
// name: Zu setzender Wert der Option
// optParams: Eventuell notwendige Parameter
// return Gesetzter Wert
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
function setOptValue(opt, value) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     if (opt !== undefined) {
// 'formWidth': Anzahl der Elemente pro Zeile
         if (! opt.ReadOnly) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
             __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));
// 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)
 
    for (let opt in optSet) {
         if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
             const __ELEMENT = getOptionElement(optSet[opt]);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";


             opt.Value = value;
             if (__ELEMENT.length > 0) {
                if (++count > __FORMBREAK) {
                    if (++column > __FORMWIDTH) {
                        column = 1;
                    }
                }
                if (column === 1) {
                    form += '</tr><tr>';
                }
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
            }
         }
         }
        return opt.Value;
    } else {
        return undefined;
     }
     }
    form += '\n' + __FORMEND;
    return form;
}
}


// Gibt den Wert einer Option zurueck
// Fuegt das Script in die Seite ein
// opt: Config und Value der Option
// optSet: Gesetzte Optionen
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// optParams: Eventuell notwendige Parameter
// load: Laedt die Option per loadOption(), falls noetig
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// return Gesetzter Wert
// return String mit dem HTML-Code fuer das Script
function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {
function getScript(optSet, optParams = { }) {
     let value;
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    //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 __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
    const __SCRIPT = "";


     if (opt !== undefined) {
     //window.eval('function activateMenu() { console.log("TADAAA!"); }');
        if (load && ! opt.Loaded) {
            if (! opt.Promise) {
                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));
     return __SCRIPT;
}
}


// ==================== Ende Abschnitt fuer diverse Utilities ====================
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// anchor: Element, das als Anker fuer die Anzeige dient
// 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
function buildForm(anchor, optSet, optParams = { }) {
    console.log("buildForm()");


// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
    const __FORM = getForm(optSet, optParams);
    const __SCRIPT = getScript(optSet, optParams);


// Namen des Default-, Temporaer- und Null-Memories...
    addForm(anchor, __FORM, __SCRIPT);
const __MEMNORMAL  = 'normal';
const __MEMSESSION  = 'begrenzt';
const __MEMINAKTIVE = 'inaktiv';
 
// 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;
 
// Infos ueber dieses Script-Modul
const __DBMOD = new ScriptModule();
 
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
const __DBTOC = { };
 
// Daten zu den Modulen (indiziert durch die Script-Namen)
const __DBDATA = { };
 
// ==================== Abschnitt fuer Speicher ====================
 
// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
// 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
// Informationen zu hinzugefuegten Forms
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
const __FORMS = { };
// return true, wenn der Speichertest erfolgreich war
function canUseMemory(memory = undefined) {
    const __STORAGE = getMemory(memory, { });
    const __MEMORY = __STORAGE.Value;
    let ret = false;


    if (__MEMORY !== undefined) {
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        const __TESTPREFIX = 'canUseStorageTest';
// anchor: Element, das als Anker fuer die Anzeige dient
        const __TESTDATA = Math.random().toString();
// form: HTML-Form des Optionsmenu (hinten angefuegt)
        const __TESTITEM = __TESTPREFIX + __TESTDATA;
// 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);


        __MEMORY.setItem(__TESTITEM, __TESTDATA);
    __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
        __MEMORY.removeItem(__TESTITEM);
    }


     __LOG[2]("canUseStorage(" + __STORAGE.Name + ") = " + ret);
     anchor.innerHTML = __REST + script + form;
 
    return ret;
}
}


// Ermittelt die Groesse des benutzten Speichers
// ==================== Ende Abschnitt fuer Optionen ====================
// 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 Spielplan und ZATs ====================


    if (__MEMORY !== undefined) {
// Beschreibungstexte aller Runden
        const __SIZE = safeStringify(__MEMORY).length;
const __POKALRUNDEN = [ "", "1. Runde", "2. Runde", "3. Runde", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
const __QUALIRUNDEN = [ "", "Quali 1", "Quali 2", "Quali 3" ];
const __OSCRUNDEN  = [ "", "Viertelfinale", "Halbfinale", "Finale" ];
const __OSERUNDEN  = [ "", "Runde 1", "Runde 2", "Runde 3", "Runde 4", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
const __HINRUECK    = [ " Hin", " R\xFCck", "" ];


        __LOG[2]("MEM: " + __SIZE + " bytes");
// Liefert einen vor den ersten ZAT zurueckgesetzten Spielplanzeiger
        return __SIZE;
// saison: Enthaelt die Nummer der laufenden Saison
    } else {
// ligaSize: Anzahl der Teams in dieser Liga (Gegner + 1)
        return 0;
// - ZATs pro Abrechnungsmonat
    }
// - Saison
}
// - ZAT
 
// - GameType
// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus
// - Heim/Auswaerts
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird
// - Gegner
// out: Logfunktion, etwa __LOG[4]
// - Tore
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen)
// - Gegentore
// name: Name des Objekts
// - Ligengroesse
function getMemUsage(value = undefined, out = undefined, depth = -1, name = '$') {
// - Ligaspieltag
     const __OUT = (out || __LOG[4]);
// - Pokalrunde
 
// - Eurorunde
    if ((typeof value) === 'string') {
// - Hin/Rueck
         const __SIZE = value.length;
// - ZAT Rueck
 
// - ZAT Korr
         __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.slice(0, 255));
function firstZAT(saison, ligaSize) {
    } else if ((typeof value) === 'object') {
     return {
         if (depth === 0) {
        'anzZATpMonth' : ((saison < 2) ? 7 : 6),    // Erste Saison 7 ZAT, danach 6 ZAT...
            const __SIZE = safeStringify(value).length;
        'saison'      : saison,
 
        'ZAT'         : 0,
            __OUT("USAGE: " + name + '\t' + __SIZE);
         'gameType'    : "spielfrei",
         } else {
        'heim'        : true,
            depth--;
         'gegner'      : "",
            for (let sub in value) {
        'gFor'         : 0,
                getMemUsage(value[sub], __OUT, depth, name + '.' + sub);
        'gAga'         : 0,
            }
        'ligaSize'     : ligaSize,
            getMemUsage(value, __OUT, 0, name);
         'ligaSpieltag' : 0,
         }
        'pokalRunde'  : 1,
    } else {
        'euroRunde'   : 0,
      const __DATA = (((typeof value) === 'function') ? "" : '\t' + value);
         'hinRueck'     : 2,   // 0: Hin, 1: Rueck, 2: unbekannt
 
         'ZATrueck'     : 0,
         __OUT("USAGE: " + name + '\t' + (typeof value) + __DATA);
         'ZATkorr'     : 0
     }
     };
}
}


// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// Liefert den ZAT als String
// opt: Option zur Wahl des Speichers
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// return Promise auf gesuchten Speicher oder Null-Speicher ('inaktiv')
// longStats: Formatiert die Langversion des Textes
async function restoreMemoryByOpt(opt) {
function getZAT(currZAT, longStats) {
    // Memory Storage fuer vorherige Speicherung...
    return (longStats ? currZAT.gameType + ' ' + (currZAT.heim ? "Heim" : "Ausw\xE4rts") + ' ' : "") +
    const __STORAGE = await getOptValue(opt, __MEMNORMAL, true, true, true);
          (longStats ? '[' + currZAT.ligaSpieltag + ' ' + currZAT.pokalRunde + ' ' + currZAT.euroRunde + "] " : "") +
 
          (longStats ? '[' + currZAT.ZATrueck + __HINRUECK[currZAT.hinRueck] +
    return __OPTMEM[__STORAGE];
                        ' ' + ((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;
}


// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
// Liefert die ZATs der Sonderspieltage fuer 10er- (2) und 20er-Ligen (4)
// opt: Option zur Wahl des Speichers
// saison: Enthaelt die Nummer der laufenden Saison
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// return [ 10erHin, 10erRueck, 20erHin, 20erRueck ], ZAT-Nummern der Zusatzspieltage
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
function getLigaExtra(saison) {
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
     if (saison < 3) {
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
        return [ 8, 64, 32, 46 ];
function startMemoryByOpt(opt, saveOpt = undefined, onFulfilled = undefined, onRejected = undefined) {
     } else {
     // Memory Storage fuer naechste Speicherung...
         return [ 9, 65, 33, 57 ];
    let storage = getOptValue(opt, __MEMNORMAL);
    let optMem = __OPTMEM[storage];
 
     if (! canUseMemory(optMem)) {
         if (storage !== __MEMINAKTIVE) {
            storage = __MEMINAKTIVE;
            optMem = __OPTMEM[storage];
        }
     }
     }
    if (saveOpt !== undefined) {
        setOpt(saveOpt, storage, false, onFulfilled, onRejected);
    }
    return optMem;
}
}


// ==================== Ende Abschnitt fuer Speicher ====================
// Spult die Daten um anzZAT ZATs vor und schreibt Parameter
// anhand des Spielplans fort. Also Spieltag, Runde, etc.
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// anzZAT: Anzahl der ZAT, um die vorgespult wird
function incZAT(currZAT, anzZAT = 1) {
    const __LIGAEXTRA = getLigaExtra(currZAT.saison);
    const __LIGAFIRST = 3 - (__LIGAEXTRA[0] % 2);


// ==================== Abschnitt fuer die Scriptdatenbank ====================
    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);


// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
            if (__POS > -1) {
// meta: Metadaten des Scripts (Default: GM.info.script)
                if (__POS < 2 * (currZAT.ligaSize % 9)) {
// return Beschreibende Daten fuer __DBMOD
                     currZAT.ligaSpieltag++;
function ScriptModule(meta) {
                }
    'use strict';
            }
 
        }
    const __META = getValue(meta, GM.info.script);
        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 __PROPS = {
             currZAT.pokalRunde++;
                'name'        : true,
                'version'    : true,
                'namespace'  : true,
                'description' : true
            };
 
    const __DBMOD = { };
 
    __LOG[5](__META);
 
    // Infos zu diesem Script...
    addProps(__DBMOD, __META, __PROPS);
 
    // Voller Name fuer die Ausgabe...
    Object.defineProperty(__DBMOD, 'Name', {
                     get : function() {
                              return this.name + " (" + this.version + ')';
                          },
                    set : undefined
                });
 
    __LOG[2](__DBMOD);
 
    return __DBMOD;
}
 
Class.define(ScriptModule, Object);
 
// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// optSet: Gesetzte Optionen (und Config)
function initScriptDB(optSet) {
    // Speicher fuer die DB-Daten...
    const __DBMEM = myOptMem.Value;
 
    __DBTOC.versions = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
    __DBTOC.namespaces = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.namespaces')), { });
 
    // 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)), { }));
         }
         }
     }
        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
                }
            }
        }
     }
}


// Setzt die Daten dieses Scriptes in der Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// Liefert die Beschreibung des Spiels am aktuellen ZAT
// optSet: Gesetzte Optionen (und Config)
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
function updateScriptDB(optSet) {
// showLink: Angabe, ob ein Link eingefuegt werden soll
     // Eintrag ins Inhaltsverzeichnis...
// return Beschreibung des Spiels
     __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
function getZusatz(currZAT, showLink = true) {
     __DBTOC.namespaces[__DBMOD.name] = __DBMOD.namespace;
     const __NAMESPACE = "http://os.ongapo.com/";
    let zusatz = "";
    let href = "";
     let prop = "stauswahl";
     let runde = 0;


     // Speicher fuer die DB-Daten...
     if (currZAT.gameType === "Liga") {
     const __DBMEM = myOptMem.Value;
        href = "ls";
        runde = currZAT.ligaSpieltag;
        if (currZAT.ZAT < 70) {
            zusatz = runde + ". Spieltag";
        } else {
            prop = "";
            zusatz = "Relegation";
        }
    } else if (currZAT.gameType === "LP") {
        href = "lp";
        runde = currZAT.pokalRunde;
        zusatz = __POKALRUNDEN[runde];
    } else if ((currZAT.gameType === "OSCQ") || (currZAT.gameType === "OSEQ")) {
        href = ((currZAT.gameType === "OSCQ") ? "oscq" : "oseq");
        prop = "runde";
        runde = currZAT.euroRunde;
        zusatz = __QUALIRUNDEN[runde] + __HINRUECK[currZAT.hinRueck];
     } else if (currZAT.gameType === "OSC") {
        if (currZAT.euroRunde < 9) {
            const __GRUPPENPHASE = ((currZAT.euroRunde < 6) ? "HR-Grp. " : "ZR-Grp. ");


    if (__DBMEM !== undefined) {
            href = ((currZAT.euroRunde < 6) ? "oschr" : "osczr");
        // Permanente Speicherung der Eintraege...
            runde = ((currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck);
        __DBMEM.setItem('__DBTOC.versions', safeStringify(__DBTOC.versions));
            zusatz = __GRUPPENPHASE + "Spiel " + runde;
         __DBMEM.setItem('__DBTOC.namespaces', safeStringify(__DBTOC.namespaces));
         } else {
         __DBMEM.setItem('__DBDATA.' + __DBMOD.name, safeStringify(optSet));
            href = "oscfr";
            runde = currZAT.euroRunde - 8;
            zusatz = __OSCRUNDEN[runde] + __HINRUECK[currZAT.hinRueck];
        }
        prop = "";
    } else if (currZAT.gameType === "OSE") {
        href = "ose";
        prop = "runde";
         runde = currZAT.euroRunde - 3;
        zusatz = __OSERUNDEN[runde] + __HINRUECK[currZAT.hinRueck];
    } else {
        prop = "";
        zusatz = "";    // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert")
    }


         // Aktualisierung der Speichergroesse...
    if (showLink && (runde > 0) && (href !== "")) {
         myOptMemSize = getMemSize(myOptMem);
         if (zusatz === "") {
            zusatz = "Link";
        }
        if (prop !== "") {
            prop = '&' + prop + '=' + runde;
        }
        prop = '?' + 'erganzeigen' + '=' + 1 + '&' + 'saauswahl' + '=' + currZAT.saison +
              '&' + 'landauswahl' + '=' + 20 + '&' + 'ligaauswahl' + '=' + 1 + prop +
              '&' + 'stataktion' + '=' + "Statistik+ausgeben";
         zusatz = '<a href="' + __NAMESPACE + href + '.php' + prop + '" target="_blank">' + zusatz + '</a>';
     }
     }


     // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
     return zusatz;
    scriptDB(__DBMOD.name, getValue(optSet, { }));
 
    __LOG[2](__DBDATA);
}
}


// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
// ==================== Abschnitt fuer Statistiken des Spielplans ====================
// 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, { });


    if (initValue !== undefined) {
// Liefert eine auf 0 zurueckgesetzte Ergebnissumme
        return (__DBMODS[module] = initValue);
// - Siege
    } else {
// - Unentschieden
         return getProp(__DBMODS, module, { });
// - Niederlagen
    }
// - Tore
}
// - Gegentore
 
// - Siegpunkte
// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
function emptyStats() {
 
    return {
// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
         'S'    : 0,
        'U'    : 0,
        'N'    : 0,
        'gFor' : 0,
        'gAga' : 0,
        'P'    : 0
    };
}


// ==================== Abschnitt fuer das Benutzermenu ====================
// 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 + ')';
}


// Zeigt den Eintrag im Menu einer Option
// Summiert ein Ergebnis auf die Stats und liefert den neuen Text zurueck
// val: Derzeitiger Wert der Option
// stats: Enthaelt die summierten Stats
// menuOn: Text zum Setzen im Menu
// longStats: Formatiert die Langversion des Textes
// funOn: Funktion zum Setzen
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT (mit dem Ergebnis)
// keyOn: Hotkey zum Setzen im Menu
function addResultToStats(stats, longStats, currZAT) {
// menuOff: Text zum Ausschalten im Menu
     let ret = "";
// funOff: Funktion zum Ausschalten
 
// keyOff: Hotkey zum Ausschalten im Menu
     if (currZAT.gFor > -1) {
// return Promise von GM.registerMenuCommand()
        let p = 0;
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
     const __ON  = (val ? '*' : "");
     const __OFF = (val ? "" : '*');


    __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
        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 (val) {
         ret = getStats(stats, longStats);
         return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);
    } else {
        return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);
     }
     }
    return ret;
}
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// ==================== Abschnitt fuer Daten des Spielplans ====================
// val: Derzeitiger Wert der Option
 
// arr: Array-Liste mit den moeglichen Optionen
// Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// cell: Tabellenzelle mit dem Namen des Gegners
// fun: Funktion zum Setzen des naechsten Wertes
// return Der Name des Gegners
// key: Hotkey zum Setzen des naechsten Wertes im Menu
function getGegnerFromCell(cell) {
// return Promise von GM.registerMenuCommand()
     const __GEGNER = cell.textContent;
function registerNextMenuOption(val, arr, menu, fun, key) {
     const __POS = __GEGNER.indexOf(" (");
     const __MENU = substParam(menu, val);
     let options = "OPTION " + __MENU;


     for (let value of arr) {
     if (__POS > -1) {
         if (value === val) {
         return __GEGNER.substr(0, __POS);
            options += " / *" + value + '*';
    } else {
        } else {
        return __GEGNER;
            options += " / " + value;
        }
     }
     }
     __LOG[3](options);
}
 
// 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 GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
     return __ERGEBNIS;
}
}


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// Ermittelt die Spielart aus einer Tabellenzelle, etwa "Liga : Heim", und liefert zwei Werte zurueck
// val: Derzeitiger Wert der Option
// cell: Tabellenzelle mit Eintrag "Liga : Heim" (Spielplan) oder "Liga  Heim: " (Managerbuero)
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// return { "Liga", "Heim" } im Beispiel
// fun: Funktion zum Setzen des naechsten Wertes
function getSpielArtFromCell(cell) {
// key: Hotkey zum Setzen des naechsten Wertes im Menu
     const __TEXT = cell.textContent.replace('\xA0', "").replace(':', "").replace("  ", ' ');
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
     const __SPIELART = __TEXT.split(' ', 2);
// serial: Serialization fuer komplexe Daten
 
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
     return __SPIELART;
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
}
     const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
     const __MENU = substParam(menu, __VALUE);
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);


     __LOG[hidden ? 4 : 3](__OPTIONS);
// 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 (hidden) {
     currZAT.gegner = __GEGNER;
        return Promise.resolve(__VALUE);
    } else {
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
    }
}
}


// Zeigt den Eintrag im Menu einer Option
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle und setzt tore/gtore im Spielplanzeiger
// opt: Config und Value der Option
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
// cell: Tabellenzelle mit Eintrag "2 : 1"
function registerOption(opt) {
function setErgebnisFromCell(currZAT, cell) {
     const __CONFIG = getOptConfig(opt);
     const __ERGEBNIS = getErgebnisFromCell(cell);
    const __VALUE = getOptValue(opt);
    const __LABEL = __CONFIG.Label;
    const __ACTION = opt.Action;
    const __HOTKEY = __CONFIG.Hotkey;
    const __HIDDEN = __CONFIG.HiddenMenu;
    const __SERIAL = __CONFIG.Serial;


     if (! __CONFIG.HiddenMenu) {
     if (__ERGEBNIS.length === 2) {
         switch (__CONFIG.Type) {
         currZAT.gFor = parseInt(__ERGEBNIS[0], 10);
        case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
         currZAT.gAga = parseInt(__ERGEBNIS[1], 10);
        case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                      __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
        case __OPTTYPES.TF : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                      __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
        case __OPTTYPES.SD : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
         case __OPTTYPES.SI : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
        default :            return Promise.resolve(__VALUE);
        }
     } else {
     } else {
         // Nur Anzeige im Log...
         currZAT.gFor = -1;
         return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
         currZAT.gAga = -1;
     }
     }
}
}


// ==================== Ende Abschnitt fuer das Benutzermenu ====================
// 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);


// Initialisiert die gesetzten Option
    currZAT.gameType = __SPIELART[0];
// config: Konfiguration der Option
    currZAT.heim    = (__SPIELART.length < 2) || (__SPIELART[1] === "Heim");
// 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


    if (config.SharedData !== undefined) {
const __GAMETYPES = {   // "Blind FSS gesucht!"
         value = config.SharedData;
    "reserviert" : 0,
     }
    "Frei"      : 0,
    "spielfrei"  : 0,
    "Friendly"  : 1,
    "Liga"      : 2,
    "LP"         : 3,
    "OSEQ"      : 4,
    "OSE"        : 5,
    "OSCQ"      : 6,
     "OSC"        : 7
};


    switch (config.Type) {
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
// gameType: Name des Wettbewerbs eines Spiels
                            value = config.Choice[0];
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
                        }
function getGameTypeID(gameType) {
                        break;
    const __ID = __GAMETYPES[gameType];
    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) {
        config.HiddenMenu = true;
    }


     return value;
     return (__ID === undefined) ? -1 : __ID;
}
}


// Initialisiert die Menue-Funktion einer Option
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// optAction: Typ der Funktion
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// item: Key der Option
// gameType: Name des Wettbewerbs eines Spiels
// optSet: Platz fuer die gesetzten Optionen (und Config)
// label: Anzuklickender Text des neuen Links
// optConfig: Konfiguration der Option
// return HTML-Link auf die Preview-Seite fuer diesen Spielbericht
// return Funktion fuer die Option
function getBilanzLinkFromCell(cell, gameType, label) {
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
    const __GAMETYPEID = getGameTypeID(gameType);
     let fun;
     let ret = "";


     if (optAction !== undefined) {
     if (cell.textContent !== "Vorschau") {   // Nur falls Link nicht bereits vorhanden
         const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
         if (__GAMETYPEID > 1) {              // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert"
        const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);
            const __SEARCHFUN = ":os_bericht(";
            let paarung = cell.innerHTML.substr(cell.innerHTML.indexOf(__SEARCHFUN) + __SEARCHFUN.length);


        switch (optAction) {
            paarung = paarung.substr(0, paarung.indexOf(')'));
        case __OPTACTION.SET : fun = function() {
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
                                      return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
                                  };
            ret = ' <a href="javascript:spielpreview(' + paarung + ',' + __GAMETYPEID + ')">' + label + "</a>";
                              break;
        case __OPTACTION.NXT : fun = function() {
                                      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;
     return ret;
}
}


// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
// Addiert einen Link auf die Bilanz hinter den Spielberichts-Link
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// item: Key der Option
// gameType: Name des Wettbewerbs eines Spiels
// return ObjRef, die das Ziel definiert
// label: Anzuklickender Text des neuen Links
function getSharedRef(shared, item = undefined) {
function addBilanzLinkToCell(cell, gameType, label) {
     if (shared === undefined) {
     const __BILANZLINK = getBilanzLinkFromCell(cell, gameType, label);
        return undefined;
    }


     const __OBJREF = new ObjRef(__DBDATA);  // Gemeinsame Daten
     if (__BILANZLINK !== "") {
    const __PROPS = [ 'namespace', 'module', 'item' ];
         cell.innerHTML += __BILANZLINK;
    const __DEFAULTS = [ __DBMOD.namespace, __DBMOD.name, item ];
 
    for (let stage in __PROPS) {
        const __DEFAULT = __DEFAULTS[stage];
        const __PROP = __PROPS[stage];
        const __NAME = shared[__PROP];
 
        if (__NAME === '$') {
            break;
         }
 
        __OBJREF.chDir(getValue(__NAME, __DEFAULT));
     }
     }
    return __OBJREF;
}
}


// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
// optConfig: Konfiguration der Option
// item: Key der Option
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
function getSharedConfig(optConfig, item = undefined) {
    let config = getValue(optConfig, { });
    const __SHARED = config.Shared;


    if (__SHARED !== undefined) {
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
        const __OBJREF = getSharedRef(__SHARED, item); // Gemeinsame Daten
// saisons: Alle "option"-Eintraege der Combo-Box
function getSaisonFromComboBox(saisons) {
    let saison = 0;


         if (getValue(__SHARED.item, '$') !== '$') { // __OBJREF ist ein Item
/*
             const __REF = valueOf(__OBJREF);
    for (let entry of saisons) {
 
         if (entry.outerHTML.match(/selected/)) {
            config = { }// Neu aufbauen...
             saison = entry.textContent;
            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
    for (i = 0; i < saisons.length; i++) {
            if (! config.Name) {
         if (saisons[i].outerHTML.match(/selected/)) {
                config.Name = __OBJREF.getPath();
             saison = saisons[i].textContent;
             }
            config.setConst('SharedData', __OBJREF); // Achtung: Ggfs. zirkulaer!
         }
         }
     }
     }


     return config;
     return saison;
}
}


// Initialisiert die gesetzten Optionen
// Ermittelt aus dem Spielplan die Ligengroesse ueber die Sonderspieltage
// optConfig: Konfiguration der Optionen
// rows: Tabellenzeilen mit dem Spielplan
// optSet: Platz fuer die gesetzten Optionen
// startIdx: Index der Zeile mit dem ersten ZAT
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
// colArtIdx: Index der Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
// return Gefuelltes Objekt mit den gesetzten Optionen
// saison: Enthaelt die Nummer der laufenden Saison
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
// return 10 bei 36 Spielen, 18 bei 34 Spielen, 20 bei 38 Spielen
     let value;
function getLigaSizeFromSpielplan(rows, startIdx, colArtIdx, saison) {
    const __LIGAEXTRA = getLigaExtra(saison);
    const __TEST10ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[0] - 1].cells[colArtIdx]);
     const __TEST20ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[2] - 1].cells[colArtIdx]);


     if (optSet === undefined) {
     if (__TEST20ER[0] === "Liga") {
         optSet = { };
         return 20;
    } else if (__TEST10ER[0] === "Liga") {
        return 10;
    } else {
        return 18;
     }
     }
}
// ==================== Ende Abschnitt fuer Spielplan und ZATs ====================


    for (let opt in optConfig) {
// ==================== Abschnitt fuer sonstige Parameter ====================
        const __OPTCONFIG = optConfig[opt];
        const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);


        if ((preInit === undefined) || (__PREINIT === preInit)) {
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
// url: Adresse der Seite
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
function getPageIdFromURL(url) {
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?
    // Variablen zur Identifikation der Seite
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);
    const __SUCH = "s=";
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);
    const __INDEXS = url.lastIndexOf(__SUCH);
            const __VALUE = (__LOADED ? optSet[opt].Value : undefined);
    const __SHOWTEAM = url.match(/showteam\.php/); // Teamansicht Hauptfenster
    const __ST = url.match(/st\.php/);             // Teamansicht Popupfenster
    let page = -1;                                 // Seitenindex (Rueckgabewert)


            optSet[opt] = {
    // Wert von page (Seitenindex) ermitteln...
                'Item'      : opt,
    // Annahme: Entscheidend ist jeweils das letzte Vorkommnis von "s=" und ggf. von '&'
                'Config'   : __CONFIG,
    if (__SHOWTEAM || __ST) {
                'Loaded'    : (__ISSHARED || __LOADED),
        if (__INDEXS < 0) {
                'Promise'  : __PROMISE,
            page = 0;
                'Value'     : initOptValue(__CONFIG, __VALUE),
        } else if (url.indexOf('&', __INDEXS) < 0) {
                'SetValue'  : __CONFIG.SetValue,
            // Wert von page setzt sich aus allen Zeichen hinter "s=" zusammen
                'ReadOnly'  : (__ISSHARED || __CONFIG.ReadOnly),
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.length), 10);
                'Action'    : initOptAction(__CONFIG.Action, opt, optSet, __CONFIG),
         } else {
                'AltAction' : initOptAction(__ALTACTION, opt, optSet, __CONFIG)
            // Wert von page setzt sich aus allen Zeichen zwischen "s=" und '&' zusammen
            };
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.indexOf('&', __INDEXS)), 10);
         } else if (preInit) { // erstmal nur Stub
            optSet[opt] = {
                'Item'     : opt,
                'Config'    : __OPTCONFIG,
                'Loaded'   : false,
                'Promise'  : undefined,
                'Value'    : initOptValue(__OPTCONFIG),
                'ReadOnly'  : (__ISSHARED || __OPTCONFIG.ReadOnly)
            };
         }
         }
     }
     }


     return optSet;
     return page;
}
}


    // Abhaengigkeiten:
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
    // ================
    // initOptions (PreInit):
    // 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
// ==================== Hauptprogramm ====================
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// return Promise auf gefuelltes Objekt mit den gesetzten Optionen
async function startOptions(optConfig, optSet = undefined, classification = undefined) {
    optSet = initOptions(optConfig, optSet, true);  // PreInit


     // Memory Storage fuer vorherige Speicherung...
// Verarbeitet Ansicht "Saisonplan"
     myOptMemSize = getMemSize(myOptMem = await restoreMemoryByOpt(optSet.oldStorage));
function procSpielplan() {
    const __ROWOFFSETUPPER = 1;     // Header-Zeile
     const __ROWOFFSETLOWER = 0;


     // Zwischengespeicherte Befehle auslesen...
     const __COLUMNINDEX = {
     const __STOREDCMDS = getStoredCmds(myOptMem);
        'Art' : 1,
        'Geg' : 2,
        'Erg' : 3,
        'Ber' : 4,
        'Zus' : 5,
        'Kom' : 6
     };


     // ... ermittelte Befehle ausfuehren...
     buildOptions(__OPTCONFIG, __OPTSET, {
    const __LOADEDCMDS = await runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad
                    'Tab'        : getTable(2),
                    'Zei'        : __ROWOFFSETUPPER,
                    'Spa'        : __COLUMNINDEX.Art,
                    'menuAnchor' : getTable(0, "div"),
                    'formWidth'  : 3,
                    'formBreak'  : 4
                });


     // Bisher noch nicht geladenene Optionen laden...
     const __ZAT = firstZAT(getOptValue(__OPTSET.saison), getOptValue(__OPTSET.ligaSize));
    await loadOptions(optSet);


     // Memory Storage fuer naechste Speicherung...
     const __ROWS = getRows(2);
    myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));


     // Globale Daten ermitteln...
     let ligaStats = emptyStats();
     initScriptDB(optSet);
     let euroStats = emptyStats();


     optSet = initOptions(optConfig, optSet, false); // Rest
     for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
        const __CELLS = __ROWS[i].cells;   // Aktuelle Eintraege


    if (classification !== undefined) {
         incZAT(__ZAT);
        // Umbenennungen durchfuehren...
         await classification.renameOptions();
    }


    // ... ermittelte Befehle ausfuehren...
        setGegnerFromCell(__ZAT, __CELLS[__COLUMNINDEX.Geg]);
    await runStoredCmds(__LOADEDCMDS, optSet, false); // Rest
        setSpielArtFromCell(__ZAT, __CELLS[__COLUMNINDEX.Art]);
        setErgebnisFromCell(__ZAT, __CELLS[__COLUMNINDEX.Erg]);


    // Als globale Daten speichern...
        if (getOptValue(__OPTSET.shortKom)) {
    updateScriptDB(optSet);
            const __CELLKOM = __CELLS[__COLUMNINDEX.Kom];
            const __CELLART = __CELLS[__COLUMNINDEX.Art];


    return optSet;
            __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");
        }


// Installiert die Visualisierung und Steuerung der Optionen
        __CELLS[__COLUMNINDEX.Zus].className = __CELLS[__COLUMNINDEX.Art].className;
// optSet: Platz fuer die gesetzten Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// '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 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)) {
        if (__CELLS[__COLUMNINDEX.Zus].textContent === "") {
        buildForm(optParams.menuAnchor, optSet, optParams);
            const __CELLBER = __CELLS[__COLUMNINDEX.Ber];
    }
            let stats = "";


    return optSet;
            addBilanzLinkToCell(__CELLBER, __ZAT.gameType, "Bilanz");
}


// Setzt eine Option auf einen vorgegebenen Wert
             if (getOptValue(__OPTSET.shortKom)) {
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
                 __CELLBER.innerHTML = __CELLBER.innerHTML.replace("Klick", "(*)").replace("Bilanz", 'V').replace("Vorschau", 'V');
// opt: Config und vorheriger Value der Option
             }
// value: (Bei allen Typen) Zu setzender 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 setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
    return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));
}
 
// Ermittelt die naechste moegliche Option
// opt: Config und Value der Option
// value: Ggfs. zu setzender Wert
// return Zu setzender 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
// 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 promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __CHOICE = __CONFIG.Choice;
 
    if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {
        return setNextOpt(opt, value, reload, onFulfilled, onRejected);
    }
 
    const __VALUE = getOptValue(opt, value);
 
    try {
        const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
        let message = "";
 
        if (selValue) {
            for (let index = 0; index < __CHOICE.length; index++) {
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
            }
            message += "\nNummer oder Wert eingeben:";
        } else {
            message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
        }
 
        const __ANSWER = prompt(message, __NEXTVAL);
 
        if (__ANSWER) {
            const __INDEX = parseInt(__ANSWER, 10) - 1;
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);
 
            if (nextVal === undefined) {
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
                const __CASTVAL = this[__VALTYPE](__ANSWER);
 
                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
                    nextVal = __CASTVAL;
                }
            }
 
            if (nextVal !== __VALUE) {
                if (nextVal) {
                    return setOpt(opt, nextVal, reload, onFulfilled, onRejected);
                }
 
                const __LABEL = substParam(__CONFIG.Label, __VALUE);
 
                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
            }
        }
    } catch (ex) {
        __LOG[0]("promptNextOpt: " + ex.message);
    }
 
    return __VALUE;
}
 
// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// 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
// 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 setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
    const __OPT = getOptByName(optSet, item);
 
    return setOpt(__OPT, value, reload, onFulfilled, onRejected);
}
 
// Ermittelt die naechste moegliche Option (Version mit Key)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
    const __OPT = getOptByName(optSet, item);
 
    return getNextOpt(__OPT, value);
}
 
// Setzt die naechste moegliche Option (Version mit Key)
// 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);
}
 
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
// 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
// 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)
// optSet: Gesetzte Optionen
// return Promise auf void
async function buildMenu(optSet) {
    __LOG[3]("buildMenu()");
 
    for (let opt in optSet) {
        await registerOption(optSet[opt]).then(
                result => __LOG[6](`REGISTEROPTION[${opt}] = ${result}`),
                defaultCatch);
    }
}
 
// Invalidiert eine (ueber Menu) gesetzte Option
// opt: Zu invalidierende Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return Promise auf resultierenden Wert
function invalidateOpt(opt, force = false) {
    return Promise.resolve(opt.Promise).then(value => {
             if (opt.Loaded && ! opt.ReadOnly) {
                const __CONFIG = getOptConfig(opt);
 
                // Wert "ungeladen"...
                opt.Loaded = (force || ! __CONFIG.AutoReset);
 
                if (opt.Loaded && __CONFIG.AutoReset) {
                    // Nur zuruecksetzen, gilt als geladen...
                    setOptValue(opt, initOptValue(__CONFIG));
                }
            }
 
            return getOptValue(opt);
        }, defaultCatch);
}
 
// 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;
}
 
// Laedt eine (ueber Menu) gesetzte Option
// opt: Zu ladende Option
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Promise auf gesetzten Wert der gelandenen Option
function loadOption(opt, force = false) {
    if (! opt.Promise) {
        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) {
            const __ERROR = "Error: Oprion '" + __NAME + "' bereits geladen!";
 
            __LOG[0](__MESSAGE);
 
            return Promise.reject(__MESSAGE);
        }
 
        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));
        }
 
        opt.Promise = Promise.resolve(value).then(value => {
                // Paranoide Sicherheitsabfrage (das sollte nie passieren!)...
                if (opt.Loaded || ! opt.Promise) {
                    showAlert("Error", "Unerwarteter Widerspruch zwischen opt.Loaded und opt.Promise", safeStringify(opt));
                }
                __LOG[5]("LOAD " + __NAME + ": " + __LOG.changed(__DEFAULT, value));
 
                // Wert intern setzen...
                const __VAL = setOptValue(opt, value);
 
                // Wert als geladen markieren...
                opt.Promise = undefined;
                 opt.Loaded = true;
 
                return __VAL;
            }, defaultCatch);
    }
 
    return opt.Promise;
}
 
// Laedt die (ueber Menu) gesetzten Optionen
// 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) {
        const __OPT = optSet[opt];
 
        if (! __OPT.Loaded) {
            const __PROMISE = loadOption(__OPT, force).then(value => {
                    __LOG[5]("LOADED " + opt + " << " + value);
 
                    return Promise.resolve({
                            'name'  : opt,
                            'value' : value
                        });
                }, defaultCatch);
 
            __PROMISES.push(__PROMISE);
        }
    }
 
    return Promise.all(__PROMISES);
}
 
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// opt: Gesetzte Option
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option (auch fuer nicht 'AutoReset')
// return Promise von GM.deleteValue() (oder void)
function deleteOption(opt, force = false, reset = true) {
    const __CONFIG = getOptConfig(opt);
 
    if (force || ! __CONFIG.Permanent) {
        const __NAME = getOptName(opt);
 
        __LOG[4]("DELETE " + __NAME);
 
        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')
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Promise auf diesen Vorgang
async function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
    const __DELETEALL = ((optSelect === undefined) || (optSelect === true));
    const __OPTSELECT = getValue(optSelect, { });
 
    for (let opt in optSet) {
        if (getValue(__OPTSELECT[opt], __DELETEALL)) {
            await deleteOption(optSet[opt], force, reset);
        }
    }
 
    return Promise.resolve();
}
 
// Benennt eine Option um und laedt sie ggfs. nach
// opt: Gesetzte Option
// name: Neu zu setzender Name (Speicheradresse)
// reload: Wert nachladen statt beizubehalten
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Promise auf umbenannte Option
async function renameOption(opt, name, reload = false, force = false) {
    const __NAME = getOptName(opt);
 
    if (__NAME !== name) {
        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()
// name: Gesetzter Name (Speicheradresse)
// prefix: Prefix, das vorangestellt werden soll
// return Neu zu setzender Name (Speicheradresse)
function prefixName(name, prefix) {
    return (prefix + name);
}
 
// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
// name: Gesetzter Name (Speicheradresse)
// postfix: Postfix, das angehaengt werden soll
// return Neu zu setzender Name (Speicheradresse)
function postfixName(name, postfix) {
    return (name + postfix);
}
 
// 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
// 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) {
            __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
// optSet: Gesetzte Optionen
// reload: Seite mit "Werkseinstellungen" neu laden
// return Promise auf diesen Vorgang
async function resetOptions(optSet, reload = true) {
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
    await deleteOptions(optSet, true, false, ! reload);
 
    // ... und ggfs. Seite neu laden (mit "Werkseinstellungen")...
    refreshPage(reload);
}
 
// ==================== Abschnitt fuer diverse Utilities ====================
 
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// form: <form>...</form>
// props: Map von name:value-Paaren
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = 'hidden') {
    for (let fieldName in props) {
        let field = form[fieldName];
        if (! field) {
            field = document.createElement('input');
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
        }
        field.value = props[fieldName];
    }
 
    return form;
}
 
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// form: <form>...</form>
// props: Map von name:value-Paaren
// return Ergaenztes Form-Konstrukt
function addHiddenField(form, props) {
    return addInputField(form, props, 'hidden');
}
 
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// 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[0]("Could not add " + type + " event:");
        __LOG[2](callback);
 
        return false;
    }
}
 
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// 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 removeEvent(obj, type, callback, capture = false) {
    if (obj.removeEventListener) {
        return obj.removeEventListener(type, callback, capture);
    } else if (obj.detachEvent) {
        return obj.detachEvent('on' + type, callback);
    } else {
        __LOG[0]("Could not remove " + type + " event:");
        __LOG[2](callback);
 
        return false;
    }
}
 
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// id: ID des betroffenen Eingabeelements
// 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 addDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);
 
    return addEvent(__OBJ, type, callback, capture);
}
 
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// id: ID des betroffenen Eingabeelements
// 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 removeDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);
 
    return removeEvent(__OBJ, type, callback, capture);
}
 
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// name: Name des Elements (siehe "name=")
// index: Laufende Nummer des Elements (0-based), Default: 0
// doc: Dokument (document)
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
function getElement(name, index = 0, doc = document) {
    const __TAGS = doc.getElementsByName(name);
    const __TABLE = (__TAGS ? __TAGS[index] : undefined);
 
    return __TABLE;
}
 
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// index: Laufende Nummer des Elements (0-based)
// tag: Tag des Elements ("table")
// doc: Dokument (document)
// return Gesuchtes Element oder undefined (falls nicht gefunden)
function getTable(index, tag = 'table', doc = document) {
    const __TAGS = doc.getElementsByTagName(tag);
    const __TABLE = (__TAGS ? __TAGS[index] : undefined);
 
    return __TABLE;
}
 
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// name: Name des Tabellen-Elements (siehe "name=")
// index: Laufende Nummer des Tabellen-Elements (0-based), Default: 0
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getElementRows(name, index = 0, doc = document) {
    const __TABLE = getElement(name, index, doc);
    const __ROWS = (__TABLE ? __TABLE.rows : undefined);
 
    return __ROWS;
}
 
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// index: Laufende Nummer des Elements (0-based)
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getRows(index, doc = document) {
    const __TABLE = getTable(index, 'table', doc);
    const __ROWS = (__TABLE ? __TABLE.rows : undefined);
 
    return __ROWS;
}
 
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// id: ID des Tabellen-Elements
// 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;
}
 
// ==================== Abschnitt fuer Optionen auf der Seite ====================
 
// Liefert den Funktionsaufruf zur Option als String
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// 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) {
        const __RELOAD = "window.location.reload()";
        const __SETITEM = function(item, val, quotes = true) {
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                          };
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                          };
        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);
 
        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;
            }
        }
    }
 
    return undefined;
}
 
// Liefert die Funktionsaufruf zur Option als String
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// 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 getFormActionEvent(opt, isAlt = false, value = undefined, type = 'click', serial = undefined, memory = undefined) {
    const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
 
    return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
}
 
// Hilfsfunktion: Wendet eine Konvertierung auf jede "Zeile" innerhalb eines Textes an
// text: Urspruenglicher Text
// convFun: function(line, index, arr): Konvertiert line in "Zeile" line des Arrays arr
// separator: Zeilentrenner im Text (Default: '\n')
// thisArg: optionaler this-Parameter fuer die Konvertierung
// limit: optionale Begrenzung der Zeilen
// return String mit dem neuen Text
function eachLine(text, convFun, separator = '\n', thisArg = undefined, limit = undefined) {
    const __ARR = text.split(separator, limit);
    const __RES = __ARR.map(convFun, thisArg);
 
    return __RES.join(separator);
}
 
// Hilfsfunktion: Ergaenzt einen HTML-Code um einen Titel (ToolTip)
// 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)
// label: Config-Eintrag fuer Label oder FormLabel
// 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)) {
        return __LABEL + (isForm ? "|$" : " $");
    } else {
        return __LABEL;
    }
}
 
// Zeigt eine Option auf der Seite als Auswahlbox an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
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))) {
        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);
}
 
// Zeigt eine Option auf der Seite als Radiobutton an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
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));
}
 
// Zeigt eine Option auf der Seite als Checkbox an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionCheckbox(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    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);
    const __TITLE = substParam(getValue(__VALUE ? __CONFIG.Title : getValue(__CONFIG.AltTitle, __CONFIG.Title), '$'), __VALUELABEL);
 
    return withTitle('<input type="checkbox" name="' + __NAME +
                    '" id="' + __NAME + '" value="' + __VALUE + '"' +
                    (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
                    __NAME + '">' + __FORMLABEL + '</label>', __TITLE);
}
 
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// 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 ];
}
 
// Zeigt eine Option auf der Seite als Button an
// 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>' +
          withTitle('<input type="button" name="" + ' + __NAME +
                    '" id="' + __NAME + '" value="' + __BUTTONLABEL +
                    '"' + __ACTION + '/>', __BUTTONTITLE);
}
 
// Zeigt eine Option auf der Seite an (je nach Typ)
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionElement(opt) {
    const __CONFIG = getOptConfig(opt);
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
    let element = "";
 
    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;
        }
 
        if ((typeof element) !== 'string') {
            element = '<div>' + Array.from(element).join('<br />') + '</div>';
        }
    }
 
    return element;
}
 
// Gruppiert die Daten eines Objects nach einem Kriterium
// data: Object mit Daten
// byFun: function(val), die das Kriterium ermittelt. Default: value
// 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);
}
 
// 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 };
    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)) {
        for (let optKey of optKeys) {
            if (checkItem(optKey, __SHOWFORM, optParams.hideForm)) {
                const __ELEMENT = getOptionElement(optSet[optKey]);
                const __TDOPT = ((~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"');
 
                if (__ELEMENT) {
                    if (++count > __FORMBREAK) {
                        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;
}
 
// Fuegt das Script in die Seite ein
// 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)
// return String mit dem HTML-Code fuer das Script
function getScript(optSet, optParams = { }) {
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    //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 __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 __SCRIPT;
}
 
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// anchor: Element, das als Anker fuer die Anzeige dient
// 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
function buildForm(anchor, optSet, optParams = { }) {
    __LOG[3]("buildForm()");
 
    const __FORM = getForm(optSet, optParams);
    const __SCRIPT = getScript(optSet, optParams);
 
    addForm(anchor, __FORM, __SCRIPT);
}
 
// Informationen zu hinzugefuegten Forms
const __FORMS = { };
 
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// anchor: Element, das als Anker fuer die Anzeige dient
// form: HTML-Form des Optionsmenu (hinten angefuegt)
// 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] = {
                          'Script' : script,
                          'Form'  : form
                      };
 
    anchor.innerHTML = __REST + script + form;
}
 
// ==================== Abschnitt fuer Klasse Classification ====================
 
// Basisklasse fuer eine Klassifikation der Optionen nach Kriterium (z.B. Erst- und Zweitteam oder Fremdteam)
function Classification() {
    'use strict';
 
    this.renameFun = prefixName;
    //this.renameParamFun = undefined;
    this.optSet = undefined;
    this.optSelect = { };
}
 
Class.define(Classification, Object, {
                    'renameOptions' : function() {
                                          const __PARAM = this.renameParamFun();
 
                                          if (__PARAM !== undefined) {
                                              // Klassifizierte Optionen umbenennen...
                                              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);
                                      }
                });
 
// ==================== Ende Abschnitt fuer Klasse Classification ====================
 
// ==================== Abschnitt fuer Klasse TeamClassification ====================
 
// Klasse fuer die Klassifikation der Optionen nach Team (Erst- und Zweitteam oder Fremdteam)
function TeamClassification() {
    'use strict';
 
    Classification.call(this);
 
    this.team = undefined;
    this.teamParams = undefined;
}
 
Class.define(TeamClassification, Classification, {
                    'renameParamFun' : function() {
                                          const __MYTEAM = (this.team = getMyTeam(this.optSet, this.teamParams, this.team));
 
                                          if (__MYTEAM.LdNr) {
                                              // Prefix fuer die Optionen mit gesonderten Behandlung...
                                              return __MYTEAM.LdNr.toString() + '.' + __MYTEAM.LgNr.toString() + ':';
                                          } else {
                                              return undefined;
                                          }
                                      }
                });
 
// ==================== Ende Abschnitt fuer Klasse TeamClassification ====================
 
// ==================== Abschnitt fuer Klasse Team ====================
 
// Klasse fuer Teamdaten
 
/*class*/ function Team /*{
    constructor*/(team, land, liga, teamId) {
        'use strict';
 
        this.Team = team;
        this.Land = land;
        this.Liga = liga;
        this.TmNr = (teamId || 0);
        this.LdNr = getLandNr(land);
        this.LgNr = getLigaNr(liga);
    }
//}
 
Class.define(Team, Object, {
                    '__TEAMITEMS' : {  // Items, die in Team als Teamdaten gesetzt werden...
                                        'Team' : true,
                                        'Liga' : true,
                                        'Land' : true,
                                        'TmNr' : true,
                                        'LdNr' : true,
                                        'LgNr' : true
                                    }
                });
 
// ==================== Ende Abschnitt fuer Klasse Team ====================
 
// ==================== Abschnitt fuer Klasse Verein ====================
 
// Klasse fuer Vereinsdaten
 
/*class*/ function Verein /*extends Team {
    constructor*/(team, land, liga, teamId, manager, flags) {
        'use strict';
 
        Team.call(this, team, land, liga, teamId);
 
        this.Manager = manager;
        this.Flags = (flags || []);
    }
//}
 
Class.define(Verein, Team, {
                    '__TEAMITEMS' : {  // Items, die in Verein als Teamdaten gesetzt werden...
                                        'Team'    : true,
                                        'Liga'    : true,
                                        'Land'    : true,
                                        'TmNr'    : true,
                                        'LdNr'    : true,
                                        'LgNr'    : true,
                                        'Manager' : true,
                                        'Flags'  : true
                                    }
                });
 
// ==================== Ende Abschnitt fuer Klasse Verein ====================
 
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
 
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
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 {
        const __TEAM = ((optSet && optSet.team) ? getOptValue(optSet.team) : undefined);  // Gespeicherte Parameter
 
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
            addProps(myTeam, __TEAM, myTeam.__TEAMITEMS);
            __LOG[2]("Gespeichert: " + safeStringify(myTeam));
        } else {
            __LOG[6]("Team nicht ermittelt: " + safeStringify(__TEAM));
        }
    }
 
    return myTeam;
}
 
// Behandelt die Optionen und laedt das Benutzermenu
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// 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)
// '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 => {
                    // Werte aus der HTML-Seite ermitteln...
                    const __BOXSAISONS = document.getElementsByTagName('option');
                    const __SAISON = getSelectionFromComboBox(__BOXSAISONS, 0, 'Number');
                    const __LIGASIZE = (optParams.Tab ? getLigaSizeFromSpielplan(optParams.Tab.rows, optParams.Zei, optParams.Spa, getOptValue(optSet.saison)) : undefined);
 
                    // ... und abspeichern...
                    setOpt(optSet.saison, __SAISON, false);
                    setOpt(optSet.ligaSize, __LIGASIZE, false);
 
                    return showOptions(optSet, optParams);
                }, defaultCatch);
}
 
// ==================== Ende Abschnitt fuer Optionen ====================
 
// ==================== Abschnitt fuer Spielplan und ZATs ====================
 
// Beschreibungstexte aller Runden
const __POKALRUNDEN = [ "", "1. Runde", "2. Runde", "3. Runde", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
const __QUALIRUNDEN = [ "", "Quali 1", "Quali 2", "Quali 3" ];
const __OSCRUNDEN  = [ "", "Viertelfinale", "Halbfinale", "Finale" ];
const __OSERUNDEN  = [ "", "Runde 1", "Runde 2", "Runde 3", "Runde 4", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
const __HINRUECK    = [ " Hin", " R\xFCck", "" ];
 
// ==================== Abschnitt fuer Klasse RundenLink ====================
 
function RundenLink(saison, team) {
    'use strict';
 
    this.uri = new URI("http://os.ongapo.com/?erganzeigen=1&stataktion=Statistik+ausgeben");
    this.runde = 0;
    this.prop = "";
    this.label = "";
 
    if (saison) {
        this.setSaison(saison);
    }
    if (team) {
        this.setTeam(team);
    }
}
 
Class.define(RundenLink, Object, {
        'setSaison'    : function(saison) {
                            this.uri.setQueryPar('saauswahl', saison);
                        },
        'setTeam'      : function(team) {
                            this.uri.setQueryPar('landauswahl', team.LdNr);
                            this.uri.setQueryPar('ligaauswahl', team.LgNr);
                            this.uri.setQueryPar('hl',          team.TmNr);                        },
        'setPage'      : function(page, label) {
                            this.uri.home();
                            this.uri.down(page + ".php");
                            this.setLabel(label);
                        },
        '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);
                                }
 
                                return "<a " + URI.prototype.formatParams({
                                                                      'href'  : this.uri.getPath(),
                                                                      'target' : (target ? target : '_blank')
                                                                  }, function(value) {
                                                                        return '"' + value + '"';
                                                                    }, ' ', '=') + '>' + this.getLabel() + "</a>";
                            }
                        }
    });
 
// ==================== Ende Abschnitt fuer Klasse RundenLink ====================
 
// Liefert einen vor den ersten ZAT zurueckgesetzten Spielplanzeiger
// saison: Enthaelt die Nummer der laufenden Saison
// ligaSize: Anzahl der Teams in dieser Liga (Gegner + 1)
// - ZATs pro Abrechnungsmonat
// - Saison
// - ZAT
// - GameType
// - Heim/Auswaerts
// - Gegner
// - 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
    };
}
 
// Liefert den ZAT als String
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// longStats: Formatiert die Langversion des Textes
function getZAT(currZAT, longStats) {
    return (longStats ? currZAT.gameType + ' ' + (currZAT.heim ? "Heim" : "Ausw\xE4rts") + ' ' : "") +
          (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)
// saison: Enthaelt die Nummer der laufenden Saison
// return [ 10erHin, 10erRueck, 20erHin, 20erRueck ], ZAT-Nummern der Zusatzspieltage
function getLigaExtra(saison) {
    if (saison < 3) {
        return [ 8, 64, 32, 46 ];
    } else {
        return [ 9, 65, 33, 57 ];
    }
}
 
// Spult die Daten um anzZAT ZATs vor und schreibt Parameter
// anhand des Spielplans fort. Also Spieltag, Runde, etc.
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// anzZAT: Anzahl der ZAT, um die vorgespult wird
function incZAT(currZAT, anzZAT = 1) {
    const __LIGAEXTRA = getLigaExtra(currZAT.saison);
    const __LIGAFIRST = 3 - (__LIGAEXTRA[0] % 2);
 
    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);
 
            if (~ __POS) {
                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
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// showLink: Angabe, ob ein Link eingefuegt werden soll
// return Beschreibung des Spiels
function getZusatz(currZAT, showLink = true) {
    const __LINK = new RundenLink(currZAT.saison, __TEAMCLASS.team);
 
    if (currZAT.gameType === 'Liga') {
        if (currZAT.ZAT < 70) {
            __LINK.setRunde('stauswahl', currZAT.ligaSpieltag);
            __LINK.setPage('ls', __LINK.runde + ". Spieltag");
        } 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. ");
 
            __LINK.setRunde("", (currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck);
            __LINK.setPage(((currZAT.euroRunde < 6) ? 'oschr' : 'osczr'), __GRUPPENPHASE + "Spiel " + __LINK.runde);
        } 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 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() : "");
}
 
// ==================== Abschnitt fuer Statistiken des Spielplans ====================
 
// Liefert eine auf 0 zurueckgesetzte Ergebnissumme
// - Siege
// - 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
// 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 + ')';
}
 
// Summiert ein Ergebnis auf die Stats und liefert den neuen Text zurueck
// 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 = "";
 
    if (currZAT.gFor > -1) {
        let p = 0;
 
        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;
 
        ret = getStats(stats, longStats);
    }
 
    return ret;
}
 
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
 
const __GAMETYPENRN = {    // "Blind FSS gesucht!"
        '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 = {
        '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 = {
        'unbekannt'  :  0,
        '1. Liga'    :  1,
        '2. Liga A'  :  2,
        '2. Liga B'  :  3,
        '3. Liga A'  :  4,
        '3. Liga B'  :  5,
        '3. Liga C'  :  6,
        '3. Liga D'  :  7
    };
const __LIGATYPES = reverseMapping(__LIGANRN);
 
const __LANDNRN = {
        'unbekannt'              :  0,
        'Albanien'              :  45,
        'Andorra'                :  95,
        'Armenien'              :  83,
        'Aserbaidschan'          : 104,
        'Belgien'                :  12,
        'Bosnien-Herzegowina'    :  66,
        'Bulgarien'              :  42,
        'D\xE4nemark'            :  8,
        'Deutschland'            :  6,
        'England'                :  1,
        'Estland'                :  57,
        'Far\xF6er'              :  68,
        'Finnland'              :  40,
        'Frankreich'            :  32,
        'Georgien'              :  49,
        'Griechenland'          :  30,
        'Irland'                :  5,
        'Island'                :  29,
        'Israel'                :  23,
        'Italien'                :  10,
        'Kasachstan'            : 105,
        'Kroatien'              :  24,
        'Lettland'              :  97,
        'Liechtenstein'          :  92,
        'Litauen'                :  72,
        'Luxemburg'              :  93,
        'Malta'                  :  69,
        'Mazedonien'            :  86,
        'Moldawien'              :  87,
        'Niederlande'            :  11,
        'Nordirland'            :  4,
        'Norwegen'              :  9,
        '\xD6sterreich'          :  14,
        'Polen'                  :  25,
        '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 = {
        undefined : 'unbekannt',
        'ALB'    : 'Albanien',
        'AND'    : 'Andorra',
        'ARM'    : 'Armenien',
        'AZE'    : 'Aserbaidschan',
        'BEL'    : 'Belgien',
        'BIH'    : 'Bosnien-Herzegowina',
        'BUL'    : 'Bulgarien',
        'DEN'    : 'D\xE4nemark',
        'GER'    : 'Deutschland',
        'ENG'    : 'England',
        'EST'    : 'Estland',
        '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 ====================
 
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// 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
// id: OS2-ID des Wettbewerbs eines Spiels (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig
// 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
// gameType: Name des Wettbewerbs eines Spiels
// 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.
// 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.
// land: Name des Landes
// defValue: Default-Wert
// 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.
// land: Name der Liga
// defValue: Default-Wert
// 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 = { };
 
    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;
}
 
// Ermittelt die auszugewaehlenden Werte eines Selects (Combo-Box) als Array zurueck
// element: 'select'-Element oder dessen Name auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// 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 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) {
                                                        return this[valType](getValue(valFun(option), defValue));
                                                    }) : undefined);
}
 
// Ermittelt den ausgewaehlten Wert eines Selects (Combo-Box) und gibt diesen zurueck
// element: 'select'-Element oder dessen Name auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// 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));
}
 
// Ermittelt den ausgewaehlten Wert einer Combo-Box und gibt diesen zurueck
// comboBox: Alle 'option'-Eintraege der Combo-Box
// defValue: Default-Wert, falls nichts selektiert ist
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
// return Ausgewaehlter Wert
function getSelectionFromComboBox(comboBox, defValue = undefined, valType = 'String') {
    let selection;
 
    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));
}
 
// Liefert den Text (textContent) einer selektierten Option
// element: 'select'-Element auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// return Wert der Selektion (textContent)
function getSelectedOptionText(element) {
    const __SELECTEDOPTIONS = getValue(element, { }).selectedOptions;
    const __OPTION = getValue(__SELECTEDOPTIONS, { })[0];
 
    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 !== "") {
            if (__ZAT.gameType === "Liga") {
        cell.innerHTML += __BILANZLINK;
                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);
            }


// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
            if (getOptValue(__OPTSET.showStats)) {
 
                if (stats !== "") {
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
                    stats = ' ' + stats;
        'Tabelle'  : 1,
                }
        'Zeile'    : 0,
            } else {
        'Spalte'    : 1,
                stats = "";
        'start'    : " von ",
            }
        'middle'    : "</b><br>",
            __CELLS[__COLUMNINDEX.Zus].innerHTML = getZusatz(__ZAT, true) + stats;
        '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);
        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);


    return __TEAM;
/*
}
            for (let entry of __CELLS) {
 
                entry.style.borderBottom = __BORDERSTRING;
// 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)
            for (let j = 0; j < __CELLS.length; j++) {
// item: Query-Parameter, der die Nummer der Unterseite angibt
                __CELLS[j].style.borderBottom = __BORDERSTRING;
// 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
// rows: Tabellenzeilen mit dem Spielplan
// startIdx: Index der Zeile mit dem ersten ZAT
// colArtIdx: Index der Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
// saison: Enthaelt die Nummer der laufenden Saison
// return 10 bei 36 Spielen, 18 bei 34 Spielen, 20 bei 38 Spielen
function getLigaSizeFromSpielplan(rows, startIdx, colArtIdx, saison) {
    const __LIGAEXTRA = getLigaExtra(saison);
    const __TEST10ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[0] - 1].cells[colArtIdx]);
    const __TEST20ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[2] - 1].cells[colArtIdx]);
    if (__TEST20ER[0] === 'Liga') {
        return 20;
    } else if (__TEST10ER[0] === 'Liga') {
        return 10;
    } else {
        return 18;
     }
     }
}
}


// ==================== Ende Abschnitt fuer sonstige Parameter des Spielplans ====================
// URL-Legende:
 
// s=0: Teamuebersicht
// ==================== Ende Abschnitt fuer Spielplan und ZATs ====================
// s=1: Vertragsdaten
 
// s=2: Einzelwerte
// ==================== Hauptprogramm ====================
// s=3: Statistik Saison
 
// s=4: Statistik Gesamt
// Verarbeitet Ansicht "Saisonplan"
// s=5: Teaminfo
function procSpielplan() {
// s=6: Saisonplan
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
// s=7: Vereinshistorie
    const __ROWOFFSETLOWER = 0;
// s=8: Transferhistorie
    const __CLASSFREI = 'DMI';
// s=9: Leihhistorie
 
    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);


/*
// Verzweige in unterschiedliche Verarbeitungen je nach Wert von s:
                    for (let entry of __CELLS) {
switch (getPageIdFromURL(window.location.href)) {
                        entry.style.borderBottom = __BORDERSTRING;
    case 6: procSpielplan(); break;
                    }
*/
                    for (let j = 0; j < __CELLS.length; j++) {
                        __CELLS[j].style.borderBottom = __BORDERSTRING;
                    }
                }
            }
        });
}
}


(() => {
console.log("SCRIPT END");
    (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)