Bearbeiten von „OS2.jugend.user.js

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:
// ==UserScript==
// ==UserScript==
// @name         OS2.jugend
// @name       OS2.jugendV4
// @namespace   http://os.ongapo.com/
// @namespace   http://os.ongapo.com/
// @version     0.48
// @version     0.46
// @copyright   2013+
// @copyright   2013+
// @author       Andreas Eckes (Strindheim BK)
// @author     Andreas Eckes (Strindheim BK)
// @author       Sven Loges (SLC)
// @author     Sven Loges (SLC)
// @description Jugendteam-Script fuer Online Soccer 2.0
// @description Jugendteam-Script fuer Online Soccer 2.0
// @include     http*://os.ongapo.com/haupt.php
// @include     http*://os.ongapo.com/haupt.php
// @include     http*://os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://os.ongapo.com/ju.php
// @include     http*://os.ongapo.com/ju.php
// @include     http*://os.ongapo.com/ju.php?page=*
// @include     http*://os.ongapo.com/ju.php?page=*
// @include     http*://www.os.ongapo.com/haupt.php
// @include     http*://www.os.ongapo.com/haupt.php
// @include     http*://www.os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://www.os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://www.os.ongapo.com/ju.php
// @include     http*://www.os.ongapo.com/ju.php
// @include     http*://www.os.ongapo.com/ju.php?page=*
// @include     http*://www.os.ongapo.com/ju.php?page=*
// @include     http*://online-soccer.eu/haupt.php
// @include     http*://online-soccer.eu/haupt.php
// @include     http*://online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://online-soccer.eu/ju.php
// @include     http*://online-soccer.eu/ju.php
// @include     http*://online-soccer.eu/ju.php?page=*
// @include     http*://online-soccer.eu/ju.php?page=*
// @include     http*://www.online-soccer.eu/haupt.php
// @include     http*://www.online-soccer.eu/haupt.php
// @include     http*://www.online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://www.online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://www.online-soccer.eu/ju.php
// @include     http*://www.online-soccer.eu/ju.php
// @include     http*://www.online-soccer.eu/ju.php?page=*
// @include     http*://www.online-soccer.eu/ju.php?page=*
// @grant       GM_getValue
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_deleteValue
// @grant       GM_registerMenuCommand
// @grant       GM_registerMenuCommand
// @grant        GM_info
// ==/UserScript==
// ==/UserScript==


Zeile 72: Zeile 71:
               }
               }
};
};
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):
const __OPTCONFIG = {
const __OPTCONFIG = {
    'zeigeGeb' : {        // Spaltenauswahl fuer Geburtstage (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showBirthday",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Geb. ein",
                  'Hotkey'    : 'G',
                  'AltLabel'  : "Geb. aus",
                  'AltHotkey' : 'G',
                  'FormLabel' : "Geburtstag"
              },
    'zeigeAlter' : {        // Spaltenauswahl fuer dezimales Alter (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showAge",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Alter ein",
                  'Hotkey'    : 'A',
                  'AltLabel'  : "Alter aus",
                  'AltHotkey' : 'A',
                  'FormLabel' : "Alter"
              },
     'zeigeTal' : {        // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
     'zeigeTal' : {        // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showTclasses",
                   'Name'      : "showTclasses",
Zeile 108: Zeile 131:
                   'FormLabel' : "Aufwertung"
                   'FormLabel' : "Aufwertung"
               },
               },
     'zeigeGeb' : {       // Spaltenauswahl fuer Geburtstage (true = anzeigen, false = nicht anzeigen)
     'zeigePosition' : {   // Position anzeigen
                   'Name'      : "showBirthday",
                   'Name'      : "showPos",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : false,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Geb. ein",
                   'Label'    : "Position ein",
                   'Hotkey'    : 'G',
                   'Hotkey'    : 'P',
                   'AltLabel'  : "Geb. aus",
                   'AltLabel'  : "Position aus",
                   'AltHotkey' : 'G',
                   'AltHotkey' : 'P',
                   'FormLabel' : "Geburtstag"
                   'FormLabel' : "Position"
              },
    'zeigeAlter' : {        // Spaltenauswahl fuer dezimales Alter (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showAge",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Alter ein",
                  'Hotkey'    : 'A',
                  'AltLabel'  : "Alter aus",
                  'AltHotkey' : 'A',
                  'FormLabel' : "Alter"
               },
               },
     'zeigeSkill' : {      // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
     'zeigeSkill' : {      // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
Zeile 140: Zeile 152:
                   'AltHotkey' : 'S',
                   'AltHotkey' : 'S',
                   'FormLabel' : "Skill"
                   'FormLabel' : "Skill"
              },
    'zeigePosition' : {  // Position anzeigen
                  'Name'      : "showPos",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Position ein",
                  'Hotkey'    : 'P',
                  'AltLabel'  : "Position aus",
                  'AltHotkey' : 'P',
                  'FormLabel' : "Position"
               },
               },
     'anzahlOpti' : {      // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
     'anzahlOpti' : {      // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
Zeile 283: Zeile 284:
                   'FormLabel' : "ZAT:|$"
                   'FormLabel' : "ZAT:|$"
               },
               },
     'datenZat' : {       // Stand der Daten zum Team und ZAT
     'birthdays' : {       // Datenspeicher fuer Geburtstage der Jugendspieler
                   'Name'      : "dataZAT",
                   'Name'      : "birthdays",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                  'ValType'  : "Number",
                   'Hidden'    : false,
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : undefined,
                   'Default'  : [],
                  'Action'    : __OPTACTION.SET,
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                  'Cols'      : 1,
                   'Cols'      : 36,
                  'Rows'      : 1,
                   'Rows'      : 2,
                  'Replace'  : null,
                  'Space'    : 0,
                  'Label'    : "Daten-ZAT:"
              },
    'birthdays' : {      // Datenspeicher fuer Geburtstage der Jugendspieler
                  'Name'      : "birthdays",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'  : [],
                  'Submit'    : undefined,
                   'Cols'      : 36,
                   'Rows'      : 2,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Geburtstage:"
                   'Label'    : "Geburtstage:"
               },
               },
     'tClasses' : {        // Datenspeicher fuer Talente der Jugendspieler (-1=wenig, 0=normal, +1=hoch)
     'tclasses' : {        // Datenspeicher fuer Talente der Jugendspieler (-1=wenig, 0=normal, +1=hoch)
                   'Name'      : "tClasses",
                   'Name'      : "tclasses",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : false,
                   'Hidden'    : false,
Zeile 345: Zeile 329:
                   'Label'    : "Aufwertungen:"
                   'Label'    : "Aufwertungen:"
               },
               },
     'zatAges' : {         // Datenspeicher fuer (gebrochene) Alter der Jugendspieler
     'team' : {           // Datenspeicher fuer Daten des Erst- bzw. Zweitteams
                   'Name'      : "zatAges",
                   'Name'      : "team",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : false,
                   'Hidden'    : false,
                   'Serial'    : true,
                   'Serial'    : true,
                  'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : [],
                   'Default'  : { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 },
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 2,
                   'Rows'      : 6,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 1,
                   'Label'    : "ZAT-Alter:"
                   'Label'    : "Verein:"
               },
               },
     'trainiert' : {       // Datenspeicher fuer Trainingsquoten der Jugendspieler
     'reset' : {           // Optionen auf die "Werkseinstellungen" zuruecksetzen
                   'Name'      : "numProgresses",
                   'Name'      : "reset",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SI,
                   'Hidden'    : false,
                   'Action'    : __OPTACTION.RST,
                   'Serial'    : true,
                   'Label'    : "Standard-Optionen",
                   'AutoReset' : true,
                  'Hotkey'    : 'r',
                   'Permanent' : true,
                   'FormLabel' : ""
                   'Default'   : [],
              },
                   'Submit'   : undefined,
    'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                   'Cols'     : 36,
                   'Name'     : "storage",
                   'Rows'     : 2,
                   'Type'     : __OPTTYPES.MC,
                   'Replace'   : null,
                   'ValType'   : "String",
                   'Space'     : 0,
                   'Choice'   : Object.keys(__OPTMEM),
                   'Label'     : "Trainiert:"
                   'Action'   : __OPTACTION.NXT,
                   'Label'     : "Speicher: $",
                   'Hotkey'   : 'c',
                   'FormLabel' : "Speicher:|$"
               },
               },
     'positions' : {       // Datenspeicher fuer optimale Positionen der Jugendspieler
     'oldStorage' : {     // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                   'Name'      : "positions",
                   'Name'      : "oldStorage",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                  'Hidden'    : true
              },
    'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showForm",
                  'Type'      : __OPTTYPES.SW,
                  'FormType'  : __OPTTYPES.SI,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : [],
                   'Default'  : false,
                   'Submit'    : undefined,
                   'Action'    : __OPTACTION.NXT,
                  'Cols'      : 36,
                   'Label'    : "Optionen anzeigen",
                  'Rows'      : 3,
                   'Hotkey'   : 'O',
                  'Replace'  : null,
                   'AltLabel' : "Optionen verbergen",
                  'Space'    : 0,
                   'AltHotkey' : 'O',
                  'Label'    : "Positionen:"
              },
    'team' : {            // Datenspeicher fuer Daten des Erst- bzw. Zweitteams
                  'Name'      : "team",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'Permanent' : true,
                  'Default'  : { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 },
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 6,
                  'Replace'  : null,
                  'Space'    : 1,
                   'Label'    : "Verein:"
              },
    'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
                  'Name'      : "reset",
                   'Type'     : __OPTTYPES.SI,
                  'Action'   : __OPTACTION.RST,
                   'Label'     : "Standard-Optionen",
                   'Hotkey'   : 'r',
                   'FormLabel' : ""
                   'FormLabel' : ""
              },
               }
    'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                  'Name'      : "storage",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : "String",
                  'Choice'    : Object.keys(__OPTMEM),
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Speicher: $",
                  'Hotkey'    : 'c',
                  'FormLabel' : "Speicher:|$"
              },
    'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'Name'      : "oldStorage",
                  'Type'      : __OPTTYPES.SD,
                  'AutoReset' : true,
                  'Hidden'    : true
              },
    'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showForm",
                  'Type'      : __OPTTYPES.SW,
                  'FormType'  : __OPTTYPES.SI,
                  'Permanent' : true,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Optionen anzeigen",
                  'Hotkey'    : 'O',
                  'AltLabel'  : "Optionen verbergen",
                  'AltHotkey' : 'O',
                  'FormLabel' : ""
               }
};
};


Zeile 447: Zeile 386:
// ==================== Abschnitt fuer diverse Utilities ====================
// ==================== Abschnitt fuer diverse Utilities ====================


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


Zeile 483: Zeile 422:


     return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
     return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
}
// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// valueA: Ein Multipliksnt. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// valueB: Ein Multipliksnt. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {
    let product = defValue;
    if ((valueA !== undefined) && (valueB !== undefined)) {
        product = parseFloat(valueA) * parseFloat(valueB);
    }
    return parseFloat(product.toFixed(digits));
}
}


Zeile 562: Zeile 485:
function setNextStored(arr, name, value, reload = true, serial = false) {
function setNextStored(arr, name, value, reload = true, serial = false) {
     return setStored(name, getNextValue(arr, value), reload, serial);
     return setStored(name, getNextValue(arr, value), reload, serial);
}
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// 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;
    if (__MEMORY !== undefined) {
        const __TESTITEM = 'canUseStorageTest';
        const __TEST = Math.random().toString();
        __MEMORY.setItem(__TESTITEM, __TEST);
        ret = (__MEMORY.getItem(__TESTITEM) === __TEST);
        __MEMORY.removeItem(__TESTITEM);
    }
    console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);
    return ret;
}
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// Fuehrt die in einem Storage gespeicherte Operation aus
// optSet: Set mit den Optionen
// optSet: Set mit den Optionen
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
function runStored(optSet, memory = undefined) {
function runStored(optSet, storage = undefined) {
     const __STORAGE = getMemory(memory);
     const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
     const __MEMORY = __STORAGE.Value;
     const __MEMORY = __STORAGE.Value;
     const __RUNPREFIX = __STORAGE.Prefix;
     const __RUNPREFIX = __STORAGE.Prefix;
Zeile 650: Zeile 594:
// return Gesetzter Name der Option
// return Gesetzter Name der Option
function setOptName(opt, name) {
function setOptName(opt, name) {
    const __NAME = getOptName(opt);
    console.log("RENAME " + __NAME + " => " + name);
     return (getOptConfig(opt).Name = name);
     return (getOptConfig(opt).Name = name);
}
}
Zeile 682: Zeile 622:
// ==================== Ende Abschnitt fuer diverse Utilities ====================
// ==================== Ende Abschnitt fuer diverse Utilities ====================


// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
// ==================== Abschnitt fuer das Benutzermenu ====================


// Namen des Default-, Dauer- und Null-Memories...
// Zeigt den Eintrag im Menu einer Option
const __MEMNORMAL  = 'normal';
// opt: Derzeitiger Wert der Option
const __MEMINFINITE = 'unbegrenzt';
// menuOn: Text zum Setzen im Menu
const __MEMINAKTIVE = 'inaktiv';
// funOn: Funktion zum Setzen
// keyOn: Hotkey zum Setzen im Menu
// menuOff: Text zum Ausschalten im Menu
// funOff: Funktion zum Ausschalten
// keyOff: Hotkey zum Ausschalten im Menu
function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
    const __ON  = (opt ? '*' : "");
    const __OFF = (opt ? "" : '*');


// Definition des Default-, Dauer- und Null-Memories...
    console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
const __OPTMEMINFINITE = __OPTMEM[__MEMINFINITE];
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];


// Medium fuer die Datenbank (Speicher)
    if (opt) {
let myOptMem = __OPTMEMNORMAL;
        GM_registerMenuCommand(menuOff, funOff, keyOff);
 
    } else {
// Speicher fuer die DB-Daten
        GM_registerMenuCommand(menuOn, funOn, keyOn);
const __DBMEM = __OPTMEMNORMAL.Value;
     }
 
// Infos ueber dieses Script-Modul
const __DBMOD = { };
 
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
const __DBTOC = { };
 
// Daten zu den Modulen (indiziert durch die Script-Namen)
const __DBDATA = { };
 
// ==================== Abschnitt fuer Speicher ====================
 
// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return memory, falls okay, sonst einen Defaultwert
function getMemory(memory = undefined) {
     return getValue(memory, getValue(myOptMem, __OPTMEMNORMAL));
}
}


// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// opt: Derzeitiger Wert der Option
function canUseMemory(memory = undefined) {
// arr: Array-Liste mit den moeglichen Optionen
     const __STORAGE = getMemory(memory);
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
    const __MEMORY = __STORAGE.Value;
// fun: Funktion zum Setzen des naechsten Wertes
     let ret = false;
// 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;


     if (__MEMORY !== undefined) {
     for (let value of arr) {
        const __TESTPREFIX = 'canUseStorageTest';
        if (value === opt) {
         const __TESTDATA = Math.random().toString();
            options += " / *" + value + '*';
        const __TESTITEM = __TESTPREFIX + __TESTDATA;
         } else {
 
            options += " / " + value;
         __MEMORY.setItem(__TESTITEM, __TESTDATA);
         }
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
        __MEMORY.removeItem(__TESTITEM);
     }
     }
    console.log(options);


     console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);
     GM_registerMenuCommand(__MENU, fun, key);
 
    return ret;
}
}


// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// opt: Option zur Wahl des Speichers
// opt: Derzeitiger Wert der Option
// return Gesuchter Speicher oder Null-Speicher ('inaktiv')
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
function restoreMemoryByOpt(opt) {
// fun: Funktion zum Setzen des naechsten Wertes
     // Memory Storage fuer vorherige Speicherung...
// key: Hotkey zum Setzen des naechsten Wertes im Menu
     const __STORAGE = loadOption(getOpt(opt), true);
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar)
// serial: Serialization fuer komplexe Daten
function registerDataOption(opt, menu, fun, key, hidden = false, serial = true) {
     const __VALUE = ((serial && (opt !== undefined)) ? JSON.stringify(opt) : opt);
    const __MENU = getValue(menu, "").replace('$', __VALUE);
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);


     return __OPTMEM[getValue(__STORAGE, __MEMNORMAL)];
     console.log(__OPTIONS);
}


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


// ==================== Ende Abschnitt fuer Speicher ====================
// Zeigt den Eintrag im Menu einer Option
// opt: Config und Value der Option
function registerOption(opt) {
    const __CONFIG = getOptConfig(opt);


// ==================== Abschnitt fuer die Scriptdatenbank ====================
    if (! __CONFIG.HiddenMenu) {
 
        switch (__CONFIG.Type) {
// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
// optSet: Gesetzte Optionen (und Config)
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
function initScriptDB(optSet) {
                            break;
    const __INFO = GM_info;
        case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    const __META = __INFO.script;
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
 
                            break;
    //console.log(__INFO);
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
 
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
    __DBTOC.versions = getValue(JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
                            break;
 
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    // Infos zu diesem Script...
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
    __DBMOD.name = __META.name;
                            break;
    __DBMOD.version = __META.version;
        case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
 
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
    console.log(__DBMOD);
                            break;
 
        default :            break;
    // Zunaechst den alten Eintrag entfernen...
        }
    __DBTOC.versions[__DBMOD.name] = undefined;
     } else {
 
        // Nur Anzeige im Log...
     // ... und die Daten der Fremdscripte laden...
        registerDataOption(getOptValue(opt), getOptName(opt), opt.Action, __CONFIG.Hotkey, __CONFIG.HiddenMenu, __CONFIG.Serial);
    for (let module in __DBTOC.versions) {
        __DBDATA[module] = getValue(JSON.parse(__DBMEM.getItem('__DBDATA.' + module)), { });
     }
     }
}
}


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


    // Permanente Speicherung der Eintraege...
// Initialisiert die gesetzten Option
    __DBMEM.setItem('__DBTOC.versions', JSON.stringify(__DBTOC.versions));
// config: Konfiguration der Option
     __DBMEM.setItem('__DBDATA.' + __DBMOD.name, JSON.stringify(optSet));
// return Initialwert der gesetzten Option
function initOptValue(config) {
     let value = config.Default; // Standard


     // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
     switch (config.Type) {
     __DBDATA[__DBMOD.name] = getValue(optSet, { });
     case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
 
                            value = config.Choice[0];
     console.log(__DBDATA);
                        }
}
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
     case __OPTTYPES.SI : break;
    default :            break;
    }


// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
    if (config.Serial || config.Hidden) {
        config.HiddenMenu = true;
    }


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


// ==================== Abschnitt fuer das Benutzermenu ====================
// Initialisiert die Menue-Funktion einer Option
// optAction: Typ der Funktion
// item: Key der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// return Funktion fuer die Option
function initOptAction(optAction, item = undefined, optSet = undefined) {
    var fun;
 
    if (optAction !== undefined) {
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);


// Zeigt den Eintrag im Menu einer Option
        switch (optAction) {
// opt: Derzeitiger Wert der Option
        case __OPTACTION.SET : fun = function() {
// menuOn: Text zum Setzen im Menu
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
// funOn: Funktion zum Setzen
                                  };
// keyOn: Hotkey zum Setzen im Menu
                              break;
// menuOff: Text zum Ausschalten im Menu
        case __OPTACTION.NXT : fun = function() {
// funOff: Funktion zum Ausschalten
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
// keyOff: Hotkey zum Ausschalten im Menu
                                  };
function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
                              break;
    const __ON  = (opt ? '*' : "");
        case __OPTACTION.RST : fun = function() {
    const __OFF = (opt ? "" : '*');
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :             break;
        }
    }


     console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
     return fun;
 
    if (opt) {
        GM_registerMenuCommand(menuOff, funOff, keyOff);
    } else {
        GM_registerMenuCommand(menuOn, funOn, keyOn);
    }
}
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// Initialisiert die gesetzten Optionen
// opt: Derzeitiger Wert der Option
// optConfig: Konfiguration der Optionen
// arr: Array-Liste mit den moeglichen Optionen
// optSet: Platz fuer die gesetzten Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// return Gefuelltes Objekt mit den gesetzten Optionen
// fun: Funktion zum Setzen des naechsten Wertes
function initOptions(optConfig, optSet = undefined) {
// key: Hotkey zum Setzen des naechsten Wertes im Menu
     var value;
function registerNextMenuOption(opt, arr, menu, fun, key) {
     const __MENU = menu.replace('$', opt);
    let options = "OPTION " + __MENU;


     for (let value of arr) {
     if (optSet === undefined) {
        if (value === opt) {
        optSet = { };
            options += " / *" + value + '*';
        } else {
            options += " / " + value;
        }
     }
     }
    console.log(options);


     GM_registerMenuCommand(__MENU, fun, key);
     for (let opt in optConfig) {
}
        const __CONFIG = optConfig[opt];
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
        optSet[opt] = {
// opt: Derzeitiger Wert der Option
            'Config'    : __CONFIG,
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
            'Value'    : initOptValue(__CONFIG),
// fun: Funktion zum Setzen des naechsten Wertes
            'SetValue'  : undefined,
// key: Hotkey zum Setzen des naechsten Wertes im Menu
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar)
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
// serial: Serialization fuer komplexe Daten
        };
function registerDataOption(opt, menu, fun, key, hidden = false, serial = true) {
     }
    const __VALUE = ((serial && (opt !== undefined)) ? JSON.stringify(opt) : opt);
    const __MENU = getValue(menu, "").replace('$', __VALUE);
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);


     console.log(__OPTIONS);
     return optSet;
}


    if (! hidden) {
// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
        GM_registerMenuCommand(__MENU, fun, key);
// 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';


// Zeigt den Eintrag im Menu einer Option
     optSet = initOptions(optConfig, optSet);
// opt: Config und Value der Option
function registerOption(opt) {
     const __CONFIG = getOptConfig(opt);


     if (! __CONFIG.HiddenMenu) {
     // Memory Storage fuer vorherige Speicherung...
        switch (__CONFIG.Type) {
    const __OLDOPT = optSet.oldStorage;
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
    const __OLDSTORAGE = loadOption(getOpt(__OLDOPT), true);
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
    myOptMem = __OPTMEM[getValue(__OLDSTORAGE, __NORMAL)];
                            break;
 
        case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    runStored(optSet);
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
    loadOptions(optSet);
                            break;
 
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    // Memory Storage fuer naechste Speicherung...
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
    const __STORAGE = getOptValue(optSet.storage, __NORMAL);
                            break;
    const __NEWSTORAGE = setOpt(__OLDOPT, __STORAGE, false);
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    if (! canUseStorage(myOptMem = __OPTMEM[getValue(__NEWSTORAGE, __NORMAL)])) {
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
        const __INAKTIV = 'inaktiv';
                            break;
 
         case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
         if (__NEWSTORAGE !== __INAKTIV) {
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
            setOpt(__OLDOPT, __INAKTIV, false);
                            break;
            myOptMem = __OPTMEM[__INAKTIV];
        default :            break;
         }
         }
    } else {
        // Nur Anzeige im Log...
        registerDataOption(getOptValue(opt), getOptName(opt), opt.Action, __CONFIG.Hotkey, __CONFIG.HiddenMenu, __CONFIG.Serial);
     }
     }
    return optSet;
}
}


// ==================== Ende Abschnitt fuer das Benutzermenu ====================
// Installiert die Visualisierung und Steuerung der Optionen
 
// optSet: Platz fuer die gesetzten Optionen
// Initialisiert die gesetzten Option
// optParams: Eventuell notwendige Parameter zur Initialisierung
// config: Konfiguration der Option
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// return Initialwert der gesetzten Option
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
function initOptValue(config) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
    let value = config.Default;  // Standard
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
 
// 'formWidth': Anzahl der Elemente pro Zeile
    switch (config.Type) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
                            value = config.Choice[0];
     if (! optParams.hideMenu) {
                        }
        buildMenu(optSet);
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
     }
     }


     if (config.Serial || config.Hidden) {
     if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEM.inaktiv)) {
         config.HiddenMenu = true;
         buildForm(optParams.menuAnchor, optSet, optParams);
     }
     }
    return value;
}
}


// Initialisiert die Menue-Funktion einer Option
// Setzt eine Option auf einen vorgegebenen Wert
// optAction: Typ der Funktion
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// item: Key der Option
// opt: Config und vorheriger Value der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// value: (Bei allen Typen) Zu setzender Wert
// return Funktion fuer die Option
// reload: Seite mit neuem Wert neu laden
function initOptAction(optAction, item = undefined, optSet = undefined) {
// return Gesetzter Wert
     var fun;
function setOpt(opt, value, reload = false) {
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
}


    if (optAction !== undefined) {
// Ermittelt die naechste moegliche Option
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
// opt: Config und Value der Option
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
function getNextOpt(opt, value = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);


        switch (optAction) {
    switch (__CONFIG.Type) {
        case __OPTACTION.SET : fun = function() {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
                                  };
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
                              break;
    case __OPTTYPES.SD : return getValue(value, __VALUE);
        case __OPTACTION.NXT : fun = function() {
    case __OPTTYPES.SI : break;
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
    default :           break;
                                  };
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :             break;
        }
     }
     }


     return fun;
     return __VALUE;
}
}


// Initialisiert die gesetzten Optionen
// Setzt die naechste moegliche Option
// optConfig: Konfiguration der Optionen
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen
// value: Default fuer ggfs. zu setzenden Wert
// return Gefuelltes Objekt mit den gesetzten Optionen
// reload: Seite mit neuem Wert neu laden
function initOptions(optConfig, optSet = undefined) {
// return Gesetzter Wert
     var value;
function setNextOpt(opt, value = undefined, reload = true) {
     const __CONFIG = getOptConfig(opt);


     if (optSet === undefined) {
     return setOpt(opt, getNextOpt(opt, value), reload);
        optSet = { };
}
    }


    for (let opt in optConfig) {
// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
        const __CONFIG = optConfig[opt];
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setOptByName(optSet, item, value, reload = false) {
    const __OPT = getOptByName(optSet, item);
 
    return setOpt(__OPT, value, reload);
}


        optSet[opt] = {
// Ermittelt die naechste moegliche Option (Version mit Key)
            'Config'    : __CONFIG,
// opt: Config und Value der Option
            'Value'    : initOptValue(__CONFIG),
// optSet: Platz fuer die gesetzten Optionen (und Config)
            'SetValue'  : undefined,
// item: Key der Option
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
// value: Ggfs. zu setzender Wert
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
// return Zu setzender Wert
        };
function getNextOptByName(optSet, item, value = undefined) {
    }
    const __OPT = getOptByName(optSet, item);


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


// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
// Setzt die naechste moegliche Option (Version mit Key)
// optConfig: Konfiguration der Optionen
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen
// optSet: Platz fuer die gesetzten Optionen (und Config)
// return Gefuelltes Objekt mit den gesetzten Optionen
// item: Key der Option
function startOptions(optConfig, optSet = undefined) {
// value: Ggfs. zu setzender Wert
     optSet = initOptions(optConfig, optSet);
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setNextOptByName(optSet, item, value = undefined, reload = true) {
     const __OPT = getOptByName(optSet, item);


     // Memory Storage fuer vorherige Speicherung...
     return setNextOpt(__OPT, value, reload);
    myOptMem = restoreMemoryByOpt(optSet.oldStorage);
}


    runStored(optSet);
// Baut das Benutzermenu auf
     loadOptions(optSet);
// optSet: Gesetzte Optionen
function buildMenu(optSet) {
     console.log("buildMenu()");


     // Memory Storage fuer naechste Speicherung...
     for (let opt in optSet) {
    myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage);
        registerOption(optSet[opt]);
 
     }
    initScriptDB(optSet);
 
     return optSet;
}
}


// Installiert die Visualisierung und Steuerung der Optionen
// Laedt eine (ueber Menu) gesetzte Option
// optSet: Platz fuer die gesetzten Optionen
// opt: Zu ladende Option
// optParams: Eventuell notwendige Parameter zur Initialisierung
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// return Gesetzter Wert der gelandenen Option
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
function loadOption(opt, force = false) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     const __CONFIG = getOptConfig(opt);
// '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 }) {
     updateScriptDB(optSet);


     if (! optParams.hideMenu) {
     if (! force && __CONFIG.AutoReset) {
         buildMenu(optSet);
         return setOptValue(opt, initOptValue(__CONFIG));
    } else if (__CONFIG.Serial) {
        return setOptValue(opt, deserialize(getOptName(opt), getOptValue(opt)));
    } else {
        return setOptValue(opt, GM_getValue(getOptName(opt), getOptValue(opt)));
     }
     }
}


    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
// Laedt die (ueber Menu) gesetzten Optionen
         buildForm(optParams.menuAnchor, optSet, optParams);
// optSet: Set mit den Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Set mit den geladenen Optionen
function loadOptions(optSet, force = false) {
    for (let opt in optSet) {
         loadOption(optSet[opt], force);
     }
     }
    return optSet;
}
}


