OS2.jugend.user.js: Unterschied zwischen den Versionen

Aus Online-Soccer-Wiki
Zur Navigation springen Zur Suche springen
(Update Version 0.46 (Hotfix Teamparameter))
(Update Version 0.48 (Alter unter 13.00, Initialversion Script-DB))
 
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
// ==UserScript==
// ==UserScript==
// @name       OS2.jugendV4
// @name         OS2.jugend
// @namespace   http://os.ongapo.com/
// @namespace   http://os.ongapo.com/
// @version     0.46
// @version     0.48
// @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 71: Zeile 72:
               }
               }
};
};
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 131: Zeile 108:
                   'FormLabel' : "Aufwertung"
                   'FormLabel' : "Aufwertung"
               },
               },
     'zeigePosition' : {   // Position anzeigen
     'zeigeGeb' : {       // Spaltenauswahl fuer Geburtstage (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showPos",
                   'Name'      : "showBirthday",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : false,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Position ein",
                   'Label'    : "Geb. ein",
                   'Hotkey'    : 'P',
                   'Hotkey'    : 'G',
                   'AltLabel'  : "Position aus",
                   'AltLabel'  : "Geb. aus",
                   'AltHotkey' : 'P',
                   'AltHotkey' : 'G',
                   'FormLabel' : "Position"
                   '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"
               },
               },
     'zeigeSkill' : {      // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
     'zeigeSkill' : {      // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
Zeile 152: Zeile 140:
                   '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 284: Zeile 283:
                   'FormLabel' : "ZAT:|$"
                   'FormLabel' : "ZAT:|$"
               },
               },
     'birthdays' : {       // Datenspeicher fuer Geburtstage der Jugendspieler
     'datenZat' : {       // Stand der Daten zum Team und ZAT
                   'Name'      : "birthdays",
                   'Name'      : "dataZAT",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : false,
                  'ValType'  : "Number",
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : [],
                   'Default'  : undefined,
                  'Action'    : __OPTACTION.SET,
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                  'Cols'      : 1,
                   'Rows'      : 2,
                  'Rows'      : 1,
                  '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 329: Zeile 345:
                   'Label'    : "Aufwertungen:"
                   'Label'    : "Aufwertungen:"
               },
               },
     'team' : {           // Datenspeicher fuer Daten des Erst- bzw. Zweitteams
     'zatAges' : {         // Datenspeicher fuer (gebrochene) Alter der Jugendspieler
                   'Name'      : "team",
                   'Name'      : "zatAges",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : false,
                   'Hidden'    : false,
                   'Serial'    : true,
                   'Serial'    : true,
                  'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 },
                   'Default'  : [],
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 6,
                   'Rows'      : 2,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 1,
                   'Space'    : 0,
                   'Label'    : "Verein:"
                   'Label'    : "ZAT-Alter:"
               },
               },
     'reset' : {           // Optionen auf die "Werkseinstellungen" zuruecksetzen
     'trainiert' : {       // Datenspeicher fuer Trainingsquoten der Jugendspieler
                   'Name'      : "reset",
                   'Name'      : "numProgresses",
                   'Type'      : __OPTTYPES.SI,
                   'Type'      : __OPTTYPES.SD,
                   'Action'    : __OPTACTION.RST,
                  'Hidden'    : false,
                   'Label'     : "Standard-Optionen",
                   'Serial'    : true,
                   'Hotkey'    : 'r',
                  'AutoReset' : true,
                   'FormLabel' : ""
                  'Permanent' : true,
                   'Default'   : [],
                   'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'     : 2,
                   'Replace'   : null,
                  'Space'    : 0,
                  'Label'    : "Trainiert:"
               },
               },
     'storage' : {         // Browserspeicher fuer die Klicks auf Optionen
     'positions' : {       // Datenspeicher fuer optimale Positionen der Jugendspieler
                   'Name'      : "storage",
                   'Name'      : "positions",
                  '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,
                   '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'  : false,
                  'Default'  : [],
                   'Action'    : __OPTACTION.NXT,
                  'Submit'    : undefined,
                   'Label'    : "Optionen anzeigen",
                  'Cols'      : 36,
                   'Hotkey'    : 'O',
                  'Rows'      : 3,
                   'AltLabel'  : "Optionen verbergen",
                  'Replace'  : null,
                   'AltHotkey' : 'O',
                  'Space'    : 0,
                  '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' : ""
              },
    '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' : ""
                   'FormLabel' : ""
               }
               }
Zeile 386: Zeile 447:
// ==================== Abschnitt fuer diverse Utilities ====================
// ==================== Abschnitt fuer diverse Utilities ====================


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


Zeile 422: Zeile 483:


     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 487: Zeile 564:
}
}


// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// Fuehrt die in einem Storage gespeicherte Operation aus
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// optSet: Set mit den Optionen
function canUseStorage(storage = undefined) {
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
    const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
function runStored(optSet, memory = undefined) {
    const __MEMORY = __STORAGE.Value;
     const __STORAGE = getMemory(memory);
    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
// optSet: Set mit den Optionen
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
function runStored(optSet, storage = undefined) {
     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 594: Zeile 650:
// 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 622: Zeile 682:
// ==================== Ende Abschnitt fuer diverse Utilities ====================
// ==================== Ende Abschnitt fuer diverse Utilities ====================


// ==================== Abschnitt fuer das Benutzermenu ====================
// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
 
// Namen des Default-, Dauer- und Null-Memories...
const __MEMNORMAL  = 'normal';
const __MEMINFINITE = 'unbegrenzt';
const __MEMINAKTIVE = 'inaktiv';


// Zeigt den Eintrag im Menu einer Option
// Definition des Default-, Dauer- und Null-Memories...
// opt: Derzeitiger Wert der Option
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
// menuOn: Text zum Setzen im Menu
const __OPTMEMINFINITE = __OPTMEM[__MEMINFINITE];
// funOn: Funktion zum Setzen
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];
// 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 ? "" : '*');


    console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
// Medium fuer die Datenbank (Speicher)
let myOptMem = __OPTMEMNORMAL;


    if (opt) {
// Speicher fuer die DB-Daten
        GM_registerMenuCommand(menuOff, funOff, keyOff);
const __DBMEM = __OPTMEMNORMAL.Value;
    } else {
 
        GM_registerMenuCommand(menuOn, funOn, keyOn);
// Infos ueber dieses Script-Modul
    }
const __DBMOD = { };
}
 
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
const __DBTOC = { };


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// Daten zu den Modulen (indiziert durch die Script-Namen)
// opt: Derzeitiger Wert der Option
const __DBDATA = { };
// arr: Array-Liste mit den moeglichen Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// fun: Funktion zum Setzen des naechsten Wertes
// key: Hotkey zum Setzen des naechsten Wertes im Menu
function registerNextMenuOption(opt, arr, menu, fun, key) {
    const __MENU = menu.replace('$', opt);
    let options = "OPTION " + __MENU;


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


     GM_registerMenuCommand(__MENU, fun, key);
// 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));
}
}


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// opt: Derzeitiger Wert der Option
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
function canUseMemory(memory = undefined) {
// fun: Funktion zum Setzen des naechsten Wertes
     const __STORAGE = getMemory(memory);
// key: Hotkey zum Setzen des naechsten Wertes im Menu
    const __MEMORY = __STORAGE.Value;
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar)
    let ret = false;
// serial: Serialization fuer komplexe Daten
 
function registerDataOption(opt, menu, fun, key, hidden = false, serial = true) {
    if (__MEMORY !== undefined) {
     const __VALUE = ((serial && (opt !== undefined)) ? JSON.stringify(opt) : opt);
        const __TESTPREFIX = 'canUseStorageTest';
    const __MENU = getValue(menu, "").replace('$', __VALUE);
        const __TESTDATA = Math.random().toString();
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
        const __TESTITEM = __TESTPREFIX + __TESTDATA;
                      getValue(__VALUE, "", " = " + __VALUE);
 
        __MEMORY.setItem(__TESTITEM, __TESTDATA);
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
        __MEMORY.removeItem(__TESTITEM);
     }
 
    console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);
 
    return ret;
}
 
// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// opt: Option zur Wahl des Speichers
// return Gesuchter Speicher oder Null-Speicher ('inaktiv')
function restoreMemoryByOpt(opt) {
    // Memory Storage fuer vorherige Speicherung...
    const __STORAGE = loadOption(getOpt(opt), true);
 
    return __OPTMEM[getValue(__STORAGE, __MEMNORMAL)];
}
 
// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
// opt: Option zur Wahl des Speichers
// 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];


     console.log(__OPTIONS);
     if (! canUseMemory(optMem)) {
        if (storage !== __MEMINAKTIVE) {
            storage = __MEMINAKTIVE;
            optMem = __OPTMEM[storage];
        }
    }


     if (! hidden) {
     if (saveOpt !== undefined) {
         GM_registerMenuCommand(__MENU, fun, key);
         setOpt(saveOpt, storage, false);
     }
     }
    return optMem;
}
}


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


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


// ==================== Ende Abschnitt fuer das Benutzermenu ====================
// Setzt die Daten dieses Scriptes in der Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// optSet: Gesetzte Optionen (und Config)
function updateScriptDB(optSet) {
    // Eintrag ins Inhaltsverzeichnis...
    __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
 
    // Permanente Speicherung der Eintraege...
    __DBMEM.setItem('__DBTOC.versions', JSON.stringify(__DBTOC.versions));
    __DBMEM.setItem('__DBDATA.' + __DBMOD.name, JSON.stringify(optSet));
 
    // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
    __DBDATA[__DBMOD.name] = getValue(optSet, { });


// Initialisiert die gesetzten Option
    console.log(__DBDATA);
// config: Konfiguration der Option
}
// return Initialwert der gesetzten Option
function initOptValue(config) {
    let value = config.Default;  // Standard


    switch (config.Type) {
// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
                            value = config.Choice[0];
                        }
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
    }


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


    return value;
// ==================== Abschnitt fuer das Benutzermenu ====================
}


// Initialisiert die Menue-Funktion einer Option
// Zeigt den Eintrag im Menu einer Option
// optAction: Typ der Funktion
// opt: Derzeitiger Wert der Option
// item: Key der Option
// menuOn: Text zum Setzen im Menu
// optSet: Platz fuer die gesetzten Optionen (und Config)
// funOn: Funktion zum Setzen
// return Funktion fuer die Option
// keyOn: Hotkey zum Setzen im Menu
function initOptAction(optAction, item = undefined, optSet = undefined) {
// menuOff: Text zum Ausschalten im Menu
     var fun;
// 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 ? "" : '*');


     if (optAction !== undefined) {
     console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);


        switch (optAction) {
    if (opt) {
         case __OPTACTION.SET : fun = function() {
         GM_registerMenuCommand(menuOff, funOff, keyOff);
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
    } else {
                                  };
         GM_registerMenuCommand(menuOn, funOn, keyOn);
                              break;
         case __OPTACTION.NXT : fun = function() {
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :              break;
        }
     }
     }
    return fun;
}
}


// Initialisiert die gesetzten Optionen
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// optConfig: Konfiguration der Optionen
// opt: Derzeitiger Wert der Option
// optSet: Platz fuer die gesetzten Optionen
// arr: Array-Liste mit den moeglichen Optionen
// return Gefuelltes Objekt mit den gesetzten Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
function initOptions(optConfig, optSet = undefined) {
// fun: Funktion zum Setzen des naechsten Wertes
     var value;
// 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 (optSet === undefined) {
     for (let value of arr) {
         optSet = { };
        if (value === opt) {
            options += " / *" + value + '*';
         } else {
            options += " / " + value;
        }
     }
     }
    console.log(options);


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


        optSet[opt] = {
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
            'Config'    : __CONFIG,
// opt: Derzeitiger Wert der Option
            'Value'    : initOptValue(__CONFIG),
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
            'SetValue'  : undefined,
// fun: Funktion zum Setzen des naechsten Wertes
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
// key: Hotkey zum Setzen des naechsten Wertes im Menu
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
// 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);
 
    console.log(__OPTIONS);
 
    if (! hidden) {
         GM_registerMenuCommand(__MENU, fun, key);
     }
     }
    return optSet;
}
}


// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
// Zeigt den Eintrag im Menu einer Option
// optConfig: Konfiguration der Optionen
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen
function registerOption(opt) {
// return Gefuelltes Objekt mit den gesetzten Optionen
     const __CONFIG = getOptConfig(opt);
function startOptions(optConfig, optSet = undefined) {
     const __NORMAL = 'normal';


     optSet = initOptions(optConfig, optSet);
     if (! __CONFIG.HiddenMenu) {
 
        switch (__CONFIG.Type) {
    // Memory Storage fuer vorherige Speicherung...
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
    const __OLDOPT = optSet.oldStorage;
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
    const __OLDSTORAGE = loadOption(getOpt(__OLDOPT), true);
                            break;
    myOptMem = __OPTMEM[getValue(__OLDSTORAGE, __NORMAL)];
        case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
 
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
    runStored(optSet);
                            break;
    loadOptions(optSet);
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
 
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
    // Memory Storage fuer naechste Speicherung...
                            break;
    const __STORAGE = getOptValue(optSet.storage, __NORMAL);
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    const __NEWSTORAGE = setOpt(__OLDOPT, __STORAGE, false);
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
    if (! canUseStorage(myOptMem = __OPTMEM[getValue(__NEWSTORAGE, __NORMAL)])) {
                            break;
        const __INAKTIV = 'inaktiv';
         case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
 
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
         if (__NEWSTORAGE !== __INAKTIV) {
                            break;
            setOpt(__OLDOPT, __INAKTIV, false);
        default :            break;
            myOptMem = __OPTMEM[__INAKTIV];
         }
         }
    } 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
// Initialisiert die gesetzten Option
// optSet: Platz fuer die gesetzten Optionen
// config: Konfiguration der Option
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Initialwert der gesetzten Option
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function initOptValue(config) {
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
    let value = config.Default;  // Standard
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
 
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
    switch (config.Type) {
// 'formWidth': Anzahl der Elemente pro Zeile
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
                            value = config.Choice[0];
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
                        }
     if (! optParams.hideMenu) {
                        break;
        buildMenu(optSet);
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
     case __OPTTYPES.SI : break;
    default :            break;
     }
     }


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


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


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


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


     return __VALUE;
     return fun;
}
}


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


     return setOpt(opt, getNextOpt(opt, value), reload);
     if (optSet === undefined) {
}
        optSet = { };
    }
 
    for (let opt in optConfig) {
        const __CONFIG = optConfig[opt];
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);


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


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


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


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


// Setzt die naechste moegliche Option (Version mit Key)
    runStored(optSet);
// opt: Config und Value der Option
     loadOptions(optSet);
// 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);
     // Memory Storage fuer naechste Speicherung...
}
    myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage);


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


     for (let opt in optSet) {
     return optSet;
        registerOption(optSet[opt]);
    }
}
}


// Laedt eine (ueber Menu) gesetzte Option
// Installiert die Visualisierung und Steuerung der Optionen
// opt: Zu ladende Option
// optSet: Platz fuer die gesetzten Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Gesetzter Wert der gelandenen Option
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function loadOption(opt, force = false) {
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
     const __CONFIG = getOptConfig(opt);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     updateScriptDB(optSet);


     if (! force && __CONFIG.AutoReset) {
     if (! optParams.hideMenu) {
         return setOptValue(opt, initOptValue(__CONFIG));
         buildMenu(optSet);
    } 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
    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
// optSet: Set mit den Optionen
         buildForm(optParams.menuAnchor, optSet, optParams);
// 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;
}
}


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


     if (force || ! __CONFIG.Permanent) {
     switch (__CONFIG.Type) {
        GM_deleteValue(getOptName(opt));
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
    }


        if (reset) {
    return __VALUE;
            setOptValue(opt, initOptValue(__CONFIG));
        }
    }
}
}


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


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


     if (__NAME !== name) {
     return setOpt(__OPT, value, reload);
        deleteOption(opt, true, ! reload);
}
 
