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.61'''  
|- 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.61
// @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 76: Zeile 71:
const __OPTMEM = {
const __OPTMEM = {
     'normal' : {
     'normal' : {
                  'Name'      : "Session",
                  'Value'    : sessionStorage,
                  'Display'  : "sessionStorage",
                  'Prefix'    : 'run'
              },
    'unbegrenzt' : {
                   'Name'      : "Browser",
                   'Name'      : "Browser",
                   'Value'    : localStorage,
                   'Value'    : localStorage,
                   'Display'  : "localStorage",
                   'Display'  : "localStorage",
                  'Prefix'    : 'run'
              },
    'begrenzt' : {
                  'Name'      : "Session",
                  'Value'    : sessionStorage,
                  'Display'  : "sessionStorage",
                   'Prefix'    : 'run'
                   'Prefix'    : 'run'
               },
               },
Zeile 94: Zeile 89:
               }
               }
};
};
let myOptMem = __OPTMEM.normal;


// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
Zeile 144: Zeile 141:
                   '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 152:
                   '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 164:
                   '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 171:
                   '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'    : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
                  'SelValue'  : false,
                   'Default'  : 10,
                   '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 185:
                   'Name'      : "ligaSize",
                   'Name'      : "ligaSize",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Choice'    : [ 10, 18, 20 ],
                   'Choice'    : [ 10, 18, 20 ],
Zeile 206: Zeile 199:
                   'Serial'    : true,
                   'Serial'    : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : undefined,  // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'TmNr' : 0, 'LdNr' : 0, 'LgNr' : 0 }
                   'Default'  : { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 },
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
Zeile 215: Zeile 208:
               },
               },
     'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
     'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
                  'FormPrio'  : undefined,
                   'Name'      : "reset",
                   'Name'      : "reset",
                   'Type'      : __OPTTYPES.SI,
                   'Type'      : __OPTTYPES.SI,
Zeile 224: Zeile 216:
               },
               },
     'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
     'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                   'Name'      : "storage",
                   'Name'      : "storage",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "String",
                   'Choice'    : Object.keys(__OPTMEM),
                   'Choice'    : Object.keys(__OPTMEM),
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
Zeile 235: Zeile 226:
               },
               },
     'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
     'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                   'Name'      : "oldStorage",
                   'Name'      : "oldStorage",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                  'PreInit'  : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Hidden'    : 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 237:
                   '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 248:
// ==================== 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));
     if ((minValue !== undefined) && (__VALUE < minValue)) {
};
        return defValue;
 
     }
// Ersetzt in einem String {0}, {1}, ... durch die entsprechenden Parameter
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
// arguments: Parameter, die fuer {0}, {1}, ... eingesetzt werden sollen
         return defValue;
// 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
// label: Eine Ueberschrift
// message: Der Meldungs-Text
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben
// return Liefert die Parameter zurueck
function showAlert(label, message, data = undefined) {
    __LOG[0](label + ": " + message);
 
    if (data !== undefined) {
         __LOG[2](data);
     }
     }


    alert(label + "\n\n" + message);
     return __VALUE;
 
     return arguments;
}
}


// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster
// Ermittelt den naechsten Wert aus einer Array-Liste
// mit der Meldung zu einer Exception oder einer Fehlermeldung
// arr: Array-Liste mit den moeglichen Werte
// label: Eine Ueberschrift
// value: Vorher gesetzter Wert
// ex: Exception oder sonstiges Fehlerobjekt
// return Naechster Wert in der Array-Liste
// return Liefert die showAlert()-Parameter zurueck
function getNextValue(arr, value) {
function showException(label, ex) {
     const __POS = arr.indexOf(value) + 1;
     if (ex && ex.message) {  // Exception
        showAlert(label, ex.message, ex);
    } else {  // sonstiger Fehler
        showAlert(label, ex);
    }
}


// Standard-Callback-Funktion fuer onRejected, also abgefangener Fehler
    return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
// in einer Promise bei Exceptions oder Fehler bzw. Rejections
// error: Parameter von reject() im Promise-Objekt, der von Promise.catch() erhalten wurde
// return Liefert die showAlert()-Parameter zurueck
function defaultCatch(error) {
    try {
        const __LABEL = `[${error.lineNumber}] ${__DBMOD.Name}`;
 
        if (error && error.message) {  // Exception
            return showException(__LABEL, error.message, error);
        } else {
            return showException(__LABEL, error);
        }
    } catch (ex) {
        return showException(`[${ex.lineNumber}] ${__DBMOD.Name}`, ex.message, ex);
    }
}
}


// ==================== Abschnitt fuer Klasse Class ====================
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// value: Beliebiger (strukturierter) Wert
// return String-Darstellung des Wertes
function serialize(name, value) {
    const __STREAM = (value !== undefined) ? JSON.stringify(value) : value;


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


     try {
     GM_setValue(name, __STREAM);
        const __BASE = ((baseClass !== undefined) ? baseClass : Object);
        const __BASEPROTO = (__BASE ? __BASE.prototype : undefined);
        const __BASECLASS = (__BASEPROTO ? __BASEPROTO.__class : undefined);


        this.className = (className || '?');
    return __STREAM;
        this.baseClass = __BASECLASS;
}
        Object.setConst(this, 'baseProto', __BASEPROTO, false);


        if (! initFun) {
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
            const __BASEINIT = (__BASECLASS || { }).init;
// 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);
 
    console.log(name + " << " + __STREAM);


            if (__BASEINIT) {
    if ((__STREAM !== undefined) && (__STREAM.length !== 0)) {
                initFun = function() {
        try {
                              // Basisklassen-Init aufrufen...
            return JSON.parse(__STREAM);
                              return __BASEINIT.call(this, arguments);
        } catch (ex) {
                          };
            console.error(name + ": " + ex.message);
            } 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
        try {
// value: Vorher gesetzter Wert
            const __MEMBERS = (members || { });
// reload: Seite mit neuem Wert neu laden
            const __CREATEPROTO = ((createProto === undefined) ? true : createProto);
// return Gespeicherter Wert fuer setOptValue()
function setNextStored(arr, name, value, reload = true, serial = false) {
    return setStored(name, getNextValue(arr, value), reload, serial);
}


            console.assert((typeof this) === 'function', "Not a function:", this);
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
            console.assert((typeof __MEMBERS) === 'object', "Not an object:", __MEMBERS);
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
function canUseStorage(storage = undefined) {
    const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
    const __MEMORY = __STORAGE.Value;
    let ret = false;


            const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
    if (__MEMORY !== undefined) {
            const __PROTO = (__CREATEPROTO ? Object.create(__CLASS.baseProto) : this.prototype);
        const __TESTITEM = 'canUseStorageTest';
        const __TEST = Math.random().toString();


            for (let item in __MEMBERS) {
        __MEMORY.setItem(__TESTITEM, __TEST);
                if ((item !== '__name') && (item !== '__init')) {
        ret = (__MEMORY.getItem(__TESTITEM) === __TEST);
                    Object.setConst(__PROTO, item, __MEMBERS[item]);
        __MEMORY.removeItem(__TESTITEM);
                }
    }
            }


            Object.setConst(__PROTO, '__class', __CLASS, ! __CREATEPROTO);
    console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);


            return __PROTO;
    return ret;
        } catch (ex) {
}
            showAlert('[' + ex.lineNumber + "] Error in subclassing", ex.message, ex);
        }
    }, false);


Class.define(Object, null, {
// Fuehrt die in einem Storage gespeicherte Operation aus
                    '__init'      : function() {
// optSet: Set mit den Optionen
                                        // Oberstes Basisklassen-Init...
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
                                        return true;
function runStored(optSet, storage = undefined) {
                                    },
     const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
                    'getClass'     : function() {
    const __MEMORY = __STORAGE.Value;
                                        return this.__class;
    const __RUNPREFIX = __STORAGE.Prefix;
                                    },
                    'getClassName' : function() {
                                        const __CLASS = this.getClass();


                                        return (__CLASS ? __CLASS.getName() : undefined);
    if (__MEMORY !== undefined) {
                                    },
        const __GETITEM = function(item) {
                    'setConst'    : function(item, value, config = undefined) {
                              return __MEMORY.getItem(__RUNPREFIX + item);
                                        return Object.setConst(this, item, value, config);
                          };
                                    }
        const __DELITEM = function(item) {
                }, undefined, false);
                              return __MEMORY.removeItem(__RUNPREFIX + item);
 
                          };
Class.define(Function, Object);
        const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);
 
Class.define(Class, Object, {
                    'getName'      : function() {
                                        return this.className;
                                    }
                });
 
// ==================== Ende Abschnitt fuer Klasse Class ====================


// ==================== Abschnitt fuer Klasse Delims ====================
        if (__CMD !== undefined) {
            const __KEY = __GETITEM('key');
            let value = __GETITEM('val');


// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von Pfaden
            try {
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
                value = JSON.parse(value);
// back: (Optional) Name des relativen Vaterverzeichnisses
            } catch (ex) {
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
                console.error("runStored(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
                // ... meist kann man den String selber aber speichern, daher kein "return"...
function Delims(delim, back, root, home) {
            }
    'use strict';


    if ((typeof delim) === 'object') {
            const __VAL = value;
        // Erster Parameter ist Objekt mit den Properties...
 
        if (back === undefined) {
            switch (__OPTACTION[__CMD]) {
             back = delim.back;
            case __OPTACTION.SET : console.log("SET '" + __KEY + "' " + __VAL);
                                  setStored(__KEY, __VAL, false, false);
                                  break;
            case __OPTACTION.NXT : console.log("SETNEXT '" + __KEY + "' " + __VAL);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
                                  setStored(__KEY, __VAL, false, false);
                                  break;
            case __OPTACTION.RST : console.log("RESET");
                                  resetOptions(optSet, false);
                                  break;
             default :              break;
            }
         }
         }
         if (root === undefined) {
 
            root = delim.root;
         __DELITEM('cmd');
         }
         __DELITEM('key');
        if (home === undefined) {
         __DELITEM('val');
            home = delim.home;
         }
        delim = delim.delim;
     }
     }
    this.setDelim(delim);
    this.setBack(back);
    this.setRoot(root);
    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 ====================


// ==================== Abschnitt fuer Klasse UriDelims ====================
// Gibt eine Option sicher zurueck (Version mit Key)
 
// optSet: Platz fuer die gesetzten Optionen (und Config)
// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von URIs
// item: Key der Option
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
// defOpt: Rueckgabewert, falls nicht zu finden
// back: (Optional) Name des relativen Vaterverzeichnisses
// return Daten zur Option (oder defOpt)
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
function getOptByName(optSet, item, defOpt = { }) {
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
     if ((optSet !== undefined) && (item !== undefined)) {
// scheme: (Optional) Trennzeichen fuer den Schema-/Protokollnamen vorne
         return getOpt(optSet[item], defOpt);
// host: (Optional) Prefix fuer Hostnamen hinter dem Scheme-Trenner
    } else {
// port: (Optional) Trennzeichen vor der Portangabe, falls vorhanden
         return defOpt;
// 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);
    this.setScheme(scheme);
    this.setHost(host);
    this.setPort(port);
    this.setQuery(query);
    this.setQrySep(qrySep);
    this.setQryAss(qryAss);
    this.setNode(node);
}
}


Class.define(UriDelims, Delims, {
// Gibt die Konfigurationsdaten einer Option zurueck
              'setScheme'      : function(scheme = undefined) {
// opt: Config und Value der Option
                                    this.scheme = scheme;
// defConfig: Rueckgabewert, falls Config nicht zu finden
                                },
// return Konfigurationsdaten der Option
              'setHost'        : function(host = undefined) {
function getOptConfig(opt, defConfig = { }) {
                                    this.host = host;
    return getValue(getOpt(opt).Config, defConfig);
                                },
}
              '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 ====================
// Setzt den Namen einer Option
// opt: Config und Value der Option
// name: Zu setzender Name der Option
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Name der Option
function setOptName(opt, name) {
    return (getOptConfig(opt).Name = name);
}


// Basisklasse fuer die Verwaltung eines Pfades
// Gibt den Namen einer Option zurueck
// homePath: Absoluter Startpfad als String
// opt: Config und Value der Option
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
// return Name der Option
// 'delim': Trennzeichen zwischen zwei Ebenen
function getOptName(opt) {
// 'back': Name des relativen Vaterverzeichnisses
     return getOptConfig(opt).Name;
// '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 = [];
// Setzt den Wert einer Option
    this.setDelims(delims);
// opt: Config und Value der Option
     this.homeDirs = this.getDirs(homePath, { 'home' : "" });
// name: Zu setzender Wert der Option
// return Gesetzter Wert
function setOptValue(opt, value) {
     return (opt !== undefined) ? (opt.Value = value) : undefined;
}


     this.home();
// Gibt den Wert einer Option zurueck
// opt: Config und Value der Option
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return Gesetzter Wert
function getOptValue(opt, defValue = undefined) {
     return getValue((opt !== undefined) ? opt.Value : undefined, defValue);
}
}


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
// 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;


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


     this.scheme = this.getSchemePrefix(homePath);
     GM_registerMenuCommand(__MENU, fun, key);
    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();
}
}


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 {
                                                    '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);
    console.log(__OPTIONS);
                                    },
              'getSchemePrefix'  : function(path = undefined) {
                                        const __SCHEMEDELIM = this.delims.scheme;
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


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


                                        return ((~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path);
// Zeigt den Eintrag im Menu einer Option
                                    },
// opt: Config und Value der Option
              'getNodeSuffix'    : function(path = undefined) {
function registerOption(opt) {
                                        const __NODEDELIM = this.delims.node;
    const __CONFIG = getOptConfig(opt);
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);


                                        return ((~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined);
    if (! __CONFIG.HiddenMenu) {
                                    },
        switch (__CONFIG.Type) {
              'stripNodeSuffix'  : 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,
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
                            break;
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                            break;
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
                            break;
        case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
                            break;
        default :            break;
        }
    } else {
        // Nur Anzeige im Log...
        registerDataOption(getOptValue(opt), getOptName(opt), opt.Action, __CONFIG.Hotkey, __CONFIG.HiddenMenu, __CONFIG.Serial);
    }
}


                                        return ((~ __INDEXNODE) ? path.substring(0, __INDEXNODE) : path);
// ==================== Ende Abschnitt fuer das Benutzermenu ====================
                                    },
              'getQueryString'    : function(path = undefined) {
                                        const __QUERYDELIM = this.delims.query;
                                        const __PATH = this.stripNodeSuffix(path);
                                        const __INDEXQUERY = (__PATH ? __PATH.indexOf(__QUERYDELIM) : -1);


                                        return ((~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined);
// Initialisiert die gesetzten Option
                                    },
// config: Konfiguration der Option
              'stripQueryString'  : function(path = undefined) {
// return Initialwert der gesetzten Option
                                        const __QUERYDELIM = this.delims.query;
function initOptValue(config) {
                                        const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);
    let value = config.Default; // Standard


                                        return ((~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path);
    switch (config.Type) {
                                    },
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
              'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
                            value = config.Choice[0];
                                        const __PARAMS = [];
                        }
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
    }


                                        for (let param in params) {
    if (config.Serial || config.Hidden) {
                                            __PARAMS.push(param + assign + formatFun(params[param]));
        config.HiddenMenu = true;
                                        }
    }


                                        return __PARAMS.join(delim);
    return value;
                                    },
}
              'parseParams'      : function(params, parseFun, delim = ' ', assign = '=') {
                                        const __RET = { };


                                        if (params) {
// Initialisiert die Menue-Funktion einer Option
                                            const __PARAMS = params.split(delim);
// 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;


                                            for (let index = 0; index < __PARAMS.length; index++) {
    if (optAction !== undefined) {
                                                const __PARAM = __PARAMS[index];
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);


                                                if (__PARAM) {
        switch (optAction) {
                                                    const __INDEX = __PARAM.indexOf(assign);
        case __OPTACTION.SET : fun = function() {
                                                    const __KEY = ((~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM);
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                                    const __VAL = ((~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true);
                                  };
 
                              break;
                                                    __RET[__KEY] = __VAL;
        case __OPTACTION.NXT : fun = function() {
                                                }
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                            }
                                  };
                                        }
                              break;
 
        case __OPTACTION.RST : fun = function() {
                                        return __RET;
                                      return resetOptions(optSet, __RELOAD);
                                    },
                                  };
              'rawValue'          : function(value) {
                              break;
                                        return value;
        default :              break;
                                    },
        }
              'parseValue'        : function(value) {
    }
                                        const __VALUE = Number(value);
 
                                        if (__VALUE == value) {  // schwacher Vergleich true, also Number
                                            return __VALUE;
                                        } else {
                                            const __LOWER = (value ? value.toLowerCase() : undefined);


                                            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);
    for (let opt in optConfig) {
                                    },
        const __CONFIG = optConfig[opt];
              'setQuery'          : function(query) {
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
                                        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;
        optSet[opt] = {
                                    },
            'Config'    : __CONFIG,
              'getDirs'           : function(path = undefined, delims = undefined) {
            'Value'    : initOptValue(__CONFIG),
                                        const __PATH = this.getServerPath(path);
            'SetValue'  : undefined,
            'Action'   : initOptAction(__CONFIG.Action, opt, optSet),
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
        };
    }


                                        return Path.prototype.getDirs.call(this, __PATH);
    return optSet;
                                    }
}
          });


// ==================== Ende Abschnitt fuer Klasse URI ====================
// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// return Gefuelltes Objekt mit den gesetzten Optionen
function startOptions(optConfig, optSet = undefined) {
    const __NORMAL = 'normal';


// ==================== Abschnitt fuer Klasse Directory ====================
    optSet = initOptions(optConfig, optSet);


// Basisklasse fuer eine Verzeichnisstruktur
    // Memory Storage fuer vorherige Speicherung...
// homePath: Absoluter Startpfad als String
    const __OLDOPT = optSet.oldStorage;
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
    const __OLDSTORAGE = loadOption(getOpt(__OLDOPT), true);
// 'delim': Trennzeichen zwischen zwei Ebenen
    myOptMem = __OPTMEM[getValue(__OLDSTORAGE, __NORMAL)];
// '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);
     runStored(optSet);
}
    loadOptions(optSet);


Class.define(Directory, Path, {
    // Memory Storage fuer naechste Speicherung...
                    'chDir' : function(subDir = undefined) {
    const __STORAGE = getOptValue(optSet.storage, __NORMAL);
                                  if (subDir === undefined) {
    const __NEWSTORAGE = setOpt(__OLDOPT, __STORAGE, false);
                                      this.root();
    if (! canUseStorage(myOptMem = __OPTMEM[getValue(__NEWSTORAGE, __NORMAL)])) {
                                  } else if ((typeof subDir) === 'object') {
        const __INAKTIV = 'inaktiv';
                                      for (let sub of subDir) {
                                          this.chDir(sub);
                                      }
                                  } 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 ====================
        if (__NEWSTORAGE !== __INAKTIV) {
            setOpt(__OLDOPT, __INAKTIV, false);
            myOptMem = __OPTMEM[__INAKTIV];
        }
    }


// ==================== Abschnitt fuer Klasse ObjRef ====================
    return optSet;
}


// Basisklasse fuer eine Objekt-Referenz
// Installiert die Visualisierung und Steuerung der Optionen
function ObjRef(rootObj) {
// optSet: Platz fuer die gesetzten Optionen
     'use strict';
// 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
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     if (! optParams.hideMenu) {
        buildMenu(optSet);
    }


     Directory.call(this, undefined, new Delims('/', "..", '/', '~'));
     if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEM.inaktiv)) {
        buildForm(optParams.menuAnchor, optSet, optParams);
    }
}


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


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
                                    for (let name of this.dirs) {
function getNextOpt(opt, value = undefined) {
                                        if (ret === undefined) {
    const __CONFIG = getOptConfig(opt);
                                            break;
    const __VALUE = getOptValue(opt, value);
                                        }
                                        ret = ret[name];
                                    }


                                    return ret;
    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;
    }


// ==================== Ende Abschnitt fuer Klasse ObjRef ====================
    return __VALUE;
}


// ==================== Abschnitt fuer diverse Utilities ====================
// 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
// return Gesetzter Wert
function setNextOpt(opt, value = undefined, reload = true) {
    const __CONFIG = getOptConfig(opt);


// 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);


     if ((minValue !== undefined) && (__VALUE < minValue)) {
     return setOpt(__OPT, value, reload);
        return defValue;
    }
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
        return defValue;
    }
 
    return __VALUE;
}
}


// Ermittelt den naechsten Wert aus einer Array-Liste
// Ermittelt 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;
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
     const __OPT = getOptByName(optSet, item);


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


// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// Setzt die naechste moegliche Option (Version mit Key)
// valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// opt: Config und Value der Option
// valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// optSet: Platz fuer die gesetzten Optionen (und Config)
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// item: Key der Option
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
// value: Ggfs. zu setzender Wert
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue
// reload: Seite mit neuem Wert neu laden
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {
// return Gesetzter Wert
     let product = defValue;
function setNextOptByName(optSet, item, value = undefined, reload = true) {
 
     const __OPT = getOptByName(optSet, item);
    if ((valueA !== undefined) && (valueB !== undefined)) {
        product = parseFloat(valueA) * parseFloat(valueB);
    }


    if (isNaN(product)) {
     return setNextOpt(__OPT, value, reload);
        product = defValue;
    }
 
     return parseFloat(product.toFixed(digits));
}
}


// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck
// Baut das Benutzermenu auf
// value: Eine ganze Zahl
// optSet: Gesetzte Optionen
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')
function buildMenu(optSet) {
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben
    console.log("buildMenu()");
function getOrdinal(value, defValue = '*') {
    return getValue(value, defValue, value + '.');
}


// Hilfsfunktion fuer Array.sort(): Vergleich zweier Zahlen
     for (let opt in optSet) {
// valueA: Erster Zahlenwert
         registerOption(optSet[opt]);
// valueB: Zweiter Zahlenwert
// return -1 = kleiner, 0 = gleich, +1 = groesser
function compareNumber(valueA, valueB) {
    return +(valueA > valueB) || (+(valueA === valueB) - 1);
}
 
// Ueberprueft, ob ein Objekt einer bestimmten Klasse angehoert (ggfs. per Vererbung)
// obj: Ein (generisches) Objekt
// base: Eine Objektklasse (Konstruktor-Funktion)
// return true, wenn der Prototyp rekursiv gefunden werden konnte
function instanceOf(obj, base) {
     while (obj !== null) {
         if (obj === base.prototype) {
            return true;
        }
        if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
            return (base.prototype === XML.prototype);
        }
        obj = Object.getPrototypeOf(obj);
     }
     }
    return false;
}
}


// Liefert alle Basisklassen des Objekts (inkl. Vererbung)
// Laedt eine (ueber Menu) gesetzte Option
// obj: Ein (generisches) Objekt
// opt: Zu ladende Option
// return true, wenn der Prototyp rekursiv gefunden werden konnte
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
function getPrototypes(obj) {
// return Gesetzter Wert der gelandenen Option
     let ret = [];
function loadOption(opt, force = false) {
     const __CONFIG = getOptConfig(opt);


     while (obj !== null) {
     if (! force && __CONFIG.AutoReset) {
         const __PROTO = Object.getPrototypeOf(obj);
         return setOptValue(opt, initOptValue(__CONFIG));
 
    } else if (__CONFIG.Serial) {
        ret.push(__PROTO);
         return setOptValue(opt, deserialize(getOptName(opt), getOptValue(opt)));
         if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
    } else {
            break;
         return setOptValue(opt, GM_getValue(getOptName(opt), getOptValue(opt)));
        }
         obj = __PROTO;
     }
     }
    return ret;
}
}


// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)
// Laedt die (ueber Menu) gesetzten Optionen
// obj: Ein (generisches) Objekt
// optSet: Set mit den Optionen
// return Array von Items (Property-Namen)
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
function getAllProperties(obj) {
// return Set mit den geladenen Optionen
    let ret = [];
function loadOptions(optSet, force = false) {
 
     for (let opt in optSet) {
     for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
        loadOption(optSet[opt], force);
      ret = ret.concat(Object.getOwnPropertyNames(o));
     }
     }


     return ret;
     return optSet;
}
}


// Ueberpruefung, ob ein Item aktiv ist oder nicht
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// item: Name des betroffenen Items
// opt: Gesetzte Option
// inList: Checkliste der inkludierten Items (Positivliste, true fuer aktiv)
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// exList: Checkliste der exkludierten Items (Negativliste, true fuer inaktiv)
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Angabe, ob das Item aktiv ist
function deleteOption(opt, force = false, reset = true) {
function checkItem(item, inList = undefined, exList = undefined) {
     const __CONFIG = getOptConfig(opt);
     let active = true;
 
    if (force || ! __CONFIG.Permanent) {
        GM_deleteValue(getOptName(opt));


    if (inList) {
        if (reset) {
        active = (inList[item] === true);  // gesetzt und true
            setOptValue(opt, initOptValue(__CONFIG));
    }
    if (exList) {
        if (exList[item] === true) {  // gesetzt und true
            active = false; // NICHT anzeigen
         }
         }
     }
     }
    return active;
}
}


// Fuegt Properties zu einem Objekt hinzu, die in einem zweiten stehen. Doppelte Werte werden ueberschrieben
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// data: Objekt, dem Daten hinzugefuegt werden
// optSet: Gesetzte Optionen
// addData: Objekt, das zusaetzliche Properties enthaelt
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// addList: Checkliste der zu setzenden Items (true fuer kopieren), falls angegeben
// reset: Setzt bei Erfolg auf Initialwert der Option
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
function deleteOptions(optSet, force = false, reset = true) {
// return Das gemergete Objekt mit allen Properties
     for (let opt in optSet) {
function addProps(data, addData, addList = undefined, ignList = undefined) {
         deleteOption(optSet[opt], force, reset);
     for (let item in getValue(addData, { })) {
         if (checkItem(item, addList, ignList)) {
            data[item] = addData[item];
        }
     }
     }
}


     return data;
// Entfernt eine (ueber Menu) gesetzte Option
}
// opt: Gesetzte Option
// name: Neu zu setzender Name (Speicheradresse)
// reload: Wert nachladen statt beizubehalten
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Umbenannte Option
function renameOption(opt, name, reload = false, force = false) {
    const __NAME = getOptName(opt);
 
     if (__NAME !== name) {
        deleteOption(opt, true, ! reload);
 
        setOptName(opt, name);


// Entfernt Properties in einem Objekt
        if (reload) {
// data: Objekt, deren Properties bearbeitet werden
            loadOption(opt, force);
// delList: Checkliste der zu loeschenden Items (true fuer loeschen), falls angegeben
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
// return Das veraenderte Objekt ohne die geloeschten Properties
function delProps(data, delList = undefined, ignList = undefined) {
    for (let item in getValue(data, { })) {
        if (checkItem(item, delList, ignList)) {
            delete data[item];
         }
         }
     }
     }


     return data;
     return opt;
}
}


// Gibt den Wert einer Property zurueck. Ist dieser nicht definiert oder null, wird er vorher gesetzt
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// obj: Ein Objekt. Ist dieses undefined oder null, wird defValue zurueckgeliefert
// optSet: Gesetzte Optionen
// item: Key des Properties
// reload: Seite mit "Werkseinstellungen" neu laden
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
function resetOptions(optSet, reload = true) {
// return Der Wert des Properties. Sind das obj oder das Property und defValue undefined oder null, dann undefined
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
function getProp(obj, item, defValue = undefined) {
    deleteOptions(optSet, false, ! reload);
     if ((obj === undefined) || (obj === null)) {
        return defValue;
    }


    const __PROP = obj[item];
     if (reload) {
 
        // ... und Seite neu laden (mit "Werkseinstellungen")...
     if ((__PROP !== undefined) && (__PROP !== null)) {
         window.location.reload();
         return __PROP;
     }
     }
    return (obj[item] = defValue);
}
}


// Sicheres obj.valueOf() fuer alle Daten
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// data: Objekt oder Wert
// return Bei Objekten valueOf() oder das Objekt selber, bei Werten der Wert
function valueOf(data) {
    return (((typeof data) === 'object') ? data.valueOf() : data);
}


// Sicheres JSON.stringify(), das auch mit Zyklen umgehen kann
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
// value: Auszugebene Daten. Siehe JSON.stringify()
const __OPTSET = { };
// replacer: Elementersetzer. Siehe JSON.stringify()
 
// space: Verschoenerung. Siehe JSON.stringify()
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// cycleReplacer: Ersetzer im Falle von Zyklen
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
// return String mit Ausgabe der Objektdaten
 
function safeStringify(value, replacer = undefined, space = undefined, cycleReplacer = undefined) {
// Behandelt die Optionen und laedt das Benutzermenu
     return JSON.stringify(value, serializer(replacer, cycleReplacer), space);
// 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 "ligaSize"-Option wird genutzt, hier: __MYTEAM 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 Gefuelltes Objekt mit den gesetzten Optionen
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
     const __TEAMPARAMS = optParams.teamParams;  // Ermittelte Parameter
    const __BOXSAISONS = document.getElementsByTagName("option");
 
    optSet = startOptions(optConfig, optSet);


// Hilfsfunktion fuer safeStringify(): Kapselt replacer und einen cycleReplacer fuer Zyklen
    if (__TEAMPARAMS !== undefined) {
// replacer: Elementersetzer. Siehe JSON.stringify()
        __MYTEAM.Team = __TEAMPARAMS.Team;
// cycleReplacer: Ersetzer im Falle von Zyklen
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
// return Ersetzer-Funktion fuer JSON.stringify(), die beide Ersetzer vereint
        __MYTEAM.Land = __TEAMPARAMS.Land;
function serializer(replacer = undefined, cycleReplacer = undefined) {
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
     const __STACK = [];
        __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
    const __KEYS = [];
        console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
        // ... und abspeichern...
        setOpt(optSet.team, __MYTEAM, false);
     } else {
        const __TEAM = getOptValue(optSet.team); // Gespeicherte Parameter


    if (! cycleReplacer) {
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
        cycleReplacer = function(key, value) {
            __MYTEAM.Team = __TEAM.Team;
                if (__STACK[0] === value) {
            __MYTEAM.Liga = __TEAM.Liga;
                    return "[~]";
            __MYTEAM.Land = __TEAM.Land;
                }
            __MYTEAM.LdNr = __TEAM.LdNr;
                return "[~." + __KEYS.slice(0, __STACK.indexOf(value)).join('.') + ']';
            __MYTEAM.LgNr = __TEAM.LgNr;
            };
            console.log("Gespeichert: " + JSON.stringify(__MYTEAM));
        } else {
            console.error("Unbekannt: " + JSON.stringify(__TEAM));
        }
     }
     }


     return function(key, value) {
     // Werte aus der HTML-Seite ermitteln...
            if (__STACK.length) {
    const __SAISON = getSaisonFromComboBox(__BOXSAISONS);
                const __THISPOS = __STACK.indexOf(this);
    const __LIGASIZE = getLigaSizeFromSpielplan(optParams.Tab.rows, optParams.Zei, optParams.Spa, getOptValue(optSet.saison));


                if (~ __THISPOS) {
    // ... und abspeichern...
                    __STACK.splice(__THISPOS + 1);
    setOpt(optSet.saison, __SAISON, false);
                    __KEYS.splice(__THISPOS, Infinity, key);
    setOpt(optSet.ligaSize, __LIGASIZE, false);
                } 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));
        };
}


// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
     showOptions(optSet, optParams);
// 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
// Substituiert '$'-Parameter in einem Text
// return Ergaenztes Form-Konstrukt
// text: Urspruenglicher Text mit '$'-Befehlen
function addHiddenField(form, props) {
// par1: Der (erste) uebergebene Parameter
     return addInputField(form, props, "hidden");
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function substParam(text, par1) {
     let ret = getValue(text, "");
 
    if (! textSubst) {
        textSubst  = {
                'n' : __DBMOD.name,
                'v' : __DBMOD.version,
                'V' : __DBMOD.Name
            };
    }
 
    for (let ch in textSubst) {
        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: Fuegt fuer ein Event eine Reaktion ein
// 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 addEvent(obj, type, callback, capture = false) {
        const __VORKOMMA = numberString.substring(0, numberString.lastIndexOf('.'));
     if (obj.addEventListener) {
        const __NACHKOMMA = numberString.substring(numberString.lastIndexOf('.') + 1, numberString.length);
         return obj.addEventListener(type, callback, capture);
 
    } else if (obj.attachEvent) {
         return getNumberString(__VORKOMMA) + ',' + __NACHKOMMA;
         return obj.attachEvent("on" + type, callback);
     } else {
     } else {
         // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
         console.log("Could not add " + 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: Entfernt eine Reaktion fuer ein Event
// string: Eine Zeichenkette
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// 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 removeEvent(obj, type, callback, capture = false) {
         result += string.substr(i, 1);
     if (obj.removeEventListener) {
     }
        return obj.removeEventListener(type, callback, capture);
     } else if (obj.detachEvent) {
         return obj.detachEvent("on" + type, callback);
     } else {
        console.log("Could not remove " + type + " event:");
        console.log(callback);


    return result;
        return false;
    }
}
}


// Speichert einen String/Integer/Boolean-Wert unter einem Namen ab
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// 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 addDocEvent(id, type, callback, capture = false) {
     const __OBJ = document.getElementById(id);


     return GM.setValue(name, value).then(voidValue => {
     return addEvent(__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 alle Browser: Entfernt eine Reaktion fuer ein Event
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
// id: ID des betroffenen Eingabeelements
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// type: Name des Events, z.B. "click"
// return Promise fuer den String/Integer/Boolean-Wert, der unter dem Namen gespeichert war
// callback: Funktion als Reaktion
function summonValue(name, defValue = undefined) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     return GM.getValue(name, defValue).then(value => {
// return false bei Misserfolg
            __LOG[4](name + " << " + value);
function removeDocEvent(id, type, callback, capture = false) {
     const __OBJ = document.getElementById(id);


            return Promise.resolve(value);
    return removeEvent(__OBJ, type, callback, capture);
        }, ex => {
}
            __LOG[0](name + ": " + ex.message);


            return Promise.reject(ex);
// Helferfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
        }, defaultCatch);
// item: Name des betroffenen Items
}
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
// return Angabe, ob das Item sichtbar sein soll
function checkVisible(item, showList, hideList = undefined) {
    let show = true;


// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
    if (showList !== undefined) {
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
        show = (showList[item] === true)// gesetzt und true
// value: Beliebiger (strukturierter) Wert
    }
// return Promise auf ein Objekt, das 'name' und 'value' in der String-Darstellung des Wertes enthaelt
     if (hideList !== undefined) {
function serialize(name, value) {
        if (hideList[item] === true) {  // gesetzt und true
     const __STREAM = ((value !== undefined) ? safeStringify(value) : value);
            show = false; // NICHT anzeigen
        }
    }


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


// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
// Helferfunktion fuer die Ermittlung eines Elements der Seite (Default: 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
// tag: Tag des Elements ("table")
// return Promise fuer das Objekt, das unter dem Namen gespeichert war
// doc: Dokument (document)
function deserialize(name, defValue = undefined) {
function getTable(index, tag = "table", doc = document) {
     return summonValue(name).then(stream => {
     const __TAGS = document.getElementsByTagName(tag);
            if (stream && stream.length) {
    const __TABLE = __TAGS[index];
                return JSON.parse(stream);
            } else {
                return defValue;
            }
        });
}


// Setzt die Seite gemaess der Aenderungen zurueck...
     return __TABLE;
// reload: Seite wird ganz neu geladen
function refreshPage(reload = true) {
     if (reload) {
        __LOG[2]("Seite wird neu geladen...");
        window.location.reload();
    }
}
}


// Setzt eine Option dauerhaft und laedt die Seite neu
// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
// name: Name der Option als Speicherort
// index: Laufende Nummer des Elements (0-based)
// value: Zu setzender Wert
// doc: Dokument (document)
// reload: Seite mit neuem Wert neu laden
function getRows(index, doc = document) {
// serial: Serialization fuer komplexe Daten
     const __TABLE = getTable(index, "table", doc);
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
    const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gespeicherter Wert fuer setOptValue()
function setStored(name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
     (serial ? serialize(name, value)
            : storeValue(name, value))
                .then(onFulfilled, onRejected)
                .then(() => refreshPage(reload), defaultCatch); // Ende der Kette...


     return value;
     return __ROWS;
}
}


// Setzt den naechsten Wert aus einer Array-Liste als Option
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// arr: Array-Liste mit den moeglichen Optionen
// name: Name der Option als Speicherort
// value: Vorher gesetzter Wert
// reload: Seite mit neuem Wert neu laden
// serial: Serialization fuer komplexe Daten
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gespeicherter Wert fuer setOptValue()
function setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
    return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// Liefert den Funktionsaufruf zur Option als String
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// opt: Auszufuehrende Option
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
// isAlt: Angabe, ob AltAction statt Action gemeint ist
function getStoredCmds(memory = undefined) {
// value: Ggfs. zu setzender Wert
     const __STORAGE = getMemory(memory);
// serial: Serialization fuer String-Werte (Select, Textarea)
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, storage = undefined) {
     const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
     const __MEMORY = __STORAGE.Value;
     const __MEMORY = __STORAGE.Value;
    const __MEMSTR = __STORAGE.Display;
     const __RUNPREFIX = __STORAGE.Prefix;
     const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];


     if (__MEMORY !== undefined) {
     if (__MEMORY !== undefined) {
         const __GETITEM = function(item) {
         const __RELOAD = "window.location.reload()";
                               return __MEMORY.getItem(__RUNPREFIX + item);
        const __SETITEM = function(item, val, quotes = true) {
                               return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                           };
                           };
         const __DELITEM = function(item) {
         const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                               return __MEMORY.removeItem(__RUNPREFIX + item);
                               return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                           };
                           };
         const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);
         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) ? JSON.stringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);


         if (__CMD !== undefined) {
         if (__ACTION !== undefined) {
             const __KEY = __GETITEM('key');
             switch (__ACTION) {
             let value = __GETITEM('val');
             case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
 
                                  return __SETITEMS('SET', getOptName(opt), __VALSTR);
            try {
             case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                value = JSON.parse(value);
                                  return __SETITEMS('NXT', getOptName(opt), __VALSTR);
             } catch (ex) {
            case __OPTACTION.RST : //return "doActionRst()";
                __LOG[0]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
                                  return __SETITEMS('RST');
                // ... meist kann man den String selber aber speichern, daher kein "return"...
            default :              break;
             }
             }
            __STOREDCMDS.push({
                                'cmd' : __CMD,
                                'key' : __KEY,
                                'val' : value
                            });
         }
         }
        __DELITEM('cmd');
        __DELITEM('key');
        __DELITEM('val');
     }
     }


     return (__STOREDCMDS.length ? __STOREDCMDS : undefined);
     return undefined;
}
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// Liefert die Funktionsaufruf zur Option als String
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// opt: Auszufuehrende Option
// optSet: Object mit den Optionen
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
// value: Ggfs. zu setzender Wert
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// serial: Serialization fuer String-Werte (Select, Textarea)
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {
// return String mit dem (reinen) Funktionsaufruf
     const __BEFORELOAD = getValue(beforeLoad, true);
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, storage = undefined) {
    const __STOREDCMDS = getValue(storedCmds, []);
     const __ACTION = getFormAction(opt, isAlt, value, serial, storage);
    const __LOADEDCMDS = [];
    let invalidated = false;


     while (__STOREDCMDS.length) {
     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
        const __STORED = __STOREDCMDS.shift();
        const __CMD = __STORED.cmd;
        const __KEY = __STORED.key;
        const __VAL = __STORED.val;
 
        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);
}
}


// Gibt eine Option sicher zurueck
// Zeigt eine Option auf der Seite als Auswahlbox 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 getOptionSelect(opt) {
function getOpt(opt, defOpt = { }) {
    const __CONFIG = getOptConfig(opt);
     return getValue(opt, defOpt);
    const __NAME = getOptName(opt);
}
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


// Gibt eine Option sicher zurueck (Version mit Key)
    for (let value of __CONFIG.Choice) {
// optSet: Platz fuer die gesetzten Optionen (und Config)
        element += '\n<option value="' + value + '"' +
// item: Key der Option
                  ((value === __VALUE) ? ' SELECTED' : "") +
// defOpt: Rueckgabewert, falls nicht zu finden
                  '>' + value + '</option>';
// return Daten zur Option (oder defOpt)
function getOptByName(optSet, item, defOpt = { }) {
    if ((optSet !== undefined) && (item !== undefined)) {
        return getOpt(optSet[item], defOpt);
    } else {
        return defOpt;
     }
     }
    element += '\n</select>';
    return __LABEL.replace('$', element);
}
}


// Gibt die Konfigurationsdaten einer Option zurueck
// Zeigt eine Option auf der Seite als Radiobutton an
// opt: Config und Value der Option
// opt: Anzuzeigende Option
// defConfig: Rueckgabewert, falls Config nicht zu finden
// return String mit dem HTML-Code
// return Konfigurationsdaten der Option
function getOptionRadio(opt) {
function getOptConfig(opt, defConfig = { }) {
    return getValue(getOpt(opt).Config, defConfig);
}
 
// Setzt den Namen einer Option
// opt: Config und Value der Option
// name: Zu setzender Name der Option
// reload: Seite mit neuem Wert neu laden
// 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, false, true, "click", false);
    const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'ON" value="1"' +
                        (__VALUE ? ' CHECKED' : __ACTION) +
                        ' /><label for="' + __NAME + 'ON">' +
                        __CONFIG.Label + '</label>';
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';


     if (__NAME !== name) {
     return [ __ELEMENTON, __ELEMENTOFF ];
        __LOG[4]("RENAME " + __NAME + " => " + name);
}


        __CONFIG.Name = name;
// 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 __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


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


// Gibt den Namen einer Option zurueck
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// opt: Config und Value der Option
// opt: Anzuzeigende Option
// return Name der Option
// return String mit dem HTML-Code
function getOptName(opt) {
function getOptionTextarea(opt) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);
     const __NAME = __CONFIG.Name;
     const __NAME = getOptName(opt);
 
    const __VALUE = getOptValue(opt);
     if (! __NAME) {
     const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
        const __SHARED = __CONFIG.Shared;
    const __SUBMIT = getValue(__CONFIG.Submit, "");
 
    const __ONSUBMIT = ((__SUBMIT.length > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
        if (__SHARED && ! opt.Loaded) {
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
            const __OBJREF = getSharedRef(__SHARED, opt.Item);
    const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
 
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
            return __OBJREF.getPath();
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
        }
                          JSON.stringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


        showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
    }
 
     return __NAME;
}
}


// Setzt den Wert einer Option
// Zeigt eine Option auf der Seite als Button an
// opt: Config und Value der Option
// opt: Anzuzeigende Option
// name: Zu setzender Wert der Option
// return String mit dem HTML-Code
// return Gesetzter Wert
function getOptionButton(opt) {
function setOptValue(opt, value) {
     const __CONFIG = getOptConfig(opt);
     if (opt !== undefined) {
    const __NAME = getOptName(opt);
        if (! opt.ReadOnly) {
    const __VALUE = getOptValue(opt, false);
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
    const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


            opt.Value = value;
    return '<label for="' + __NAME + '">' + __FORMLABEL +
        }
          '</label><input type="button" name="' + __NAME +
        return opt.Value;
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
    } else {
          __ACTION + '/>';
        return undefined;
    }
}
}


// Gibt den Wert einer Option zurueck
// Zeigt eine Option auf der Seite an (je nach Typ)
// opt: Config und Value der Option
// opt: Anzuzeigende Option
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return String mit dem HTML-Code
// load: Laedt die Option per loadOption(), falls noetig
function getOptionElement(opt) {
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
    const __CONFIG = getOptConfig(opt);
// return Gesetzter Wert
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {
     let element = "";
     let value;


     if (opt !== undefined) {
     if (! __CONFIG.Hidden) {
         if (load && ! opt.Loaded) {
        switch (__TYPE) {
            if (! opt.Promise) {
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                loadOption(opt, force);
                            break;
            }
         case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
            if (! asyncLoad) {
                                element = getOptionCheckbox(opt);
                __LOG[0]("Warnung: getOptValue(" + getOptName(opt) + ") fordert zum Nachladen auf, daher nur Default-Wert!");
                            } else {
            }
                                element = getOptionRadio(opt);
         } else {
                            }
             value = opt.Value;
                            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 (element.length === 2) {
             element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
         }
         }
     }
     }


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


// ==================== Ende Abschnitt fuer diverse Utilities ====================
// Baut das Benutzermenu auf der Seite auf
 
// optSet: Gesetzte Optionen
// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
// optParams: Eventuell notwendige Parameter
 
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// Namen des Default-, Temporaer- und Null-Memories...
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
const __MEMNORMAL  = 'normal';
// 'formWidth': Anzahl der Elemente pro Zeile
const __MEMSESSION  = 'begrenzt';
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
const __MEMINAKTIVE = 'inaktiv';
// return String mit dem HTML-Code
 
function getForm(optSet, optParams = { }) {
// Definition des Default-, Dauer- und Null-Memories...
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
    const __FORMEND = '</tr></tbody></table></form>';
const __OPTMEMSESSION  = __OPTMEM[__MEMSESSION];
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
 
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
// Medium fuer die Datenbank (Speicher)
    let form = __FORM;
let myOptMem = __OPTMEMNORMAL;
    let count = 0;   // Bisher angezeigte Optionen
let myOptMemSize;
    let column = 0; // Spalte der letzten Option (1-basierend)


// Infos ueber dieses Script-Modul
    for (let opt in optSet) {
const __DBMOD = new ScriptModule();
        if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
            const __ELEMENT = getOptionElement(optSet[opt]);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";


// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
            if (__ELEMENT.length > 0) {
const __DBTOC = { };
                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;


// Daten zu den Modulen (indiziert durch die Script-Namen)
     return form;
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
// Fuegt das Script in die Seite ein
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// optSet: Gesetzte Optionen
// return true, wenn der Speichertest erfolgreich war
// optParams: Eventuell notwendige Parameter
function canUseMemory(memory = undefined) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     const __STORAGE = getMemory(memory, { });
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __MEMORY = __STORAGE.Value;
// return String mit dem HTML-Code fuer das Script
     let ret = false;
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 = "";


     if (__MEMORY !== undefined) {
     //window.eval('function activateMenu() { console.log("TADAAA!"); }');
        const __TESTPREFIX = 'canUseStorageTest';
 
        const __TESTDATA = Math.random().toString();
    return __SCRIPT;
        const __TESTITEM = __TESTPREFIX + __TESTDATA;
}


        __MEMORY.setItem(__TESTITEM, __TESTDATA);
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
// anchor: Element, das als Anker fuer die Anzeige dient
        __MEMORY.removeItem(__TESTITEM);
// 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()");


     __LOG[2]("canUseStorage(" + __STORAGE.Name + ") = " + ret);
     const __FORM = getForm(optSet, optParams);
    const __SCRIPT = getScript(optSet, optParams);


     return ret;
     addForm(anchor, __FORM, __SCRIPT);
}
}


// Ermittelt die Groesse des benutzten Speichers
// Informationen zu hinzugefuegten Forms
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
const __FORMS = { };
// return Groesse des genutzten Speichers in Bytes
function getMemSize(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;


    //getMemUsage(__MEMORY);
// 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);


     if (__MEMORY !== undefined) {
     __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        const __SIZE = safeStringify(__MEMORY).length;


        __LOG[2]("MEM: " + __SIZE + " bytes");
    anchor.innerHTML = __REST + script + form;
        return __SIZE;
    } else {
        return 0;
    }
}
}


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


    if ((typeof value) === 'string') {
// ==================== Abschnitt fuer Spielplan und ZATs ====================
        const __SIZE = value.length;


        __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.slice(0, 255));
// Beschreibungstexte aller Runden
    } else if ((typeof value) === 'object') {
const __POKALRUNDEN = [ "", "1. Runde", "2. Runde", "3. Runde", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
        if (depth === 0) {
const __QUALIRUNDEN = [ "", "Quali 1", "Quali 2", "Quali 3" ];
            const __SIZE = safeStringify(value).length;
const __OSCRUNDEN  = [ "", "Viertelfinale", "Halbfinale", "Finale" ];
const __OSERUNDEN  = [ "", "Runde 1", "Runde 2", "Runde 3", "Runde 4", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ];
const __HINRUECK    = [ " Hin", " R\xFCck", "" ];


            __OUT("USAGE: " + name + '\t' + __SIZE);
// Liefert einen vor den ersten ZAT zurueckgesetzten Spielplanzeiger
        } else {
// saison: Enthaelt die Nummer der laufenden Saison
            depth--;
// ligaSize: Anzahl der Teams in dieser Liga (Gegner + 1)
            for (let sub in value) {
// - ZATs pro Abrechnungsmonat
                getMemUsage(value[sub], __OUT, depth, name + '.' + sub);
// - Saison
            }
// - ZAT
            getMemUsage(value, __OUT, 0, name);
// - GameType
        }
// - Heim/Auswaerts
    } else {
// - Gegner
      const __DATA = (((typeof value) === 'function') ? "" : '\t' + value);
// - Tore
 
// - Gegentore
        __OUT("USAGE: " + name + '\t' + (typeof value) + __DATA);
// - Ligengroesse
    }
// - Ligaspieltag
}
// - Pokalrunde
 
// - Eurorunde
// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// - Hin/Rueck
// opt: Option zur Wahl des Speichers
// - ZAT Rueck
// return Promise auf gesuchten Speicher oder Null-Speicher ('inaktiv')
// - ZAT Korr
async function restoreMemoryByOpt(opt) {
function firstZAT(saison, ligaSize) {
    // Memory Storage fuer vorherige Speicherung...
     return {
    const __STORAGE = await getOptValue(opt, __MEMNORMAL, true, true, true);
        'anzZATpMonth' : ((saison < 2) ? 7 : 6),    // Erste Saison 7 ZAT, danach 6 ZAT...
 
        'saison'      : saison,
    return __OPTMEM[__STORAGE];
        'ZAT'          : 0,
}
        'gameType'     : "spielfrei",
 
         'heim'        : true,
// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
        'gegner'      : "",
// opt: Option zur Wahl des Speichers
        'gFor'        : 0,
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
         'gAga'        : 0,
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
        'ligaSize'     : ligaSize,
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
        'ligaSpieltag' : 0,
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
        'pokalRunde'  : 1,
function startMemoryByOpt(opt, saveOpt = undefined, onFulfilled = undefined, onRejected = undefined) {
        'euroRunde'    : 0,
     // Memory Storage fuer naechste Speicherung...
         'hinRueck'    : 2,   // 0: Hin, 1: Rueck, 2: unbekannt
    let storage = getOptValue(opt, __MEMNORMAL);
        'ZATrueck'    : 0,
    let optMem = __OPTMEM[storage];
        'ZATkorr'      : 0
 
    };
     if (! canUseMemory(optMem)) {
}
         if (storage !== __MEMINAKTIVE) {
            storage = __MEMINAKTIVE;
            optMem = __OPTMEM[storage];
         }
     }
 
    if (saveOpt !== undefined) {
         setOpt(saveOpt, storage, false, onFulfilled, onRejected);
    }


     return optMem;
// 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;
}
}


// ==================== Ende Abschnitt fuer Speicher ====================
// Liefert die ZATs der Sonderspieltage fuer 10er- (2) und 20er-Ligen (4)
 
// saison: Enthaelt die Nummer der laufenden Saison
// ==================== Abschnitt fuer die Scriptdatenbank ====================
// return [ 10erHin, 10erRueck, 20erHin, 20erRueck ], ZAT-Nummern der Zusatzspieltage
 
function getLigaExtra(saison) {
// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
     if (saison < 3) {
// meta: Metadaten des Scripts (Default: GM.info.script)
        return [ 8, 64, 32, 46 ];
// return Beschreibende Daten fuer __DBMOD
     } else {
function ScriptModule(meta) {
        return [ 9, 65, 33, 57 ];
     'use strict';
     }
 
    const __META = getValue(meta, GM.info.script);
    const __PROPS = {
                '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);
// 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);


// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
    for (let i = anzZAT; i > 0; i--) {
// optSet: Gesetzte Optionen (und Config)
        currZAT.ZAT++;
function initScriptDB(optSet) {
        if ((currZAT.ZAT - __LIGAFIRST + 1) % 2 === 1) {
    // Speicher fuer die DB-Daten...
            currZAT.ligaSpieltag++;
    const __DBMEM = myOptMem.Value;
        } else {
 
            const __POS = __LIGAEXTRA.indexOf(currZAT.ZAT);
    __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) {
            if (__POS > -1) {
         // ... und die Daten der Fremdscripte laden...
                if (__POS < 2 * (currZAT.ligaSize % 9)) {
         for (let module in __DBTOC.versions) {
                    currZAT.ligaSpieltag++;
             scriptDB(module, getValue(JSON.parse(__DBMEM.getItem('__DBDATA.' + module)), { }));
                }
            }
        }
        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
                }
            }
         }
         }
     }
     }
}
}


// 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 = "";
    // Speicher fuer die DB-Daten...
     let href = "";
    const __DBMEM = myOptMem.Value;
     let prop = "stauswahl";
 
     let runde = 0;
    if (__DBMEM !== undefined) {
        // Permanente Speicherung der Eintraege...
        __DBMEM.setItem('__DBTOC.versions', safeStringify(__DBTOC.versions));
        __DBMEM.setItem('__DBTOC.namespaces', safeStringify(__DBTOC.namespaces));
        __DBMEM.setItem('__DBDATA.' + __DBMOD.name, safeStringify(optSet));
 
        // Aktualisierung der Speichergroesse...
        myOptMemSize = getMemSize(myOptMem);
    }
 
    // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
    scriptDB(__DBMOD.name, getValue(optSet, { }));
 
    __LOG[2](__DBDATA);
}
 
// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
// 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) {
        return (__DBMODS[module] = initValue);
     } else {
        return getProp(__DBMODS, module, { });
     }
}
 
// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================


// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
    if (currZAT.gameType === "Liga") {
        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. ");


// ==================== Abschnitt fuer das Benutzermenu ====================
            href = ((currZAT.euroRunde < 6) ? "oschr" : "osczr");
 
            runde = ((currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck);
// Zeigt den Eintrag im Menu einer Option
            zusatz = __GRUPPENPHASE + "Spiel " + runde;
// val: Derzeitiger Wert der Option
        } else {
// menuOn: Text zum Setzen im Menu
            href = "oscfr";
// funOn: Funktion zum Setzen
            runde = currZAT.euroRunde - 8;
// keyOn: Hotkey zum Setzen im Menu
            zusatz = __OSCRUNDEN[runde] + __HINRUECK[currZAT.hinRueck];
// menuOff: Text zum Ausschalten im Menu
        }
// funOff: Funktion zum Ausschalten
        prop = "";
// keyOff: Hotkey zum Ausschalten im Menu
     } else if (currZAT.gameType === "OSE") {
// return Promise von GM.registerMenuCommand()
         href = "ose";
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
        prop = "runde";
    const __ON  = (val ? '*' : "");
        runde = currZAT.euroRunde - 3;
    const __OFF = (val ? "" : '*');
        zusatz = __OSERUNDEN[runde] + __HINRUECK[currZAT.hinRueck];
 
    __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
 
     if (val) {
         return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);
     } else {
     } else {
         return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);
         prop = "";
        zusatz = "";    // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert")
     }
     }
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
    if (showLink && (runde > 0) && (href !== "")) {
// val: Derzeitiger Wert der Option
         if (zusatz === "") {
// arr: Array-Liste mit den moeglichen Optionen
             zusatz = "Link";
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
         }
// fun: Funktion zum Setzen des naechsten Wertes
        if (prop !== "") {
// key: Hotkey zum Setzen des naechsten Wertes im Menu
             prop = '&' + prop + '=' + runde;
// return Promise von GM.registerMenuCommand()
function registerNextMenuOption(val, arr, menu, fun, key) {
    const __MENU = substParam(menu, val);
    let options = "OPTION " + __MENU;
 
    for (let value of arr) {
         if (value === val) {
             options += " / *" + value + '*';
         } else {
             options += " / " + value;
         }
         }
        prop = '?' + 'erganzeigen' + '=' + 1 + '&' + 'saauswahl' + '=' + currZAT.saison +
              '&' + 'landauswahl' + '=' + __MYTEAM.LdNr + '&' + 'ligaauswahl' + '=' + __MYTEAM.LgNr + prop +
              '&' + 'stataktion' + '=' + "Statistik+ausgeben";
        zusatz = '<a href="' + __NAMESPACE + href + '.php' + prop + '" target="_blank">' + zusatz + '</a>';
     }
     }
    __LOG[3](options);


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


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// ==================== Abschnitt fuer Statistiken des Spielplans ====================
// val: Derzeitiger Wert der Option
 
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// Liefert eine auf 0 zurueckgesetzte Ergebnissumme
// fun: Funktion zum Setzen des naechsten Wertes
// - Siege
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// - Unentschieden
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
// - Niederlagen
// serial: Serialization fuer komplexe Daten
// - Tore
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
// - Gegentore
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
// - Siegpunkte
     const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
function emptyStats() {
    const __MENU = substParam(menu, __VALUE);
     return {
    const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
        'S'    : 0,
                      getValue(__VALUE, "", " = " + __VALUE);
        'U'    : 0,
 
        'N'    : 0,
    __LOG[hidden ? 4 : 3](__OPTIONS);
        'gFor' : 0,
 
        'gAga' : 0,
    if (hidden) {
         'P'    : 0
         return Promise.resolve(__VALUE);
     };
     } else {
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
    }
}
}


// Zeigt den Eintrag im Menu einer Option
// Liefert die Stats als String
// opt: Config und Value der Option
// stats: Enthaelt die summierten Stats
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
// longStats: Formatiert die Langversion des Textes
function registerOption(opt) {
function getStats(stats, longStats) {
     const __CONFIG = getOptConfig(opt);
     return (longStats ? '[' + stats.S + ' ' + stats.U + ' ' + stats.N + "] " : "/ ") +
    const __VALUE = getOptValue(opt);
          stats.gFor + ':' + stats.gAga + ' ' + ((stats.gFor < stats.gAga) ? "" : (stats.gFor > stats.gAga) ? '+' : "") +
    const __LABEL = __CONFIG.Label;
           (stats.gFor - stats.gAga) + " (" + stats.P + ')';
    const __ACTION = opt.Action;
    const __HOTKEY = __CONFIG.Hotkey;
    const __HIDDEN = __CONFIG.HiddenMenu;
    const __SERIAL = __CONFIG.Serial;
 
    if (! __CONFIG.HiddenMenu) {
        switch (__CONFIG.Type) {
        case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
        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 {
        // Nur Anzeige im Log...
        return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
    }
}
}


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


// Initialisiert die gesetzten Option
    if (currZAT.gFor > -1) {
// config: Konfiguration der Option
        let p = 0;
// 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) {
        if (currZAT.gFor > currZAT.gAga) {
         value = config.SharedData;
            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;


    switch (config.Type) {
        ret = getStats(stats, longStats);
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
                            value = config.Choice[0];
                        }
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
     }
     }


     if (config.Serial || config.Hidden) {
     return ret;
        config.HiddenMenu = true;
}
    }


    return value;
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
}


// Initialisiert die Menue-Funktion einer Option
const __GAMETYPES = {    // "Blind FSS gesucht!"
// optAction: Typ der Funktion
        'unbekannt'  : -1,
// item: Key der Option
        "reserviert" :  0,
// optSet: Platz fuer die gesetzten Optionen (und Config)
        "Frei"      : 0,
// optConfig: Konfiguration der Option
        "spielfrei"  : 0,
// return Funktion fuer die Option
        "Friendly"  : 1,
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
        "Liga"      : 2,
     let fun;
        "LP"        :  3,
        "OSEQ"      :  4,
        "OSE"        :  5,
        "OSCQ"      :  6,
        "OSC"        :  7
     };


    if (optAction !== undefined) {
const __LIGANRN = {
         const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
         'unbekannt'  :  0,
         const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);
        '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
    };


        switch (optAction) {
const __LANDNRN = {
         case __OPTACTION.SET : fun = function() {
         'unbekannt'              :   0,
                                      return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);
        'Albanien'              :  45,
                                  };
        'Andorra'                :  95,
                              break;
        'Armenien'              :  83,
         case __OPTACTION.NXT : fun = function() {
         'Aserbaidschan'          : 104,
                                      return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
        'Belgien'                :  12,
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice).catch(defaultCatch);
         'Bosnien-Herzegowina'    : 66,
                                  };
        'Bulgarien'              :  42,
                              break;
         'Dänemark'              :   8,
         case __OPTACTION.RST : fun = function() {
         'Deutschland'           :  6,
                                      return resetOptions(optSet, __RELOAD).then(
        'England'               :  1,
                                              result => __LOG[3]("RESETTING (" + result + ")..."),
        'Estland'               :  57,
                                              defaultCatch);
        'Faröer'                 : 68,
                                  };
        'Finnland'              :  40,
                              break;
         'Frankreich'            : 32,
         default :             break;
        'Georgien'               :  49,
         }
        'Griechenland'           :  30,
    }
        'Irland'                 :  5,
 
         'Island'                :  29,
    return fun;
         'Israel'                :  23,
}
         'Italien'                :  10,
 
         'Kasachstan'            : 105,
// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
         'Kroatien'              :  24,
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
        'Lettland'              :  97,
// item: Key der Option
         'Liechtenstein'          :  92,
// return ObjRef, die das Ziel definiert
        'Litauen'                :  72,
function getSharedRef(shared, item = undefined) {
        'Luxemburg'              :  93,
    if (shared === undefined) {
        'Malta'                  :  69,
         return undefined;
        'Mazedonien'            :  86,
    }
        'Moldawien'              :  87,
 
        'Niederlande'           :  11,
    const __OBJREF = new ObjRef(__DBDATA); // Gemeinsame Daten
        'Nordirland'            :   4,
    const __PROPS = [ 'namespace', 'module', 'item' ];
        'Norwegen'              :   9,
    const __DEFAULTS = [ __DBMOD.namespace, __DBMOD.name, item ];
        'Österreich'             :  14,
 
        'Polen'                  :  25,
    for (let stage in __PROPS) {
         'Portugal'              :  17,
         const __DEFAULT = __DEFAULTS[stage];
         'Rumänien'               :  28,
         const __PROP = __PROPS[stage];
        'Russland'               : 19,
         const __NAME = shared[__PROP];
        'San Marino'             : 98,
 
        'Schottland'             :  2,
         if (__NAME === '$') {
        'Schweden'              :  27,
             break;
        'Schweiz'               :  37,
         }
        'Serbien und Montenegro' :  41,
 
        'Slowakei'              :  70,
         __OBJREF.chDir(getValue(__NAME, __DEFAULT));
        'Slowenien'              :  21,
    }
         'Spanien'                : 13,
 
        'Tschechien'             :  18,
    return __OBJREF;
        'Türkei'                 :  39,
}
        'Ukraine'                :  20,
 
        'Ungarn'                :  26,
// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
        'Wales'                 :  3,
// optConfig: Konfiguration der Option
        'Weissrussland'          : 71,
// item: Key der Option
         'Zypern'                :  38
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
     };
function getSharedConfig(optConfig, item = undefined) {
 
    let config = getValue(optConfig, { });
// ==================== Abschnitt fuer Daten des Spielplans ====================
    const __SHARED = config.Shared;
 
    if (__SHARED !== undefined) {
         const __OBJREF = getSharedRef(__SHARED, item);  // Gemeinsame Daten
 
         if (getValue(__SHARED.item, '$') !== '$') { // __OBJREF ist ein Item
             const __REF = valueOf(__OBJREF);
 
            config = { }; // Neu aufbauen...
             addProps(config, getOptConfig(__REF));
            addProps(config, optConfig);
            config.setConst('SharedData', getOptValue(__REF), false);  // Wert muss schon da sein, NICHT nachladen, sonst ggfs. Promise
         } else { // __OBJREF enthaelt die Daten selbst
             if (! config.Name) {
                 config.Name = __OBJREF.getPath();
            }
            config.setConst('SharedData', __OBJREF); // Achtung: Ggfs. zirkulaer!
         }
     }


     return config;
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// gameType: Name des Wettbewerbs eines Spiels
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
function getGameTypeID(gameType) {
     return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);
}
}


// Initialisiert die gesetzten Optionen
// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
// optConfig: Konfiguration der Optionen
// land: Name des Landes
// optSet: Platz fuer die gesetzten Optionen
// return OS2-ID des Landes, 0 fuer ungueltig
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
function getLandNr(land) {
// return Gefuelltes Objekt mit den gesetzten Optionen
     return getValue(__LANDNRN[land], __LANDNRN.unbekannt);
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
}
     let value;


    if (optSet === undefined) {
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
        optSet = { };
// land: Name der Liga
    }
// return OS2-ID der Liga, 0 fuer ungueltig
function getLigaNr(liga) {
    return getValue(__LIGANRN[liga], __LIGANRN.unbekannt);
}


    for (let opt in optConfig) {
// Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck
        const __OPTCONFIG = optConfig[opt];
// cell: Tabellenzelle mit dem Namen des Gegners
        const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
// return Der Name des Gegners
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);
function getGegnerFromCell(cell) {
    const __GEGNER = cell.textContent;
    const __POS = __GEGNER.indexOf(" (");


        if ((preInit === undefined) || (__PREINIT === preInit)) {
    if (__POS > -1) {
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
        return __GEGNER.substr(0, __POS);
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
    } else {
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?
        return __GEGNER;
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);
            const __VALUE = (__LOADED ? optSet[opt].Value : undefined);
 
            optSet[opt] = {
                'Item'      : opt,
                'Config'    : __CONFIG,
                'Loaded'    : (__ISSHARED || __LOADED),
                'Promise'  : __PROMISE,
                'Value'    : initOptValue(__CONFIG, __VALUE),
                'SetValue'  : __CONFIG.SetValue,
                'ReadOnly'  : (__ISSHARED || __CONFIG.ReadOnly),
                'Action'    : initOptAction(__CONFIG.Action, opt, optSet, __CONFIG),
                'AltAction' : initOptAction(__ALTACTION, opt, optSet, __CONFIG)
            };
        } else if (preInit) { // erstmal nur Stub
            optSet[opt] = {
                'Item'      : opt,
                'Config'    : __OPTCONFIG,
                'Loaded'    : false,
                'Promise'  : undefined,
                'Value'    : initOptValue(__OPTCONFIG),
                'ReadOnly'  : (__ISSHARED || __OPTCONFIG.ReadOnly)
            };
        }
     }
     }
    return optSet;
}
}


    // Abhaengigkeiten:
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle, etwa "2 : 1", und liefert zwei Werte zurueck
    // ================
// cell: Tabellenzelle mit Eintrag "2 : 1"
    // initOptions (PreInit):
// return { '2', '1' } im Beispiel
    // restoreMemoryByOpt: PreInit oldStorage
function getErgebnisFromCell(cell) {
    // getStoredCmds: restoreMemoryByOpt
     const __ERGEBNIS = cell.textContent.split(" : ", 2);
    // 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
    return __ERGEBNIS;
// 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...
// Ermittelt die Spielart aus einer Tabellenzelle, etwa "Liga : Heim", und liefert zwei Werte zurueck
     myOptMemSize = getMemSize(myOptMem = await restoreMemoryByOpt(optSet.oldStorage));
// 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);


     // Zwischengespeicherte Befehle auslesen...
     return __SPIELART;
    const __STOREDCMDS = getStoredCmds(myOptMem);
}


    // ... ermittelte Befehle ausfuehren...
// Ermittelt den Namen des Spielgegners aus einer Tabellenzelle und setzt gegner im Spielplanzeiger
     const __LOADEDCMDS = await runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// cell: Tabellenzelle mit dem Namen des Gegners
function setGegnerFromCell(currZAT, cell) {
     const __GEGNER = getGegnerFromCell(cell);


     // Bisher noch nicht geladenene Optionen laden...
     currZAT.gegner = __GEGNER;
    await loadOptions(optSet);
}


    // Memory Storage fuer naechste Speicherung...
// Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle und setzt tore/gtore im Spielplanzeiger
     myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT
// cell: Tabellenzelle mit Eintrag "2 : 1"
function setErgebnisFromCell(currZAT, cell) {
     const __ERGEBNIS = getErgebnisFromCell(cell);


     // Globale Daten ermitteln...
     if (__ERGEBNIS.length === 2) {
    initScriptDB(optSet);
        currZAT.gFor = parseInt(__ERGEBNIS[0], 10);
 
        currZAT.gAga = parseInt(__ERGEBNIS[1], 10);
    optSet = initOptions(optConfig, optSet, false); // Rest
     } else {
 
         currZAT.gFor = -1;
     if (classification !== undefined) {
         currZAT.gAga = -1;
         // Umbenennungen durchfuehren...
         await classification.renameOptions();
     }
     }
}


    // ... ermittelte Befehle ausfuehren...
// Ermittelt die Spielart aus einer Tabellenzelle und setzt gameType/heim im Spielplanzeiger
     await runStoredCmds(__LOADEDCMDS, optSet, false); // Rest
// 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);


     // Als globale Daten speichern...
     currZAT.gameType = __SPIELART[0];
    updateScriptDB(optSet);
    currZAT.heim    = (__SPIELART.length < 2) || (__SPIELART[1] === "Heim");
 
    return optSet;
}
}


// Installiert die Visualisierung und Steuerung der Optionen
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// optSet: Platz fuer die gesetzten Optionen
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// optParams: Eventuell notwendige Parameter zur Initialisierung
// gameType: Name des Wettbewerbs eines Spiels
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// label: Anzuklickender Text des neuen Links
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// return HTML-Link auf die Preview-Seite fuer diesen Spielbericht
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
function getBilanzLinkFromCell(cell, gameType, label) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
    const __GAMETYPEID = getGameTypeID(gameType);
// 'formWidth': Anzahl der Elemente pro Zeile
    let ret = "";
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
 
// return Liefert die gesetzten Optionen zurueck
    if (cell.textContent !== "Vorschau") {   // Nur falls Link nicht bereits vorhanden
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
        if (__GAMETYPEID > 1) {             // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert"
    if (! optParams.hideMenu) {
            const __SEARCHFUN = ":os_bericht(";
        buildMenu(optSet).then(() => __LOG[3]("Menu OK"));
            let paarung = cell.innerHTML.substr(cell.innerHTML.indexOf(__SEARCHFUN) + __SEARCHFUN.length);
    }


    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
            paarung = paarung.substr(0, paarung.indexOf(')'));
        buildForm(optParams.menuAnchor, optSet, optParams);
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
            paarung = paarung.substr(0, paarung.lastIndexOf(','));
            ret = ' <a href="javascript:spielpreview(' + paarung + ',' + __GAMETYPEID + ')">' + label + "</a>";
        }
     }
     }


     return optSet;
     return ret;
}
}


// Setzt eine Option auf einen vorgegebenen Wert
// Addiert einen Link auf die Bilanz hinter den Spielberichts-Link
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// cell: Tabellenzelle mit Link auf den Spielberichts-Link
// opt: Config und vorheriger Value der Option
// gameType: Name des Wettbewerbs eines Spiels
// value: (Bei allen Typen) Zu setzender Wert
// label: Anzuklickender Text des neuen Links
// reload: Seite mit neuem Wert neu laden
function addBilanzLinkToCell(cell, gameType, label) {
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
     const __BILANZLINK = getBilanzLinkFromCell(cell, gameType, label);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
 
// return Gesetzter Wert
    if (__BILANZLINK !== "") {
function setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
        cell.innerHTML += __BILANZLINK;
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));
    }
}
}


// Ermittelt die naechste moegliche Option
// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
// 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) {
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
        'Zeile'  : 0,
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
        'Spalte' : 1,
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
        'start'  : " von ",
    case __OPTTYPES.SD : return getValue(value, __VALUE);
        'middle' : "</b><br>",
    case __OPTTYPES.SI : break;
        'liga'  : ". Liga",
    default :           break;
        'land'  : ' ',
     }
        'end'    : "<a href="
     };


     return __VALUE;
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
}
        'Zeile'  : 0,
        'Spalte' : 0,
        'start'  : "<b>",
        'middle' : " - ",
        'liga'  : ". Liga",
        'land'  : 'target="_blank">',
        'end'    : "</a></b>"
     };


// Setzt die naechste moegliche Option
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
// opt: Config und Value der Option
// cell: Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
// value: Default fuer ggfs. zu setzenden Wert
// teamSeach: Muster fuer die Suche, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt
// reload: Seite mit neuem Wert neu laden
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
//       z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
function getTeamParamsFromTable(table, teamSearch = undefined) {
// return Gesetzter Wert
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
function setNextOpt(opt, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
     return setOpt(opt, getNextOpt(opt, value), reload, onFulfilled, onRejected);
     const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
}
    const __TEAMCELLSTR  = table.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML;
    const __SEARCHSTART  = __TEAMSEARCH.start;
    const __SEARCHMIDDLE = __TEAMSEARCH.middle;
    const __SEARCHLIGA  = __TEAMSEARCH.liga;
    const __SEARCHLAND  = __TEAMSEARCH.land;
    const __SEARCHEND    = __TEAMSEARCH.end;
    const __INDEXSTART  = __TEAMCELLSTR.indexOf(__SEARCHSTART);
    const __INDEXEND    = __TEAMCELLSTR.indexOf(__SEARCHEND);


// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab
    let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
// opt: Config und Value der Option
     const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
// value: Default fuer ggfs. zu setzenden Wert
     const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
// 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)) {
     let land = (__INDEXLIGA > 0) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
        return setNextOpt(opt, value, reload, onFulfilled, onRejected);
    const __TEAM = (__INDEXMIDDLE > 0) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
     }
     let liga = ((__INDEXLIGA > 0) && (__INDEXMIDDLE > 0)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;


     const __VALUE = getOptValue(opt, value);
     if (land !== undefined) {
 
         if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
    try {
            land = land.substr(2);
         const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
        }
        let message = "";
         if (liga !== undefined) {
 
             liga = liga.substring(0, liga.length - land.length);
         if (selValue) {
        }
             for (let index = 0; index < __CHOICE.length; index++) {
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
         if (__INDEXLAND > -1) {
            }
             land = land.substr(__INDEXLAND + __SEARCHLAND.length);
            message += "\nNummer oder Wert eingeben:";
         } else {
             message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
         }
         }
    }


        const __ANSWER = prompt(message, __NEXTVAL);
    const __RET = {
        'Team' : __TEAM,
        'Liga' : liga,
        'Land' : land,
        'LdNr' : getLandNr(land),
        'LgNr' : getLigaNr(liga)
    };


        if (__ANSWER) {
    return __RET;
            const __INDEX = parseInt(__ANSWER, 10) - 1;
}
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);


            if (nextVal === undefined) {
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
// url: Adresse der Seite
                const __CASTVAL = this[__VALTYPE](__ANSWER);
function getPageIdFromURL(url) {
    // Variablen zur Identifikation der Seite
    const __SUCH = "s=";
    const __INDEXS = url.lastIndexOf(__SUCH);
    const __SHOWTEAM = url.match(/showteam\.php/); // Teamansicht Hauptfenster
    const __ST = url.match(/st\.php/);             // Teamansicht Popupfenster
    let page = -1;                                  // Seitenindex (Rueckgabewert)


                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
    // Wert von page (Seitenindex) ermitteln...
                    nextVal = __CASTVAL;
    // Annahme: Entscheidend ist jeweils das letzte Vorkommnis von "s=" und ggf. von '&'
                }
    if (__SHOWTEAM || __ST) {
            }
        if (__INDEXS < 0) {
            page = 0;
        } else if (url.indexOf('&', __INDEXS) < 0) {
            // Wert von page setzt sich aus allen Zeichen hinter "s=" zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.length), 10);
        } else {
            // Wert von page setzt sich aus allen Zeichen zwischen "s=" und '&' zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.indexOf('&', __INDEXS)), 10);
        }
    }


            if (nextVal !== __VALUE) {
    return page;
                if (nextVal) {
}
                    return setOpt(opt, nextVal, reload, onFulfilled, onRejected);
                }


                const __LABEL = substParam(__CONFIG.Label, __VALUE);
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// saisons: Alle "option"-Eintraege der Combo-Box
function getSaisonFromComboBox(saisons) {
    let saison = 0;


                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
/*
             }
    for (let entry of saisons) {
        if (entry.outerHTML.match(/selected/)) {
            saison = entry.textContent;
        }
    }
*/
    for (i = 0; i < saisons.length; i++) {
        if (saisons[i].outerHTML.match(/selected/)) {
             saison = saisons[i].textContent;
         }
         }
    } catch (ex) {
        __LOG[0]("promptNextOpt: " + ex.message);
     }
     }


     return __VALUE;
     return saison;
}
}


// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// Ermittelt aus dem Spielplan die Ligengroesse ueber die Sonderspieltage
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// rows: Tabellenzeilen mit dem Spielplan
// optSet: Platz fuer die gesetzten Optionen (und Config)
// startIdx: Index der Zeile mit dem ersten ZAT
// item: Key der Option
// colArtIdx: Index der Spalte der Tabellenzelle mit der Spielart (z.B. "Liga : Heim")
// value: (Bei allen Typen) Zu setzender Wert
// saison: Enthaelt die Nummer der laufenden Saison
// reload: Seite mit neuem Wert neu laden
// return 10 bei 36 Spielen, 18 bei 34 Spielen, 20 bei 38 Spielen
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
function getLigaSizeFromSpielplan(rows, startIdx, colArtIdx, saison) {
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
    const __LIGAEXTRA = getLigaExtra(saison);
// return Gesetzter Wert
    const __TEST10ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[0] - 1].cells[colArtIdx]);
function setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
     const __TEST20ER = getSpielArtFromCell(rows[startIdx + __LIGAEXTRA[2] - 1].cells[colArtIdx]);
     const __OPT = getOptByName(optSet, item);


     return setOpt(__OPT, value, reload, onFulfilled, onRejected);
     if (__TEST20ER[0] === "Liga") {
        return 20;
    } else if (__TEST10ER[0] === "Liga") {
        return 10;
    } else {
        return 18;
    }
}
}


// Ermittelt die naechste moegliche Option (Version mit Key)
// ==================== Ende Abschnitt fuer Spielplan und ZATs ====================
// optSet: Platz fuer die gesetzten Optionen (und Config)
 
// item: Key der Option
// ==================== Hauptprogramm ====================
// value: Default fuer ggfs. zu setzenden Wert
 
// return Zu setzender Wert
// Verarbeitet Ansicht "Saisonplan"
function getNextOptByName(optSet, item, value = undefined) {
function procSpielplan() {
     const __OPT = getOptByName(optSet, item);
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
    const __ROWOFFSETLOWER = 0;
 
    const __COLUMNINDEX = {
        'Art' : 1,
        'Geg' : 2,
        'Erg' : 3,
        'Ber' : 4,
        'Zus' : 5,
        'Kom' : 6
    };
 
     const __TEAMPARAMS = getTeamParamsFromTable(getTable(1), __TEAMSEARCHTEAM); // Link mit Team, Liga, Land...


     return getNextOpt(__OPT, value);
     buildOptions(__OPTCONFIG, __OPTSET, {
}
                    'Tab'        : getTable(2),
                    'Zei'        : __ROWOFFSETUPPER,
                    'Spa'        : __COLUMNINDEX.Art,
                    'teamParams' : __TEAMPARAMS,
                    'menuAnchor' : getTable(0, "div"),
                    'hideForm' : {
                                    'team'        : true
                                  },
                    'formWidth'  : 3,
                    'formBreak'  : 4
                });


// Setzt die naechste moegliche Option (Version mit Key)
    const __ZAT = firstZAT(getOptValue(__OPTSET.saison), getOptValue(__OPTSET.ligaSize));
// 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);
     const __ROWS = getRows(2);
}


// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
    let ligaStats = emptyStats();
// optSet: Platz fuer die gesetzten Optionen (und Config)
    let euroStats = emptyStats();
// item: Key der Option
 
// value: Default fuer ggfs. zu setzenden Wert
    for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
// reload: Seite mit neuem Wert neu laden
        const __CELLS = __ROWS[i].cells;   // Aktuelle Eintraege
// 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);
        incZAT(__ZAT);
}


// Baut das Benutzermenu auf (asynchron im Hintergrund)
        setGegnerFromCell(__ZAT, __CELLS[__COLUMNINDEX.Geg]);
// optSet: Gesetzte Optionen
        setSpielArtFromCell(__ZAT, __CELLS[__COLUMNINDEX.Art]);
// return Promise auf void
        setErgebnisFromCell(__ZAT, __CELLS[__COLUMNINDEX.Erg]);
async function buildMenu(optSet) {
    __LOG[3]("buildMenu()");


    for (let opt in optSet) {
        if (getOptValue(__OPTSET.shortKom)) {
        await registerOption(optSet[opt]).then(
            const __CELLKOM = __CELLS[__COLUMNINDEX.Kom];
                result => __LOG[6](`REGISTEROPTION[${opt}] = ${result}`),
            const __CELLART = __CELLS[__COLUMNINDEX.Art];
                defaultCatch);
    }
}


// Invalidiert eine (ueber Menu) gesetzte Option
            __CELLKOM.innerHTML = __CELLKOM.innerHTML.replace("Vorbericht(e)", 'V').replace("Kommentar(e)", 'K').replace("&amp;amp;", '/').replace('&', '/');
// opt: Zu invalidierende Option
             __CELLART.innerHTML = __CELLART.innerHTML.replace(": Heim", "(H)").replace(": Ausw\xE4rts", "(A)").replace("Friendly", "FSS");
// 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"...
        __CELLS[__COLUMNINDEX.Zus].className = __CELLS[__COLUMNINDEX.Art].className;
                opt.Loaded = (force || ! __CONFIG.AutoReset);


                if (opt.Loaded && __CONFIG.AutoReset) {
        if (__CELLS[__COLUMNINDEX.Zus].textContent === "") {
                    // Nur zuruecksetzen, gilt als geladen...
            const __CELLBER = __CELLS[__COLUMNINDEX.Ber];
                    setOptValue(opt, initOptValue(__CONFIG));
             let stats = "";
                }
             }


             return getOptValue(opt);
             addBilanzLinkToCell(__CELLBER, __ZAT.gameType, "Bilanz");
        }, defaultCatch);
}


// Invalidiert die (ueber Menu) gesetzten Optionen
            if (getOptValue(__OPTSET.shortKom)) {
// optSet: Object mit den Optionen
                __CELLBER.innerHTML = __CELLBER.innerHTML.replace("Klick", "(*)").replace("Bilanz", 'V').replace("Vorschau", 'V');
// 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);
            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);
            }


    return optSet;
            if (getOptValue(__OPTSET.showStats)) {
}
                if (stats !== "") {
                    stats = ' ' + stats;
                }
            } else {
                stats = "";
            }
            __CELLS[__COLUMNINDEX.Zus].innerHTML = getZusatz(__ZAT, true) + stats;
        }


// Laedt eine (ueber Menu) gesetzte Option
        if (getOptValue(__OPTSET.sepMonths) && (__ZAT.ZAT % __ZAT.anzZATpMonth === 0) && (i < __ROWS.length - __ROWOFFSETLOWER - 1)) {
// opt: Zu ladende Option
            // Format der Trennlinie zwischen den Monaten...
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
            const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
// 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!";
             for (let entry of __CELLS) {
 
                 entry.style.borderBottom = __BORDERSTRING;
            __LOG[0](__MESSAGE);
             }
 
*/
            return Promise.reject(__MESSAGE);
             for (let j = 0; j < __CELLS.length; j++) {
        }
                 __CELLS[j].style.borderBottom = __BORDERSTRING;
 
             }
        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 !== "") {
        cell.innerHTML += __BILANZLINK;
    }
}
 
// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
 
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
        'Tabelle'  : 1,
        'Zeile'    : 0,
        'Spalte'    : 1,
        'start'    : " von ",
        'middle'    : "</b><br>",
        'liga'      : ". Liga",
        'land'      : ' ',
        'end'      : "<a href="
    };
 
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
        'Tabelle'  : 1,
        'Zeile'    : 0,
        'Spalte'    : 0,
        'start'    : "<b>",
        'middle'    : " - ",
        'liga'      : ". Liga",
        'land'      : 'target="_blank">',
        'end'      : "</a></b>"
    };
 
const __TEAMIDSEARCHHAUPT = {  // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>"
        'Tabelle'  : 0,
        'Zeile'    : 6,
        'Spalte'    : 0,
        'start'    : '<a href="livegame/index.php?spiele=',
        'end'      : ',0">LIVEGAME</a>'
    };
 
const __TEAMIDSEARCHTEAM = {  // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>"
        'Tabelle'  : 0,
        'Zeile'    : 1,
        'Spalte'    : 1,
        'start'    : '<a hspace="20" href="javascript:tabellenplatz(',
        'end'      : ')">Tabellenpl\xE4tze</a>'
    };
 
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
// teamSearch: Muster fuer die Suche nach Team, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt, ausserdem die
//              Adresse der Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
// teamIdSearch: Muster fuer die Suche nach Team-ID, die Eintraege fuer 'start' und 'end' enthaelt, ausserdem die
//              Adresse der Tabellenzelle mit den Parametern zur Team-ID "startTEAMIDend"
// doc: Optionale Angabe des Dokuments, in dem die Tabelle gesucht wird  (Default: document)
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'TmNr' : TEAMID, 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'TmNr' : 930, 'LdNr' : 20, 'LgNr' : 1 }
function getTeamParamsFromTable(teamSearch, teamIdSearch, doc = document) {
    // Ermittlung von Team, Liga und Land...
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
    const __TEAMTABLE    = getTable(getValue(__TEAMSEARCH.Tabelle, 1), 'table', doc);
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
    const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
    const __TEAMCELLSTR  = (__TEAMTABLE === undefined) ? "" : __TEAMTABLE.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML;
    const __SEARCHSTART  = __TEAMSEARCH.start;
    const __SEARCHMIDDLE = __TEAMSEARCH.middle;
    const __SEARCHLIGA  = __TEAMSEARCH.liga;
    const __SEARCHLAND  = __TEAMSEARCH.land;
    const __SEARCHEND    = __TEAMSEARCH.end;
    const __INDEXSTART  = __TEAMCELLSTR.indexOf(__SEARCHSTART);
    const __INDEXEND    = __TEAMCELLSTR.indexOf(__SEARCHEND);
 
    let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
    const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
 
    let land = ((~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined);
    const __TEAMNAME = ((~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined);
    let liga = (((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined);
 
    if (land !== undefined) {
        if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
            land = land.substr(2);
        }
        if (liga !== undefined) {
            liga = liga.substring(0, liga.length - land.length);
        }
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
        if (~ __INDEXLAND) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
         }
         }
    }
    // Ermittlung der Team-ID (indirekt ueber den Livegame- bzw. Tabellenplatz-Link)...
    const __TEAMIDSEARCH  = getValue(teamIdSearch, __TEAMIDSEARCHHAUPT);
    const __TEAMIDTABLE    = getTable(getValue(__TEAMIDSEARCH.Tabelle, 0), 'table', doc);
    const __TEAMIDCELLROW  = getValue(__TEAMIDSEARCH.Zeile, 6);
    const __TEAMIDCELLCOL  = getValue(__TEAMIDSEARCH.Spalte, 0);
    const __TEAMIDCELLSTR  = (__TEAMIDTABLE === undefined) ? "" : __TEAMIDTABLE.rows[__TEAMIDCELLROW].cells[__TEAMIDCELLCOL].innerHTML;
    const __SEARCHIDSTART  = __TEAMIDSEARCH.start;
    const __SEARCHIDEND    = __TEAMIDSEARCH.end;
    const __INDEXIDSTART  = __TEAMIDCELLSTR.indexOf(__SEARCHIDSTART);
    const __INDEXIDEND    = __TEAMIDCELLSTR.indexOf(__SEARCHIDEND);
    const __TEAMIDSTR      = __TEAMIDCELLSTR.substring(__INDEXIDSTART + __SEARCHIDSTART.length, __INDEXIDEND);
    const __TEAMID        = Number.parseInt(__TEAMIDSTR, 10);
    const __TEAM = new Team(__TEAMNAME, land, liga, __TEAMID);
    return __TEAM;
}
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// url: Adresse der Seite
// leafs: Liste von Filenamen mit der Default-Seitennummer (falls Query-Parameter nicht gefunden)
// item: Query-Parameter, der die Nummer der Unterseite angibt
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url, leafs, item = 'page') {
    const __URI = new URI(url);
    const __LEAF = __URI.getLeaf();
    for (let leaf in leafs) {
        if (__LEAF === leaf) {
            const __DEFAULT = leafs[leaf];
            return getValue(__URI.getQueryPar(item), __DEFAULT);
        }
    }
    return -1;
}
// Ermittelt aus dem Spielplan die Ligengroesse ueber die Sonderspieltage
// 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");
// Verzweige in unterschiedliche Verarbeitungen je nach Wert von s:
 
switch (getPageIdFromURL(window.location.href)) {
                    if (getOptValue(__OPTSET.shortKom)) {
    case 6: procSpielplan(); break;
                        __CELLBER.innerHTML = __CELLBER.innerHTML.replace("Klick", "(*)").replace("Bilanz", 'V').replace("Vorschau", 'V');
                    }
 
                    if (__ZAT.gameType === 'Liga') {
                        if (__ZAT.ZAT < 70) {
                            stats = addResultToStats(ligaStats, getOptValue(__OPTSET.longStats), __ZAT);
                        }
                    } else if ((__ZAT.gameType === 'OSCQ') || (__ZAT.gameType === 'OSEQ') || (__ZAT.gameType === 'OSE')) {
                        if (__ZAT.hinRueck !== 1) {
                            euroStats = emptyStats();
                        }
                        stats = addResultToStats(euroStats, getOptValue(__OPTSET.longStats), __ZAT);
                    } else if (__ZAT.gameType === 'OSC') {
                        if ((__ZAT.hinRueck !== 1) && ((__ZAT.euroRunde >= 9) || ((__ZAT.euroRunde % 3) === 0))) {
                            euroStats = emptyStats();
                        }
                        stats = addResultToStats(euroStats, getOptValue(__OPTSET.longStats), __ZAT);
                    }
 
                    if (getOptValue(__OPTSET.showStats)) {
                        if (stats !== "") {
                            stats = ' ' + stats;
                        }
                    } else {
                        stats = "";
                    }
                    __CELLS[__COLUMNINDEX.Zus].innerHTML = getZusatz(__ZAT, true) + stats;
                }
 
                if (getOptValue(__OPTSET.sepMonths) && (__ZAT.ZAT % __ZAT.anzZATpMonth === 0) && (i < __ROWS.length - __ROWOFFSETLOWER - 1)) {
                    // Format der Trennlinie zwischen den Monaten...
                    const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
 
/*
                    for (let entry of __CELLS) {
                        entry.style.borderBottom = __BORDERSTRING;
                    }
*/
                    for (let j = 0; j < __CELLS.length; j++) {
                        __CELLS[j].style.borderBottom = __BORDERSTRING;
                    }
                }
            }
        });
}
}


(() => {
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)