// Setzt eine Option auf einen vorgegebenen Wert
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// opt: Gesetzte Option
// opt: Config und vorheriger Value der Option
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// value: (Bei allen Typen) Zu setzender Wert
// reset: Setzt bei Erfolg auf Initialwert der Option
// reload: Seite mit neuem Wert neu laden
function deleteOption(opt, force = false, reset = true) {
// return Gesetzter Wert
     const __CONFIG = getOptConfig(opt);
function setOpt(opt, value, reload = false) {
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
}


// Ermittelt die naechste moegliche Option
    if (force || ! __CONFIG.Permanent) {
// opt: Config und Value der Option
        GM_deleteValue(getOptName(opt));
// 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) {
        if (reset) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
            setOptValue(opt, initOptValue(__CONFIG));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
        }
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
     }
     }
    return __VALUE;
}
}


// Setzt die naechste moegliche Option
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// opt: Config und Value der Option
// optSet: Gesetzte Optionen
// value: Default fuer ggfs. zu setzenden Wert
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reload: Seite mit neuem Wert neu laden
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Gesetzter Wert
function deleteOptions(optSet, force = false, reset = true) {
function setNextOpt(opt, value = undefined, reload = true) {
     for (let opt in optSet) {
     const __CONFIG = getOptConfig(opt);
        deleteOption(optSet[opt], force, reset);
 
    }
    return setOpt(opt, getNextOpt(opt, value), reload);
}
}


// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// Entfernt eine (ueber Menu) gesetzte Option
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// opt: Gesetzte Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// name: Neu zu setzender Name (Speicheradresse)
// item: Key der Option
// reload: Wert nachladen statt beizubehalten
// value: (Bei allen Typen) Zu setzender Wert
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// reload: Seite mit neuem Wert neu laden
// return Umbenannte Option
// return Gesetzter Wert
function renameOption(opt, name, reload = false, force = false) {
function setOptByName(optSet, item, value, reload = false) {
     const __NAME = getOptName(opt);
     const __OPT = getOptByName(optSet, item);


     return setOpt(__OPT, value, reload);
     if (__NAME !== name) {
}
        deleteOption(opt, true, ! reload);


// Ermittelt die naechste moegliche Option (Version mit Key)
        setOptName(opt, name);
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
    const __OPT = getOptByName(optSet, item);


    return getNextOpt(__OPT, value);
        if (reload) {
}
            loadOption(opt, force);
        }
    }


// Setzt die naechste moegliche Option (Version mit Key)
     return opt;
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: Ggfs. zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setNextOptByName(optSet, item, value = undefined, reload = true) {
    const __OPT = getOptByName(optSet, item);
 
     return setNextOpt(__OPT, value, reload);
}
}


// Baut das Benutzermenu auf
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// optSet: Gesetzte Optionen
// optSet: Gesetzte Optionen
function buildMenu(optSet) {
// reload: Seite mit "Werkseinstellungen" neu laden
     console.log("buildMenu()");
function resetOptions(optSet, reload = true) {
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
    deleteOptions(optSet, false, ! reload);


     for (let opt in optSet) {
     if (reload) {
         registerOption(optSet[opt]);
         // ... und Seite neu laden (mit "Werkseinstellungen")...
        window.location.reload();
     }
     }
}
}


// Laedt eine (ueber Menu) gesetzte Option
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// opt: Zu ladende Option
 
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
// return Gesetzter Wert der gelandenen Option
const __OPTSET = { };
function loadOption(opt, force = false) {
    const __CONFIG = getOptConfig(opt);


    if (! force && __CONFIG.AutoReset) {
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
        return setOptValue(opt, initOptValue(__CONFIG));
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
    } else if (__CONFIG.Serial) {
        return setOptValue(opt, deserialize(getOptName(opt), getOptValue(opt)));
    } else {
        return setOptValue(opt, GM_getValue(getOptName(opt), getOptValue(opt)));
    }
}


// Laedt die (ueber Menu) gesetzten Optionen
// Behandelt die Optionen und laedt das Benutzermenu
// optSet: Set mit den Optionen
// optConfig: Konfiguration der Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// optSet: Platz fuer die gesetzten Optionen
// return Set mit den geladenen Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
function loadOptions(optSet, force = false) {
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
     for (let opt in optSet) {
// 'teamParams': Getrennte "ligaSize"-Option wird genutzt, hier: __MYTEAM mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
        loadOption(optSet[opt], force);
// '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


     return optSet;
     optSet = startOptions(optConfig, optSet);
}


// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
    if (__TEAMPARAMS !== undefined) {
// opt: Gesetzte Option
        __MYTEAM.Team = __TEAMPARAMS.Team;
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
// reset: Setzt bei Erfolg auf Initialwert der Option
        __MYTEAM.Land = __TEAMPARAMS.Land;
function deleteOption(opt, force = false, reset = true) {
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
    const __CONFIG = getOptConfig(opt);
        __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
        console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
        // ... und abspeichern...
        setOpt(optSet.team, __MYTEAM, false);
    } else {
        const __TEAM = getOptValue(optSet.team); // Gespeicherte Parameter


    if (force || ! __CONFIG.Permanent) {
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
         const __NAME = getOptName(opt);
            __MYTEAM.Team = __TEAM.Team;
            __MYTEAM.Liga = __TEAM.Liga;
            __MYTEAM.Land = __TEAM.Land;
            __MYTEAM.LdNr = __TEAM.LdNr;
            __MYTEAM.LgNr = __TEAM.LgNr;
            console.log("Gespeichert: " + JSON.stringify(__MYTEAM));
         } else {
            console.error("Unbekannt: " + JSON.stringify(__TEAM));
        }
    }


         console.log("DELETE " + __NAME);
    if (__MYTEAM.LdNr !== undefined) {
        // Prefix fuer die Optionen "birthdays", "tclasses" und "progresses"...
         const __PREFIX = __MYTEAM.LdNr.toString() + __MYTEAM.LgNr.toString();


         GM_deleteValue(__NAME);
         renameOption(optSet.birthdays, __PREFIX + getOptName(optSet.birthdays), true);
        renameOption(optSet.tclasses, __PREFIX + getOptName(optSet.tclasses), true);
        renameOption(optSet.progresses, __PREFIX + getOptName(optSet.progresses), true);


         if (reset) {
         // ... und nachladen...
            setOptValue(opt, initOptValue(__CONFIG));
        loadOption(optSet.birthdays, true);
         }
        loadOption(optSet.tclasses, true);
         loadOption(optSet.progresses, true);
     }
     }
}


// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
    showOptions(optSet, optParams);
// 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
function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
    const __DELETEALL = (optSelect === undefined) || (optSelect === true);
    const __OPTSELECT = getValue(optSelect, { });


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


// Benennt eine Option um und laedt sie ggfs. nach
// ==================== Abschnitt fuer diverse Utilities ====================
// 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) {
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
        deleteOption(opt, true, ! reload);
// form: <form>...</form>
 
// props: Map von name:value-Paaren
         setOptName(opt, name);
// type: Typ der Input-Felder (Default: unsichtbare Daten)
 
// return Ergaenztes Form-Konstrukt
         if (reload) {
function addInputField(form, props, type = "hidden") {
             loadOption(opt, force);
    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 opt;
     return form;
}
}


// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// name: Gesetzter Name (Speicheradresse)
// form: <form>...</form>
// prefix: Prefix, das vorangestellt werden soll
// props: Map von name:value-Paaren
// return Neu zu setzender Name (Speicheradresse)
// return Ergaenztes Form-Konstrukt
function prefixName(name, prefix) {
function addHiddenField(form, props) {
     return (prefix + name);
     return addInputField(form, props, "hidden");
}
 
// 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
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// optSet: Gesetzte Optionen
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// type: Name des Events, z.B. "click"
// 'reload': Option nachladen?
// callback: Funktion als Reaktion
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// renameParam: Wird an renameFun uebergeen
// return false bei Misserfolg
// renameFun: function(name, param) zur Ermittlung des neuen Namens
function addEvent(obj, type, callback, capture = false) {
// name: Neu zu setzender Name (Speicheradresse)
     if (obj.addEventListener) {
// reload: Wert nachladen statt beizubehalten
         return obj.addEventListener(type, callback, capture);
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
     } else if (obj.attachEvent) {
// return Umbenannte Option
        return obj.attachEvent("on" + type, callback);
function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
    } else {
     if (renameFun === undefined) {
         console.log("Could not add " + type + " event:");
         console.error("RENAME: Illegale Funktion!");
         console.log(callback);
     }
    for (let opt in optSelect) {
         const __OPTPARAMS = optSelect[opt];
         const __OPT = optSet[opt];


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


// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// optSet: Gesetzte Optionen
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// reload: Seite mit "Werkseinstellungen" neu laden
// type: Name des Events, z.B. "click"
function resetOptions(optSet, reload = true) {
// callback: Funktion als Reaktion
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     deleteOptions(optSet, true, false, ! reload);
// 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 {
        console.log("Could not remove " + type + " event:");
        console.log(callback);


    if (reload) {
         return false;
         // ... und Seite neu laden (mit "Werkseinstellungen")...
        window.location.reload();
     }
     }
}
}


// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// Helferfunktion 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);


// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
    return addEvent(__OBJ, type, callback, capture);
const __OPTSET = { };
}


// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
// 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);


// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
    return removeEvent(__OBJ, type, callback, capture);
const __DATAOPTS = {
}
                      'datenZat'  : true,
                      'birthdays'  : true,
                      'tClasses'  : true,
                      'progresses' : true,
                      'zatAges'    : true,
                      'trainiert'  : true,
                      'positions'  : true
                  };


// Behandelt die Optionen und laedt das Benutzermenu
// Helferfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// optConfig: Konfiguration der Optionen
// item: Name des betroffenen Items
// optSet: Platz fuer die gesetzten Optionen
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// optParams: Eventuell notwendige Parameter zur Initialisierung
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// return Angabe, ob das Item sichtbar sein soll
// 'teamParams': Getrennte "ligaSize"-Option wird genutzt, hier: __MYTEAM mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
function checkVisible(item, showList, hideList = undefined) {
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
     let show = true;
// '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


    optSet = startOptions(optConfig, optSet);
     if (showList !== undefined) {
 
         show = (showList[item] === true); // gesetzt und true
     if (__TEAMPARAMS !== undefined) {
     }
         __MYTEAM.Team = __TEAMPARAMS.Team;
    if (hideList !== undefined) {
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
        if (hideList[item] === true) { // gesetzt und true
        __MYTEAM.Land = __TEAMPARAMS.Land;
             show = false; // NICHT anzeigen
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
        __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
        console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
        // ... und abspeichern...
        setOpt(optSet.team, __MYTEAM, false);
     } else {
        const __TEAM = getOptValue(optSet.team);  // Gespeicherte Parameter
 
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
            __MYTEAM.Team = __TEAM.Team;
            __MYTEAM.Liga = __TEAM.Liga;
            __MYTEAM.Land = __TEAM.Land;
            __MYTEAM.LdNr = __TEAM.LdNr;
            __MYTEAM.LgNr = __TEAM.LgNr;
            console.log("Gespeichert: " + JSON.stringify(__MYTEAM));
        } else {
             console.error("Unbekannt: " + JSON.stringify(__TEAM));
         }
         }
     }
     }


    if (__MYTEAM.LdNr !== undefined) {
     return show;
        // Prefix fuer die Optionen 'datenZat', 'birthdays', 'tClasses', 'progresses',
        // 'zatAges', 'trainiert' und 'positions' zur gesonderten Behandlung...
        const __PREFIX = __MYTEAM.LdNr.toString() + __MYTEAM.LgNr.toString();
 
        // Team-bezogene Daten umbenennen...
        renameOptions(optSet, __DATAOPTS, __PREFIX, prefixName);
    }
 
    showOptions(optSet, optParams);
 
     return optSet;
}
}


// ==================== Abschnitt fuer diverse Utilities ====================
// Helferfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// index: Laufende Nummer des Elements (0-based)
// tag: Tag des Elements ("table")
// doc: Dokument (document)
function getTable(index, tag = "table", doc = document) {
    const __TAGS = document.getElementsByTagName(tag);
    const __TABLE = __TAGS[index];
 
    return __TABLE;
}


// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
// form: <form>...</form>
// index: Laufende Nummer des Elements (0-based)
// props: Map von name:value-Paaren
// doc: Dokument (document)
// type: Typ der Input-Felder (Default: unsichtbare Daten)
function getRows(index, doc = document) {
// return Ergaenztes Form-Konstrukt
     const __TABLE = getTable(index, "table", doc);
function addInputField(form, props, type = "hidden") {
    const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
     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;
     return __ROWS;
}
}


// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// 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
// Liefert den Funktionsaufruf zur Option als String
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// opt: Auszufuehrende Option
// type: Name des Events, z.B. "click"
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// callback: Funktion als Reaktion
// value: Ggfs. zu setzender Wert
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// serial: Serialization fuer String-Werte (Select, Textarea)
// return false bei Misserfolg
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
function addEvent(obj, type, callback, capture = false) {
// return String mit dem (reinen) Funktionsaufruf
     if (obj.addEventListener) {
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, storage = undefined) {
        return obj.addEventListener(type, callback, capture);
     const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
     } else if (obj.attachEvent) {
     const __MEMORY = __STORAGE.Value;
        return obj.attachEvent("on" + type, callback);
     const __MEMSTR = __STORAGE.Display;
     } else {
    const __RUNPREFIX = __STORAGE.Prefix;
        console.log("Could not add " + type + " event:");
        console.log(callback);


         return false;
    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) ? JSON.stringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);
 
        if (__ACTION !== undefined) {
            switch (__ACTION) {
            case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                  return __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;
}
}


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


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


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


     return addEvent(__OBJ, type, callback, capture);
     for (let value of __CONFIG.Choice) {
}
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
    return __LABEL.replace('$', element);
// 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 Ueberpruefung, ob ein Item sichtbar sein soll
// Zeigt eine Option auf der Seite als Radiobutton an
// item: Name des betroffenen Items
// opt: Anzuzeigende Option
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// return String mit dem HTML-Code
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
function getOptionRadio(opt) {
// return Angabe, ob das Item sichtbar sein soll
    const __CONFIG = getOptConfig(opt);
function checkVisible(item, showList, hideList = undefined) {
    const __NAME = getOptName(opt);
     let show = true;
    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 (showList !== undefined) {
     return [ __ELEMENTON, __ELEMENTOFF ];
        show = (showList[item] === true);  // gesetzt und true
    }
    if (hideList !== undefined) {
        if (hideList[item] === true) {  // gesetzt und true
            show = false;  // NICHT anzeigen
        }
    }
 
    return show;
}
}


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


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
    return '<input type="checkbox" name="' + __NAME +