// Ermittelt die naechste moegliche Option (Version mit Key)
// 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);


        setOptName(opt, name);
    return getNextOpt(__OPT, value);
}


        if (reload) {
// Setzt die naechste moegliche Option (Version mit Key)
            loadOption(opt, force);
// 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 opt;
     return setNextOpt(__OPT, value, reload);
}
}


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


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


// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// Laedt eine (ueber Menu) gesetzte Option
// opt: Zu ladende Option
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Gesetzter Wert der gelandenen Option
function loadOption(opt, force = false) {
    const __CONFIG = getOptConfig(opt);


// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
    if (! force && __CONFIG.AutoReset) {
const __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)));
    }
}


// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// Laedt die (ueber Menu) gesetzten Optionen
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
// optSet: Set mit den Optionen
 
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// Behandelt die Optionen und laedt das Benutzermenu
// return Set mit den geladenen Optionen
// optConfig: Konfiguration der Optionen
function loadOptions(optSet, force = false) {
// optSet: Platz fuer die gesetzten Optionen
     for (let opt in optSet) {
// optParams: Eventuell notwendige Parameter zur Initialisierung
        loadOption(optSet[opt], force);
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
    }
// 'teamParams': Getrennte "ligaSize"-Option wird genutzt, hier: __MYTEAM mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return Gefuelltes Objekt mit den gesetzten Optionen
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
     const __TEAMPARAMS = optParams.teamParams; // Ermittelte Parameter


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


    if (__TEAMPARAMS !== undefined) {
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
        __MYTEAM.Team = __TEAMPARAMS.Team;
// opt: Gesetzte Option
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
        __MYTEAM.Land = __TEAMPARAMS.Land;
// reset: Setzt bei Erfolg auf Initialwert der Option
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
function deleteOption(opt, force = false, reset = true) {
        __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
     const __CONFIG = getOptConfig(opt);
        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)) {
    if (force || ! __CONFIG.Permanent) {
            __MYTEAM.Team = __TEAM.Team;
        const __NAME = getOptName(opt);
            __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) {
        console.log("DELETE " + __NAME);
        // Prefix fuer die Optionen "birthdays", "tclasses" und "progresses"...
        const __PREFIX = __MYTEAM.LdNr.toString() + __MYTEAM.LgNr.toString();


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


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


     showOptions(optSet, optParams);
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option
function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
     const __DELETEALL = (optSelect === undefined) || (optSelect === true);
    const __OPTSELECT = getValue(optSelect, { });


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


// ==================== Abschnitt fuer diverse Utilities ====================
// Benennt eine Option um und laedt sie ggfs. nach
// opt: Gesetzte Option
// name: Neu zu setzender Name (Speicheradresse)
// reload: Wert nachladen statt beizubehalten
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Umbenannte Option
function renameOption(opt, name, reload = false, force = false) {
    const __NAME = getOptName(opt);
 
    if (__NAME !== name) {
        deleteOption(opt, true, ! reload);
 
        setOptName(opt, name);


// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
         if (reload) {
// form: <form>...</form>
             loadOption(opt, force);
// props: Map von name:value-Paaren
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = "hidden") {
    for (let fieldName in props) {
        let field = form[fieldName];
         if (! field) {
             field = document.createElement("input");
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
         }
         }
        field.value = props[fieldName];
     }
     }


     return form;
     return opt;
}
}


// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
// form: <form>...</form>
// name: Gesetzter Name (Speicheradresse)
// props: Map von name:value-Paaren
// prefix: Prefix, das vorangestellt werden soll
// return Ergaenztes Form-Konstrukt
// return Neu zu setzender Name (Speicheradresse)
function addHiddenField(form, props) {
function prefixName(name, prefix) {
     return addInputField(form, props, "hidden");
    return (prefix + name);
}
 
// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
// name: Gesetzter Name (Speicheradresse)
// postfix: Postfix, das angehaengt werden soll
// return Neu zu setzender Name (Speicheradresse)
function postfixName(name, postfix) {
     return (name + postfix);
}
}


// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// optSet: Gesetzte Optionen
// type: Name des Events, z.B. "click"
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// callback: Funktion als Reaktion
// 'reload': Option nachladen?
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// return false bei Misserfolg
// renameParam: Wird an renameFun uebergeen
function addEvent(obj, type, callback, capture = false) {
// renameFun: function(name, param) zur Ermittlung des neuen Namens
     if (obj.addEventListener) {
// name: Neu zu setzender Name (Speicheradresse)
         return obj.addEventListener(type, callback, capture);
// reload: Wert nachladen statt beizubehalten
     } else if (obj.attachEvent) {
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
        return obj.attachEvent("on" + type, callback);
// return Umbenannte Option
    } else {
function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
        console.log("Could not add " + type + " event:");
     if (renameFun === undefined) {
        console.log(callback);
         console.error("RENAME: Illegale Funktion!");
     }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];
 
        if (__OPT === undefined) {
            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);


         return false;
            if (__RELOAD) {
                // ... und nachladen...
                loadOption(__OPT, __FORCE);
            }
         }
     }
     }
}
}


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


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


// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// id: ID des betroffenen Eingabeelements
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function addDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);


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


// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// id: ID des betroffenen Eingabeelements
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
// type: Name des Events, z.B. "click"
 
// callback: Funktion als Reaktion
// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
const __DATAOPTS = {
// return false bei Misserfolg
                      'datenZat'  : true,
function removeDocEvent(id, type, callback, capture = false) {
                      'birthdays'  : true,
    const __OBJ = document.getElementById(id);
                      'tClasses'  : true,
                      'progresses' : true,
                      'zatAges'    : true,
                      'trainiert'  : true,
                      'positions'  : true
                  };


    return removeEvent(__OBJ, type, callback, capture);
// Behandelt die Optionen und laedt das Benutzermenu
}
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// 'teamParams': Getrennte "ligaSize"-Option wird genutzt, hier: __MYTEAM mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return Gefuelltes Objekt mit den gesetzten Optionen
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
    const __TEAMPARAMS = optParams.teamParams; // Ermittelte Parameter


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


     if (showList !== undefined) {
     if (__TEAMPARAMS !== undefined) {
         show = (showList[item] === true); // gesetzt und true
         __MYTEAM.Team = __TEAMPARAMS.Team;
     }
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
    if (hideList !== undefined) {
        __MYTEAM.Land = __TEAMPARAMS.Land;
        if (hideList[item] === true) { // gesetzt und true
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
             show = false; // NICHT anzeigen
        __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));
         }
         }
     }
     }


     return show;
     if (__MYTEAM.LdNr !== undefined) {
}
        // 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);
    }


// Helferfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
    showOptions(optSet, optParams);
// 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;
     return optSet;
}
}


// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
// ==================== Abschnitt fuer diverse Utilities ====================
// index: Laufende Nummer des Elements (0-based)
 
// doc: Dokument (document)
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
function getRows(index, doc = document) {
// form: <form>...</form>
     const __TABLE = getTable(index, "table", doc);
// props: Map von name:value-Paaren
    const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = "hidden") {
     for (let fieldName in props) {
        let field = form[fieldName];
        if (! field) {
            field = document.createElement("input");
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
        }
        field.value = props[fieldName];
    }


     return __ROWS;
     return form;
}
}


// ==================== Abschnitt fuer Optionen auf der Seite ====================
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// form: <form>...</form>
// props: Map von name:value-Paaren
// return Ergaenztes Form-Konstrukt
function addHiddenField(form, props) {
    return addInputField(form, props, "hidden");
}


// Liefert den Funktionsaufruf zur Option als String
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// opt: Auszufuehrende Option
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// type: Name des Events, z.B. "click"
// value: Ggfs. zu setzender Wert
// callback: Funktion als Reaktion
// serial: Serialization fuer String-Werte (Select, Textarea)
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return false bei Misserfolg
// return String mit dem (reinen) Funktionsaufruf
function addEvent(obj, type, callback, capture = false) {
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, storage = undefined) {
     if (obj.addEventListener) {
     const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
        return obj.addEventListener(type, callback, capture);
     const __MEMORY = __STORAGE.Value;
     } else if (obj.attachEvent) {
     const __MEMSTR = __STORAGE.Display;
        return obj.attachEvent("on" + type, callback);
    const __RUNPREFIX = __STORAGE.Prefix;
     } else {
        console.log("Could not add " + type + " event:");
        console.log(callback);


    if (__MEMORY !== undefined) {
         return false;
        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
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// opt: Auszufuehrende Option
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// type: Name des Events, z.B. "click"
// value: Ggfs. zu setzender Wert
// callback: Funktion als Reaktion
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// 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 removeEvent(obj, type, callback, capture = false) {
// return String mit dem (reinen) Funktionsaufruf
     if (obj.removeEventListener) {
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, storage = undefined) {
        return obj.removeEventListener(type, callback, capture);
     const __ACTION = getFormAction(opt, isAlt, value, serial, storage);
    } else if (obj.detachEvent) {
        return obj.detachEvent("on" + type, callback);
    } else {
        console.log("Could not remove " + type + " event:");
        console.log(callback);


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


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


    for (let value of __CONFIG.Choice) {
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
        element += '\n<option value="' + value + '"' +
// id: ID des betroffenen Eingabeelements
                  ((value === __VALUE) ? ' SELECTED' : "") +
// type: Name des Events, z.B. "click"
                  '>' + value + '</option>';
// callback: Funktion als Reaktion
    }
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     element += '\n</select>';
// return false bei Misserfolg
function removeDocEvent(id, type, callback, capture = false) {
     const __OBJ = document.getElementById(id);


     return __LABEL.replace('$', element);
     return removeEvent(__OBJ, type, callback, capture);
}
}


// Zeigt eine Option auf der Seite als Radiobutton an
// Hilfsfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// opt: Anzuzeigende Option
// item: Name des betroffenen Items
// return String mit dem HTML-Code
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
function getOptionRadio(opt) {
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
     const __CONFIG = getOptConfig(opt);
// return Angabe, ob das Item sichtbar sein soll
     const __NAME = getOptName(opt);
function checkVisible(item, showList, hideList = undefined) {
    const __VALUE = getOptValue(opt, false);
     let show = true;
     const __ACTION = getFormActionEvent(opt, false, true, "click", false);
 
    const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
     if (showList !== undefined) {
     const __ELEMENTON  = '<input type="radio" name="' + __NAME +
        show = (showList[item] === true); // gesetzt und true
                        '" id="' + __NAME + 'ON" value="1"' +
    }
                        (__VALUE ? ' CHECKED' : __ACTION) +
     if (hideList !== undefined) {
                        ' /><label for="' + __NAME + 'ON">' +
        if (hideList[item] === true) {  // gesetzt und true
                        __CONFIG.Label + '</label>';
            show = false; // NICHT anzeigen
     const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
        }
                        '" id="' + __NAME + 'OFF" value="0"' +
     }
                        (__VALUE ? __ALTACTION : ' CHECKED') +
 
                        ' /><label for="' + __NAME + 'OFF">' +
    return show;
                        __CONFIG.AltLabel + '</label>';
}
 
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// name: Name des Elements (siehe "name=")
// index: Laufende Nummer des Elements (0-based), Default: 0
// doc: Dokument (document)
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
function getElement(name, index = 0, doc = document) {
    const __TAGS = document.getElementsByName(name);
     const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];


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


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


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


// Zeigt eine Option auf der Seite als Daten-Textfeld an
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// 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 [ __ELEMENTLABEL, __ELEMENTTEXT ];
// Liefert den Funktionsaufruf zur Option als String
}
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __MEMSTR = __STORAGE.Display;
    const __RUNPREFIX = __STORAGE.Prefix;


// Zeigt eine Option auf der Seite als Button an
    if (__MEMORY !== undefined) {
// opt: Anzuzeigende Option
        const __RELOAD = "window.location.reload()";
// return String mit dem HTML-Code
        const __SETITEM = function(item, val, quotes = true) {
function getOptionButton(opt) {
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
    const __CONFIG = getOptConfig(opt);
                          };
    const __NAME = getOptName(opt);
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
    const __VALUE = getOptValue(opt, false);
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
    const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
                          };
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
        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 '<label for="' + __NAME + '">' + __FORMLABEL +
     return undefined;
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';
}
}


// Zeigt eine Option auf der Seite an (je nach Typ)
// Liefert die Funktionsaufruf zur Option als String
// opt: Anzuzeigende Option
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {
    const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
 
    return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
}
 
// Zeigt eine Option auf der Seite als Auswahlbox an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
// return String mit dem HTML-Code
function getOptionElement(opt) {
function getOptionSelect(opt) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);
     const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
     const __NAME = getOptName(opt);
     let element = "";
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';
 
    for (let value of __CONFIG.Choice) {
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';


     if (! __CONFIG.Hidden) {
     return __LABEL.replace('$', element);
        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) {
            element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
        }
    }
 
    return element;
}
}


// Baut das Benutzermenu auf der Seite auf
// Zeigt eine Option auf der Seite als Radiobutton an
// optSet: Gesetzte Optionen
// opt: Anzuzeigende Option
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return String mit dem HTML-Code
// return String mit dem HTML-Code
function getForm(optSet, optParams = { }) {
function getOptionRadio(opt) {
     const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
     const __CONFIG = getOptConfig(opt);
     const __FORMEND = '</tr></tbody></table></form>';
     const __NAME = getOptName(opt);
     const __FORMWIDTH = getValue(optParams.formWidth, 3);
     const __VALUE = getOptValue(opt, false);
     const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
     const __ACTION = getFormActionEvent(opt, false, true, "click", false);
     const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
     const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
    let form = __FORM;
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
     let count = 0;  // Bisher angezeigte Optionen
                        '" id="' + __NAME + 'ON" value="1"' +
    let column = 0// Spalte der letzten Option (1-basierend)
                        (__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>';


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


            if (__ELEMENT.length > 0) {
// Zeigt eine Option auf der Seite als Checkbox an
                if (++count > __FORMBREAK) {
// opt: Anzuzeigende Option
                    if (++column > __FORMWIDTH) {
// return String mit dem HTML-Code
                        column = 1;
function getOptionCheckbox(opt) {
                    }
    const __CONFIG = getOptConfig(opt);
                }
    const __NAME = getOptName(opt);
                if (column === 1) {
    const __VALUE = getOptValue(opt, false);
                    form += '</tr><tr>';
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
                }
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
            }
        }
    }
     form += '\n' + __FORMEND;


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


// Fuegt das Script in die Seite ein
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// optSet: Gesetzte Optionen
// opt: Anzuzeigende Option
// optParams: Eventuell notwendige Parameter
// return String mit dem HTML-Code
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
function getOptionTextarea(opt) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __CONFIG = getOptConfig(opt);
// return String mit dem HTML-Code fuer das Script
    const __NAME = getOptName(opt);
function getScript(optSet, optParams = { }) {
    const __VALUE = getOptValue(opt);
     //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
     const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
     //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
    const __SUBMIT = getValue(__CONFIG.Submit, "");
     //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
    const __ONSUBMIT = ((__SUBMIT.length > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __SCRIPT = "";
    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>';


    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
 
     return __SCRIPT;
}
}


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// Zeigt eine Option auf der Seite als Button an
// anchor: Element, das als Anker fuer die Anzeige dient
// opt: Anzuzeigende Option
// optSet: Gesetzte Optionen
// return String mit dem HTML-Code
// optParams: Eventuell notwendige Parameter
function getOptionButton(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);
// 'formWidth': Anzahl der Elemente pro Zeile
    const __VALUE = getOptValue(opt, false);
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
function buildForm(anchor, optSet, optParams = { }) {
    const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
     console.log("buildForm()");
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     const __FORM = getForm(optSet, optParams);
     return '<label for="' + __NAME + '">' + __FORMLABEL +
    const __SCRIPT = getScript(optSet, optParams);
          '</label><input type="button" name="' + __NAME +
 
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
    addForm(anchor, __FORM, __SCRIPT);
          __ACTION + '/>';
}
}


// Informationen zu hinzugefuegten Forms
// Zeigt eine Option auf der Seite an (je nach Typ)
const __FORMS = { };
// 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 = "";


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
    if (! __CONFIG.Hidden) {
// anchor: Element, das als Anker fuer die Anzeige dient
        switch (__TYPE) {
// form: HTML-Form des Optionsmenu (hinten angefuegt)
        case __OPTTYPES.MC : element = getOptionSelect(opt);
// script: Script mit Reaktionen
                            break;
function addForm(anchor, form = "", script = "") {
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
    const __OLDFORM = __FORMS[anchor];
                                element = getOptionCheckbox(opt);
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                            } else {
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
                                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;
        }


    __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        if (element.length === 2) {
            element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
        }
    }


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


// ==================== Ende Abschnitt fuer Optionen ====================
// Baut das Benutzermenu auf der Seite auf
 
// optSet: Gesetzte Optionen
// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
// optParams: Eventuell notwendige Parameter
 
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// Zeitpunktangaben
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
const __TIME = {
// 'formWidth': Anzahl der Elemente pro Zeile
    'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     'beg' : 1,  // Jugendspieler darf trainieren (wird 13 Jahre alt)
// return String mit dem HTML-Code
     'now' : 2,  // Aktueller ZAT
function getForm(optSet, optParams = { }) {
     'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
     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)


// Funktionen ***************************************************************************
    for (let opt in optSet) {
        if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
            const __ELEMENT = getOptionElement(optSet[opt]);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";


// Erschafft die Spieler-Objekte und fuellt sie mit Werten
            if (__ELEMENT.length > 0) {
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
                if (++count > __FORMBREAK) {
    const __SAISON = getOptValue(optSet.saison);
                    if (++column > __FORMWIDTH) {
    const __CURRZAT = getOptValue(optSet.aktuellerZat);
                        column = 1;
    const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
                    }
    const __TCLASSES = getOptValue(optSet.tclasses, []);
                }
    const __PROGRESSES = getOptValue(optSet.progresses, []);
                if (column === 1) {
    const __PLAYERS = [];
                    form += '</tr><tr>';
 
                }
    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
        const __CELLS = playerRows[i].cells;
        const __AGE = getAgeFromHTML(__CELLS, colIdx.Age);
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
        const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);
 
        __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
        __PLAYERS[j] = __NEWPLAYER;
    }
 
    return __PLAYERS;
}
 
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {
    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;
}
}


// Klasse ColumnManager *****************************************************************
// Fuegt das Script in die Seite ein
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// return String mit dem HTML-Code fuer das Script
function getScript(optSet, optParams = { }) {
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
    //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
    const __SCRIPT = "";


function ColumnManager(optSet) {
     //window.eval('function activateMenu() { console.log("TADAAA!"); }');
     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);


     this.toString = function() {
     return __SCRIPT;
        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";


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


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


     this.addAndFillCell = function(tableRow, value, color, digits = 2) {
     addForm(anchor, __FORM, __SCRIPT);
        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;
    };


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


        // Titel fuer die aktuellen Werte
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        if (this.tal) {
// anchor: Element, das als Anker fuer die Anzeige dient
            this.addAndFillCell(headers, "Talent", titleColor);
// form: HTML-Form des Optionsmenu (hinten angefuegt)
        }
// script: Script mit Reaktionen
        if (this.quo) {
function addForm(anchor, form = "", script = "") {
            this.addAndFillCell(headers, "Quote", titleColor);
    const __OLDFORM = __FORMS[anchor];
        }
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
        if (this.aufw) {
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
            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);
            }
        }


        // Titel fuer die Werte mit Ende 18
    __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        if (this.skillE) {
 
            this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
    anchor.innerHTML = __REST + script + form;
        }
}
        if (this.optiE) {
 
            for (let i = 1; i <= this.anzOptiE; i++) {
// ==================== Ende Abschnitt fuer Optionen ====================
                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()


    this.addValues = function(player, playerRow, color = "#FFFFFF") {
// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
        const __COLOR = (player.isGoalie ? getColor("TOR") : color);
        const __POS1COLOR = (player.isGoalie ? getColor("TOR") : getColor(player.getPos(1)));


        // Aktuelle Werte
// Zeitpunktangaben
        if (this.tal) {
const __TIME = {
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
    'cre' : 0, // Jugendspieler angelegt (mit 12 Jahren)
        }
    'beg' : 1, // Jugendspieler darf trainieren (wird 13 Jahre alt)
        if (this.quo) {
    'now' : 2, // Aktueller ZAT
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
    '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) {
// Erschafft die Spieler-Objekte und fuellt sie mit Werten (reloadData: true = Teamuebersicht, false = Spielereinzelwerte)
            this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, reloadData = false) {
        }
    const __SAISON = getOptValue(optSet.saison);
        if (this.alter) {
    const __CURRZAT = getOptValue(optSet.aktuellerZat);
            this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
    const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
        }
    const __TCLASSES = getOptValue(optSet.tClasses, []);
        if (this.skill) {
    const __PROGRESSES = getOptValue(optSet.progresses, []);
            this.addAndFillCell(playerRow, player.getSkill(), __COLOR, 2);
    const __ZATAGES = getOptValue(optSet.zatAges, []);
        }
    const __TRAINIERT = getOptValue(optSet.trainiert, []);
        if (this.pos) {
    const __POSITIONS = getOptValue(optSet.positions, []);
            this.addAndFillCell(playerRow, player.getPos(1), __POS1COLOR);
    const __PLAYERS = [];
        }
 
        if (this.opti) {
    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
            for (let i = 1; i <= this.anzOpti; i++) {
        const __CELLS = playerRows[i].cells;
                if (player.isGoalie) {
        const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
                    if (i === 1) {
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
                        // TOR-Opti anzeigen
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
                        this.addAndFillCell(playerRow, player.getOpti("TOR"), getColor("TOR"), 2);
        const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);
                    } else {
 
                        // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
         __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
                        this.addCell(playerRow);
 
                    }
        if (reloadData) {
                } else {
            __NEWPLAYER.setZusatz(__ZATAGES[j], __TRAINIERT[j], __POSITIONS[j]);
                    // Feld-Opti anzeigen
                    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);
                }
            }
         }
         }


         // Werte mit Ende 18
         __PLAYERS[j] = __NEWPLAYER;
        if (this.skillE) {
    }
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
 
        }
    if (reloadData) {
        if (this.optiE) {
        storePlayerData(__PLAYERS, playerRows, optSet, colIdx, offsetUpper, offsetLower);
            for (let i = 1; i <= this.anzOptiE; i++) {
    } else {
                if (player.isGoalie) {
        calcPlayerData(__PLAYERS, optSet);
                    if (i === 1) {
    }
                        // TOR-Opti anzeigen
 
                        this.addAndFillCell(playerRow, player.getOpti("TOR", __TIME.end), getColor("TOR"), 2);
    return __PLAYERS;
                    } else {
}
                        // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
 
                        this.addCell(playerRow);
// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
                    }
function calcPlayerData(players, optSet) {
                } else {
    const __ZATAGES = [];
                    // Feld-Opti anzeigen
    const __TRAINIERT = [];
                    this.addAndFillCell(playerRow, player.getOpti(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 2);
    const __POSITIONS = [];
                }
 
                if (this.mwE && (this.anzMwE >= i)) {
    for (let i = 0; i < players.length; i++) {
                    if (player.isGoalie) {
        const __ZUSATZ = players[i].calcZusatz();
                        if (i === 1) {
 
                            // TOR-MW anzeigen
        __ZATAGES[i]  = __ZUSATZ.zatAge;
                            this.addAndFillCell(playerRow, player.getMarketValue("TOR", __TIME.end), getColor("TOR"), 0);
        __TRAINIERT[i] = __ZUSATZ.trainiert;
                        } else {
         __POSITIONS[i] = __ZUSATZ.bestPos;
                            // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
    }
                            this.addCell(playerRow);
 
                        }
    setOpt(optSet.zatAges, __ZATAGES, false);
                    } else {
    setOpt(optSet.trainiert, __TRAINIERT, false);
                        // Feld-MW anzeigen
     setOpt(optSet.positions, __POSITIONS, false);
                        this.addAndFillCell(playerRow, player.getMarketValue(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 0);
}
                    }
                }
            }
            // Verbleibende MW anzeigen
            if (this.mwE) {
                for (let i = this.anzOptiE + 1; i <= this.anzMwE; i++) {
                    if (player.isGoalie) {
                        if (i === 1) {
                            // TOR-MW anzeigen
                            this.addAndFillCell(playerRow, player.getMarketValue("TOR", __TIME.end), 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), __TIME.end), getColor(player.getPos(i)), 0);
                    }
                }
            }
         } else if (this.mwE) {
            // Opti soll nicht angezeigt werden, dafuer aber MW
            for (let i = 1; i <= this.anzMwE; i++) {
                if (player.isGoalie) {
                    if (i === 1) {
                        // TOR-MW anzeigen
                        this.addAndFillCell(playerRow, player.getMarketValue("TOR", __TIME.end), 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), __TIME.end), getColor(player.getPos(i)), 0);
                }
            }
        }
     };  // Ende addValues(player, playerRow)
}


// Klasse PlayerRecord ******************************************************************
// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
function storePlayerData(players, playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
    const __BIRTHDAYS = [];
    const __TCLASSES = [];
    const __PROGRESSES = [];


function PlayerRecord(age, skills, isGoalie) {
    for (let i = offsetUpper; i < playerRows.length - offsetLower; i++) {
    // Zu benutzende Marktwertformel
        const __CELLS = playerRows[i].cells;
    const __MWFORMEL = {
        'alt' : 0,  // Marktwertformel bis Saison 9 inklusive
        'S10' : 1  // Marktwertformel MW5 ab Saison 10
    };


    this.mwFormel = __MWFORMEL.S10; // Neue Formel, genauer in initPlayer()
        __BIRTHDAYS[i - offsetUpper] = getIntFromHTML(__CELLS, colIdx.Geb);
        __TCLASSES[i - offsetUpper] = getTalentFromHTML(__CELLS, colIdx.Tal);
        __PROGRESSES[i - offsetUpper] = getStringFromHTML(__CELLS, colIdx.Auf);
    }


     this.age = age;
     setOpt(optSet.birthdays, __BIRTHDAYS, false);
     this.skills = skills;
     setOpt(optSet.tClasses, __TCLASSES, false);
     this.isGoalie = isGoalie;
     setOpt(optSet.progresses, __PROGRESSES, false);
}


    // in this.initPlayer() definiert:
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
    // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {
    // this.zatAge: Bisherige erfolgte Trainings-ZATs
     if (offsetLeft < 0) {
    // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
        offsetLeft = colIdxSort;  // ab Sortierspalte
     // 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.toString = function() {
     for (let i = offsetUpper; i < rows.length - offsetLower - 1; i++) {
         let result = "Alter\t\t" + this.age + "\n\n";
         if (rows[i].cells[colIdxSort].textContent !== rows[i + 1].cells[colIdxSort].textContent) {
        result += "Aktuelle Werte\n";
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
        result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
                rows[i].cells[j].style.borderBottom = borderString;
         result += "Optis und Marktwerte";
            }
         }
    }
}


        for (let pos of this.positions) {
// Klasse ColumnManager *****************************************************************
            result += "\n\t" + pos + '\t';
            result += this.getOpti(pos).toFixed(2) + '\t';
            result += getNumberString(this.getMarketValue(pos).toString());
        }


        result += "\n\nWerte mit Ende 18\n";