// index: Laufende Nummer des Elements (0-based)
          '" id="' + __NAME + '" value="' + __VALUE + '"' +
// tag: Tag des Elements ("table")
          (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
// doc: Dokument (document)
          __NAME + '">' + __FORMLABEL + '</label>';
// return Gesuchtes Element oder undefined (falls nicht gefunden)
}
function getTable(index, tag = "table", doc = document) {
 
     const __TAGS = document.getElementsByTagName(tag);
// Zeigt eine Option auf der Seite als Daten-Textfeld an
     const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
// 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 > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                          JSON.stringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


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


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


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


// ==================== Abschnitt fuer Optionen auf der Seite ====================
// 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 = "";


// Liefert den Funktionsaufruf zur Option als String
    if (! __CONFIG.Hidden) {
// opt: Auszufuehrende Option
        switch (__TYPE) {
// isAlt: Angabe, ob AltAction statt Action gemeint ist
        case __OPTTYPES.MC : element = getOptionSelect(opt);
// value: Ggfs. zu setzender Wert
                            break;
// serial: Serialization fuer String-Werte (Select, Textarea)
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
                                element = getOptionCheckbox(opt);
// return String mit dem (reinen) Funktionsaufruf
                            } else {
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
                                element = getOptionRadio(opt);
    const __STORAGE = getMemory(memory);
                            }
    const __MEMORY = __STORAGE.Value;
                            break;
    const __MEMSTR = __STORAGE.Display;
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
     const __RUNPREFIX = __STORAGE.Prefix;
                            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>';
        }
     }


     if (__MEMORY !== undefined) {
     return element;
        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) ? JSON.stringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);
 
        if (__ACTION !== undefined) {
            switch (__ACTION) {
            case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                  return __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
// Baut das Benutzermenu auf der Seite auf
// opt: Auszufuehrende Option
// optSet: Gesetzte Optionen
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// optParams: Eventuell notwendige Parameter
// value: Ggfs. zu setzender Wert
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// serial: Serialization fuer String-Werte (Select, Textarea)
// 'formWidth': Anzahl der Elemente pro Zeile
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return String mit dem (reinen) Funktionsaufruf
// return String mit dem HTML-Code
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {
function getForm(optSet, optParams = { }) {
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
     const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    let form = __FORM;
    let count = 0;   // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)


     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
     for (let opt in optSet) {
}
        if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
            const __ELEMENT = getOptionElement(optSet[opt]);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";


// Zeigt eine Option auf der Seite als Auswahlbox an
            if (__ELEMENT.length > 0) {
// opt: Anzuzeigende Option
                if (++count > __FORMBREAK) {
// return String mit dem HTML-Code
                    if (++column > __FORMWIDTH) {
function getOptionSelect(opt) {
                        column = 1;
    const __CONFIG = getOptConfig(opt);
                    }
    const __NAME = getOptName(opt);
                }
    const __VALUE = getOptValue(opt);
                if (column === 1) {
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
                    form += '</tr><tr>';
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
                }
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';
            }
 
        }
    for (let value of __CONFIG.Choice) {
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
     }
     }
     element += '\n</select>';
     form += '\n' + __FORMEND;


     return __LABEL.replace('$', element);
     return form;
}
}


// Zeigt eine Option auf der Seite als Radiobutton an
// Fuegt das Script in die Seite ein
// opt: Anzuzeigende Option
// optSet: Gesetzte Optionen
// return String mit dem HTML-Code
// optParams: Eventuell notwendige Parameter
function getOptionRadio(opt) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     const __CONFIG = getOptConfig(opt);
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
    const __NAME = getOptName(opt);
// return String mit dem HTML-Code fuer das Script
    const __VALUE = getOptValue(opt, false);
function getScript(optSet, optParams = { }) {
    const __ACTION = getFormActionEvent(opt, false, true, "click", false);
     //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
     //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 __ELEMENTON  = '<input type="radio" name="' + __NAME +
     //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
                        '" id="' + __NAME + 'ON" value="1"' +
    const __SCRIPT = "";
                        (__VALUE ? ' CHECKED' : __ACTION) +
 
                        ' /><label for="' + __NAME + 'ON">' +
    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
                        __CONFIG.Label + '</label>';
     const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';


     return [ __ELEMENTON, __ELEMENTOFF ];
     return __SCRIPT;
}
}


// Zeigt eine Option auf der Seite als Checkbox an
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// opt: Anzuzeigende Option
// anchor: Element, das als Anker fuer die Anzeige dient
// return String mit dem HTML-Code
// optSet: Gesetzte Optionen
function getOptionCheckbox(opt) {
// optParams: Eventuell notwendige Parameter
    const __CONFIG = getOptConfig(opt);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
    const __NAME = getOptName(opt);
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __VALUE = getOptValue(opt, false);
// 'formWidth': Anzahl der Elemente pro Zeile
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
function buildForm(anchor, optSet, optParams = { }) {
     console.log("buildForm()");
 
     const __FORM = getForm(optSet, optParams);
     const __SCRIPT = getScript(optSet, optParams);


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


// Zeigt eine Option auf der Seite als Daten-Textfeld an
// Informationen zu hinzugefuegten Forms
// opt: Anzuzeigende Option
const __FORMS = { };
// 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 > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
    const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                          JSON.stringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
// 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);


// Zeigt eine Option auf der Seite als Button an
     __FORMS[anchor] = { 'Script' : script, 'Form' : form };
// 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 ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     return '<label for="' + __NAME + '">' + __FORMLABEL +
     anchor.innerHTML = __REST + script + form;
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';
}
}


// Zeigt eine Option auf der Seite an (je nach Typ)
// ==================== Ende Abschnitt fuer Optionen ====================
// 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) {
// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
        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 (element.length === 2) {
// Zeitpunktangaben
            element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
const __TIME = {
        }
    'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
    }
    'beg' : 1,  // Jugendspieler darf trainieren (wird 13 Jahre alt)
    'now' : 2,  // Aktueller ZAT
    'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
};


    return element;
// Funktionen ***************************************************************************
}


// Baut das Benutzermenu auf der Seite auf
// Erschafft die Spieler-Objekte und fuellt sie mit Werten
// optSet: Gesetzte Optionen
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
// optParams: Eventuell notwendige Parameter
     const __SAISON = getOptValue(optSet.saison);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     const __CURRZAT = getOptValue(optSet.aktuellerZat);
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
// 'formWidth': Anzahl der Elemente pro Zeile
     const __TCLASSES = getOptValue(optSet.tclasses, []);
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     const __PROGRESSES = getOptValue(optSet.progresses, []);
// return String mit dem HTML-Code
     const __PLAYERS = [];
function getForm(optSet, optParams = { }) {
     const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
     const __FORMEND = '</tr></tbody></table></form>';
     const __FORMWIDTH = getValue(optParams.formWidth, 3);
     const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
     const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
     let form = __FORM;
    let count = 0;  // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)


     for (let opt in optSet) {
     for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
         if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
         const __CELLS = playerRows[i].cells;
            const __ELEMENT = getOptionElement(optSet[opt]);
        const __AGE = getAgeFromHTML(__CELLS, colIdx.Age);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
        const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);


            if (__ELEMENT.length > 0) {
        __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
                if (++count > __FORMBREAK) {
        __PLAYERS[j] = __NEWPLAYER;
                    if (++column > __FORMWIDTH) {
    }
                        column = 1;
 
                    }
    return __PLAYERS;
                }
}
                if (column === 1) {
 
                    form += '</tr><tr>';
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
                 }
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
    if (offsetLeft < 0) {
        offsetLeft = colIdxSort; // ab Sortierspalte
    }
 
    for (let i = offsetUpper; i < rows.length - offsetLower - 1; i++) {
        if (rows[i].cells[colIdxSort].textContent !== rows[i + 1].cells[colIdxSort].textContent) {
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
                 rows[i].cells[j].style.borderBottom = borderString;
             }
             }
         }
         }
     }
     }
    form += '\n' + __FORMEND;
    return form;
}
}


// Fuegt das Script in die Seite ein
// Klasse ColumnManager *****************************************************************
// 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!"); }');
function ColumnManager(optSet) {
    this.geb = getOptValue(optSet.zeigeGeb);
    this.tal = getOptValue(optSet.zeigeTal);
    this.quo = getOptValue(optSet.zeigeQuote);
     this.aufw = getOptValue(optSet.zeigeAufw);
    this.alter = getOptValue(optSet.zeigeAlter);
    this.skill = getOptValue(optSet.zeigeSkill);
    this.pos = getOptValue(optSet.zeigePosition);
    this.opti = ((getOptValue(optSet.anzahlOpti) >= 1) && (getOptValue(optSet.anzahlOpti) <= 6));
    this.mw = ((getOptValue(optSet.anzahlMW) >= 1) && (getOptValue(optSet.anzahlMW) <= 6));
    this.anzOpti = getOptValue(optSet.anzahlOpti);
    this.anzMw = getOptValue(optSet.anzahlMW);
    this.skillE = getOptValue(optSet.zeigeSkillEnde);
    this.optiE = ((getOptValue(optSet.anzahlOptiEnde) >= 1) && (getOptValue(optSet.anzahlOptiEnde) <= 6));
    this.mwE = ((getOptValue(optSet.anzahlMWEnde) >= 1) && (getOptValue(optSet.anzahlMWEnde) <= 6));
    this.anzOptiE = getOptValue(optSet.anzahlOptiEnde);
    this.anzMwE = getOptValue(optSet.anzahlMWEnde);
    this.kennzE = getOptValue(optSet.kennzeichenEnde);


     return __SCRIPT;
     this.toString = function() {
}
        let result = "Skillschnitt\t\t" + this.skill + '\n';
        result += "Beste Position\t" + this.pos + '\n';
        result += "Optis\t\t\t" + this.opti + " (" + this.anzOpti + ")\n";
        result += "Marktwerte\t\t" + this.mw + " (" + this.anzMw + ")\n";
        result += "Skillschnitt Ende\t" + this.skillE + '\n';
        result += "Optis Ende\t\t" + this.optiE + " (" + this.anzOptiE + ")\n";
        result += "Marktwerte Ende\t" + this.mwE + " (" + this.anzMwE + ")\n";


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        return result;
// anchor: Element, das als Anker fuer die Anzeige dient
    };
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function buildForm(anchor, optSet, optParams = { }) {
    console.log("buildForm()");


     const __FORM = getForm(optSet, optParams);
     this.addCell = function(tableRow) {
     const __SCRIPT = getScript(optSet, optParams);
        tableRow.insertCell(-1);
        return tableRow.cells.length - 1;
     };


     addForm(anchor, __FORM, __SCRIPT);
     this.addAndFillCell = function(tableRow, value, color, digits = 2) {
}
        if (isFinite(value) && (value !== true) && (value !== false)) {
            // Zahl einfuegen
            if (value < 1000) {
                // Mit Nachkommastellen darstellen
                tableRow.cells[this.addCell(tableRow)].textContent = parseFloat(value).toFixed(digits);
            } else {
                // Mit Tausenderpunkten darstellen
                tableRow.cells[this.addCell(tableRow)].textContent = getNumberString(value.toString());
            }
        } else {
            // String einfuegen
            tableRow.cells[this.addCell(tableRow)].textContent = value;
        }
        tableRow.cells[tableRow.cells.length - 1].style.color = color;
    };


// Informationen zu hinzugefuegten Forms
    this.addTitles = function(headers, titleColor = "#FFFFFF") {
const __FORMS = { };
        // Spaltentitel zentrieren
        headers.align = "center";


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        // Titel fuer die aktuellen Werte
// anchor: Element, das als Anker fuer die Anzeige dient
        if (this.tal) {
// form: HTML-Form des Optionsmenu (hinten angefuegt)
            this.addAndFillCell(headers, "Talent", titleColor);
// script: Script mit Reaktionen
        }
function addForm(anchor, form = "", script = "") {
        if (this.quo) {
    const __OLDFORM = __FORMS[anchor];
            this.addAndFillCell(headers, "Quote", titleColor);
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
        }
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
        if (this.aufw) {
            this.addAndFillCell(headers, "Aufwertung", titleColor);
        }
        if (this.geb) {
            this.addAndFillCell(headers, "Geb.", titleColor);
        }
        if (this.alter) {
            this.addAndFillCell(headers, "Alter", titleColor);
        }
        if (this.skill) {
            this.addAndFillCell(headers, "Skill", titleColor);
        }
        if (this.pos) {
            this.addAndFillCell(headers, "Pos", titleColor);
        }
        if (this.opti) {
            for (let i = 1; i <= this.anzOpti; i++) {
                this.addAndFillCell(headers, "Opti " + i, titleColor);
                if (this.mw && (this.anzMw >= i)) {
                    this.addAndFillCell(headers, "MW " + i, titleColor);
                }
            }
            if (this.mw) {
                for (let i = this.anzOpti + 1; i <= this.anzMw; i++) {
                    // Mehr MW- als Opti-Spalten
                    this.addAndFillCell(headers, "MW " + i, titleColor);
                }
            }
        } else if (this.mw) {
            // Keine Opti-, dafuer MW-Spalten
            for (let i = 1; i <= this.anzMw; i++) {
                this.addAndFillCell(headers, "MW " + i, titleColor);
            }
        }


    __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        // Titel fuer die Werte mit Ende 18
 
        if (this.skillE) {
    anchor.innerHTML = __REST + script + form;
            this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
}
        }
        if (this.optiE) {
            for (let i = 1; i <= this.anzOptiE; i++) {
                this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
                if (this.mwE && (this.anzMwE >= i)) {
                    this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
                }
            }
            if (this.mwE) {
                for (let i = this.anzOptiE + 1; i <= this.anzMwE; i++) {
                    this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
                }
            }
        } else if (this.mwE) {
            for (let i = 1; i <= this.anzMwE; i++) {
                this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
            }
        }
    };  // Ende addTitles()