function ColumnManager(optSet, showCol = undefined) {
        result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
    const __SHOWALL = (showCol === undefined) || (showCol === true);
        result += "Optis und Marktwerte";
    const __SHOWCOL = getValue(showCol, { });


        for (let pos of this.positions) {
    this.geb = getValue(__SHOWCOL.zeigeGeb, __SHOWALL) && getOptValue(optSet.zeigeGeb);
            result += "\n\t" + this.getPos()[i] + '\t';
    this.tal = getValue(__SHOWCOL.zeigeTal, __SHOWALL) && getOptValue(optSet.zeigeTal);
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
    this.quo = getValue(__SHOWCOL.zeigeQuote, __SHOWALL) && getOptValue(optSet.zeigeQuote);
            result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
    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() {
        let result = "Skillschnitt\t\t" + this.skill + '\n';
        result += "Beste Position\t" + this.pos + '\n';
        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;
         return result;
     }; // Ende this.toString()
     };


    // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
     this.addCell = function(tableRow) {
     this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
         tableRow.insertCell(-1);
         this.zatGeb = gebZAT;
         return tableRow.cells.length - 1;
        this.zatAge = this.calcZatAge(currZAT);
    };
         this.talent = tclass;
        this.aufwert = progresses;
        this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;


        this.positions = [];
    this.addAndFillCell = function(tableRow, value, color, digits = 2) {
        // ABW
         if (isFinite(value) && (value !== true) && (value !== false)) {
        this.positions[0] = [];
            // Zahl einfuegen
         this.positions[0][0] = "ABW";
            if (value < 1000) {
        this.positions[0][1] = this.getOpti("ABW");
                // Mit Nachkommastellen darstellen
        // DMI
                tableRow.cells[this.addCell(tableRow)].textContent = parseFloat(value).toFixed(digits);
        this.positions[1] = [];
            } else {
        this.positions[1][0] = "DMI";
                // Mit Tausenderpunkten darstellen
        this.positions[1][1] = this.getOpti("DMI");
                tableRow.cells[this.addCell(tableRow)].textContent = getNumberString(value.toString());
        // MIT
            }
        this.positions[2] = [];
         } else {
        this.positions[2][0] = "MIT";
            // String einfuegen
        this.positions[2][1] = this.getOpti("MIT");
            tableRow.cells[this.addCell(tableRow)].textContent = value;
        // OMI
         }
        this.positions[3] = [];
         tableRow.cells[tableRow.cells.length - 1].style.color = color;
        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
    this.addTitles = function(headers, titleColor = "#FFFFFF") {
         sortPositionArray(this.positions);
         // Spaltentitel zentrieren
         headers.align = "center";


         // Einzelskills mit Ende 18 berechnen
         // Titel fuer die aktuellen Werte
         this.skillsEnd = [];
         if (this.tal) {
 
            this.addAndFillCell(headers, "Talent", titleColor);
        const __ADDRATIO = (this.getZatAge(__TIME.end) - this.getZatAge()) / this.getZatAge();
        }
 
         if (this.quo) {
         for (let i in this.skills) {
             this.addAndFillCell(headers, "Quote", titleColor);
             const __SKILL = this.skills[i];
        }
            let progSkill = __SKILL;
        if (this.aufw) {
 
             this.addAndFillCell(headers, "Aufwertung", titleColor);
            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);
 
                progSkill += __ADDSKILL;
            }
 
             this.skillsEnd[i] = Math.min(progSkill, 99);
         }
         }
    };  // Ende this.initPlayer()
        if (this.geb) {
 
            this.addAndFillCell(headers, "Geb.", titleColor);
    this.getGeb = function() {
        }
         return (this.zatGeb < 0) ? '?' : this.zatGeb;
         if (this.alter) {
    };
            this.addAndFillCell(headers, "Alter", titleColor);
 
        }
    this.calcZatAge = function(currZAT) {
        if (this.skill) {
         if (this.age < 13) {
            this.addAndFillCell(headers, "Skill", titleColor);
             return 0; // noch nicht trainiert
        }
         } else {
         if (this.pos) {
            let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72;  // Basiszeit fuer die Jahre seit Jahrgang 13
             this.addAndFillCell(headers, "Pos", titleColor);
 
         }
             if (this.zatGeb < 0) {
        for (let i = 1; i <= 6; i++) {
                 return ZATs + currZAT; // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
             if (i <= this.anzOpti) {
             } else {
                 this.addAndFillCell(headers, "Opti " + i, titleColor);
                 return ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
             }
            if (i <= this.anzMw) {
                 this.addAndFillCell(headers, "MW " + i, titleColor);
             }
             }
         }
         }
    };


    this.getZatAge = function(when = __TIME.now) {
        // Titel fuer die Werte mit Ende 18
         if (when === __TIME.end) {
         if (this.skillE) {
             return (18 - 12) * 72 - 1;  // (max.) Trainings-ZATs bis Ende 18
             this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
        } else {
            return this.zatAge;
         }
         }
    };
        for (let i = 1; i <= 6; i++) {
 
            if (i <= this.anzOptiE) {
    this.getAge = function(when = __TIME.now) {
                this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
        if (this.mwFormel === __MWFORMEL.alt) {
            }
            return (when === __TIME.end) ? 18 : this.age;
             if (i <= this.anzMwE) {
        } else {  // Geburtstage ab Saison 10...
                this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
             return (13.00 + this.getZatAge(when) / 72);
            }
         }
         }
     };
     }; // Ende addTitles()


     this.getTrainierteSkills = function() {
     this.addValues = function(player, playerRow, color = "#FFFFFF") {
         let sum = 0;
         const __COLOR = (player.isGoalie ? getColor("TOR") : color);
        const __POS1COLOR = getColor(player.getPos());


         for (let i in this.skills) {
         // Aktuelle Werte
             if (isTrainableSkill(i)) {
        if (this.tal) {
                sum += this.skills[i];
             this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
             }
        }
        if (this.quo) {
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
        }
        if (this.aufw) {
             this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
         }
         }
        if (this.geb) {
            this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
        }
        if (this.alter) {
            this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
        }
        if (this.skill) {
            this.addAndFillCell(playerRow, player.getSkill(), __COLOR, 2);
        }
        if (this.pos) {
            this.addAndFillCell(playerRow, player.getPos(), __POS1COLOR);
        }
        for (let i = 1; i <= 6; i++) {
            const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
            const __COLI = getColor(__POSI);


        return sum;
            if (i <= this.anzOpti) {
    };
                if ((i === 1) || ! player.isGoalie) {
 
                    // Opti anzeigen
    this.getAufwertungsSchnitt = function() {
                    this.addAndFillCell(playerRow, player.getOpti(__POSI), __COLI, 2);
        return parseFloat(this.getTrainierteSkills() / this.getZatAge());
                } else {
    };
                    // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
 
                    this.addCell(playerRow);
    this.getPos = function(idx) {
                }
        const __IDXOFFSET = 1;
            }
        return this.positions[idx - __IDXOFFSET][0];
            if (i <= this.anzMw) {
    };
                if ((i === 1) || ! player.isGoalie) {
 
                    // MW anzeigen
    this.getTalent = function() {
                    this.addAndFillCell(playerRow, player.getMarketValue(__POSI), __COLI, 0);
        return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
                } else {
    };
                    // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                    this.addCell(playerRow);
                }
            }
        }


    this.getAufwert = function() {
        // Werte mit Ende 18
        return (this.aufwert.length > 0) ? this.aufwert : "keine";
        if (this.skillE) {
    };
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
        }
        for (let i = 1; i <= 6; i++) {
            const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
            const __COLI = getColor(__POSI);


    this.getSkill = function(when = __TIME.now) {
            if (i <= this.anzOptiE) {
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
                if ((i === 1) || ! player.isGoalie) {
        let result = 0;
                    // Opti anzeigen
 
                    this.addAndFillCell(playerRow, player.getOpti(__POSI, __TIME.end), __COLI, 2);
        for (let skill of __SKILLS) {
                } else {
             result += skill;
                    // 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);
                }
             }
         }
         }
    };  // Ende addValues(player, playerRow)
}


         return result / __SKILLS.length;
// Klasse PlayerRecord ******************************************************************
 
function PlayerRecord(age, skills, isGoalie) {
    // Zu benutzende Marktwertformel
    const __MWFORMEL = {
        'alt' : 0,  // Marktwertformel bis Saison 9 inklusive
         'S10' : 1  // Marktwertformel MW5 ab Saison 10
     };
     };


     this.getOpti = function(pos, when = __TIME.now) {
     this.mwFormel = __MWFORMEL.S10; // Neue Formel, genauer in initPlayer()
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
        const __IDXPRISKILLS = getIdxPriSkills(pos);
        const __IDXSECSKILLS = getIdxSecSkills(pos);
        let sumPriSkills = 0;
        let sumSecSkills = 0;


        for (let idx of __IDXPRISKILLS) {
    this.age = age;
            sumPriSkills += __SKILLS[idx];
    this.skills = skills;
        }
    this.isGoalie = isGoalie;
        for (let idx of __IDXSECSKILLS) {
            sumSecSkills += __SKILLS[idx];
        }


        return (5 * sumPriSkills + sumSecSkills) / 27;
    // 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.getMarketValue = function(pos, when = __TIME.now) {
     // in this calcZusatz()/setZusatz() definiert:
        const __AGE = this.getAge(when);
    // this.trainiert: Anzahl der erfolgreichen Trainingspunkte
    // this.bestPos: erster (bester) Positionstext


        if (this.mwFormel === __MWFORMEL.alt) {
    this.toString = function() {
            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
        let result = "Alter\t\t" + this.age + "\n\n";
         } else {  // MW-Formel ab Saison 10...
        result += "Aktuelle Werte\n";
            const __MW5TF = 1.00; // Zwischen 0.97 und 1.03
        result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
         result += "Optis und Marktwerte";


            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);
        for (let pos of this.positions) {
            result += "\n\t" + pos + '\t';
            result += this.getOpti(pos).toFixed(2) + '\t';
            result += getNumberString(this.getMarketValue(pos).toString());
         }
         }
    };
}


// Funktionen fuer die HTML-Seite *******************************************************
        result += "\n\nWerte mit Ende 18\n";
        result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
        result += "Optis und Marktwerte";


// Liest das Alter aus
        for (let pos of this.positions) {
// return Alter als Zahl
            result += "\n\t" + this.getPos()[i] + '\t';
function getAgeFromHTML(cells, colIdxAge) {
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
     return parseInt(cells[colIdxAge].textContent, 10);
            result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
}
        }
 
 
// Liest das Geburtsdatum aus
        return result;
// return Geburtsdatum als ZAT
    };  // Ende this.toString()
function getGebFromHTML(cells, colIdxGeb) {
 
     const __TEXT = ((cells[colIdxGeb] === undefined) ? '?' : cells[colIdxGeb].textContent);
    // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
 
    this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
     return parseInt((__TEXT === '?') ? -1 : __TEXT, 10);
        this.zatGeb = gebZAT;
}
        this.zatAge = this.calcZatAge(currZAT);
 
        this.talent = tclass;
// Liest die Talentklasse ("wenig", "normal", "hoch") aus
        this.aufwert = progresses;
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
        this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;
function getTalentFromHTML(cells, colIdxTal) {
 
     const __TEXT = ((cells[colIdxTal] === undefined) ? '?' : cells[colIdxTal].textContent);
        this.positions = [];
 
        // ABW
     return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
        this.positions[0] = [];
}
        this.positions[0][0] = "ABW";
 
        this.positions[0][1] = this.getOpti("ABW");
// Liest die Aufwertungen aus
        // DMI
// return Aufwertungen als String
        this.positions[1] = [];
function getAufwertFromHTML(cells, colIdxAuf) {
        this.positions[1][0] = "DMI";
    const __TEXT = ((cells[colIdxAuf] === undefined) ? '?' : cells[colIdxAuf].textContent);
        this.positions[1][1] = this.getOpti("DMI");
 
        // MIT
    return __TEXT.toString();
        this.positions[2] = [];
}
        this.positions[2][0] = "MIT";
 
        this.positions[2][1] = this.getOpti("MIT");
// Liest die Einzelskills aus
        // OMI
// return Skills als Array von Zahlen
        this.positions[3] = [];
function getSkillsFromHTML(cells, colIdx) {
        this.positions[3][0] = "OMI";
     const __RESULT = [];
        this.positions[3][1] = this.getOpti("OMI");
 
        // STU
     for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
        this.positions[4] = [];
         __RESULT[i - colIdx.Einz] = parseInt(cells[i].textContent, 10);
        this.positions[4][0] = "STU";
     }
        this.positions[4][1] = this.getOpti("STU");
 
        // TOR
     return __RESULT;
        this.positions[5] = [];
}
        this.positions[5][0] = "TOR";
 
        this.positions[5][1] = this.getOpti("TOR");
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
 
// return Angabe, der Spieler Torwart oder Feldspieler ist
        // Sortieren
function isGoalieFromHTML(cells, colIdxClass) {
        sortPositionArray(this.positions);
     return (cells[colIdxClass].className === "TOR");
 
}
        // Einzelskills mit Ende 18 berechnen
 
        this.skillsEnd = [];
// Hilfsfunktionen **********************************************************************
 
 
        const __ADDRATIO = (this.getZatDone(__TIME.end) - this.getZatDone()) / this.getZatDone();
// Sortiert das Positionsfeld per BubbleSort
 
function sortPositionArray(array) {
        for (let i in this.skills) {
     const __TEMP = [];
            const __SKILL = this.skills[i];
     let transposed = true;
            let progSkill = __SKILL;
     // TOR soll immer die letzte Position im Feld sein, deshalb - 1
 
            if (isTrainableSkill(i)) {
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
                const __ADDSKILL = getMulValue(__ADDRATIO, __SKILL, 0, NaN);
 
                progSkill += __ADDSKILL;
            }
 
            this.skillsEnd[i] = Math.min(progSkill, 99);
        }
    };  // Ende this.initPlayer()
 
    // Setzt Nebenwerte fuer den Spieler (geht ohne initPlayer())
    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())
    this.calcZusatz = function() {
        // this.zatAge bereits in initPlayer() berechnet
        this.trainiert = this.getTrainiert(true);  // neu berechnet aus Skills
        this.bestPos = this.getPos(-1);  // hier: -1 explizit angeben, da neu ermittelt (this.bestPos noch nicht belegt)
 
        return {
                  zatAge    : this.zatAge,
                  trainiert : this.trainiert,
                  bestPos  : this.bestPos
              };
    };
 
    this.getGeb = function() {
        return (this.zatGeb < 0) ? '?' : this.zatGeb;
    };
 
    this.calcZatAge = function(currZAT) {
        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.getZatDone = function(when = __TIME.now) {
        return Math.max(0, this.getZatAge(when));
    }
 
    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.getTrainiert = function(recalc = false) {
        let sum = 0;
 
        if (recalc || (this.trainiert === undefined)) {
            for (let i in this.skills) {
                if (isTrainableSkill(i)) {
                    sum += this.skills[i];
                }
            }
        } else {
            sum += this.trainiert;
        }
 
        return sum;
    };
 
    this.getAufwertungsSchnitt = function() {
        return parseFloat(this.getTrainiert() / this.getZatDone());
    };
 
    this.getPos = function(idx = undefined) {
        const __IDXOFFSET = 1;
 
        switch (getValue(idx, 0)) {
        case -1 : return (this.bestPos = this.positions[isGoalie ? 5 : 0][0]);
        case  0 : return this.bestPos;
        default : return this.positions[idx - __IDXOFFSET][0];
        }
    };
 
    this.getTalent = function() {
        return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
    };
 
    this.getAufwert = function() {
        return (this.aufwert.length > 0) ? this.aufwert : "keine";
    };
 
    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 / __SKILLS.length;
    };
 
    this.getOpti = function(pos, when = __TIME.now) {
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
        const __IDXPRISKILLS = getIdxPriSkills(pos);
        const __IDXSECSKILLS = getIdxSecSkills(pos);
        let sumPriSkills = 0;
        let sumSecSkills = 0;
 
        for (let idx of __IDXPRISKILLS) {
            sumPriSkills += __SKILLS[idx];
        }
        for (let idx of __IDXSECSKILLS) {
            sumSecSkills += __SKILLS[idx];
        }
 
        return (5 * sumPriSkills + sumSecSkills) / 27;
    };
 
    this.getMarketValue = function(pos, when = __TIME.now) {
        const __AGE = this.getAge(when);
 
        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
 
            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;
}
 
// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdxInt: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Dezimalzahl (undefined fuer "keine Zahl" oder "nicht gefunden")
function getFloatFromHTML(cells, colIdxFloat) {
     const __CELL = cells[colIdxFloat];
    const __TEXT = getValue(__CELL, { }).textContent;
 
    if (__TEXT !== undefined) {
        try {
            return parseFloat(__TEXT);
        } catch (ex) { }
    }
 
    return undefined;
}
 
// Liest einen String aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// return Spalteneintrag als String ("" fuer "nicht gefunden")
function getStringFromHTML(cells, colIdxStr) {
    const __CELL = cells[colIdxStr];
    const __TEXT = getValue(__CELL, { }).textContent;
 
     return getValue(__TEXT.toString(), "");
}
 
// Liest die Talentklasse ("wenig", "normal", "hoch") aus der Spalte einer Zeile der Tabelle aus
// 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);
}
 
// Liest die Einzelskills aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdx: Liste von Spaltenindices der gesuchten Werte mit den Eintraegen
// 'Einz' (erste Spalte) und 'Zus' (Spalte hinter dem letzten Eintrag)
// return Skills als Array von Zahlen
function getSkillsFromHTML(cells, colIdx) {
     const __RESULT = [];
 
     for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
         __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
     }
 
     return __RESULT;
}
 
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
// return Angabe, der Spieler Torwart oder Feldspieler ist
function isGoalieFromHTML(cells, colIdxClass) {
     return (cells[colIdxClass].className === "TOR");
}
 
// Hilfsfunktionen **********************************************************************
 
// 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;
     let length = array.length - 1;


Zeile 2.275: Zeile 2.569:
         'Bosnien-Herzegowina'    :  66,
         'Bosnien-Herzegowina'    :  66,
         'Bulgarien'              :  42,
         'Bulgarien'              :  42,
         'Dänemark'               :  8,
         'D\xE4nemark'           :  8,
         'Deutschland'            :  6,
         'Deutschland'            :  6,
         'England'                :  1,
         'England'                :  1,
         'Estland'                :  57,
         'Estland'                :  57,
         'Faröer'                 :  68,
         'Far\xF6er'             :  68,
         'Finnland'              :  40,
         'Finnland'              :  40,
         'Frankreich'            :  32,
         'Frankreich'            :  32,
Zeile 2.300: Zeile 2.594:
         'Nordirland'            :  4,
         'Nordirland'            :  4,
         'Norwegen'              :  9,
         'Norwegen'              :  9,
         'Österreich'             :  14,
         '\xD6sterreich'         :  14,
         'Polen'                  :  25,
         'Polen'                  :  25,
         'Portugal'              :  17,
         'Portugal'              :  17,
         'Rumänien'               :  28,
         'Rum\xE4nien'           :  28,
         'Russland'              :  19,
         'Russland'              :  19,
         'San Marino'            :  98,
         'San Marino'            :  98,
Zeile 2.314: Zeile 2.608:
         'Spanien'                :  13,
         'Spanien'                :  13,
         'Tschechien'            :  18,
         'Tschechien'            :  18,
         'Türkei'                 :  39,
         'T\xFCrkei'             :  39,
         'Ukraine'                :  20,
         'Ukraine'                :  20,
         'Ungarn'                :  26,
         'Ungarn'                :  26,
Zeile 2.480: Zeile 2.774:
     const __NEXTZAT = getZATNrFromCell(getRows(0)[2].cells[0]);  // "Der naechste ZAT ist ZAT xx und ..."
     const __NEXTZAT = getZATNrFromCell(getRows(0)[2].cells[0]);  // "Der naechste ZAT ist ZAT xx und ..."
     const __CURRZAT = __NEXTZAT - 1;
     const __CURRZAT = __NEXTZAT - 1;
    const __DATAZAT = getOptValue(__OPTSET.datenZat);


     if (__CURRZAT >= 0) {
     if (__CURRZAT >= 0) {
         console.log("Aktueller ZAT: " + __CURRZAT);
         console.log("Aktueller ZAT: " + __CURRZAT);


        // Neuen aktuellen ZAT speichern...
         setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
         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);
        }
     }
     }
}
}
Zeile 2.506: Zeile 2.812:
     };
     };


     const __BIRTHDAYS = [];
     if (getElement('transfer') !== undefined) {
    const __TCLASSES = [];
        console.log("Ziehen-Seite");
    const __PROGRESSES = [];
     } else if (getRows(1) === undefined) {
 
     if (getRows(1) === undefined) {
         console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
         console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
    } else if (getTable(1).length < 3) {
        console.log("Ziehen-Seite");
     } else {
     } else {
         buildOptions(__OPTCONFIG, __OPTSET, {
         buildOptions(__OPTCONFIG, __OPTSET, {
                         'menuAnchor' : getTable(0, "div"),
                         'menuAnchor' : getTable(0, "div"),
                         'showForm' : {
                         'showForm' : {
                                         'sepStyle'     : true,
                                         'sepStyle'     : true,
                                         'sepColor'     : true,
                                         'sepColor'     : true,
                                         'sepWidth'     : true,
                                         'sepWidth'     : true,
                                         'aktuellerZat' : true,
                                         'aktuellerZat' : true,
                                         'team'         : true,
                                         'team'         : true,
                                         'reset'       : true,
                                        'zeigeAlter'    : true,
                                         'showForm'     : true
                                        'zeigeQuote'    : true,
                                        'zeigePosition' : true,
                                        'zatAges'      : true,
                                        'trainiert'    : true,
                                        'positions'    : true,
                                         'reset'         : true,
                                         'showForm'     : true
                                       },
                                       },
                         'formWidth' : 1
                         '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 __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 = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
         for (let i = 0; i < __PLAYERS.length; i++) {
             __BIRTHDAYS[i - __ROWOFFSETUPPER] = getGebFromHTML(__ROWS[i].cells, __COLUMNINDEX.Geb);
             __COLMAN.addValues(__PLAYERS[i], __ROWS[i + __ROWOFFSETUPPER], __TITLECOLOR);
            __TCLASSES[i - __ROWOFFSETUPPER] = getTalentFromHTML(__ROWS[i].cells, __COLUMNINDEX.Tal);
            __PROGRESSES[i - __ROWOFFSETUPPER] = getAufwertFromHTML(__ROWS[i].cells, __COLUMNINDEX.Auf);
         }
         }
        setOpt(__OPTSET.birthdays, __BIRTHDAYS, false);
        setOpt(__OPTSET.tclasses, __TCLASSES, false);
        setOpt(__OPTSET.progresses, __PROGRESSES, false);


         // Format der Trennlinie zwischen den Monaten...
         // Format der Trennlinie zwischen den Monaten...
Zeile 2.590: Zeile 2.903:
         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.600: Zeile 2.918:
         __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
         __COLMAN.addTitles(__HEADERS, __TITLECOLOR);


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


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

Aktuelle Version vom 11. Oktober 2016, 23:22 Uhr

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

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

// ==================== Konfigurations-Abschnitt fuer Optionen ====================

// Options-Typen const __OPTTYPES = {

   'MC' : "multiple choice",
   'SW' : "switch",
   'TF' : "true/false",
   'SD' : "simple data",
   'SI' : "simple option"

};

// Options-Typen const __OPTACTION = {

   'SET' : "set option value",
   'NXT' : "set next option value",
   'RST' : "reset options"

};

const __OPTMEM = {

   'normal' : {
                  'Name'      : "Session",
                  'Value'     : sessionStorage,
                  'Display'   : "sessionStorage",
                  'Prefix'    : 'run'
              },
   'unbegrenzt' : {
                  'Name'      : "Browser",
                  'Value'     : localStorage,
                  'Display'   : "localStorage",
                  'Prefix'    : 'run'
              },
   'inaktiv' : {
                  'Name'      : "inaktiv",
                  'Value'     : undefined,
                  'Display'   : "",
                  'Prefix'    : ""
              }

};

// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen): const __OPTCONFIG = {

   'zeigeTal' : {        // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showTclasses",
                  'Type'      : __OPTTYPES.SW,
                  'Default'   : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Talent ein",
                  'Hotkey'    : 'T',
                  'AltLabel'  : "Talent aus",
                  'AltHotkey' : 'T',
                  'FormLabel' : "Talent"
              },
   'zeigeQuote' : {      // Spaltenauswahl fuer Aufwertungsschnitt (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showRatio",
                  'Type'      : __OPTTYPES.SW,
                  'Default'   : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Quote ein",
                  'Hotkey'    : 'T',
                  'AltLabel'  : "Quote aus",
                  'AltHotkey' : 'T',
                  'FormLabel' : "Quote"
              },
   'zeigeAufw' : {       // Spaltenauswahl fuer Aufwertungen (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showProgresses",
                  'Type'      : __OPTTYPES.SW,
                  'Default'   : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Aufw. ein",
                  'Hotkey'    : 'W',
                  'AltLabel'  : "Aufw. aus",
                  'AltHotkey' : 'W',
                  'FormLabel' : "Aufwertung"
              },
   '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"
              },
   'zeigeSkill' : {      // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showSkill",
                  'Type'      : __OPTTYPES.SW,
                  'Default'   : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Skill ein",
                  'Hotkey'    : 'S',
                  'AltLabel'  : "Skill aus",
                  'AltHotkey' : 'S',
                  '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
                         // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Name'      : "anzOpti",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "Number",
                  'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                  'Default'   : 1,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Opti: beste $",
                  'Hotkey'    : 'O',
                  'FormLabel' : "Opti:|beste $"
              },
   'anzahlMW' : {        // Gibt die Anzahl der MW-Spalten an / 1: nur hoechsten MW, 2: die beiden hoechsten, ..., 6: Alle inklusive TOR
                         // Bei Torhuetern wird immer nur der TOR-MW angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Name'      : "anzMW",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "Number",
                  'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                  'Default'   : 1,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "MW: beste $",
                  'Hotkey'    : 'M',
                  'FormLabel' : "MW:|beste $"
              },
   'zeigeSkillEnde' : {  // Spaltenauswahl fuer die Werte mit Ende 18 (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showSkillEnde",
                  'Type'      : __OPTTYPES.SW,
                  'Default'   : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Skill Ende ein",
                  'Hotkey'    : 'i',
                  'AltLabel'  : "Skill Ende aus",
                  'AltHotkey' : 'i',
                  'FormLabel' : "Skill \u03A9"
              },
   'anzahlOptiEnde' : {  // Spaltenauswahl fuer die Werte mit Ende 18:
                         // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
                         // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Name'      : "anzOptiEnde",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "Number",
                  'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                  'Default'   : 1,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Opti Ende: beste $",
                  'Hotkey'    : 't',
                  'FormLabel' : "Opti \u03A9:|beste $"
              },
   'anzahlMWEnde' : {    // Spaltenauswahl fuer die Werte mit Ende 18:
                         // Gibt die Anzahl der MW-Spalten an / 1: nur hoechsten MW, 2: die beiden hoechsten, ..., 6: Alle inklusive TOR
                         // Bei Torhuetern wird immer nur der TOR-MW angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Name'      : "anzMWEnde",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "Number",
                  'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                  'Default'   : 1,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "MW Ende: beste $",
                  'Hotkey'    : 'W',
                  'FormLabel' : "MW \u03A9:|beste $"
              },
   'kennzeichenEnde' : { // Markierung fuer Ende 18
                  'Name'      : "charEnde",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "String",
                  'Choice'    : [ " \u03A9", " 18" ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Ende: $",
                  'Hotkey'    : 'E',
                  'FormLabel' : "Ende 18:|$"
              },
   'sepStyle' : {        // Stil der Trennlinie
                  'Name'      : "sepStyle",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "String",
                  'Choice'    : [ "solid", "hidden", "dotted", "dashed", "double", "groove", "ridge",
                                  "inset", "outset", "none" ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Stil: $",
                  'Hotkey'    : 'l',
                  'FormLabel' : "Stil:|$"
              },
   'sepColor' : {        // Farbe der Trennlinie
                  'Name'      : "sepColor",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "String",
                  'Choice'    : [ "white", "yellow", "black", "blue", "cyan", "gold", "grey", "green",
                                  "lime", "magenta", "maroon", "navy", "olive", "orange", "purple",
                                  "red", "teal", "transparent" ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Farbe: $",
                  'Hotkey'    : 'F',
                  'FormLabel' : "Farbe:|$"
              },
   'sepWidth' : {        // Dicke der Trennlinie
                  'Name'      : "sepWidth",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "String",
                  'Choice'    : [ "thin", "medium", "thick" ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Dicke: $",
                  'Hotkey'    : 'D',
                  'FormLabel' : "Dicke:|$"
              },
   'saison' : {          // Laufende Saison
                  'Name'      : "saison",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "Number",
                  'Choice'    : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
                  'Default'   : 10,
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "Saison: $",
                  'Hotkey'    : 'a',
                  'FormLabel' : "Saison:|$"
              },
   'aktuellerZat' : {    // Laufender ZAT
                  'Name'      : "currZAT",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'   : "Number",
                  'Permanent' : true,
                  'Choice'    : [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
                                 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
                                 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
                                 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
                                 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
                                 70, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71 ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'     : "ZAT: $",
                  'Hotkey'    : 'Z',
                  'FormLabel' : "ZAT:|$"
              },
   'datenZat' : {        // Stand der Daten zum Team und ZAT
                  'Name'      : "dataZAT",
                  'Type'      : __OPTTYPES.SD,
                  'ValType'   : "Number",
                  'Hidden'    : true,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'   : undefined,
                  'Action'    : __OPTACTION.SET,
                  'Submit'    : undefined,
                  'Cols'      : 1,
                  'Rows'      : 1,
                  '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,
                  'Space'     : 0,
                  'Label'     : "Geburtstage:"
              },
   'tClasses' : {        // Datenspeicher fuer Talente der Jugendspieler (-1=wenig, 0=normal, +1=hoch)
                  'Name'      : "tClasses",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'   : [],
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 2,
                  'Replace'   : null,
                  'Space'     : 0,
                  'Label'     : "Talente:"
              },
   'progresses' : {      // Datenspeicher fuer Aufwertungen der Jugendspieler (als Strings)
                  'Name'      : "progresses",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'   : [],
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 7,
                  'Replace'   : null,
                  'Space'     : 0,
                  'Label'     : "Aufwertungen:"
              },
   'zatAges' : {         // Datenspeicher fuer (gebrochene) Alter der Jugendspieler
                  'Name'      : "zatAges",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'   : [],
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 2,
                  'Replace'   : null,
                  'Space'     : 0,
                  'Label'     : "ZAT-Alter:"
              },
   'trainiert' : {        // Datenspeicher fuer Trainingsquoten der Jugendspieler
                  'Name'      : "numProgresses",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'   : [],
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 2,
                  'Replace'   : null,
                  'Space'     : 0,
                  'Label'     : "Trainiert:"
              },
   'positions' : {       // Datenspeicher fuer optimale Positionen der Jugendspieler
                  'Name'      : "positions",
                  'Type'      : __OPTTYPES.SD,
                  'Hidden'    : false,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'   : [],
                  'Submit'    : undefined,
                  'Cols'      : 36,
                  'Rows'      : 3,
                  'Replace'   : null,
                  'Space'     : 0,
                  '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' : ""
              },
   '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' : ""
              }

};

// ==================== Invarianter Abschnitt fuer Optionen ====================

// ==================== Abschnitt fuer diverse Utilities ====================

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

   return ((value === undefined) || (value === null)) ? defValue : (retValue === undefined) ? value : retValue;

}

// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert // value: Ein Wert. Ist dieser definiet und in den Grenzen, wird er zurueckgeliefert // minValue: Untere Grenze fuer den Wert, falls angegeben // minValue: Obere Grenze fuer den Wert, falls angegeben // defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist oder der Wert ausserhalb liegt // return Der Wert. Sind weder value (in den Grenzen) noch defValue definiert, dann undefined function getValueIn(value, minValue = undefined, maxValue = undefined, defValue = undefined) {

   const __VALUE = getValue(value, defValue);
   if ((minValue !== undefined) && (__VALUE < minValue)) {
       return defValue;
   }
   if ((maxValue !== undefined) && (__VALUE > maxValue)) {
       return defValue;
   }
   return __VALUE;

}

// Ermittelt den naechsten Wert aus einer Array-Liste // arr: Array-Liste mit den moeglichen Werte // value: Vorher gesetzter Wert // return Naechster Wert in der Array-Liste function getNextValue(arr, value) {

   const __POS = arr.indexOf(value) + 1;
   return 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));

}

// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab // name: GM_setValue-Name, unter dem die Daten gespeichert werden // value: Beliebiger (strukturierter) Wert // return String-Darstellung des Wertes function serialize(name, value) {

   const __STREAM = (value !== undefined) ? JSON.stringify(value) : value;
   console.log(name + " >> " + __STREAM);
   GM_setValue(name, __STREAM);
   return __STREAM;

}

// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck // name: GM_setValue-Name, unter dem die Daten gespeichert werden // defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist // return Objekt, das unter dem Namen gespeichert war function deserialize(name, defValue = undefined) {

   const __STREAM = GM_getValue(name, defValue);
   console.log(name + " << " + __STREAM);
   if ((__STREAM !== undefined) && (__STREAM.length !== 0)) {
       try {
           return JSON.parse(__STREAM);
       } catch (ex) {
           console.error(name + ": " + ex.message);
       }
   }
   return undefined;

}

// Setzt eine Option dauerhaft und laedt die Seite neu // name: Name der Option als Speicherort // value: Zu setzender Wert // reload: Seite mit neuem Wert neu laden // return Gespeicherter Wert fuer setOptValue() function setStored(name, value, reload = true, serial = false) {

   if (serial) {
       serialize(name, value);
   } else {
       GM_setValue(name, value);
   }
   if (reload) {
       window.location.reload();
   }
   return value;

}

// Setzt den naechsten Wert aus einer Array-Liste als Option // arr: Array-Liste mit den moeglichen Optionen // name: Name der Option als Speicherort // value: Vorher gesetzter Wert // reload: Seite mit neuem Wert neu laden // return Gespeicherter Wert fuer setOptValue() function setNextStored(arr, name, value, reload = true, serial = false) {

   return setStored(name, getNextValue(arr, value), reload, serial);

}