// ==================== Ende Abschnitt fuer Optionen ====================
    this.addValues = function(player, playerRow, color = "#FFFFFF") {
        const __COLOR = (player.isGoalie ? getColor("TOR") : color);
        const __POS1COLOR = (player.isGoalie ? getColor("TOR") : getColor(player.getPos(1)));


// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
        // Aktuelle Werte
 
        if (this.tal) {
// Zeitpunktangaben
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
const __TIME = {
        }
    'cre' : 0, // Jugendspieler angelegt (mit 12 Jahren)
        if (this.quo) {
    'beg' : 1, // Jugendspieler darf trainieren (wird 13 Jahre alt)
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
    'now' : 2,  // Aktueller ZAT
        }
    'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
        if (this.aufw) {
};
            this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
 
        }
// Funktionen ***************************************************************************
        if (this.geb) {
 
            this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
// Erschafft die Spieler-Objekte und fuellt sie mit Werten (reloadData: true = Teamuebersicht, false = Spielereinzelwerte)
        }
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, reloadData = false) {
        if (this.alter) {
    const __SAISON = getOptValue(optSet.saison);
            this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
    const __CURRZAT = getOptValue(optSet.aktuellerZat);
        }
    const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
        if (this.skill) {
    const __TCLASSES = getOptValue(optSet.tClasses, []);
            this.addAndFillCell(playerRow, player.getSkill(), __COLOR, 2);
    const __PROGRESSES = getOptValue(optSet.progresses, []);
        }
    const __ZATAGES = getOptValue(optSet.zatAges, []);
        if (this.pos) {
    const __TRAINIERT = getOptValue(optSet.trainiert, []);
            this.addAndFillCell(playerRow, player.getPos(1), __POS1COLOR);
    const __POSITIONS = getOptValue(optSet.positions, []);
        }
    const __PLAYERS = [];
        if (this.opti) {
 
            for (let i = 1; i <= this.anzOpti; i++) {
    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
                if (player.isGoalie) {
        const __CELLS = playerRows[i].cells;
                    if (i === 1) {
        const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
                        // TOR-Opti anzeigen
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
                        this.addAndFillCell(playerRow, player.getOpti("TOR"), getColor("TOR"), 2);
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
                    } else {
         const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);
                        // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
 
                        this.addCell(playerRow);
        __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
                    }
 
                } else {
        if (reloadData) {
                    // Feld-Opti anzeigen
            __NEWPLAYER.setZusatz(__ZATAGES[j], __TRAINIERT[j], __POSITIONS[j]);
                    this.addAndFillCell(playerRow, player.getOpti(player.getPos(i)), getColor(player.getPos(i)), 2);
                }
                if (this.mw && (this.anzMw >= i)) {
                    if (player.isGoalie) {
                        if (i === 1) {
                            // TOR-MW anzeigen
                            this.addAndFillCell(playerRow, player.getMarketValue("TOR"), getColor("TOR"), 0);
                        } else {
                            // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                            this.addCell(playerRow);
                        }
                    } else {
                        // Feld-MW anzeigen
                        this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i)), getColor(player.getPos(i)), 0);
                    }
                }
            }
            // Verbleibende MW anzeigen
            if (this.mw) {
                for (let i = this.anzOpti + 1; i <= this.anzMw; i++) {
                    if (player.isGoalie) {
                        if (i === 1) {
                            // TOR-MW anzeigen
                            this.addAndFillCell(playerRow, player.getMarketValue("TOR"), getColor("TOR"), 0);
                        } else {
                            // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                            this.addCell(playerRow);
                        }
                    } else {
                        // Feld-MW anzeigen
                        this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i)), getColor(player.getPos(i)), 0);
                    }
                }
            }
         } else if (this.mw) {
            // Opti soll nicht angezeigt werden, dafuer aber MW
            for (let i = 1; i <= this.anzMw; i++) {
                if (player.isGoalie) {
                    if (i === 1) {
                        // TOR-MW anzeigen
                        this.addAndFillCell(playerRow, player.getMarketValue("TOR"), getColor("TOR"), 0);
                    } else {
                        // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                        this.addCell(playerRow);
                    }
                } else {
                    // Feld-MW anzeigen
                    this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i)), getColor(player.getPos(i)), 0);
                }
            }
         }
         }


         __PLAYERS[j] = __NEWPLAYER;
         // Werte mit Ende 18
    }
        if (this.skillE) {
 
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
    if (reloadData) {
        }
        storePlayerData(__PLAYERS, playerRows, optSet, colIdx, offsetUpper, offsetLower);
         if (this.optiE) {
    } else {
            for (let i = 1; i <= this.anzOptiE; i++) {
         calcPlayerData(__PLAYERS, optSet);
                if (player.isGoalie) {
    }
                    if (i === 1) {
 
                        // TOR-Opti anzeigen
    return __PLAYERS;
                        this.addAndFillCell(playerRow, player.getOpti("TOR", __TIME.end), getColor("TOR"), 2);
}
                    } else {
 
                        // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
                        this.addCell(playerRow);
function calcPlayerData(players, optSet) {
                    }
    const __ZATAGES = [];
                } else {
    const __TRAINIERT = [];
                    // Feld-Opti anzeigen
    const __POSITIONS = [];
                    this.addAndFillCell(playerRow, player.getOpti(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 2);
 
                }
    for (let i = 0; i < players.length; i++) {
                if (this.mwE && (this.anzMwE >= i)) {
        const __ZUSATZ = players[i].calcZusatz();
                    if (player.isGoalie) {
 
                        if (i === 1) {
        __ZATAGES[i]  = __ZUSATZ.zatAge;
                            // TOR-MW anzeigen
        __TRAINIERT[i] = __ZUSATZ.trainiert;
                            this.addAndFillCell(playerRow, player.getMarketValue("TOR", __TIME.end), getColor("TOR"), 0);
        __POSITIONS[i] = __ZUSATZ.bestPos;
                        } else {
    }
                            // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
 
                            this.addCell(playerRow);
    setOpt(optSet.zatAges, __ZATAGES, false);
                        }
    setOpt(optSet.trainiert, __TRAINIERT, false);
                    } else {
    setOpt(optSet.positions, __POSITIONS, false);
                        // Feld-MW anzeigen
}
                        this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 0);
 
                    }
// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
                }
function storePlayerData(players, playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
            }
    const __BIRTHDAYS = [];
            // Verbleibende MW anzeigen
    const __TCLASSES = [];
            if (this.mwE) {
    const __PROGRESSES = [];
                for (let i = this.anzOptiE + 1; i <= this.anzMwE; i++) {
 
                    if (player.isGoalie) {
    for (let i = offsetUpper; i < playerRows.length - offsetLower; i++) {
                        if (i === 1) {
        const __CELLS = playerRows[i].cells;
                            // TOR-MW anzeigen
 
                            this.addAndFillCell(playerRow, player.getMarketValue("TOR", __TIME.end), getColor("TOR"), 0);
        __BIRTHDAYS[i - offsetUpper] = getIntFromHTML(__CELLS, colIdx.Geb);
                        } else {
        __TCLASSES[i - offsetUpper] = getTalentFromHTML(__CELLS, colIdx.Tal);
                            // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
        __PROGRESSES[i - offsetUpper] = getStringFromHTML(__CELLS, colIdx.Auf);
                            this.addCell(playerRow);
    }
                        }
 
                    } else {
    setOpt(optSet.birthdays, __BIRTHDAYS, false);
                        // Feld-MW anzeigen
    setOpt(optSet.tClasses, __TCLASSES, false);
                        this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 0);
    setOpt(optSet.progresses, __PROGRESSES, false);
                    }
}
                }
 
            }
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
        } else if (this.mwE) {
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {
            // Opti soll nicht angezeigt werden, dafuer aber MW
    if (offsetLeft < 0) {
            for (let i = 1; i <= this.anzMwE; i++) {
        offsetLeft = colIdxSort;  // ab Sortierspalte
                if (player.isGoalie) {
    }
                    if (i === 1) {
 
                        // TOR-MW anzeigen
    for (let i = offsetUpper; i < rows.length - offsetLower - 1; i++) {
                        this.addAndFillCell(playerRow, player.getMarketValue("TOR", __TIME.end), getColor("TOR"), 0);
        if (rows[i].cells[colIdxSort].textContent !== rows[i + 1].cells[colIdxSort].textContent) {
                    } else {
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
                        // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                rows[i].cells[j].style.borderBottom = borderString;
                        this.addCell(playerRow);
                    }
                } else {
                    // Feld-MW anzeigen
                    this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 0);
                }
             }
             }
         }
         }
     }
     };  // Ende addValues(player, playerRow)
}
}


// Klasse ColumnManager *****************************************************************
// Klasse PlayerRecord ******************************************************************


function ColumnManager(optSet, showCol = undefined) {
function PlayerRecord(age, skills, isGoalie) {
     const __SHOWALL = (showCol === undefined) || (showCol === true);
     // Zu benutzende Marktwertformel
     const __SHOWCOL = getValue(showCol, { });
     const __MWFORMEL = {
        'alt' : 0, // Marktwertformel bis Saison 9 inklusive
        'S10' : 1  // Marktwertformel MW5 ab Saison 10
    };


     this.geb = getValue(__SHOWCOL.zeigeGeb, __SHOWALL) && getOptValue(optSet.zeigeGeb);
     this.mwFormel = __MWFORMEL.S10; // Neue Formel, genauer in initPlayer()
    this.tal = getValue(__SHOWCOL.zeigeTal, __SHOWALL) && getOptValue(optSet.zeigeTal);
    this.quo = getValue(__SHOWCOL.zeigeQuote, __SHOWALL) && getOptValue(optSet.zeigeQuote);
    this.aufw = getValue(__SHOWCOL.zeigeAufw, __SHOWALL) && getOptValue(optSet.zeigeAufw);
    this.alter = getValue(__SHOWCOL.zeigeAlter, __SHOWALL) && getOptValue(optSet.zeigeAlter);
    this.skill = getValue(__SHOWCOL.zeigeSkill, __SHOWALL) && getOptValue(optSet.zeigeSkill);
    this.pos = getValue(__SHOWCOL.zeigePosition, __SHOWALL) && getOptValue(optSet.zeigePosition);
    this.anzOpti = getValue(__SHOWCOL.zeigeOpti, __SHOWALL) ? getOptValue(optSet.anzahlOpti) : 0;
    this.anzMw = getValue(__SHOWCOL.zeigeMW, __SHOWALL) ? getOptValue(optSet.anzahlMW) : 0;
    this.skillE = getValue(__SHOWCOL.zeigeSkillEnde, __SHOWALL) && getOptValue(optSet.zeigeSkillEnde);
    this.anzOptiE = getValue(__SHOWCOL.zeigeOptiEnde, __SHOWALL) ? getOptValue(optSet.anzahlOptiEnde) : 0;
    this.anzMwE = getValue(__SHOWCOL.zeigeMWEnde, __SHOWALL) ? getOptValue(optSet.anzahlMWEnde) : 0;
    this.kennzE = getOptValue(optSet.kennzeichenEnde);


     this.toString = function() {
     this.age = age;
        let result = "Skillschnitt\t\t" + this.skill + '\n';
    this.skills = skills;
        result += "Beste Position\t" + this.pos + '\n';
    this.isGoalie = isGoalie;
        result += "Optis\t\t\t" + this.anzOpti + '\n';
        result += "Marktwerte\t\t" + this.anzMw + '\n';
        result += "Skillschnitt Ende\t" + this.skillE + '\n';
        result += "Optis Ende\t\t" + this.anzOptiE + '\n';
        result += "Marktwerte Ende\t" + this.anzMwE + '\n';


        return result;
    // in this.initPlayer() definiert:
     };
    // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
    // this.zatAge: Bisherige erfolgte Trainings-ZATs
    // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
    // this.aufwert: Aufwertungsstring
    // this.mwFormel: Benutzte MW-Formel, siehe __MWFORMEL
    // this.positions[][]: Positionstext und Opti; TOR-Index ist 5
     // this.skillsEnd[]: Berechnet aus this.skills, this.age und aktuellerZat


     this.addCell = function(tableRow) {
     this.toString = function() {
         tableRow.insertCell(-1);
         let result = "Alter\t\t" + this.age + "\n\n";
         return tableRow.cells.length - 1;
        result += "Aktuelle Werte\n";
    };
         result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
        result += "Optis und Marktwerte";


    this.addAndFillCell = function(tableRow, value, color, digits = 2) {
        for (let pos of this.positions) {
        if (isFinite(value) && (value !== true) && (value !== false)) {
            result += "\n\t" + pos + '\t';
             // Zahl einfuegen
             result += this.getOpti(pos).toFixed(2) + '\t';
            if (value < 1000) {
             result += getNumberString(this.getMarketValue(pos).toString());
                // Mit Nachkommastellen darstellen
                tableRow.cells[this.addCell(tableRow)].textContent = parseFloat(value).toFixed(digits);
             } else {
                // Mit Tausenderpunkten darstellen
                tableRow.cells[this.addCell(tableRow)].textContent = getNumberString(value.toString());
            }
        } else {
            // String einfuegen
            tableRow.cells[this.addCell(tableRow)].textContent = value;
         }
         }
        tableRow.cells[tableRow.cells.length - 1].style.color = color;
    };


    this.addTitles = function(headers, titleColor = "#FFFFFF") {
        result += "\n\nWerte mit Ende 18\n";
        // Spaltentitel zentrieren
        result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
         headers.align = "center";
         result += "Optis und Marktwerte";


         // Titel fuer die aktuellen Werte
         for (let pos of this.positions) {
        if (this.tal) {
             result += "\n\t" + this.getPos()[i] + '\t';
             this.addAndFillCell(headers, "Talent", titleColor);
             result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
        }
             result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
        if (this.quo) {
            this.addAndFillCell(headers, "Quote", titleColor);
        }
        if (this.aufw) {
             this.addAndFillCell(headers, "Aufwertung", titleColor);
        }
        if (this.geb) {
            this.addAndFillCell(headers, "Geb.", titleColor);
        }
        if (this.alter) {
             this.addAndFillCell(headers, "Alter", titleColor);
        }
        if (this.skill) {
            this.addAndFillCell(headers, "Skill", titleColor);
        }
        if (this.pos) {
            this.addAndFillCell(headers, "Pos", titleColor);
        }
        for (let i = 1; i <= 6; i++) {
            if (i <= this.anzOpti) {
                this.addAndFillCell(headers, "Opti " + i, titleColor);
            }
            if (i <= this.anzMw) {
                this.addAndFillCell(headers, "MW " + i, titleColor);
            }
         }
         }


         // Titel fuer die Werte mit Ende 18
         return result;
        if (this.skillE) {
    };  // Ende this.toString()
            this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
 
         }
    // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
        for (let i = 1; i <= 6; i++) {
    this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
            if (i <= this.anzOptiE) {
         this.zatGeb = gebZAT;
                this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
        this.zatAge = this.calcZatAge(currZAT);
            }
        this.talent = tclass;
            if (i <= this.anzMwE) {
        this.aufwert = progresses;
                this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
         this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;
            }
         }
    };  // Ende addTitles()


    this.addValues = function(player, playerRow, color = "#FFFFFF") {
        this.positions = [];
         const __COLOR = (player.isGoalie ? getColor("TOR") : color);
        // ABW
         const __POS1COLOR = getColor(player.getPos());
        this.positions[0] = [];
 
         this.positions[0][0] = "ABW";
         // Aktuelle Werte
         this.positions[0][1] = this.getOpti("ABW");
         if (this.tal) {
         // DMI
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
        this.positions[1] = [];
         }
         this.positions[1][0] = "DMI";
         if (this.quo) {
        this.positions[1][1] = this.getOpti("DMI");
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
         // MIT
         }
         this.positions[2] = [];
        if (this.aufw) {
        this.positions[2][0] = "MIT";
            this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
         this.positions[2][1] = this.getOpti("MIT");
         }
         // OMI
         if (this.geb) {
         this.positions[3] = [];
            this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
        this.positions[3][0] = "OMI";
         }
         this.positions[3][1] = this.getOpti("OMI");
        if (this.alter) {
         // STU
            this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
         this.positions[4] = [];
         }
        this.positions[4][0] = "STU";
         if (this.skill) {
         this.positions[4][1] = this.getOpti("STU");
            this.addAndFillCell(playerRow, player.getSkill(), __COLOR, 2);
         // TOR
         }
         this.positions[5] = [];
        if (this.pos) {
        this.positions[5][0] = "TOR";
            this.addAndFillCell(playerRow, player.getPos(), __POS1COLOR);
        this.positions[5][1] = this.getOpti("TOR");
         }
         for (let i = 1; i <= 6; i++) {
            const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
            const __COLI = getColor(__POSI);


            if (i <= this.anzOpti) {
        // Sortieren
                if ((i === 1) || ! player.isGoalie) {
        sortPositionArray(this.positions);
                    // Opti anzeigen
                    this.addAndFillCell(playerRow, player.getOpti(__POSI), __COLI, 2);
                } else {
                    // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
                    this.addCell(playerRow);
                }
            }
            if (i <= this.anzMw) {
                if ((i === 1) || ! player.isGoalie) {
                    // MW anzeigen
                    this.addAndFillCell(playerRow, player.getMarketValue(__POSI), __COLI, 0);
                } else {
                    // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                    this.addCell(playerRow);
                }
            }
        }


         // Werte mit Ende 18
         // Einzelskills mit Ende 18 berechnen
         if (this.skillE) {
         this.skillsEnd = [];
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
 
        }
        const __ADDRATIO = (this.getZatAge(__TIME.end) - this.getZatAge()) / this.getZatAge();
         for (let i = 1; i <= 6; i++) {
 
             const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
         for (let i in this.skills) {
            const __COLI = getColor(__POSI);
            const __SKILL = this.skills[i];
            let progSkill = __SKILL;
 
             if (isTrainableSkill(i)) {
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
                const __ADDSKILL = parseInt((__ADDRATIO * __SKILL).toFixed(0), 10);


            if (i <= this.anzOptiE) {
                 progSkill += __ADDSKILL;
                 if ((i === 1) || ! player.isGoalie) {
                    // Opti anzeigen
                    this.addAndFillCell(playerRow, player.getOpti(__POSI, __TIME.end), __COLI, 2);
                } else {
                    // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
                    this.addCell(playerRow);
                }
            }
            if (i <= this.anzMwE) {
                if ((i === 1) || ! player.isGoalie) {
                    // MW anzeigen
                    this.addAndFillCell(playerRow, player.getMarketValue(__POSI, __TIME.end), __COLI, 0);
                } else {
                    // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                    this.addCell(playerRow);
                }
             }
             }
            this.skillsEnd[i] = Math.min(progSkill, 99);
         }
         }
     };  // Ende addValues(player, playerRow)
     };  // Ende this.initPlayer()
}