// Fuehrt die in einem Storage gespeicherte Operation aus // optSet: Set mit den Optionen // memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv function runStored(optSet, memory = undefined) {

   const __STORAGE = getMemory(memory);
   const __MEMORY = __STORAGE.Value;
   const __RUNPREFIX = __STORAGE.Prefix;
   if (__MEMORY !== undefined) {
       const __GETITEM = function(item) {
                             return __MEMORY.getItem(__RUNPREFIX + item);
                         };
       const __DELITEM = function(item) {
                             return __MEMORY.removeItem(__RUNPREFIX + item);
                         };
       const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);
       if (__CMD !== undefined) {
           const __KEY = __GETITEM('key');
           let value = __GETITEM('val');
           try {
               value = JSON.parse(value);
           } catch (ex) {
               console.error("runStored(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
               // ... meist kann man den String selber aber speichern, daher kein "return"...
           }
           const __VAL = value;
           switch (__OPTACTION[__CMD]) {
           case __OPTACTION.SET : console.log("SET '" + __KEY + "' " + __VAL);
                                  setStored(__KEY, __VAL, false, false);
                                  break;
           case __OPTACTION.NXT : console.log("SETNEXT '" + __KEY + "' " + __VAL);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
                                  setStored(__KEY, __VAL, false, false);
                                  break;
           case __OPTACTION.RST : console.log("RESET");
                                  resetOptions(optSet, false);
                                  break;
           default :              break;
           }
       }
       __DELITEM('cmd');
       __DELITEM('key');
       __DELITEM('val');
   }

}

// Gibt eine Option sicher zurueck // opt: Config und Value der Option, ggfs. undefined // defOpt: Rueckgabewert, falls undefined // return Daten zur Option (oder defOpt) function getOpt(opt, defOpt = { }) {

   return getValue(opt, defOpt);

}

// Gibt eine Option sicher zurueck (Version mit Key) // optSet: Platz fuer die gesetzten Optionen (und Config) // item: Key der Option // defOpt: Rueckgabewert, falls nicht zu finden // return Daten zur Option (oder defOpt) function getOptByName(optSet, item, defOpt = { }) {

   if ((optSet !== undefined) && (item !== undefined)) {
       return getOpt(optSet[item], defOpt);
   } else {
       return defOpt;
   }

}

// Gibt die Konfigurationsdaten einer Option zurueck // opt: Config und Value der Option // defConfig: Rueckgabewert, falls Config nicht zu finden // return Konfigurationsdaten der Option function getOptConfig(opt, defConfig = { }) {

   return getValue(getOpt(opt).Config, defConfig);

}

// Setzt den Namen einer Option // opt: Config und Value der Option // name: Zu setzender Name der Option // reload: Seite mit neuem Wert neu laden // return Gesetzter Name der Option function setOptName(opt, name) {

   const __NAME = getOptName(opt);
   console.log("RENAME " + __NAME + " => " + name);
   return (getOptConfig(opt).Name = name);

}

// Gibt den Namen einer Option zurueck // opt: Config und Value der Option // return Name der Option function getOptName(opt) {

   return getOptConfig(opt).Name;

}

// Setzt den Wert einer Option // opt: Config und Value der Option // name: Zu setzender Wert der Option // return Gesetzter Wert function setOptValue(opt, value) {

   return (opt !== undefined) ? (opt.Value = value) : undefined;

}

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

   return getValue((opt !== undefined) ? opt.Value : undefined, defValue);

}

// ==================== Ende Abschnitt fuer diverse Utilities ====================

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

// Namen des Default-, Dauer- und Null-Memories... const __MEMNORMAL = 'normal'; const __MEMINFINITE = 'unbegrenzt'; const __MEMINAKTIVE = 'inaktiv';

// Definition des Default-, Dauer- und Null-Memories... const __OPTMEMNORMAL = __OPTMEM[__MEMNORMAL]; const __OPTMEMINFINITE = __OPTMEM[__MEMINFINITE]; const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];

// Medium fuer die Datenbank (Speicher) let myOptMem = __OPTMEMNORMAL;

// Speicher fuer die DB-Daten 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 // memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv function canUseMemory(memory = undefined) {

   const __STORAGE = getMemory(memory);
   const __MEMORY = __STORAGE.Value;
   let ret = false;
   if (__MEMORY !== undefined) {
       const __TESTPREFIX = 'canUseStorageTest';
       const __TESTDATA = Math.random().toString();
       const __TESTITEM = __TESTPREFIX + __TESTDATA;
       __MEMORY.setItem(__TESTITEM, __TESTDATA);
       ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
       __MEMORY.removeItem(__TESTITEM);
   }
   console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);
   return ret;

}

// Restauriert den vorherigen Speicher (der in einer Option definiert ist) // opt: Option zur Wahl des Speichers // return Gesuchter Speicher oder Null-Speicher ('inaktiv') function restoreMemoryByOpt(opt) {

   // Memory Storage fuer vorherige Speicherung...
   const __STORAGE = loadOption(getOpt(opt), true);
   return __OPTMEM[getValue(__STORAGE, __MEMNORMAL)];

}

// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs. // opt: Option zur Wahl des Speichers // 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 ====================

// ==================== Abschnitt fuer die Scriptdatenbank ====================

// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht // optSet: Gesetzte Optionen (und Config) function initScriptDB(optSet) {

   const __INFO = GM_info;
   const __META = __INFO.script;
   //console.log(__INFO);
   __DBTOC.versions = getValue(JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
   // Infos zu diesem Script...
   __DBMOD.name = __META.name;
   __DBMOD.version = __META.version;
   console.log(__DBMOD);
   // Zunaechst den alten Eintrag entfernen...
   __DBTOC.versions[__DBMOD.name] = undefined;
   // ... und die Daten der Fremdscripte laden...
   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 // optSet: Gesetzte Optionen (und Config) function updateScriptDB(optSet) {

   // Eintrag ins Inhaltsverzeichnis...
   __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
   // Permanente Speicherung der Eintraege...
   __DBMEM.setItem('__DBTOC.versions', JSON.stringify(__DBTOC.versions));
   __DBMEM.setItem('__DBDATA.' + __DBMOD.name, JSON.stringify(optSet));
   // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
   __DBDATA[__DBMOD.name] = getValue(optSet, { });
   console.log(__DBDATA);

}

// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================

// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================

// ==================== Abschnitt fuer das Benutzermenu ====================

// Zeigt den Eintrag im Menu einer Option // opt: Derzeitiger Wert der Option // menuOn: Text zum Setzen im Menu // funOn: Funktion zum Setzen // keyOn: Hotkey zum Setzen im Menu // menuOff: Text zum Ausschalten im Menu // funOff: Funktion zum Ausschalten // keyOff: Hotkey zum Ausschalten im Menu function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {

   const __ON  = (opt ? '*' : "");
   const __OFF = (opt ? "" : '*');
   console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
   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 // opt: Derzeitiger Wert der Option // arr: Array-Liste mit den moeglichen Optionen // menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt) // fun: Funktion zum Setzen des naechsten Wertes // key: Hotkey zum Setzen des naechsten Wertes im Menu function registerNextMenuOption(opt, arr, menu, fun, key) {

   const __MENU = menu.replace('$', opt);
   let options = "OPTION " + __MENU;
   for (let value of arr) {
       if (value === opt) {
           options += " / *" + value + '*';
       } else {
           options += " / " + value;
       }
   }
   console.log(options);
   GM_registerMenuCommand(__MENU, fun, key);

}

// Zeigt den Eintrag im Menu einer Option, falls nicht hidden // opt: Derzeitiger Wert der Option // menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt) // fun: Funktion zum Setzen des naechsten Wertes // key: Hotkey zum Setzen des naechsten Wertes im Menu // hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar) // serial: Serialization fuer komplexe Daten function registerDataOption(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);
   if (! hidden) {
       GM_registerMenuCommand(__MENU, fun, key);
   }

}

// Zeigt den Eintrag im Menu einer Option // opt: Config und Value der Option function registerOption(opt) {

   const __CONFIG = getOptConfig(opt);
   if (! __CONFIG.HiddenMenu) {
       switch (__CONFIG.Type) {
       case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
                                                                 __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
                            break;
       case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                 __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
                            break;
       case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                 __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                            break;
       case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                 __CONFIG.HiddenMenu, __CONFIG.Serial);
                            break;
       case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                 __CONFIG.HiddenMenu, __CONFIG.Serial);
                            break;
       default :            break;
       }
   } else {
       // Nur Anzeige im Log...
       registerDataOption(getOptValue(opt), getOptName(opt), opt.Action, __CONFIG.Hotkey, __CONFIG.HiddenMenu, __CONFIG.Serial);
   }

}

// ==================== Ende Abschnitt fuer das Benutzermenu ====================

// Initialisiert die gesetzten Option // config: Konfiguration der Option // return Initialwert der gesetzten Option function initOptValue(config) {

   let value = config.Default;  // Standard
   switch (config.Type) {
   case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
                            value = config.Choice[0];
                        }
                        break;
   case __OPTTYPES.SW : break;
   case __OPTTYPES.TF : break;
   case __OPTTYPES.SD : config.Serial = true;
                        break;
   case __OPTTYPES.SI : break;
   default :            break;
   }
   if (config.Serial || config.Hidden) {
       config.HiddenMenu = true;
   }
   return value;

}

// 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);
       switch (optAction) {
       case __OPTACTION.SET : fun = function() {
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
       case __OPTACTION.NXT : fun = function() {
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
       case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
       default :              break;
       }
   }
   return fun;

}

// Initialisiert die gesetzten Optionen // optConfig: Konfiguration der Optionen // optSet: Platz fuer die gesetzten Optionen // return Gefuelltes Objekt mit den gesetzten Optionen function initOptions(optConfig, optSet = undefined) {

   var value;
   if (optSet === undefined) {
       optSet = { };
   }
   for (let opt in optConfig) {
       const __CONFIG = optConfig[opt];
       const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
       optSet[opt] = {
           'Config'    : __CONFIG,
           'Value'     : initOptValue(__CONFIG),
           'SetValue'  : undefined,
           'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
           'AltAction' : initOptAction(__ALTACTION, opt, optSet)
       };
   }
   return optSet;

}

// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start // optConfig: Konfiguration der Optionen // optSet: Platz fuer die gesetzten Optionen // return Gefuelltes Objekt mit den gesetzten Optionen function startOptions(optConfig, optSet = undefined) {

   optSet = initOptions(optConfig, optSet);
   // Memory Storage fuer vorherige Speicherung...
   myOptMem = restoreMemoryByOpt(optSet.oldStorage);
   runStored(optSet);
   loadOptions(optSet);
   // Memory Storage fuer naechste Speicherung...
   myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage);
   initScriptDB(optSet);
   return optSet;

}

// Installiert die Visualisierung und Steuerung der Optionen // optSet: Platz fuer die gesetzten Optionen // optParams: Eventuell notwendige Parameter zur Initialisierung // 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf // 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite // 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar) // 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar) // 'formWidth': Anzahl der Elemente pro Zeile // 'formBreak': Elementnummer des ersten Zeilenumbruchs function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {

   updateScriptDB(optSet);
   if (! optParams.hideMenu) {
       buildMenu(optSet);
   }
   if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
       buildForm(optParams.menuAnchor, optSet, optParams);
   }

}

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

   return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));

}

// Ermittelt die naechste moegliche Option // opt: Config und Value der Option // value: Ggfs. zu setzender Wert // return Zu setzender Wert function getNextOpt(opt, value = undefined) {

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

}

// Setzt die naechste moegliche Option // opt: Config und Value der Option // value: Default fuer ggfs. zu setzenden Wert // reload: Seite mit neuem Wert neu laden // return Gesetzter Wert function setNextOpt(opt, value = undefined, reload = true) {

   const __CONFIG = getOptConfig(opt);
   return setOpt(opt, getNextOpt(opt, value), reload);

}

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

   const __OPT = getOptByName(optSet, item);
   return setOpt(__OPT, value, reload);

}

// Ermittelt die naechste moegliche Option (Version mit Key) // 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);

}

// Setzt die naechste moegliche Option (Version mit Key) // 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 // optSet: Gesetzte Optionen function buildMenu(optSet) {

   console.log("buildMenu()");
   for (let opt in optSet) {
       registerOption(optSet[opt]);
   }

}

// Laedt eine (ueber Menu) gesetzte Option // opt: Zu ladende Option // force: Laedt auch Optionen mit 'AutoReset'-Attribut // return Gesetzter Wert der gelandenen Option function loadOption(opt, force = false) {

   const __CONFIG = getOptConfig(opt);
   if (! force && __CONFIG.AutoReset) {
       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)));
   }

}

// Laedt die (ueber Menu) gesetzten Optionen // 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;

}

// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent') // opt: Gesetzte Option // force: Entfernt auch Optionen mit 'Permanent'-Attribut // reset: Setzt bei Erfolg auf Initialwert der Option function deleteOption(opt, force = false, reset = true) {

   const __CONFIG = getOptConfig(opt);
   if (force || ! __CONFIG.Permanent) {
       const __NAME = getOptName(opt);
       console.log("DELETE " + __NAME);
       GM_deleteValue(__NAME);
       if (reset) {
           setOptValue(opt, initOptValue(__CONFIG));
       }
   }

}

// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent') // optSet: Gesetzte Optionen // optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen // force: Entfernt auch Optionen mit 'Permanent'-Attribut // reset: Setzt bei Erfolg auf Initialwert der Option function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {

   const __DELETEALL = (optSelect === undefined) || (optSelect === true);
   const __OPTSELECT = getValue(optSelect, { });
   for (let opt in optSet) {
       if (getValue(__OPTSELECT[opt], __DELETEALL)) {
           deleteOption(optSet[opt], force, reset);
       }
   }

}

// Benennt eine Option um und laedt sie ggfs. nach // opt: Gesetzte Option // name: Neu zu setzender Name (Speicheradresse) // reload: Wert nachladen statt beizubehalten // force: Laedt auch Optionen mit 'AutoReset'-Attribut // return Umbenannte Option function renameOption(opt, name, reload = false, force = false) {

   const __NAME = getOptName(opt);
   if (__NAME !== name) {
       deleteOption(opt, true, ! reload);
       setOptName(opt, name);
       if (reload) {
           loadOption(opt, force);
       }
   }
   return opt;

}

// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions() // name: Gesetzter Name (Speicheradresse) // prefix: Prefix, das vorangestellt werden soll // return Neu zu setzender Name (Speicheradresse) function prefixName(name, prefix) {

   return (prefix + name);

}

// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions() // name: Gesetzter Name (Speicheradresse) // postfix: Postfix, das angehaengt werden soll // return Neu zu setzender Name (Speicheradresse) function postfixName(name, postfix) {

   return (name + postfix);

}

// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach // optSet: Gesetzte Optionen // optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen // 'reload': Option nachladen? // 'force': Option auch mit 'AutoReset'-Attribut nachladen? // renameParam: Wird an renameFun uebergeen // renameFun: function(name, param) zur Ermittlung des neuen Namens // name: Neu zu setzender Name (Speicheradresse) // reload: Wert nachladen statt beizubehalten // force: Laedt auch Optionen mit 'AutoReset'-Attribut // return Umbenannte Option function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {

   if (renameFun === undefined) {
       console.error("RENAME: Illegale Funktion!");
   }
   for (let opt in optSelect) {
       const __OPTPARAMS = optSelect[opt];
       const __OPT = optSet[opt];
       if (__OPT === undefined) {
           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 // optSet: Gesetzte Optionen // reload: Seite mit "Werkseinstellungen" neu laden function resetOptions(optSet, reload = true) {

   // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
   deleteOptions(optSet, true, false, ! reload);
   if (reload) {
       // ... und Seite neu laden (mit "Werkseinstellungen")...
       window.location.reload();
   }

}

// ==================== Spezialisierter Abschnitt fuer Optionen ====================

// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt): const __OPTSET = { };

// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam... const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };

// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden... const __DATAOPTS = {

                      'datenZat'   : true,
                      'birthdays'  : true,
                      'tClasses'   : true,
                      'progresses' : true,
                      'zatAges'    : true,
                      'trainiert'  : true,
                      'positions'  : true
                  };