// Klasse PlayerRecord ******************************************************************
    this.getGeb = function() {
        return (this.zatGeb < 0) ? '?' : this.zatGeb;
    };


function PlayerRecord(age, skills, isGoalie) {
    this.calcZatAge = function(currZAT) {
    // Zu benutzende Marktwertformel
        if (this.age < 13) {
    const __MWFORMEL = {
            return 0;  // noch nicht trainiert
        'alt' : 0, // Marktwertformel bis Saison 9 inklusive
        } else {
         'S10' : 1   // Marktwertformel MW5 ab Saison 10
            let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72;  // Basiszeit fuer die Jahre seit Jahrgang 13
 
            if (this.zatGeb < 0) {
                return ZATs + currZAT; // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
            } else {
                return ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
            }
        }
    };
 
    this.getZatAge = function(when = __TIME.now) {
         if (when === __TIME.end) {
            return (18 - 12) * 72 - 1// (max.) Trainings-ZATs bis Ende 18
        } else {
            return this.zatAge;
        }
     };
     };


     this.mwFormel = __MWFORMEL.S10;  // Neue Formel, genauer in initPlayer()
     this.getAge = function(when = __TIME.now) {
        if (this.mwFormel === __MWFORMEL.alt) {
            return (when === __TIME.end) ? 18 : this.age;
        } else { // Geburtstage ab Saison 10...
            return (13.00 + this.getZatAge(when) / 72);
        }
    };


     this.age = age;
     this.getTrainierteSkills = function() {
    this.skills = skills;
        let sum = 0;
    this.isGoalie = isGoalie;


    // in this.initPlayer() definiert:
        for (let i in this.skills) {
    // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
            if (isTrainableSkill(i)) {
    // this.zatAge: Bisherige erfolgte Trainings-ZATs
                sum += this.skills[i];
    // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
            }
    // this.aufwert: Aufwertungsstring
        }
    // this.mwFormel: Benutzte MW-Formel, siehe __MWFORMEL
    // this.positions[][]: Positionstext und Opti; TOR-Index ist 5
    // this.skillsEnd[]: Berechnet aus this.skills, this.age und aktuellerZat


    // in this calcZusatz()/setZusatz() definiert:
        return sum;
    // this.trainiert: Anzahl der erfolgreichen Trainingspunkte
     };
     // this.bestPos: erster (bester) Positionstext


     this.toString = function() {
     this.getAufwertungsSchnitt = function() {
         let result = "Alter\t\t" + this.age + "\n\n";
         return parseFloat(this.getTrainierteSkills() / this.getZatAge());
        result += "Aktuelle Werte\n";
    };
        result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
        result += "Optis und Marktwerte";


        for (let pos of this.positions) {
    this.getPos = function(idx) {
            result += "\n\t" + pos + '\t';
        const __IDXOFFSET = 1;
            result += this.getOpti(pos).toFixed(2) + '\t';
        return this.positions[idx - __IDXOFFSET][0];
            result += getNumberString(this.getMarketValue(pos).toString());
    };
        }


        result += "\n\nWerte mit Ende 18\n";
    this.getTalent = function() {
         result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
         return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
        result += "Optis und Marktwerte";
    };


         for (let pos of this.positions) {
    this.getAufwert = function() {
            result += "\n\t" + this.getPos()[i] + '\t';
         return (this.aufwert.length > 0) ? this.aufwert : "keine";
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
    };
             result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
 
    this.getSkill = function(when = __TIME.now) {
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
        let result = 0;
 
        for (let skill of __SKILLS) {
             result += skill;
         }
         }


         return result;
         return result / __SKILLS.length;
     }; // Ende this.toString()
     };


    // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
     this.getOpti = function(pos, when = __TIME.now) {
     this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
         const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
         this.zatGeb = gebZAT;
         const __IDXPRISKILLS = getIdxPriSkills(pos);
         this.zatAge = this.calcZatAge(currZAT);
         const __IDXSECSKILLS = getIdxSecSkills(pos);
         this.talent = tclass;
         let sumPriSkills = 0;
         this.aufwert = progresses;
         let sumSecSkills = 0;
         this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;


         this.positions = [];
         for (let idx of __IDXPRISKILLS) {
        // ABW
            sumPriSkills += __SKILLS[idx];
        this.positions[0] = [];
         }
        this.positions[0][0] = "ABW";
         for (let idx of __IDXSECSKILLS) {
        this.positions[0][1] = this.getOpti("ABW");
            sumSecSkills += __SKILLS[idx];
        // DMI
         }
        this.positions[1] = [];
        this.positions[1][0] = "DMI";
        this.positions[1][1] = this.getOpti("DMI");
        // MIT
        this.positions[2] = [];
        this.positions[2][0] = "MIT";
        this.positions[2][1] = this.getOpti("MIT");
        // OMI
        this.positions[3] = [];
        this.positions[3][0] = "OMI";
        this.positions[3][1] = this.getOpti("OMI");
        // STU
        this.positions[4] = [];
         this.positions[4][0] = "STU";
         this.positions[4][1] = this.getOpti("STU");
        // TOR
        this.positions[5] = [];
         this.positions[5][0] = "TOR";
        this.positions[5][1] = this.getOpti("TOR");


         // Sortieren
         return (5 * sumPriSkills + sumSecSkills) / 27;
        sortPositionArray(this.positions);
    };


        // Einzelskills mit Ende 18 berechnen
    this.getMarketValue = function(pos, when = __TIME.now) {
         this.skillsEnd = [];
         const __AGE = this.getAge(when);


         const __ADDRATIO = (this.getZatDone(__TIME.end) - this.getZatDone()) / this.getZatDone();
         if (this.mwFormel === __MWFORMEL.alt) {
            return Math.round(Math.pow((1 + this.getSkill(when)/100) * (1 + this.getOpti(pos, when)/100) * (2 - __AGE/100), 10) * 2);   // Alte Formel bis Saison 9
        } else {  // MW-Formel ab Saison 10...
            const __MW5TF = 1.00;  // Zwischen 0.97 und 1.03


        for (let i in this.skills) {
            return Math.round(Math.pow(1 + this.getSkill(when)/100, 5.65) * Math.pow(1 + this.getOpti(pos, when)/100, 8.1) * Math.pow(1 + (100 - __AGE)/49, 10) * __MW5TF);
            const __SKILL = this.skills[i];
        }
            let progSkill = __SKILL;
    };
}


            if (isTrainableSkill(i)) {
// Funktionen fuer die HTML-Seite *******************************************************
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
                const __ADDSKILL = getMulValue(__ADDRATIO, __SKILL, 0, NaN);


                progSkill += __ADDSKILL;
// Liest das Alter aus
            }
// return Alter als Zahl
function getAgeFromHTML(cells, colIdxAge) {
    return parseInt(cells[colIdxAge].textContent, 10);
}


            this.skillsEnd[i] = Math.min(progSkill, 99);
// Liest das Geburtsdatum aus
        }
// return Geburtsdatum als ZAT
     };  // Ende this.initPlayer()
function getGebFromHTML(cells, colIdxGeb) {
     const __TEXT = ((cells[colIdxGeb] === undefined) ? '?' : cells[colIdxGeb].textContent);


     // Setzt Nebenwerte fuer den Spieler (geht ohne initPlayer())
     return parseInt((__TEXT === '?') ? -1 : __TEXT, 10);
    this.setZusatz = function(zatAge, trainiert, bestPos) {
}
        this.zatAge = zatAge;
        this.trainiert = trainiert;
        this.bestPos = bestPos;
    };


    // Ermittelt Nebenwerte fuer den Spieler und gibt sie alle zurueck (nach initPlayer())
// Liest die Talentklasse ("wenig", "normal", "hoch") aus
    this.calcZusatz = function() {
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
        // this.zatAge bereits in initPlayer() berechnet
function getTalentFromHTML(cells, colIdxTal) {
        this.trainiert = this.getTrainiert(true);  // neu berechnet aus Skills
    const __TEXT = ((cells[colIdxTal] === undefined) ? '?' : cells[colIdxTal].textContent);
        this.bestPos = this.getPos(-1);  // hier: -1 explizit angeben, da neu ermittelt (this.bestPos noch nicht belegt)


        return {
    return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
                  zatAge    : this.zatAge,
}
                  trainiert : this.trainiert,
                  bestPos  : this.bestPos
              };
    };


    this.getGeb = function() {
// Liest die Aufwertungen aus
        return (this.zatGeb < 0) ? '?' : this.zatGeb;
// return Aufwertungen als String
    };
function getAufwertFromHTML(cells, colIdxAuf) {
    const __TEXT = ((cells[colIdxAuf] === undefined) ? '?' : cells[colIdxAuf].textContent);


     this.calcZatAge = function(currZAT) {
     return __TEXT.toString();
        let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72;  // Basiszeit fuer die Jahre seit Jahrgang 13
}


        if (this.zatGeb < 0) {
// Liest die Einzelskills aus
            return ZATs + currZAT;  // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
// return Skills als Array von Zahlen
        } else {
function getSkillsFromHTML(cells, colIdx) {
            return ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
     const __RESULT = [];
        }
     };


     this.getZatAge = function(when = __TIME.now) {
     for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
        if (when === __TIME.end) {
         __RESULT[i - colIdx.Einz] = parseInt(cells[i].textContent, 10);
            return (18 - 12) * 72 - 1; // (max.) Trainings-ZATs bis Ende 18
        } else {
            return this.zatAge;
         }
    };
 
    this.getZatDone = function(when = __TIME.now) {
        return Math.max(0, this.getZatAge(when));
     }
     }


     this.getAge = function(when = __TIME.now) {
     return __RESULT;
        if (this.mwFormel === __MWFORMEL.alt) {
}
            return (when === __TIME.end) ? 18 : this.age;
        } else {  // Geburtstage ab Saison 10...
            return (13.00 + this.getZatAge(when) / 72);
        }
    };


    this.getTrainiert = function(recalc = false) {
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
        let sum = 0;
// return Angabe, der Spieler Torwart oder Feldspieler ist
function isGoalieFromHTML(cells, colIdxClass) {
    return (cells[colIdxClass].className === "TOR");
}


        if (recalc || (this.trainiert === undefined)) {
// Hilfsfunktionen **********************************************************************
            for (let i in this.skills) {
                if (isTrainableSkill(i)) {
                    sum += this.skills[i];
                }
            }
        } else {
            sum += this.trainiert;
        }


        return sum;
// Sortiert das Positionsfeld per BubbleSort
     };