// Behandelt die Optionen und laedt das Benutzermenu // optConfig: Konfiguration der Optionen // optSet: Platz fuer die gesetzten Optionen // optParams: Eventuell notwendige Parameter zur Initialisierung // 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf // 'teamParams': Getrennte "ligaSize"-Option wird genutzt, hier: __MYTEAM mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams // 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite // 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar) // 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar) // 'formWidth': Anzahl der Elemente pro Zeile // 'formBreak': Elementnummer des ersten Zeilenumbruchs // return Gefuelltes Objekt mit den gesetzten Optionen function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {

   const __TEAMPARAMS = optParams.teamParams;  // Ermittelte Parameter
   optSet = startOptions(optConfig, optSet);
   if (__TEAMPARAMS !== undefined) {
       __MYTEAM.Team = __TEAMPARAMS.Team;
       __MYTEAM.Liga = __TEAMPARAMS.Liga;
       __MYTEAM.Land = __TEAMPARAMS.Land;
       __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) {
       // 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 ====================

// Legt Input-Felder in einem Form-Konstrukt an, falls noetig // form: <form>...</form> // props: Map von name:value-Paaren // type: Typ der Input-Felder (Default: unsichtbare Daten) // return Ergaenztes Form-Konstrukt function addInputField(form, props, type = "hidden") {

   for (let fieldName in props) {
       let field = form[fieldName];
       if (! field) {
           field = document.createElement("input");
           field.type = type;
           field.name = fieldName;
           form.appendChild(field);
       }
       field.value = props[fieldName];
   }
   return form;

}

// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig // form: <form>...</form> // props: Map von name:value-Paaren // return Ergaenztes Form-Konstrukt function addHiddenField(form, props) {

   return addInputField(form, props, "hidden");

}

// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein // obj: Betroffenes Objekt, z.B. ein Eingabeelement // type: Name des Events, z.B. "click" // callback: Funktion als Reaktion // capture: Event fuer Parent zuerst (true) oder Child (false als Default) // return false bei Misserfolg function addEvent(obj, type, callback, capture = false) {

   if (obj.addEventListener) {
       return obj.addEventListener(type, callback, capture);
   } else if (obj.attachEvent) {
       return obj.attachEvent("on" + type, callback);
   } else {
       console.log("Could not add " + type + " event:");
       console.log(callback);
       return false;
   }

}

// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event // obj: Betroffenes Objekt, z.B. ein Eingabeelement // type: Name des Events, z.B. "click" // callback: Funktion als Reaktion // capture: Event fuer Parent zuerst (true) oder Child (false als Default) // return false bei Misserfolg function removeEvent(obj, type, callback, capture = false) {

   if (obj.removeEventListener) {
       return obj.removeEventListener(type, callback, capture);
   } else if (obj.detachEvent) {
       return obj.detachEvent("on" + type, callback);
   } else {
       console.log("Could not remove " + type + " event:");
       console.log(callback);
       return false;
   }

}

// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein // id: ID des betroffenen Eingabeelements // type: Name des Events, z.B. "click" // callback: Funktion als Reaktion // capture: Event fuer Parent zuerst (true) oder Child (false als Default) // return false bei Misserfolg function addDocEvent(id, type, callback, capture = false) {

   const __OBJ = document.getElementById(id);
   return addEvent(__OBJ, type, callback, capture);

}

// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event // id: ID des betroffenen Eingabeelements // type: Name des Events, z.B. "click" // callback: Funktion als Reaktion // capture: Event fuer Parent zuerst (true) oder Child (false als Default) // return false bei Misserfolg function removeDocEvent(id, type, callback, capture = false) {

   const __OBJ = document.getElementById(id);
   return removeEvent(__OBJ, type, callback, capture);

}

// Hilfsfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll // item: Name des betroffenen Items // showList: Checkliste der sichtbaren Items (true fuer sichtbar) // hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar) // return Angabe, ob das Item sichtbar sein soll function checkVisible(item, showList, hideList = undefined) {

   let show = true;
   if (showList !== undefined) {
       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 // name: Name des Elements (siehe "name=") // index: Laufende Nummer des Elements (0-based), Default: 0 // doc: Dokument (document) // return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden) function getElement(name, index = 0, doc = document) {

   const __TAGS = document.getElementsByName(name);
   const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
   return __TABLE;

}

// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle) // index: Laufende Nummer des Elements (0-based) // tag: Tag des Elements ("table") // doc: Dokument (document) // return Gesuchtes Element oder undefined (falls nicht gefunden) function getTable(index, tag = "table", doc = document) {

   const __TAGS = document.getElementsByTagName(tag);
   const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
   return __TABLE;

}

// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle // index: Laufende Nummer des Elements (0-based) // doc: Dokument (document) // return Gesuchte Zeilen oder undefined (falls nicht gefunden) function getRows(index, doc = document) {

   const __TABLE = getTable(index, "table", doc);
   const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
   return __ROWS;

}

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

// Liefert den Funktionsaufruf zur Option als String // opt: Auszufuehrende Option // isAlt: Angabe, ob AltAction statt Action gemeint ist // value: Ggfs. zu setzender Wert // serial: Serialization fuer String-Werte (Select, Textarea) // memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv // return String mit dem (reinen) Funktionsaufruf function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {

   const __STORAGE = getMemory(memory);
   const __MEMORY = __STORAGE.Value;
   const __MEMSTR = __STORAGE.Display;
   const __RUNPREFIX = __STORAGE.Prefix;
   if (__MEMORY !== undefined) {
       const __RELOAD = "window.location.reload()";
       const __SETITEM = function(item, val, quotes = true) {
                             return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                         };
       const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                             return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                                     __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                         };
       const __CONFIG = getOptConfig(opt);
       const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
       const __THISVAL = ((__CONFIG.ValType === "String") ? "'\\x22' + this.value + '\\x22'" : "this.value");
       const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
       const __VALSTR = ((value !== undefined) ? 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 // opt: Auszufuehrende Option // isAlt: Angabe, ob AltAction statt Action gemeint ist // value: Ggfs. zu setzender Wert // type: Event-Typ fuer <input>, z.B. "click" fuer "onclick=" // serial: Serialization fuer String-Werte (Select, Textarea) // memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv // return String mit dem (reinen) Funktionsaufruf function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {

   const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
   return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');

}

// Zeigt eine Option auf der Seite als Auswahlbox an // opt: Anzuzeigende Option // return String mit dem HTML-Code function getOptionSelect(opt) {

   const __CONFIG = getOptConfig(opt);
   const __NAME = getOptName(opt);
   const __VALUE = getOptValue(opt);
   const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
   const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
   const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
   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>';
   return __LABEL.replace('$', element);

}

// Zeigt eine Option auf der Seite als Radiobutton an // opt: Anzuzeigende Option // return String mit dem HTML-Code function getOptionRadio(opt) {

   const __CONFIG = getOptConfig(opt);
   const __NAME = getOptName(opt);
   const __VALUE = getOptValue(opt, false);
   const __ACTION = getFormActionEvent(opt, false, true, "click", false);
   const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
   const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'ON" value="1"' +
                        (__VALUE ? ' CHECKED' : __ACTION) +
                        ' /><label for="' + __NAME + 'ON">' +
                        __CONFIG.Label + '</label>';
   const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';
   return [ __ELEMENTON, __ELEMENTOFF ];

}

// Zeigt eine Option auf der Seite als Checkbox an // opt: Anzuzeigende Option // return String mit dem HTML-Code function getOptionCheckbox(opt) {

   const __CONFIG = getOptConfig(opt);
   const __NAME = getOptName(opt);
   const __VALUE = getOptValue(opt, false);
   const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
   const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
   return '<input type="checkbox" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __VALUE + '"' +
          (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
          __NAME + '">' + __FORMLABEL + '</label>';

}

// Zeigt eine Option auf der Seite als Daten-Textfeld an // opt: Anzuzeigende Option // return String mit dem HTML-Code function getOptionTextarea(opt) {

   const __CONFIG = getOptConfig(opt);
   const __NAME = getOptName(opt);
   const __VALUE = getOptValue(opt);
   const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
   const __SUBMIT = getValue(__CONFIG.Submit, "");
   const __ONSUBMIT = ((__SUBMIT.length > 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 eine Option auf der Seite als Button an // opt: Anzuzeigende Option // return String mit dem HTML-Code function getOptionButton(opt) {

   const __CONFIG = getOptConfig(opt);
   const __NAME = getOptName(opt);
   const __VALUE = getOptValue(opt, false);
   const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
   const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
   const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
   return '<label for="' + __NAME + '">' + __FORMLABEL +
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';

}

// Zeigt eine Option auf der Seite an (je nach Typ) // opt: Anzuzeigende Option // return String mit dem HTML-Code function getOptionElement(opt) {

   const __CONFIG = getOptConfig(opt);
   const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
   let element = "";
   if (! __CONFIG.Hidden) {
       switch (__TYPE) {
       case __OPTTYPES.MC : element = getOptionSelect(opt);
                            break;
       case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                                element = getOptionCheckbox(opt);
                            } else {
                                element = getOptionRadio(opt);
                            }
                            break;
       case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                            break;
       case __OPTTYPES.SD : element = getOptionTextarea(opt);
                            break;
       case __OPTTYPES.SI : element = getOptionButton(opt);
                            break;
       default :            break;
       }
       if (element.length === 2) {

element = '

' + element[0] + '
' + element[1] + '

';

       }
   }
   return element;

}

// Baut das Benutzermenu auf der Seite auf // optSet: Gesetzte Optionen // optParams: Eventuell notwendige Parameter // 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar) // 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar) // 'formWidth': Anzahl der Elemente pro Zeile // 'formBreak': Elementnummer des ersten Zeilenumbruchs // return String mit dem HTML-Code function getForm(optSet, optParams = { }) {

const __FORM = '<form id="options" method="POST">

<tbody>'; const __FORMEND = '</tbody>

</form>';

   const __FORMWIDTH = getValue(optParams.formWidth, 3);
   const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
   const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
   let form = __FORM;
   let count = 0;   // Bisher angezeigte Optionen
   let column = 0;  // Spalte der letzten Option (1-basierend)
   for (let opt in optSet) {
       if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
           const __ELEMENT = getOptionElement(optSet[opt]);
           const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";
           if (__ELEMENT.length > 0) {
               if (++count > __FORMBREAK) {
                   if (++column > __FORMWIDTH) {
                       column = 1;
                   }
               }
               if (column === 1) {

form += ''; } form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '') + '';

           }
       }
   }
   form += '\n' + __FORMEND;
   return form;

}

// Fuegt das Script in die Seite ein // optSet: Gesetzte Optionen // optParams: Eventuell notwendige Parameter // 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar) // 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar) // return String mit dem HTML-Code fuer das Script function getScript(optSet, optParams = { }) {

   //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
   //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
   //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
   const __SCRIPT = "";
   //window.eval('function activateMenu() { console.log("TADAAA!"); }');
   return __SCRIPT;

}

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

   console.log("buildForm()");
   const __FORM = getForm(optSet, optParams);
   const __SCRIPT = getScript(optSet, optParams);
   addForm(anchor, __FORM, __SCRIPT);

}

// Informationen zu hinzugefuegten Forms const __FORMS = { };

// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu) // anchor: Element, das als Anker fuer die Anzeige dient // form: HTML-Form des Optionsmenu (hinten angefuegt) // script: Script mit Reaktionen function addForm(anchor, form = "", script = "") {

   const __OLDFORM = __FORMS[anchor];
   const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
   __FORMS[anchor] = { 'Script' : script, 'Form' : form };
   anchor.innerHTML = __REST + script + form;

}

// ==================== Ende Abschnitt fuer Optionen ====================

// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================

// Zeitpunktangaben 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 '?')

};

// Funktionen ***************************************************************************

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

   const __SAISON = getOptValue(optSet.saison);
   const __CURRZAT = getOptValue(optSet.aktuellerZat);
   const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
   const __TCLASSES = getOptValue(optSet.tClasses, []);
   const __PROGRESSES = getOptValue(optSet.progresses, []);
   const __ZATAGES = getOptValue(optSet.zatAges, []);
   const __TRAINIERT = getOptValue(optSet.trainiert, []);
   const __POSITIONS = getOptValue(optSet.positions, []);
   const __PLAYERS = [];
   for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
       const __CELLS = playerRows[i].cells;
       const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
       const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
       const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
       const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);
       __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
       if (reloadData) {
           __NEWPLAYER.setZusatz(__ZATAGES[j], __TRAINIERT[j], __POSITIONS[j]);
       }
       __PLAYERS[j] = __NEWPLAYER;
   }
   if (reloadData) {
       storePlayerData(__PLAYERS, playerRows, optSet, colIdx, offsetUpper, offsetLower);
   } else {
       calcPlayerData(__PLAYERS, optSet);
   }
   return __PLAYERS;

}

// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese function calcPlayerData(players, optSet) {

   const __ZATAGES = [];
   const __TRAINIERT = [];
   const __POSITIONS = [];
   for (let i = 0; i < players.length; i++) {
       const __ZUSATZ = players[i].calcZusatz();
       __ZATAGES[i]   = __ZUSATZ.zatAge;
       __TRAINIERT[i] = __ZUSATZ.trainiert;
       __POSITIONS[i] = __ZUSATZ.bestPos;
   }
   setOpt(optSet.zatAges, __ZATAGES, false);
   setOpt(optSet.trainiert, __TRAINIERT, false);
   setOpt(optSet.positions, __POSITIONS, false);

}

// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese function storePlayerData(players, playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {

   const __BIRTHDAYS = [];
   const __TCLASSES = [];
   const __PROGRESSES = [];
   for (let i = offsetUpper; i < playerRows.length - offsetLower; i++) {
       const __CELLS = playerRows[i].cells;
       __BIRTHDAYS[i - offsetUpper] = getIntFromHTML(__CELLS, colIdx.Geb);
       __TCLASSES[i - offsetUpper] = getTalentFromHTML(__CELLS, colIdx.Tal);
       __PROGRESSES[i - offsetUpper] = getStringFromHTML(__CELLS, colIdx.Auf);
   }
   setOpt(optSet.birthdays, __BIRTHDAYS, false);
   setOpt(optSet.tClasses, __TCLASSES, false);
   setOpt(optSet.progresses, __PROGRESSES, false);

}

// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {

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

}

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