function sortPositionArray(array) {
    const __TEMP = [];
    let transposed = true;
     // TOR soll immer die letzte Position im Feld sein, deshalb - 1
    let length = array.length - 1;


     this.getAufwertungsSchnitt = function() {
     while (transposed && (length > 1)) {
         return parseFloat(this.getTrainiert() / this.getZatDone());
         transposed = false;
    };
        for (let i = 0; i < length - 1; i++) {
 
            // Vergleich Opti-Werte:
    this.getPos = function(idx = undefined) {
            if (array[i][1] < array[i + 1][1]) {
        const __IDXOFFSET = 1;
                // vertauschen
 
                __TEMP[0] = array[i][0];
        switch (getValue(idx, 0)) {
                __TEMP[1] = array[i][1];
        case -1 : return (this.bestPos = this.positions[isGoalie ? 5 : 0][0]);
                array[i][0] = array[i + 1][0];
        case  0 : return this.bestPos;
                array[i][1] = array[i + 1][1];
        default : return this.positions[idx - __IDXOFFSET][0];
                array[i + 1][0] = __TEMP[0];
                array[i + 1][1] = __TEMP[1];
                transposed = true;
            }
         }
         }
     };
        length--;
     }
}


     this.getTalent = function() {
// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
         return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
    };
function getNumberString(numberString) {
     if (numberString.lastIndexOf(".") !== -1) {
         // Zahl enthaelt Dezimalpunkt
        const __VORKOMMA = numberString.substring(0, numberString.lastIndexOf("."));
        const __NACHKOMMA = numberString.substring(numberString.lastIndexOf(".") + 1, numberString.length);


    this.getAufwert = function() {
         return getNumberString(__VORKOMMA) + "," + __NACHKOMMA;
         return (this.aufwert.length > 0) ? this.aufwert : "keine";
     } else {
     };
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
        const __TEMP = reverseString(numberString);
        let result = "";


    this.getSkill = function(when = __TIME.now) {
        for (let i = 0; i < __TEMP.length; i++) {
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
            if ((i > 0) && (i % 3 === 0)) {
        let result = 0;
                result += ".";
 
            }
        for (let skill of __SKILLS) {
             result += __TEMP.substr(i, 1);
             result += skill;
         }
         }


         return result / __SKILLS.length;
         return reverseString(result);
     };
     }
}


    this.getOpti = function(pos, when = __TIME.now) {
// Dreht den uebergebenen String um
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
function reverseString(string) {
        const __IDXPRISKILLS = getIdxPriSkills(pos);
    let result = "";
        const __IDXSECSKILLS = getIdxSecSkills(pos);
        let sumPriSkills = 0;
        let sumSecSkills = 0;


        for (let idx of __IDXPRISKILLS) {
    for (let i = string.length - 1; i >= 0; i--) {
            sumPriSkills += __SKILLS[idx];
        result += string.substr(i, 1);
        }
    }
        for (let idx of __IDXSECSKILLS) {
            sumSecSkills += __SKILLS[idx];
        }


        return (5 * sumPriSkills + sumSecSkills) / 27;
    return result;
    };
}


    this.getMarketValue = function(pos, when = __TIME.now) {
// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert
        const __AGE = this.getAge(when);
// Die Indizes gehen von 0 (SCH) bis 16 (EIN)
function isTrainableSkill(idx) {
    const __TRAINABLESKILLS = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15];
    const __IDX = parseInt(idx, 10);
    let result = false;


         if (this.mwFormel === __MWFORMEL.alt) {
    for (let idxTrainable of __TRAINABLESKILLS) {
             return Math.round(Math.pow((1 + this.getSkill(when)/100) * (1 + this.getOpti(pos, when)/100) * (2 - __AGE/100), 10) * 2);    // Alte Formel bis Saison 9
         if (__IDX === idxTrainable) {
        } else {  // MW-Formel ab Saison 10...
             result = true;
            const __MW5TF = 1.00; // Zwischen 0.97 und 1.03
             break;
 
             return Math.round(Math.pow(1 + this.getSkill(when)/100, 5.65) * Math.pow(1 + this.getOpti(pos, when)/100, 8.1) * Math.pow(1 + (100 - __AGE)/49, 10) * __MW5TF);
         }
         }
    };
}
// Funktionen fuer die HTML-Seite *******************************************************
// Liest eine Zahl aus der Spalte einer Zeile der Tabelle aus (z.B. Alter, Geburtsdatum)
// cells: Die Zellen einer Zeile
// colIdxInt: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Zahl (-1 fuer "keine Zahl", undefined fuer "nicht gefunden")
function getIntFromHTML(cells, colIdxInt) {
    const __CELL = cells[colIdxInt];
    const __TEXT = getValue(__CELL, { }).textContent;
    if (__TEXT !== undefined) {
        try {
            const __VALUE = parseInt(__TEXT, 10);
            if (! isNaN(__VALUE)) {
                return __VALUE;
            }
        } catch (ex) { }
        return -1;
     }
     }


     return undefined;
     return result;
}
}


// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus
// Gibt die Indizes der Primaerskills zurueck
// cells: Die Zellen einer Zeile
function getIdxPriSkills(pos) {
// colIdxInt: Spaltenindex der gesuchten Werte
    switch (pos) {
// return Spalteneintrag als Dezimalzahl (undefined fuer "keine Zahl" oder "nicht gefunden")
        case "TOR" : return new Array(2, 3, 4, 5);
function getFloatFromHTML(cells, colIdxFloat) {
        case "ABW" : return new Array(2, 3, 4, 15);
    const __CELL = cells[colIdxFloat];
        case "DMI" : return new Array(1, 4, 9, 11);
    const __TEXT = getValue(__CELL, { }).textContent;
        case "MIT" : return new Array(1, 3, 9, 11);
 
        case "OMI" : return new Array(1, 5, 9, 11);
    if (__TEXT !== undefined) {
         case "STU" : return new Array(0, 2, 3, 5);
         try {
         default : return [];
            return parseFloat(__TEXT);
         } catch (ex) { }
     }
     }
    return undefined;
}
}