function ColumnManager(optSet, showCol = undefined) {

   const __SHOWALL = (showCol === undefined) || (showCol === true);
   const __SHOWCOL = getValue(showCol, { });
   this.geb = getValue(__SHOWCOL.zeigeGeb, __SHOWALL) && getOptValue(optSet.zeigeGeb);
   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() {
       let result = "Skillschnitt\t\t" + this.skill + '\n';
       result += "Beste Position\t" + this.pos + '\n';
       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;
   };
   this.addCell = function(tableRow) {
       tableRow.insertCell(-1);
       return tableRow.cells.length - 1;
   };
   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;
   };
   this.addTitles = function(headers, titleColor = "#FFFFFF") {
       // Spaltentitel zentrieren
       headers.align = "center";
       // Titel fuer die aktuellen Werte
       if (this.tal) {
           this.addAndFillCell(headers, "Talent", titleColor);
       }
       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
       if (this.skillE) {
           this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
       }
       for (let i = 1; i <= 6; i++) {
           if (i <= this.anzOptiE) {
               this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
           }
           if (i <= this.anzMwE) {
               this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
           }
       }
   };  // Ende addTitles()
   this.addValues = function(player, playerRow, color = "#FFFFFF") {
       const __COLOR = (player.isGoalie ? getColor("TOR") : color);
       const __POS1COLOR = getColor(player.getPos());
       // Aktuelle Werte
       if (this.tal) {
           this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
       }
       if (this.quo) {
           this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
       }
       if (this.aufw) {
           this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
       }
       if (this.geb) {
           this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
       }
       if (this.alter) {
           this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
       }
       if (this.skill) {
           this.addAndFillCell(playerRow, player.getSkill(), __COLOR, 2);
       }
       if (this.pos) {
           this.addAndFillCell(playerRow, player.getPos(), __POS1COLOR);
       }
       for (let i = 1; i <= 6; i++) {
           const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
           const __COLI = getColor(__POSI);
           if (i <= this.anzOpti) {
               if ((i === 1) || ! player.isGoalie) {
                   // 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
       if (this.skillE) {
           this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
       }
       for (let i = 1; i <= 6; i++) {
           const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
           const __COLI = getColor(__POSI);
           if (i <= this.anzOptiE) {
               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);
               }
           }
       }
   };  // Ende addValues(player, playerRow)

}

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

function PlayerRecord(age, skills, isGoalie) {

   // Zu benutzende Marktwertformel
   const __MWFORMEL = {
       'alt' : 0,  // Marktwertformel bis Saison 9 inklusive
       'S10' : 1   // Marktwertformel MW5 ab Saison 10
   };
   this.mwFormel = __MWFORMEL.S10;  // Neue Formel, genauer in initPlayer()
   this.age = age;
   this.skills = skills;
   this.isGoalie = isGoalie;
   // 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
   // in this calcZusatz()/setZusatz() definiert:
   // this.trainiert: Anzahl der erfolgreichen Trainingspunkte
   // this.bestPos: erster (bester) Positionstext
   this.toString = function() {
       let result = "Alter\t\t" + this.age + "\n\n";
       result += "Aktuelle Werte\n";
       result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
       result += "Optis und Marktwerte";
       for (let pos of this.positions) {
           result += "\n\t" + pos + '\t';
           result += this.getOpti(pos).toFixed(2) + '\t';
           result += getNumberString(this.getMarketValue(pos).toString());
       }
       result += "\n\nWerte mit Ende 18\n";
       result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
       result += "Optis und Marktwerte";
       for (let pos of this.positions) {
           result += "\n\t" + this.getPos()[i] + '\t';
           result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
           result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
       }
       return result;
   };  // Ende this.toString()
   // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
   this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
       this.zatGeb = gebZAT;
       this.zatAge = this.calcZatAge(currZAT);
       this.talent = tclass;
       this.aufwert = progresses;
       this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;
       this.positions = [];
       // ABW
       this.positions[0] = [];
       this.positions[0][0] = "ABW";
       this.positions[0][1] = this.getOpti("ABW");
       // 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
       sortPositionArray(this.positions);
       // Einzelskills mit Ende 18 berechnen
       this.skillsEnd = [];
       const __ADDRATIO = (this.getZatDone(__TIME.end) - this.getZatDone()) / this.getZatDone();
       for (let i in this.skills) {
           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 = getMulValue(__ADDRATIO, __SKILL, 0, NaN);
               progSkill += __ADDSKILL;
           }
           this.skillsEnd[i] = Math.min(progSkill, 99);
       }
   };  // Ende this.initPlayer()
   // Setzt Nebenwerte fuer den Spieler (geht ohne initPlayer())
   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())
   this.calcZusatz = function() {
       // this.zatAge bereits in initPlayer() berechnet
       this.trainiert = this.getTrainiert(true);  // neu berechnet aus Skills
       this.bestPos = this.getPos(-1);  // hier: -1 explizit angeben, da neu ermittelt (this.bestPos noch nicht belegt)
       return {
                  zatAge    : this.zatAge,
                  trainiert : this.trainiert,
                  bestPos   : this.bestPos
              };
   };
   this.getGeb = function() {
       return (this.zatGeb < 0) ? '?' : this.zatGeb;
   };
   this.calcZatAge = function(currZAT) {
       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.getZatDone = function(when = __TIME.now) {
       return Math.max(0, this.getZatAge(when));
   }
   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.getTrainiert = function(recalc = false) {
       let sum = 0;
       if (recalc || (this.trainiert === undefined)) {
           for (let i in this.skills) {
               if (isTrainableSkill(i)) {
                   sum += this.skills[i];
               }
           }
       } else {
           sum += this.trainiert;
       }
       return sum;
   };
   this.getAufwertungsSchnitt = function() {
       return parseFloat(this.getTrainiert() / this.getZatDone());
   };
   this.getPos = function(idx = undefined) {
       const __IDXOFFSET = 1;
       switch (getValue(idx, 0)) {
       case -1 : return (this.bestPos = this.positions[isGoalie ? 5 : 0][0]);
       case  0 : return this.bestPos;
       default : return this.positions[idx - __IDXOFFSET][0];
       }
   };
   this.getTalent = function() {
       return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
   };
   this.getAufwert = function() {
       return (this.aufwert.length > 0) ? this.aufwert : "keine";
   };
   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 / __SKILLS.length;
   };
   this.getOpti = function(pos, when = __TIME.now) {
       const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
       const __IDXPRISKILLS = getIdxPriSkills(pos);
       const __IDXSECSKILLS = getIdxSecSkills(pos);
       let sumPriSkills = 0;
       let sumSecSkills = 0;
       for (let idx of __IDXPRISKILLS) {
           sumPriSkills += __SKILLS[idx];
       }
       for (let idx of __IDXSECSKILLS) {
           sumSecSkills += __SKILLS[idx];
       }
       return (5 * sumPriSkills + sumSecSkills) / 27;
   };
   this.getMarketValue = function(pos, when = __TIME.now) {
       const __AGE = this.getAge(when);
       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
           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;

}

// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus // cells: Die Zellen einer Zeile // colIdxInt: Spaltenindex der gesuchten Werte // return Spalteneintrag als Dezimalzahl (undefined fuer "keine Zahl" oder "nicht gefunden") function getFloatFromHTML(cells, colIdxFloat) {

   const __CELL = cells[colIdxFloat];
   const __TEXT = getValue(__CELL, { }).textContent;
   if (__TEXT !== undefined) {
       try {
           return parseFloat(__TEXT);
       } catch (ex) { }
   }
   return undefined;

}

// Liest einen String aus der Spalte einer Zeile der Tabelle aus // cells: Die Zellen einer Zeile // colIdxStr: Spaltenindex der gesuchten Werte // return Spalteneintrag als String ("" fuer "nicht gefunden") function getStringFromHTML(cells, colIdxStr) {

   const __CELL = cells[colIdxStr];
   const __TEXT = getValue(__CELL, { }).textContent;
   return getValue(__TEXT.toString(), "");

}

// Liest die Talentklasse ("wenig", "normal", "hoch") aus der Spalte einer Zeile der Tabelle aus // 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);

}

// Liest die Einzelskills aus der Spalte einer Zeile der Tabelle aus // cells: Die Zellen einer Zeile // colIdx: Liste von Spaltenindices der gesuchten Werte mit den Eintraegen // 'Einz' (erste Spalte) und 'Zus' (Spalte hinter dem letzten Eintrag) // return Skills als Array von Zahlen function getSkillsFromHTML(cells, colIdx) {

   const __RESULT = [];
   for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
       __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
   }
   return __RESULT;

}

// Liest aus, ob der Spieler Torwart oder Feldspieler ist // return Angabe, der Spieler Torwart oder Feldspieler ist function isGoalieFromHTML(cells, colIdxClass) {

   return (cells[colIdxClass].className === "TOR");

}

// Hilfsfunktionen **********************************************************************

// 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;
   while (transposed && (length > 1)) {
       transposed = false;
       for (let i = 0; i < length - 1; i++) {
           // Vergleich Opti-Werte:
           if (array[i][1] < array[i + 1][1]) {
               // vertauschen
               __TEMP[0] = array[i][0];
               __TEMP[1] = array[i][1];
               array[i][0] = array[i + 1][0];
               array[i][1] = array[i + 1][1];
               array[i + 1][0] = __TEMP[0];
               array[i + 1][1] = __TEMP[1];
               transposed = true;
           }
       }
       length--;
   }

}

// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein // 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;
   } else {
       // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
       // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
       const __TEMP = reverseString(numberString);
       let result = "";
       for (let i = 0; i < __TEMP.length; i++) {
           if ((i > 0) && (i % 3 === 0)) {
               result += ".";
           }
           result += __TEMP.substr(i, 1);
       }
       return reverseString(result);
   }

}

// Dreht den uebergebenen String um function reverseString(string) {

   let result = "";
   for (let i = string.length - 1; i >= 0; i--) {
       result += string.substr(i, 1);
   }
   return result;

}

// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert // 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;
   for (let idxTrainable of __TRAINABLESKILLS) {
       if (__IDX === idxTrainable) {
           result = true;
           break;
       }
   }
   return result;

}

// Gibt die Indizes der Primaerskills zurueck function getIdxPriSkills(pos) {

   switch (pos) {
       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 function getIdxSecSkills(pos) {

   switch (pos) {
       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);
       case "DMI" : return new Array(0, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
       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 [];
   }

}

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

}

// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================

// ==================== Abschnitt fuer interne IDs auf den Seiten ====================

const __GAMETYPES = { // "Blind FSS gesucht!"

       'unbekannt'  : -1,
       "reserviert" :  0,
       "Frei"       :  0,
       "spielfrei"  :  0,
       "Friendly"   :  1,
       "Liga"       :  2,
       "LP"         :  3,
       "OSEQ"       :  4,
       "OSE"        :  5,
       "OSCQ"       :  6,
       "OSC"        :  7
   };

const __LIGANRN = {

       'unbekannt'  :  0,
       '1. Liga'    :  1,
       '2. Liga A'  :  2,
       '2. Liga B'  :  3,
       '3. Liga A'  :  4,
       '3. Liga B'  :  5,
       '3. Liga C'  :  6,
       '3. Liga D'  :  7
   };

const __LANDNRN = {

       'unbekannt'              :   0,
       'Albanien'               :  45,
       'Andorra'                :  95,
       'Armenien'               :  83,
       'Aserbaidschan'          : 104,
       'Belgien'                :  12,
       'Bosnien-Herzegowina'    :  66,
       'Bulgarien'              :  42,
       'D\xE4nemark'            :   8,
       'Deutschland'            :   6,
       'England'                :   1,
       'Estland'                :  57,
       'Far\xF6er'              :  68,
       'Finnland'               :  40,
       'Frankreich'             :  32,
       'Georgien'               :  49,
       'Griechenland'           :  30,
       'Irland'                 :   5,
       'Island'                 :  29,
       'Israel'                 :  23,
       'Italien'                :  10,
       'Kasachstan'             : 105,
       'Kroatien'               :  24,
       'Lettland'               :  97,
       'Liechtenstein'          :  92,
       'Litauen'                :  72,
       'Luxemburg'              :  93,
       'Malta'                  :  69,
       'Mazedonien'             :  86,
       'Moldawien'              :  87,
       'Niederlande'            :  11,
       'Nordirland'             :   4,
       'Norwegen'               :   9,
       '\xD6sterreich'          :  14,
       'Polen'                  :  25,
       'Portugal'               :  17,
       'Rum\xE4nien'            :  28,
       'Russland'               :  19,
       'San Marino'             :  98,
       'Schottland'             :   2,
       'Schweden'               :  27,
       'Schweiz'                :  37,
       'Serbien und Montenegro' :  41,
       'Slowakei'               :  70,
       'Slowenien'              :  21,
       'Spanien'                :  13,
       'Tschechien'             :  18,
       'T\xFCrkei'              :  39,
       'Ukraine'                :  20,
       'Ungarn'                 :  26,
       'Wales'                  :   3,
       'Weissrussland'          :  71,
       'Zypern'                 :  38
   };

// ==================== Abschnitt fuer Daten des Spielplans ====================

// Gibt die ID fuer den Namen eines Wettbewerbs zurueck // gameType: Name des Wettbewerbs eines Spiels // return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig function getGameTypeID(gameType) {

   return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);

}

// Gibt die ID des Landes mit dem uebergebenen Namen zurueck. // 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 "Willkommen im Managerbüro von TEAM
LIGA LAND<a href=..."

       'Zeile'  : 0,
       'Spalte' : 1,
       'start'  : " von ",
       'middle' : "
", 'liga'  : ". Liga", 'land'  : ' ', 'end'  : "<a href=" };

const __TEAMSEARCHTEAM = { // Parameter zum Team "TEAM - LIGA <a href=...>LAND</a>"

       'Zeile'  : 0,
       'Spalte' : 0,
       'start'  : "",
       'middle' : " - ",
       'liga'   : ". Liga",
       'land'   : 'target="_blank">',
       'end'    : "</a>"
   };

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

}

// ==================== Ende Abschnitt fuer sonstige Parameter ====================

// ==================== 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 = {
       'Age'   : 0,
       'Geb'   : 1,
       '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...
       const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
       separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Age, __ROWOFFSETUPPER, __ROWOFFSETLOWER, -1, 0);
   }

}

// Verarbeitet Ansicht "Spielereinzelwerte" function procSpielereinzelwerte() {

   const __ROWOFFSETUPPER = 1;     // Header-Zeile
   const __ROWOFFSETLOWER = 0;
   const __COLUMNINDEX = {
       'Flg'   : 0,
       'Land'  : 1,
       'U'     : 2,
       'Age'   : 3,
       'Einz'  : 4,    // ab hier die Einzelskills
       'SCH'   : 4,
       'ABS'   : 4,    // TOR
       'BAK'   : 5,
       'STS'   : 5,    // TOR
       'KOB'   : 6,
       'FAN'   : 6,    // TOR
       'ZWK'   : 7,
       'STB'   : 7,    // TOR
       'DEC'   : 8,
       'SPL'   : 8,    // TOR
       'GES'   : 9,
       'REF'   : 9,    // TOR
       'FUQ'   : 10,
       'ERF'   : 11,
       'AGG'   : 12,
       'PAS'   : 13,
       'AUS'   : 14,
       'UEB'   : 15,
       'WID'   : 16,
       'SEL'   : 17,
       'DIS'   : 18,
       'ZUV'   : 19,
       'EIN'   : 20,
       'Zus'   : 21     // Zusaetze hinter den Einzelskills
   };
   if (getRows(1) === undefined) {
       console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
   } else {
       buildOptions(__OPTCONFIG, __OPTSET, {
                        'menuAnchor' : getTable(0, "div"),
                        'hideForm' : {
                                       'zatAges'       : true,
                                       'trainiert'     : true,
                                       'positions'     : true
                                     },
                        'formWidth'  : 1
                    });
       const __COLMAN = new ColumnManager(__OPTSET);
       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, false);
       for (let i = 0; i < __PLAYERS.length; i++) {
           __COLMAN.addValues(__PLAYERS[i], __ROWS[i + __ROWOFFSETUPPER], __TITLECOLOR);
       }
       // Format der Trennlinie zwischen den Monaten...
       const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
       separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Age, __ROWOFFSETUPPER, __ROWOFFSETLOWER, -1, 0);
   }

}

// URL-Legende: // page=0: Managerbuero // page=1: Teamuebersicht // page=2: Spielereinzelwerte

// Verzweige in unterschiedliche Verarbeitungen je nach Wert von page: switch (getPageIdFromURL(window.location.href)) {

   case 0: procHaupt(); break;
   case 1: procTeamuebersicht(); break;
   case 2: procSpielereinzelwerte(); break;
   default: break;

}

console.log("SCRIPT END");

// *** EOF ***