// Liest einen String aus der Spalte einer Zeile der Tabelle aus
// Gibt die Indizes der Sekundaerskills zurueck
// cells: Die Zellen einer Zeile
function getIdxSecSkills(pos) {
// colIdxStr: Spaltenindex der gesuchten Werte
    switch (pos) {
// return Spalteneintrag als String ("" fuer "nicht gefunden")
        case "TOR" : return new Array(0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
function getStringFromHTML(cells, colIdxStr) {
        case "ABW" : return new Array(0, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16);
    const __CELL = cells[colIdxStr];
        case "DMI" : return new Array(0, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
     const __TEXT = getValue(__CELL, { }).textContent;
        case "MIT" : return new Array(0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
        case "OMI" : return new Array(0, 2, 3, 4, 6, 7, 8, 10, 12, 13, 14, 15, 16);
        case "STU" : return new Array(1, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
        default : return [];
     }
}


     return getValue(__TEXT.toString(), "");
// Gibt die zur Position gehoerige Farbe zurueck
function getColor(pos) {
     switch (pos) {
        case "TOR" : return "#FFFF00";
        case "ABW" : return "#00FF00";
        case "DMI" : return "#3366FF";
        case "MIT" : return "#66FFFF";
        case "OMI" : return "#FF66FF";
        case "STU" : return "#FF0000";
        case "LEI" : return "#FFFFFF";
        default : return "";
    }
}
}


// Liest die Talentklasse ("wenig", "normal", "hoch") aus der Spalte einer Zeile der Tabelle aus
// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
function getTalentFromHTML(cells, colIdxTal) {
    const __TEXT = getStringFromHTML(cells, colIdxTal);


    return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
}


// Liest die Einzelskills aus der Spalte einer Zeile der Tabelle aus
const __GAMETYPES = {    // "Blind FSS gesucht!"
// cells: Die Zellen einer Zeile
        'unbekannt'  : -1,
// colIdx: Liste von Spaltenindices der gesuchten Werte mit den Eintraegen
        "reserviert" :  0,
// 'Einz' (erste Spalte) und 'Zus' (Spalte hinter dem letzten Eintrag)
        "Frei"      :  0,
// return Skills als Array von Zahlen
        "spielfrei"  :  0,
function getSkillsFromHTML(cells, colIdx) {
        "Friendly"  :  1,
     const __RESULT = [];
        "Liga"      : 2,
        "LP"        : 3,
        "OSEQ"      :  4,
        "OSE"        :  5,
        "OSCQ"      :  6,
        "OSC"        :  7
     };


    for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
const __LIGANRN = {
         __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
        '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
     };


    return __RESULT;
const __LANDNRN = {
}
        'unbekannt'              :  0,
 
        'Albanien'              :  45,
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
        'Andorra'                :  95,
// return Angabe, der Spieler Torwart oder Feldspieler ist
        'Armenien'              :  83,
function isGoalieFromHTML(cells, colIdxClass) {
        'Aserbaidschan'          : 104,
    return (cells[colIdxClass].className === "TOR");
        'Belgien'                :  12,
}
        'Bosnien-Herzegowina'    :  66,
 
        'Bulgarien'              :  42,
// Hilfsfunktionen **********************************************************************
        'Dänemark'              :  8,
 
        'Deutschland'            :  6,
// Sortiert das Positionsfeld per BubbleSort
        'England'                :  1,
function sortPositionArray(array) {
        'Estland'                :  57,
    const __TEMP = [];
        'Faröer'                :  68,
    let transposed = true;
        'Finnland'              :  40,
    // TOR soll immer die letzte Position im Feld sein, deshalb - 1
        'Frankreich'            :  32,
    let length = array.length - 1;
        'Georgien'              :  49,
 
        'Griechenland'          :  30,
    while (transposed && (length > 1)) {
        'Irland'                :  5,
         transposed = false;
        'Island'                :  29,
         for (let i = 0; i < length - 1; i++) {
        'Israel'                :  23,
             // Vergleich Opti-Werte:
        'Italien'                :  10,
             if (array[i][1] < array[i + 1][1]) {
        'Kasachstan'            : 105,
                // vertauschen
        'Kroatien'              :  24,
                __TEMP[0] = array[i][0];
        'Lettland'              :  97,
                __TEMP[1] = array[i][1];
        'Liechtenstein'          :  92,
                array[i][0] = array[i + 1][0];
        'Litauen'                :  72,
                array[i][1] = array[i + 1][1];
        'Luxemburg'              :  93,
                 array[i + 1][0] = __TEMP[0];
        'Malta'                  :  69,
                array[i + 1][1] = __TEMP[1];
        'Mazedonien'            :  86,
                 transposed = true;
        'Moldawien'              :  87,
            }
        'Niederlande'            :  11,
         }
        'Nordirland'            :  4,
         length--;
        'Norwegen'              :  9,
     }
        'Österreich'            :  14,
}
        'Polen'                  :  25,
        'Portugal'              :  17,
         'Rumänien'              :  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ürkei'                 :  39,
        'Ukraine'                :  20,
        'Ungarn'                 :  26,
        'Wales'                  :  3,
         'Weissrussland'          :  71,
         'Zypern'                :  38
     };


// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
// ==================== Abschnitt fuer Daten des Spielplans ====================
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
function getNumberString(numberString) {
    if (numberString.lastIndexOf(".") !== -1) {
        // Zahl enthaelt Dezimalpunkt
        const __VORKOMMA = numberString.substring(0, numberString.lastIndexOf("."));
        const __NACHKOMMA = numberString.substring(numberString.lastIndexOf(".") + 1, numberString.length);


        return getNumberString(__VORKOMMA) + "," + __NACHKOMMA;
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
    } else {
// gameType: Name des Wettbewerbs eines Spiels
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
function getGameTypeID(gameType) {
        const __TEMP = reverseString(numberString);
    return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);
        let result = "";
}


        for (let i = 0; i < __TEMP.length; i++) {
// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
            if ((i > 0) && (i % 3 === 0)) {
// land: Name des Landes
                result += ".";
// return OS2-ID des Landes, 0 fuer ungueltig
            }
function getLandNr(land) {
            result += __TEMP.substr(i, 1);
    return getValue(__LANDNRN[land], __LANDNRN.unbekannt);
        }
}


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


// Dreht den uebergebenen String um
// ==================== Abschnitt fuer sonstige Parameter ====================
function reverseString(string) {
    let result = "";


    for (let i = string.length - 1; i >= 0; i--) {
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
         result += string.substr(i, 1);
        'Zeile'  : 0,
    }
        'Spalte' : 1,
 
        'start'  : " von ",
     return result;
        'middle' : "</b><br>",
}
         'liga'  : ". Liga",
        'land'  : ' ',
        'end'    : "<a href="
     };


// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
// Die Indizes gehen von 0 (SCH) bis 16 (EIN)
        'Zeile'  : 0,
function isTrainableSkill(idx) {
        'Spalte' : 0,
    const __TRAINABLESKILLS = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15];
        'start'  : "<b>",
    const __IDX = parseInt(idx, 10);
        'middle' : " - ",
     let result = false;
        'liga'  : ". Liga",
        'land'  : 'target="_blank">',
        'end'    : "</a></b>"
     };


    for (let idxTrainable of __TRAINABLESKILLS) {
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
        if (__IDX === idxTrainable) {
// cell: Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
            result = true;
// teamSeach: Muster fuer die Suche, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt
            break;
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
        }
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
     }
function getTeamParamsFromTable(table, teamSearch = undefined) {
 
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
     return result;
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
}
    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);


// Gibt die Indizes der Primaerskills zurueck
     let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
function getIdxPriSkills(pos) {
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
     switch (pos) {
    const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
        case "TOR" : return new Array(2, 3, 4, 5);
        case "ABW" : return new Array(2, 3, 4, 15);
        case "DMI" : return new Array(1, 4, 9, 11);
        case "MIT" : return new Array(1, 3, 9, 11);
        case "OMI" : return new Array(1, 5, 9, 11);
        case "STU" : return new Array(0, 2, 3, 5);
        default : return [];
    }
}


// Gibt die Indizes der Sekundaerskills zurueck
    let land = (__INDEXLIGA > 0) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
function getIdxSecSkills(pos) {
     const __TEAM = (__INDEXMIDDLE > 0) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
     switch (pos) {
    let liga = ((__INDEXLIGA > 0) && (__INDEXMIDDLE > 0)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;
        case "TOR" : return new Array(0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
 
        case "ABW" : return new Array(0, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16);
    if (land !== undefined) {
         case "DMI" : return new Array(0, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
         if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
         case "MIT" : return new Array(0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
            land = land.substr(2);
         case "OMI" : return new Array(0, 2, 3, 4, 6, 7, 8, 10, 12, 13, 14, 15, 16);
         }
         case "STU" : return new Array(1, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
        if (liga !== undefined) {
         default : return [];
            liga = liga.substring(0, liga.length - land.length);
         }
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
         if (__INDEXLAND > -1) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
         }
     }
     }
    const __RET = {
        'Team' : __TEAM,
        'Liga' : liga,
        'Land' : land,
        'LdNr' : getLandNr(land),
        'LgNr' : getLigaNr(liga)
    };
    return __RET;
}
}


// Gibt die zur Position gehoerige Farbe zurueck
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
function getColor(pos) {
// url: Adresse der Seite
     switch (pos) {
// return Parameter aus der URL der Seite als Nummer
        case "TOR" : return "#FFFF00";
function getPageIdFromURL(url) {
        case "ABW" : return "#00FF00";
     // Variablen zur Identifikation der Seite
        case "DMI" : return "#3366FF";
    const __SUCH = "page=";
         case "MIT" : return "#66FFFF";
    const __INDEXS = url.lastIndexOf(__SUCH);
         case "OMI" : return "#FF66FF";
    const __HAUPT = url.match(/haupt\.php/);        // Ansicht "Haupt" (Managerbuero)
         case "STU" : return "#FF0000";
    const __JU = url.match(/ju\.php/);              // Ansicht "Jugendteam"
         case "LEI" : return "#FFFFFF";
    let page = -1;                                 // Seitenindex (Rueckgabewert)
         default : return "";
 
    // Wert von page (Seitenindex) ermitteln...
    // Annahme: Entscheidend ist jeweils das letzte Vorkommnis von "page=" und ggf. von '&'
    if (__HAUPT) {
         page = 0;
    } else if (__JU) {
         if (__INDEXS < 0) {
            page = 1;
         } else if (url.indexOf('&', __INDEXS) < 0) {
            // Wert von page setzt sich aus allen Zeichen hinter "page=" zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.length), 10);
         } else {
            // Wert von page setzt sich aus allen Zeichen zwischen "page=" und '&' zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.indexOf('&', __INDEXS)), 10);
         }
     }
     }
}


// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
    return page;
}


// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
// Gibt die laufende Nummer des ZATs im Text einer Zelle zurueck
// cell: Tabellenzelle mit der ZAT-Nummer im Text
// return ZAT-Nummer im Text
function getZATNrFromCell(cell) {
    const __TEXT = cell.textContent.split(' ');
    let ZATNr = 0;


const __GAMETYPES = {   // "Blind FSS gesucht!"
    for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
         'unbekannt'  : -1,
         if (__TEXT[i - 1] === "ZAT") {
        "reserviert" :  0,
            if (__TEXT[i] !== "ist") {
        "Frei"       :  0,
                ZATNr = parseInt(__TEXT[i], 10);
        "spielfrei"  :  0,
            }
        "Friendly"  :  1,
         }
         "Liga"      :  2,
     }
        "LP"        :  3,
        "OSEQ"      :  4,
        "OSE"        :  5,
        "OSCQ"      :  6,
        "OSC"        :  7
     };


const __LIGANRN = {
    return ZATNr;
        'unbekannt'  :  0,
}
        '1. Liga'    :  1,
 
        '2. Liga A' :  2,
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
        '2. Liga B'  :  3,
 
        '3. Liga A'  :  4,
// ==================== Hauptprogramm ====================
        '3. Liga B'  :  5,
 
        '3. Liga C'  :  6,
// Verarbeitet Ansicht "Haupt" (Managerbuero) zur Ermittlung des aktuellen ZATs
        '3. Liga D'  :  7
function procHaupt() {
    };
    const __TEAMPARAMS = getTeamParamsFromTable(getTable(1), __TEAMSEARCHHAUPT); // Link mit Team, Liga, Land...


const __LANDNRN = {
    buildOptions(__OPTCONFIG, __OPTSET, {
        'unbekannt'             :   0,
                    'teamParams' : __TEAMPARAMS,
        'Albanien'               : 45,
                    'hideMenu'   : true
        'Andorra'                :  95,
                });
        'Armenien'              :  83,
 
        'Aserbaidschan'          : 104,
    const __NEXTZAT = getZATNrFromCell(getRows(0)[2].cells[0]); // "Der naechste ZAT ist ZAT xx und ..."
        'Belgien'                : 12,
    const __CURRZAT = __NEXTZAT - 1;
        'Bosnien-Herzegowina'    :  66,
 
        'Bulgarien'              :  42,
    if (__CURRZAT >= 0) {
        'D\xE4nemark'            :  8,
         console.log("Aktueller ZAT: " + __CURRZAT);
        'Deutschland'            :  6,
 
        'England'                :  1,
         setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
        'Estland'                :  57,
    }
        'Far\xF6er'              :  68,
}
         'Finnland'              : 40,
 
        'Frankreich'            :  32,
// Verarbeitet Ansicht "Teamuebersicht"
         'Georgien'              :  49,
function procTeamuebersicht() {
        'Griechenland'          :  30,
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
        'Irland'                :  5,
    const __ROWOFFSETLOWER = 1;    // Ziehen-Button
        'Island'                :  29,
 
        'Israel'                :  23,
    const __COLUMNINDEX = {
        'Italien'                :  10,
         'Age'   : 0,
        'Kasachstan'            : 105,
         'Geb'   : 1,
        'Kroatien'              :  24,
         'Flg'   : 2,
        'Lettland'              :  97,
         'Land'  : 3,
        'Liechtenstein'          :  92,
         'U'     : 4,
        'Litauen'                :  72,
         'Skill' : 5,
         'Luxemburg'             : 93,
         'Tal'   : 6,
         'Malta'                 : 69,
         'Akt'   : 7,
         'Mazedonien'             : 86,
         'Auf'  : 8,
         'Moldawien'             : 87,
         'Zus'  : 9
        '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
     };
     };


// ==================== Abschnitt fuer Daten des Spielplans ====================
    const __BIRTHDAYS = [];
    const __TCLASSES = [];
    const __PROGRESSES = [];


// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
    if (getRows(1) === undefined) {
// gameType: Name des Wettbewerbs eines Spiels
        console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
    } else if (getTable(1).length < 3) {
function getGameTypeID(gameType) {
        console.log("Ziehen-Seite");
     return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);
    } else {
}
        buildOptions(__OPTCONFIG, __OPTSET, {
                        'menuAnchor' : getTable(0, "div"),
                        'showForm' : {
                                        'sepStyle'    : true,
                                        'sepColor'     : true,
                                        'sepWidth'    : true,
                                        'aktuellerZat' : true,
                                        'team'        : true,
                                        'reset'        : true,
                                        'showForm'    : true
                                      },
                        'formWidth' : 1
                    });


// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
         const __ROWS = getRows(1);
// land: Name des Landes
// return OS2-ID des Landes, 0 fuer ungueltig
function getLandNr(land) {
    return getValue(__LANDNRN[land], __LANDNRN.unbekannt);
}
 
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
// land: Name der Liga
// return OS2-ID der Liga, 0 fuer ungueltig
function getLigaNr(liga) {
    return getValue(__LIGANRN[liga], __LIGANRN.unbekannt);
}
 
// ==================== Abschnitt fuer sonstige Parameter ====================
 
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
        '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>"
        'Zeile'  : 0,
        'Spalte' : 0,
        'start'  : "<b>",
        'middle' : " - ",
        'liga'  : ". Liga",
        'land'  : 'target="_blank">',
        'end'    : "</a></b>"
    };
 
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
// cell: Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
// teamSeach: Muster fuer die Suche, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
function getTeamParamsFromTable(table, teamSearch = undefined) {
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
    const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
    const __TEAMCELLSTR  = table.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 > 0) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
    const __TEAM = (__INDEXMIDDLE > 0) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
    let liga = ((__INDEXLIGA > 0) && (__INDEXMIDDLE > 0)) ? 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 > -1) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
        }
    }
 
    const __RET = {
        'Team' : __TEAM,
        'Liga' : liga,
        'Land' : land,
        'LdNr' : getLandNr(land),
        'LgNr' : getLigaNr(liga)
    };
 
    return __RET;
}
 
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// url: Adresse der Seite
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url) {
    // Variablen zur Identifikation der Seite
    const __SUCH = "page=";
    const __INDEXS = url.lastIndexOf(__SUCH);
    const __HAUPT = url.match(/haupt\.php/);        // Ansicht "Haupt" (Managerbuero)
    const __JU = url.match(/ju\.php/);              // Ansicht "Jugendteam"
    let page = -1;                                  // Seitenindex (Rueckgabewert)
 
    // Wert von page (Seitenindex) ermitteln...
    // Annahme: Entscheidend ist jeweils das letzte Vorkommnis von "page=" und ggf. von '&'
    if (__HAUPT) {
        page = 0;
    } else if (__JU) {
        if (__INDEXS < 0) {
            page = 1;
        } else if (url.indexOf('&', __INDEXS) < 0) {
            // Wert von page setzt sich aus allen Zeichen hinter "page=" zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.length), 10);
        } else {
            // Wert von page setzt sich aus allen Zeichen zwischen "page=" und '&' zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.indexOf('&', __INDEXS)), 10);
        }
    }
 
    return page;
}
 
// Gibt die laufende Nummer des ZATs im Text einer Zelle zurueck
// cell: Tabellenzelle mit der ZAT-Nummer im Text
// return ZAT-Nummer im Text
function getZATNrFromCell(cell) {
    const __TEXT = cell.textContent.split(' ');
    let ZATNr = 0;
 
    for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
        if (__TEXT[i - 1] === "ZAT") {
            if (__TEXT[i] !== "ist") {
                ZATNr = parseInt(__TEXT[i], 10);
            }
        }
    }


    return ZATNr;
        for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
}
            __BIRTHDAYS[i - __ROWOFFSETUPPER] = getGebFromHTML(__ROWS[i].cells, __COLUMNINDEX.Geb);
 
            __TCLASSES[i - __ROWOFFSETUPPER] = getTalentFromHTML(__ROWS[i].cells, __COLUMNINDEX.Tal);
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
             __PROGRESSES[i - __ROWOFFSETUPPER] = getAufwertFromHTML(__ROWS[i].cells, __COLUMNINDEX.Auf);
 
// ==================== Hauptprogramm ====================
 
// Verarbeitet Ansicht "Haupt" (Managerbuero) zur Ermittlung des aktuellen ZATs
function procHaupt() {
    const __TEAMPARAMS = getTeamParamsFromTable(getTable(1), __TEAMSEARCHHAUPT); // Link mit Team, Liga, Land...
 
    buildOptions(__OPTCONFIG, __OPTSET, {
                    'teamParams' : __TEAMPARAMS,
                    'hideMenu'  : true
                });
 
    const __NEXTZAT = getZATNrFromCell(getRows(0)[2].cells[0]); // "Der naechste ZAT ist ZAT xx und ..."
    const __CURRZAT = __NEXTZAT - 1;
    const __DATAZAT = getOptValue(__OPTSET.datenZat);
 
    if (__CURRZAT >= 0) {
        console.log("Aktueller ZAT: " + __CURRZAT);
 
        // Neuen aktuellen ZAT speichern...
        setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
 
        if (__CURRZAT !== __DATAZAT) {
            console.log(__DATAZAT + " => " + __CURRZAT);
 
             // ... und ZAT-bezogene Daten als veraltet markieren
            deleteOptions(__OPTSET, __DATAOPTS, true, true);
 
            // Neuen Daten-ZAT speichern...
            setOpt(__OPTSET.datenZat, __CURRZAT, false);
         }
         }
    }
}
// Verarbeitet Ansicht "Teamuebersicht"
function procTeamuebersicht() {
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
    const __ROWOFFSETLOWER = 1;    // Ziehen-Button


    const __COLUMNINDEX = {
         setOpt(__OPTSET.birthdays, __BIRTHDAYS, false);
        'Age'  : 0,
         setOpt(__OPTSET.tclasses, __TCLASSES, false);
         'Geb'  : 1,
         setOpt(__OPTSET.progresses, __PROGRESSES, false);
        'Flg'  : 2,
        'Land'  : 3,
        'U'    : 4,
        'Skill' : 5,
        'Tal'  : 6,
        'Akt'  : 7,
        'Auf'  : 8,
        'Zus'  : 9
    };
 
    if (getElement('transfer') !== undefined) {
        console.log("Ziehen-Seite");
    } else if (getRows(1) === undefined) {
        console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
    } else {
        buildOptions(__OPTCONFIG, __OPTSET, {
                        'menuAnchor' : getTable(0, "div"),
                        'showForm' : {
                                        'sepStyle'      : true,
                                        'sepColor'      : true,
                                        'sepWidth'      : true,
                                        'aktuellerZat'  : true,
                                        'team'          : true,
                                        'zeigeAlter'    : true,
                                        'zeigeQuote'    : true,
                                        'zeigePosition' : true,
                                        'zatAges'      : true,
                                        'trainiert'    : true,
                                        'positions'    : true,
                                        'reset'        : true,
                                        'showForm'      : true
                                      },
                        'formWidth' : 1
                    });
 
         const __COLMAN = new ColumnManager(__OPTSET, {
                                        'zeigeAlter'    : getOptValue(__OPTSET.zatAges, false, true),
                                        'zeigeQuote'    : getOptValue(__OPTSET.trainiert, false, true),
                                        'zeigePosition' : getOptValue(__OPTSET.positions, false, true)
                                    });
        const __ROWS = getRows(1);
        const __HEADERS = __ROWS[0];
        const __TITLECOLOR = getColor("LEI");  // "#FFFFFF"
 
        __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
 
         const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, true);
 
        for (let i = 0; i < __PLAYERS.length; i++) {
            __COLMAN.addValues(__PLAYERS[i], __ROWS[i + __ROWOFFSETUPPER], __TITLECOLOR);
        }


         // Format der Trennlinie zwischen den Monaten...
         // Format der Trennlinie zwischen den Monaten...
Zeile 2.903: Zeile 2.590:
         buildOptions(__OPTCONFIG, __OPTSET, {
         buildOptions(__OPTCONFIG, __OPTSET, {
                         'menuAnchor' : getTable(0, "div"),
                         'menuAnchor' : getTable(0, "div"),
                        'hideForm' : {
                                        'zatAges'      : true,
                                        'trainiert'    : true,
                                        'positions'    : true
                                      },
                         'formWidth'  : 1
                         'formWidth'  : 1
                     });
                     });
Zeile 2.918: Zeile 2.600:
         __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
         __COLMAN.addTitles(__HEADERS, __TITLECOLOR);


         const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, false);
         const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER);


         for (let i = 0; i < __PLAYERS.length; i++) {
         for (let i = 0; i < __PLAYERS.length; i++) {

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)