Bearbeiten von „OS2.jugend.user.js

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

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

Aktuelle Version Dein Text
Zeile 1: Zeile 1:
// ==UserScript==
// ==UserScript==
// @name         OS2.jugend
// @name       OS2.jugendV4
// @namespace   http://os.ongapo.com/
// @namespace   http://os.ongapo.com/
// @version     0.48
// @version     0.45
// @copyright   2013+
// @copyright   2013+
// @author       Andreas Eckes (Strindheim BK)
// @author     Andreas Eckes (Strindheim BK)
// @author       Sven Loges (SLC)
// @author     Sven Loges (SLC)
// @description Jugendteam-Script fuer Online Soccer 2.0
// @description Jugendteam-Script fuer Online Soccer 2.0
// @include     http*://os.ongapo.com/haupt.php
// @include     http*://os.ongapo.com/haupt.php
// @include     http*://os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://os.ongapo.com/ju.php
// @include     http*://os.ongapo.com/ju.php
// @include     http*://os.ongapo.com/ju.php?page=*
// @include     http*://os.ongapo.com/ju.php?page=*
// @include     http*://www.os.ongapo.com/haupt.php
// @include     http*://www.os.ongapo.com/haupt.php
// @include     http*://www.os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://www.os.ongapo.com/haupt.php?changetosecond=*
// @include     http*://www.os.ongapo.com/ju.php
// @include     http*://www.os.ongapo.com/ju.php
// @include     http*://www.os.ongapo.com/ju.php?page=*
// @include     http*://www.os.ongapo.com/ju.php?page=*
// @include     http*://online-soccer.eu/haupt.php
// @include     http*://online-soccer.eu/haupt.php
// @include     http*://online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://online-soccer.eu/ju.php
// @include     http*://online-soccer.eu/ju.php
// @include     http*://online-soccer.eu/ju.php?page=*
// @include     http*://online-soccer.eu/ju.php?page=*
// @include     http*://www.online-soccer.eu/haupt.php
// @include     http*://www.online-soccer.eu/haupt.php
// @include     http*://www.online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://www.online-soccer.eu/haupt.php?changetosecond=*
// @include     http*://www.online-soccer.eu/ju.php
// @include     http*://www.online-soccer.eu/ju.php
// @include     http*://www.online-soccer.eu/ju.php?page=*
// @include     http*://www.online-soccer.eu/ju.php?page=*
// @grant       GM_getValue
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_deleteValue
// @grant       GM_registerMenuCommand
// @grant       GM_registerMenuCommand
// @grant        GM_info
// ==/UserScript==
// ==/UserScript==


Zeile 72: Zeile 71:
               }
               }
};
};
let myOptMem = __OPTMEM.normal;


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


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


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


Zeile 485: Zeile 424:
}
}


// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// valueA: Ein Multipliksnt. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// 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
// value: Beliebiger (strukturierter) Wert
// return String-Darstellung des Wertes
// return String-Darstellung des Wertes
Zeile 562: Zeile 485:
function setNextStored(arr, name, value, reload = true, serial = false) {
function setNextStored(arr, name, value, reload = true, serial = false) {
     return setStored(name, getNextValue(arr, value), reload, serial);
     return setStored(name, getNextValue(arr, value), reload, serial);
}
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
function canUseStorage(storage = undefined) {
    const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
    const __MEMORY = __STORAGE.Value;
    let ret = false;
    if (__MEMORY !== undefined) {
        const __TESTITEM = 'canUseStorageTest';
        const __TEST = Math.random().toString();
        __MEMORY.setItem(__TESTITEM, __TEST);
        ret = (__MEMORY.getItem(__TESTITEM) === __TEST);
        __MEMORY.removeItem(__TESTITEM);
    }
    console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);
    return ret;
}
}


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


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


// Namen des Default-, Dauer- und Null-Memories...
// Zeigt den Eintrag im Menu einer Option
const __MEMNORMAL  = 'normal';
// opt: Derzeitiger Wert der Option
const __MEMINFINITE = 'unbegrenzt';
// menuOn: Text zum Setzen im Menu
const __MEMINAKTIVE = 'inaktiv';
// funOn: Funktion zum Setzen
 
// keyOn: Hotkey zum Setzen im Menu
// Definition des Default-, Dauer- und Null-Memories...
// menuOff: Text zum Ausschalten im Menu
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
// funOff: Funktion zum Ausschalten
const __OPTMEMINFINITE = __OPTMEM[__MEMINFINITE];
// keyOff: Hotkey zum Ausschalten im Menu
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];
function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
 
    const __ON  = (opt ? '*' : "");
// Medium fuer die Datenbank (Speicher)
    const __OFF = (opt ? "" : '*');
let myOptMem = __OPTMEMNORMAL;
 
 
    console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
// 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)
    if (opt) {
const __DBTOC = { };
        GM_registerMenuCommand(menuOff, funOff, keyOff);
    } else {
        GM_registerMenuCommand(menuOn, funOn, keyOn);
    }
}


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


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


// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
     GM_registerMenuCommand(__MENU, fun, key);
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return memory, falls okay, sonst einen Defaultwert
function getMemory(memory = undefined) {
     return getValue(memory, getValue(myOptMem, __OPTMEMNORMAL));
}
}


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


     if (__MEMORY !== undefined) {
     console.log(__OPTIONS);
        const __TESTPREFIX = 'canUseStorageTest';
        const __TESTDATA = Math.random().toString();
        const __TESTITEM = __TESTPREFIX + __TESTDATA;


        __MEMORY.setItem(__TESTITEM, __TESTDATA);
    if (! hidden) {
         ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
         GM_registerMenuCommand(__MENU, fun, key);
        __MEMORY.removeItem(__TESTITEM);
     }
     }
    console.log("canUseStorage(" + __STORAGE.Name + ") = " + ret);
    return ret;
}
}


// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// Zeigt den Eintrag im Menu einer Option
// opt: Option zur Wahl des Speichers
// opt: Config und Value der Option
// return Gesuchter Speicher oder Null-Speicher ('inaktiv')
function registerOption(opt) {
function restoreMemoryByOpt(opt) {
     const __CONFIG = getOptConfig(opt);
    // Memory Storage fuer vorherige Speicherung...
     const __STORAGE = loadOption(getOpt(opt), true);


     return __OPTMEM[getValue(__STORAGE, __MEMNORMAL)];
     if (! __CONFIG.HiddenMenu) {
}
        switch (__CONFIG.Type) {
 
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
// opt: Option zur Wahl des Speichers
                            break;
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
        case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
function startMemoryByOpt(opt, saveOpt = undefined) {
                            break;
    // Memory Storage fuer naechste Speicherung...
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    let storage = getOptValue(opt, __MEMNORMAL);
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
    let optMem = __OPTMEM[storage];
                            break;
 
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
    if (! canUseMemory(optMem)) {
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
         if (storage !== __MEMINAKTIVE) {
                            break;
            storage = __MEMINAKTIVE;
         case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
            optMem = __OPTMEM[storage];
                                                                  __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);
     }
     }
    if (saveOpt !== undefined) {
        setOpt(saveOpt, storage, false);
    }
    return optMem;
}
}


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


// ==================== Abschnitt fuer die Scriptdatenbank ====================
// Initialisiert die gesetzten Option
// config: Konfiguration der Option
// return Initialwert der gesetzten Option
function initOptValue(config) {
    let value = config.Default;  // Standard


// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
    switch (config.Type) {
// optSet: Gesetzte Optionen (und Config)
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
function initScriptDB(optSet) {
                            value = config.Choice[0];
     const __INFO = GM_info;
                        }
     const __META = __INFO.script;
                        break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
     case __OPTTYPES.SD : config.Serial = true;
                        break;
     case __OPTTYPES.SI : break;
    default :            break;
    }


     //console.log(__INFO);
     if (config.Serial || config.Hidden) {
        config.HiddenMenu = true;
    }


     __DBTOC.versions = getValue(JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
     return value;
 
    // 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
// Initialisiert die Menue-Funktion einer Option
// optSet: Gesetzte Optionen (und Config)
// optAction: Typ der Funktion
function updateScriptDB(optSet) {
// item: Key der Option
     // Eintrag ins Inhaltsverzeichnis...
// optSet: Platz fuer die gesetzten Optionen (und Config)
    __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
// return Funktion fuer die Option
function initOptAction(optAction, item = undefined, optSet = undefined) {
     var fun;


     // Permanente Speicherung der Eintraege...
     if (optAction !== undefined) {
    __DBMEM.setItem('__DBTOC.versions', JSON.stringify(__DBTOC.versions));
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
    __DBMEM.setItem('__DBDATA.' + __DBMOD.name, JSON.stringify(optSet));
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);
 
    // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
    __DBDATA[__DBMOD.name] = getValue(optSet, { });


    console.log(__DBDATA);
        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;
        }
    }


// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
    return fun;
}


// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
// 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;


// ==================== Abschnitt fuer das Benutzermenu ====================
    if (optSet === undefined) {
        optSet = { };
    }


// Zeigt den Eintrag im Menu einer Option
    for (let opt in optConfig) {
// opt: Derzeitiger Wert der Option
        const __CONFIG = optConfig[opt];
// menuOn: Text zum Setzen im Menu
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
// 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);
        optSet[opt] = {
            'Config'    : __CONFIG,
            'Value'     : initOptValue(__CONFIG),
            'SetValue'  : undefined,
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
        };
    }


     if (opt) {
     return optSet;
        GM_registerMenuCommand(menuOff, funOff, keyOff);
    } else {
        GM_registerMenuCommand(menuOn, funOn, keyOn);
    }
}
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
// opt: Derzeitiger Wert der Option
// optConfig: Konfiguration der Optionen
// arr: Array-Liste mit den moeglichen Optionen
// optSet: Platz fuer die gesetzten Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// return Gefuelltes Objekt mit den gesetzten Optionen
// fun: Funktion zum Setzen des naechsten Wertes
function startOptions(optConfig, optSet = undefined) {
// key: Hotkey zum Setzen des naechsten Wertes im Menu
    const __NORMAL = 'normal';
function registerNextMenuOption(opt, arr, menu, fun, key) {
 
     const __MENU = menu.replace('$', opt);
    optSet = initOptions(optConfig, optSet);
     let options = "OPTION " + __MENU;
 
    // Memory Storage fuer vorherige Speicherung...
    const __OLDOPT = optSet.oldStorage;
    const __OLDSTORAGE = loadOption(getOpt(__OLDOPT), true);
    myOptMem = __OPTMEM[getValue(__OLDSTORAGE, __NORMAL)];
 
    runStored(optSet);
    loadOptions(optSet);
 
    // Memory Storage fuer naechste Speicherung...
    const __STORAGE = getOptValue(optSet.storage, __NORMAL);
     const __NEWSTORAGE = setOpt(__OLDOPT, __STORAGE, false);
     if (! canUseStorage(myOptMem = __OPTMEM[getValue(__NEWSTORAGE, __NORMAL)])) {
        const __INAKTIV = 'inaktiv';


    for (let value of arr) {
         if (__NEWSTORAGE !== __INAKTIV) {
         if (value === opt) {
             setOpt(__OLDOPT, __INAKTIV, false);
             options += " / *" + value + '*';
             myOptMem = __OPTMEM[__INAKTIV];
        } else {
             options += " / " + value;
         }
         }
     }
     }
    console.log(options);


     GM_registerMenuCommand(__MENU, fun, key);
     return optSet;
}
}


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// Installiert die Visualisierung und Steuerung der Optionen
// opt: Derzeitiger Wert der Option
// optSet: Platz fuer die gesetzten Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// optParams: Eventuell notwendige Parameter zur Initialisierung
// fun: Funktion zum Setzen des naechsten Wertes
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar)
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// serial: Serialization fuer komplexe Daten
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
function registerDataOption(opt, menu, fun, key, hidden = false, serial = true) {
// 'formWidth': Anzahl der Elemente pro Zeile
     const __VALUE = ((serial && (opt !== undefined)) ? JSON.stringify(opt) : opt);
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
    const __MENU = getValue(menu, "").replace('$', __VALUE);
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
     if (! optParams.hideMenu) {
                      getValue(__VALUE, "", " = " + __VALUE);
        buildMenu(optSet);
     }


     console.log(__OPTIONS);
     if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEM.inaktiv)) {
 
         buildForm(optParams.menuAnchor, optSet, optParams);
    if (! hidden) {
         GM_registerMenuCommand(__MENU, fun, key);
     }
     }
}
}


// Zeigt den Eintrag im Menu einer Option
// Setzt eine Option auf einen vorgegebenen Wert
// opt: Config und Value der Option
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
function registerOption(opt) {
// opt: Config und vorheriger Value der Option
    const __CONFIG = getOptConfig(opt);
// value: (Bei allen Typen) Zu setzender Wert
 
// reload: Seite mit neuem Wert neu laden
    if (! __CONFIG.HiddenMenu) {
// return Gesetzter Wert
        switch (__CONFIG.Type) {
function setOpt(opt, value, reload = false) {
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
    return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
                                                                  __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 ====================
// Ermittelt die naechste moegliche Option
 
// opt: Config und Value der Option
// Initialisiert die gesetzten Option
// value: Ggfs. zu setzender Wert
// config: Konfiguration der Option
// return Zu setzender Wert
// return Initialwert der gesetzten Option
function getNextOpt(opt, value = undefined) {
function initOptValue(config) {
     const __CONFIG = getOptConfig(opt);
     let value = config.Default; // Standard
    const __VALUE = getOptValue(opt, value);


     switch (config.Type) {
     switch (__CONFIG.Type) {
     case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
     case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
                            value = config.Choice[0];
     case __OPTTYPES.SW : return getValue(value, ! __VALUE);
                        }
     case __OPTTYPES.TF : return getValue(value, ! __VALUE);
                        break;
     case __OPTTYPES.SD : return getValue(value, __VALUE);
     case __OPTTYPES.SW : break;
     case __OPTTYPES.TF : break;
     case __OPTTYPES.SD : config.Serial = true;
                        break;
     case __OPTTYPES.SI : break;
     case __OPTTYPES.SI : break;
     default :            break;
     default :            break;
     }
     }


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


// Initialisiert die Menue-Funktion einer Option
// Setzt die naechste moegliche Option
// optAction: Typ der Funktion
// 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
// item: Key der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// value: (Bei allen Typen) Zu setzender Wert
// return Funktion fuer die Option
// reload: Seite mit neuem Wert neu laden
function initOptAction(optAction, item = undefined, optSet = undefined) {
// return Gesetzter Wert
     var fun;
function setOptByName(optSet, item, value, reload = false) {
     const __OPT = getOptByName(optSet, item);


     if (optAction !== undefined) {
     return setOpt(__OPT, value, reload);
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
}
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);


        switch (optAction) {
// Ermittelt die naechste moegliche Option (Version mit Key)
        case __OPTACTION.SET : fun = function() {
// opt: Config und Value der Option
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
// optSet: Platz fuer die gesetzten Optionen (und Config)
                                  };
// item: Key der Option
                              break;
// value: Ggfs. zu setzender Wert
        case __OPTACTION.NXT : fun = function() {
// return Zu setzender Wert
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
function getNextOptByName(optSet, item, value = undefined) {
                                  };
    const __OPT = getOptByName(optSet, item);
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :              break;
        }
    }


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


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


     if (optSet === undefined) {
     return setNextOpt(__OPT, value, reload);
        optSet = { };
}
    }


    for (let opt in optConfig) {
// Baut das Benutzermenu auf
        const __CONFIG = optConfig[opt];
// optSet: Gesetzte Optionen
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
function buildMenu(optSet) {
    console.log("buildMenu()");


        optSet[opt] = {
     for (let opt in optSet) {
            'Config'    : __CONFIG,
        registerOption(optSet[opt]);
            '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
// Laedt eine (ueber Menu) gesetzte Option
// optConfig: Konfiguration der Optionen
// opt: Zu ladende Option
// optSet: Platz fuer die gesetzten Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Gefuelltes Objekt mit den gesetzten Optionen
// return Gesetzter Wert der gelandenen Option
function startOptions(optConfig, optSet = undefined) {
function loadOption(opt, force = false) {
     optSet = initOptions(optConfig, optSet);
     const __CONFIG = getOptConfig(opt);


     // Memory Storage fuer vorherige Speicherung...
     if (! force && __CONFIG.AutoReset) {
     myOptMem = restoreMemoryByOpt(optSet.oldStorage);
        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)));
    }
}


    runStored(optSet);
// Laedt die (ueber Menu) gesetzten Optionen
    loadOptions(optSet);
// optSet: Set mit den Optionen
 
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
    // Memory Storage fuer naechste Speicherung...
// return Set mit den geladenen Optionen
    myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage);
function loadOptions(optSet, force = false) {
 
    for (let opt in optSet) {
    initScriptDB(optSet);
        loadOption(optSet[opt], force);
    }


     return optSet;
     return optSet;
}
}


// Installiert die Visualisierung und Steuerung der Optionen
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// optSet: Platz fuer die gesetzten Optionen
// opt: Gesetzte Option
// optParams: Eventuell notwendige Parameter zur Initialisierung
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// reset: Setzt bei Erfolg auf Initialwert der Option
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
function deleteOption(opt, force = false, reset = true) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
    const __CONFIG = getOptConfig(opt);
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
 
// 'formWidth': Anzahl der Elemente pro Zeile
    if (force || ! __CONFIG.Permanent) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
        GM_deleteValue(getOptName(opt));
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
    updateScriptDB(optSet);


    if (! optParams.hideMenu) {
        if (reset) {
        buildMenu(optSet);
            setOptValue(opt, initOptValue(__CONFIG));
        }
     }
     }
}


    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
         buildForm(optParams.menuAnchor, optSet, optParams);
// optSet: Gesetzte Optionen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option
function deleteOptions(optSet, force = false, reset = true) {
    for (let opt in optSet) {
         deleteOption(optSet[opt], force, reset);
     }
     }
}
}


// Setzt eine Option auf einen vorgegebenen Wert
// Entfernt eine (ueber Menu) gesetzte Option
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// opt: Gesetzte Option
// opt: Config und vorheriger Value der Option
// name: Neu zu setzender Name (Speicheradresse)
// value: (Bei allen Typen) Zu setzender Wert
// reload: Wert nachladen statt beizubehalten
// reload: Seite mit neuem Wert neu laden
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Gesetzter Wert
// return Umbenannte Option
function setOpt(opt, value, reload = false) {
function renameOption(opt, name, reload = false, force = false) {
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
     const __NAME = getOptName(opt);
}
 
    if (__NAME !== name) {
        deleteOption(opt, true, ! reload);


// Ermittelt die naechste moegliche Option
        setOptName(opt, name);
// 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) {
        if (reload) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
            loadOption(opt, force);
    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;
     return opt;
}
}


// Setzt die naechste moegliche Option
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// opt: Config und Value der Option
// optSet: Gesetzte Optionen
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit "Werkseinstellungen" neu laden
// reload: Seite mit neuem Wert neu laden
function resetOptions(optSet, reload = true) {
// return Gesetzter Wert
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
function setNextOpt(opt, value = undefined, reload = true) {
    deleteOptions(optSet, false, ! reload);
     const __CONFIG = getOptConfig(opt);


     return setOpt(opt, getNextOpt(opt, value), reload);
     if (reload) {
        // ... und Seite neu laden (mit "Werkseinstellungen")...
        window.location.reload();
    }
}
}


// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// 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);
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
}
const __OPTSET = { };


// Ermittelt die naechste moegliche Option (Version mit Key)
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// opt: Config und Value der Option
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
// optSet: Platz fuer die gesetzten Optionen (und Config)
 
// item: Key der Option
// Behandelt die Optionen und laedt das Benutzermenu
// value: Ggfs. zu setzender Wert
// optConfig: Konfiguration der Optionen
// return Zu setzender Wert
// optSet: Platz fuer die gesetzten Optionen
function getNextOptByName(optSet, item, value = undefined) {
// optParams: Eventuell notwendige Parameter zur Initialisierung
     const __OPT = getOptByName(optSet, item);
// '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


     return getNextOpt(__OPT, value);
     optSet = startOptions(optConfig, optSet);
}


// Setzt die naechste moegliche Option (Version mit Key)
    if (__TEAMPARAMS !== undefined) {
// opt: Config und Value der Option
        __MYTEAM.Team = __TEAMPARAMS.Team;
// optSet: Platz fuer die gesetzten Optionen (und Config)
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
// item: Key der Option
        __MYTEAM.Land = __TEAMPARAMS.Land;
// value: Ggfs. zu setzender Wert
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
// reload: Seite mit neuem Wert neu laden
        __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
// return Gesetzter Wert
        console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
function setNextOptByName(optSet, item, value = undefined, reload = true) {
        // ... und abspeichern...
    const __OPT = getOptByName(optSet, item);
        setOpt(optSet.team, __MYTEAM, false);
    } else {
        const __TEAM = getOptValue(optSet.team); // Gespeicherte Parameter


    return setNextOpt(__OPT, value, reload);
        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));
        }
    }


// Baut das Benutzermenu auf
    if (__MYTEAM.LdNr !== undefined) {
// optSet: Gesetzte Optionen
        // Prefix fuer die Optionen "birthdays", "tclasses" und "progresses"...
function buildMenu(optSet) {
        const __PREFIX = __MYTEAM.LdNr.toString() + __MYTEAM.LgNr.toString();
    console.log("buildMenu()");
 
    for (let opt in optSet) {
        registerOption(optSet[opt]);
    }
}


// Laedt eine (ueber Menu) gesetzte Option
        renameOption(optSet.birthdays, __PREFIX + getOptName(optSet.birthdays), true);
// opt: Zu ladende Option
        renameOption(optSet.tclasses, __PREFIX + getOptName(optSet.tclasses), true);
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
        renameOption(optSet.progresses, __PREFIX + getOptName(optSet.progresses), true);
// return Gesetzter Wert der gelandenen Option
function loadOption(opt, force = false) {
    const __CONFIG = getOptConfig(opt);


    if (! force && __CONFIG.AutoReset) {
        // ... und nachladen...
         return setOptValue(opt, initOptValue(__CONFIG));
         loadOption(optSet.birthdays, true);
    } else if (__CONFIG.Serial) {
        loadOption(optSet.tclasses, true);
        return setOptValue(opt, deserialize(getOptName(opt), getOptValue(opt)));
         loadOption(optSet.progresses, true);
    } else {
         return setOptValue(opt, GM_getValue(getOptName(opt), getOptValue(opt)));
     }
     }
}


// Laedt die (ueber Menu) gesetzten Optionen
     showOptions(optSet, optParams);
// optSet: Set mit den Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Set mit den geladenen Optionen
function loadOptions(optSet, force = false) {
     for (let opt in optSet) {
        loadOption(optSet[opt], force);
    }


     return optSet;
     return optSet;
}
}


// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// ==================== Abschnitt fuer diverse Utilities ====================
// 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) {
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
        const __NAME = getOptName(opt);
// form: <form>...</form>
 
// props: Map von name:value-Paaren
        console.log("DELETE " + __NAME);
// type: Typ der Input-Felder (Default: unsichtbare Daten)
 
// return Ergaenztes Form-Konstrukt
         GM_deleteValue(__NAME);
function addInputField(form, props, type = "hidden") {
 
    for (let fieldName in props) {
         if (reset) {
         let field = form[fieldName];
             setOptValue(opt, initOptValue(__CONFIG));
         if (! field) {
             field = document.createElement("input");
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
         }
         }
        field.value = props[fieldName];
     }
     }
    return form;
}
}


// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// optSet: Gesetzte Optionen
// form: <form>...</form>
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// props: Map von name:value-Paaren
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// return Ergaenztes Form-Konstrukt
// reset: Setzt bei Erfolg auf Initialwert der Option
function addHiddenField(form, props) {
function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
    return addInputField(form, props, "hidden");
     const __DELETEALL = (optSelect === undefined) || (optSelect === true);
}
     const __OPTSELECT = getValue(optSelect, { });
 
// Helferfunktion 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);


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


// Benennt eine Option um und laedt sie ggfs. nach
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// opt: Gesetzte Option
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// name: Neu zu setzender Name (Speicheradresse)
// type: Name des Events, z.B. "click"
// reload: Wert nachladen statt beizubehalten
// callback: Funktion als Reaktion
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return Umbenannte Option
// return false bei Misserfolg
function renameOption(opt, name, reload = false, force = false) {
function removeEvent(obj, type, callback, capture = false) {
     const __NAME = getOptName(opt);
    if (obj.removeEventListener) {
        return obj.removeEventListener(type, callback, capture);
    } else if (obj.detachEvent) {
        return obj.detachEvent("on" + type, callback);
     } else {
        console.log("Could not remove " + type + " event:");
        console.log(callback);


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


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


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


// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// optSet: Gesetzte Optionen
// id: ID des betroffenen Eingabeelements
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// type: Name des Events, z.B. "click"
// 'reload': Option nachladen?
// callback: Funktion als Reaktion
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// renameParam: Wird an renameFun uebergeen
// return false bei Misserfolg
// renameFun: function(name, param) zur Ermittlung des neuen Namens
function removeDocEvent(id, type, callback, capture = false) {
// name: Neu zu setzender Name (Speicheradresse)
     const __OBJ = document.getElementById(id);
// 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) {
    return removeEvent(__OBJ, type, callback, capture);
            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);
// Helferfunktion 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 (__RELOAD) {
    if (showList !== undefined) {
                // ... und nachladen...
        show = (showList[item] === true);  // gesetzt und true
                loadOption(__OPT, __FORCE);
    }
             }
    if (hideList !== undefined) {
        if (hideList[item] === true) {  // gesetzt und true
             show = false;  // NICHT anzeigen
         }
         }
     }
     }
    return show;
}
}


// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// Helferfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// optSet: Gesetzte Optionen
// index: Laufende Nummer des Elements (0-based)
// reload: Seite mit "Werkseinstellungen" neu laden
// tag: Tag des Elements ("table")
function resetOptions(optSet, reload = true) {
// doc: Dokument (document)
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
function getTable(index, tag = "table", doc = document) {
     deleteOptions(optSet, true, false, ! reload);
     const __TAGS = document.getElementsByTagName(tag);
     const __TABLE = __TAGS[index];


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


// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
// index: Laufende Nummer des Elements (0-based)
// doc: Dokument (document)
function getRows(index, doc = document) {
    const __TABLE = getTable(index, "table", doc);
    const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;


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


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


// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
// Liefert den Funktionsaufruf zur Option als String
const __DATAOPTS = {
// opt: Auszufuehrende Option
                      'datenZat'  : true,
// isAlt: Angabe, ob AltAction statt Action gemeint ist
                      'birthdays'  : true,
// value: Ggfs. zu setzender Wert
                      'tClasses'  : true,
// serial: Serialization fuer String-Werte (Select, Textarea)
                      'progresses' : true,
// storage: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
                      'zatAges'    : true,
// return String mit dem (reinen) Funktionsaufruf
                      'trainiert'  : true,
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, storage = undefined) {
                      'positions'  : true
    const __STORAGE = getValue(storage, getValue(myOptMem, __OPTMEM.normal));
                  };
    const __MEMORY = __STORAGE.Value;
 
    const __MEMSTR = __STORAGE.Display;
// Behandelt die Optionen und laedt das Benutzermenu
     const __RUNPREFIX = __STORAGE.Prefix;
// 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 (__MEMORY !== undefined) {
 
        const __RELOAD = "window.location.reload()";
    if (__TEAMPARAMS !== undefined) {
        const __SETITEM = function(item, val, quotes = true) {
        __MYTEAM.Team = __TEAMPARAMS.Team;
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
        __MYTEAM.Liga = __TEAMPARAMS.Liga;
                          };
         __MYTEAM.Land = __TEAMPARAMS.Land;
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
         __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
         __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
         console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
                          };
         // ... und abspeichern...
         const __CONFIG = getOptConfig(opt);
        setOpt(optSet.team, __MYTEAM, false);
         const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
    } else {
         const __THISVAL = ((__CONFIG.ValType === "String") ? "'\\x22' + this.value + '\\x22'" : "this.value");
         const __TEAM = getOptValue(optSet.team); // Gespeicherte Parameter
         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 ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
         if (__ACTION !== undefined) {
             __MYTEAM.Team = __TEAM.Team;
            switch (__ACTION) {
            __MYTEAM.Liga = __TEAM.Liga;
             case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
             __MYTEAM.Land = __TEAM.Land;
                                  return __SETITEMS('SET', getOptName(opt), __VALSTR);
            __MYTEAM.LdNr = __TEAM.LdNr;
             case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
            __MYTEAM.LgNr = __TEAM.LgNr;
                                  return __SETITEMS('NXT', getOptName(opt), __VALSTR);
            console.log("Gespeichert: " + JSON.stringify(__MYTEAM));
             case __OPTACTION.RST : //return "doActionRst()";
        } else {
                                  return __SETITEMS('RST');
             console.error("Unbekannt: " + JSON.stringify(__TEAM));
            default :              break;
            }
         }
         }
     }
     }


     if (__MYTEAM.LdNr !== undefined) {
     return 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...
// Liefert die Funktionsaufruf zur Option als String
        renameOptions(optSet, __DATAOPTS, __PREFIX, prefixName);
// 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)
// storage: __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, storage = undefined) {
     const __ACTION = getFormAction(opt, isAlt, value, serial, storage);


     showOptions(optSet, optParams);
     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
 
    return optSet;
}
}


// ==================== Abschnitt fuer diverse Utilities ====================
// 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 + '>';


// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
     for (let value of __CONFIG.Choice) {
// form: <form>...</form>
         element += '\n<option value="' + value + '"' +
// props: Map von name:value-Paaren
                  ((value === __VALUE) ? ' SELECTED' : "") +
// type: Typ der Input-Felder (Default: unsichtbare Daten)
                  '>' + value + '</option>';
// 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];
     }
     }
    element += '\n</select>';


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


// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// Zeigt eine Option auf der Seite als Radiobutton an
// form: <form>...</form>
// opt: Anzuzeigende Option
// props: Map von name:value-Paaren
// return String mit dem HTML-Code
// return Ergaenztes Form-Konstrukt
function getOptionRadio(opt) {
function addHiddenField(form, props) {
    const __CONFIG = getOptConfig(opt);
     return addInputField(form, props, "hidden");
    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>';


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
     return [ __ELEMENTON, __ELEMENTOFF ];
// 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
// Zeigt eine Option auf der Seite als Checkbox an
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// opt: Anzuzeigende Option
// type: Name des Events, z.B. "click"
// return String mit dem HTML-Code
// callback: Funktion als Reaktion
function getOptionCheckbox(opt) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
     const __CONFIG = getOptConfig(opt);
// return false bei Misserfolg
    const __NAME = getOptName(opt);
function removeEvent(obj, type, callback, capture = false) {
     const __VALUE = getOptValue(opt, false);
     if (obj.removeEventListener) {
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
        return obj.removeEventListener(type, callback, capture);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     } else if (obj.detachEvent) {
        return obj.detachEvent("on" + type, callback);
     } else {
        console.log("Could not remove " + type + " event:");
        console.log(callback);


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


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// id: ID des betroffenen Eingabeelements
// opt: Anzuzeigende Option
// type: Name des Events, z.B. "click"
// return String mit dem HTML-Code
// callback: Funktion als Reaktion
function getOptionTextarea(opt) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
    const __CONFIG = getOptConfig(opt);
// return false bei Misserfolg
    const __NAME = getOptName(opt);
function addDocEvent(id, type, callback, capture = false) {
    const __VALUE = getOptValue(opt);
     const __OBJ = document.getElementById(id);
    const __ACTION = getFormActionEvent(opt, false, undefined, "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 addEvent(__OBJ, type, callback, capture);
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
}
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Zeigt eine Option auf der Seite als Button an
// id: ID des betroffenen Eingabeelements
// opt: Anzuzeigende Option
// type: Name des Events, z.B. "click"
// return String mit dem HTML-Code
// callback: Funktion als Reaktion
function getOptionButton(opt) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
    const __CONFIG = getOptConfig(opt);
// return false bei Misserfolg
    const __NAME = getOptName(opt);
function removeDocEvent(id, type, callback, capture = false) {
    const __VALUE = getOptValue(opt, false);
     const __OBJ = document.getElementById(id);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
     const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     return removeEvent(__OBJ, type, callback, capture);
     return '<label for="' + __NAME + '">' + __FORMLABEL +
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';
}
}


// Hilfsfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// Zeigt eine Option auf der Seite an (je nach Typ)
// item: Name des betroffenen Items
// opt: Anzuzeigende Option
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// return String mit dem HTML-Code
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
function getOptionElement(opt) {
// return Angabe, ob das Item sichtbar sein soll
    const __CONFIG = getOptConfig(opt);
function checkVisible(item, showList, hideList = undefined) {
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
     let show = true;
     let element = "";


     if (showList !== undefined) {
     if (! __CONFIG.Hidden) {
        show = (showList[item] === true); // gesetzt und true
        switch (__TYPE) {
    }
        case __OPTTYPES.MC : element = getOptionSelect(opt);
    if (hideList !== undefined) {
                            break;
         if (hideList[item] === true) { // gesetzt und true
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
             show = false;  // NICHT anzeigen
                                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 show;
     return element;
}
}


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// Baut das Benutzermenu auf der Seite auf
// name: Name des Elements (siehe "name=")
// optSet: Gesetzte Optionen
// index: Laufende Nummer des Elements (0-based), Default: 0
// optParams: Eventuell notwendige Parameter
// doc: Dokument (document)
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
function getElement(name, index = 0, doc = document) {
// 'formWidth': Anzahl der Elemente pro Zeile
     const __TAGS = document.getElementsByName(name);
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
// return String mit dem HTML-Code
 
function getForm(optSet, optParams = { }) {
    return __TABLE;
     const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
}
     const __FORMEND = '</tr></tbody></table></form>';
 
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
// index: Laufende Nummer des Elements (0-based)
     const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
// tag: Tag des Elements ("table")
     let form = __FORM;
// doc: Dokument (document)
    let count = 0;  // Bisher angezeigte Optionen
// return Gesuchtes Element oder undefined (falls nicht gefunden)
    let column = 0;  // Spalte der letzten Option (1-basierend)
function getTable(index, tag = "table", doc = document) {
     const __TAGS = document.getElementsByTagName(tag);
     const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];


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


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
            if (__ELEMENT.length > 0) {
// index: Laufende Nummer des Elements (0-based)
                if (++count > __FORMBREAK) {
// doc: Dokument (document)
                    if (++column > __FORMWIDTH) {
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
                        column = 1;
function getRows(index, doc = document) {
                    }
    const __TABLE = getTable(index, "table", doc);
                }
     const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
                if (column === 1) {
 
                    form += '</tr><tr>';
     return __ROWS;
                }
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
            }
        }
     }
    form += '\n' + __FORMEND;
 
     return form;
}
}


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


// Liefert den Funktionsaufruf zur Option als String
    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
// 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) {
     return __SCRIPT;
        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
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// opt: Auszufuehrende Option
// anchor: Element, das als Anker fuer die Anzeige dient
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// optSet: Gesetzte Optionen
// value: Ggfs. zu setzender Wert
// optParams: Eventuell notwendige Parameter
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// serial: Serialization fuer String-Werte (Select, Textarea)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// 'formWidth': Anzahl der Elemente pro Zeile
// return String mit dem (reinen) Funktionsaufruf
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {
function buildForm(anchor, optSet, optParams = { }) {
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
    console.log("buildForm()");
 
    const __FORM = getForm(optSet, optParams);
     const __SCRIPT = getScript(optSet, optParams);


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


// Zeigt eine Option auf der Seite als Auswahlbox an
// Informationen zu hinzugefuegten Forms
// opt: Anzuzeigende Option
const __FORMS = { };
// return String mit dem HTML-Code
 
function getOptionSelect(opt) {
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
     const __CONFIG = getOptConfig(opt);
// anchor: Element, das als Anker fuer die Anzeige dient
     const __NAME = getOptName(opt);
// form: HTML-Form des Optionsmenu (hinten angefuegt)
    const __VALUE = getOptValue(opt);
// script: Script mit Reaktionen
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
function addForm(anchor, form = "", script = "") {
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __OLDFORM = __FORMS[anchor];
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);


     for (let value of __CONFIG.Choice) {
     __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';


     return __LABEL.replace('$', element);
     anchor.innerHTML = __REST + script + form;
}
}


// Zeigt eine Option auf der Seite als Radiobutton an
// ==================== Ende Abschnitt fuer Optionen ====================
// opt: Anzuzeigende Option
 
// return String mit dem HTML-Code
// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
function getOptionRadio(opt) {
 
    const __CONFIG = getOptConfig(opt);
// Zeitpunktangaben
    const __NAME = getOptName(opt);
const __TIME = {
    const __VALUE = getOptValue(opt, false);
    'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
    const __ACTION = getFormActionEvent(opt, false, true, "click", false);
    'beg' : 1,  // Jugendspieler darf trainieren (wird 13 Jahre alt)
    const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
    'now' : 2,  // Aktueller ZAT
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
    'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
                        '" id="' + __NAME + 'ON" value="1"' +
};
                        (__VALUE ? ' CHECKED' : __ACTION) +
 
                        ' /><label for="' + __NAME + 'ON">' +
// Funktionen ***************************************************************************
                        __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 ];
// Erschafft die Spieler-Objekte und fuellt sie mit Werten
}
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
    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 __PLAYERS = [];


// Zeigt eine Option auf der Seite als Checkbox an
    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
// opt: Anzuzeigende Option
        const __CELLS = playerRows[i].cells;
// return String mit dem HTML-Code
        const __AGE = getAgeFromHTML(__CELLS, colIdx.Age);
function getOptionCheckbox(opt) {
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
    const __CONFIG = getOptConfig(opt);
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
    const __NAME = getOptName(opt);
        const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);
    const __VALUE = getOptValue(opt, false);
 
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
        __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
        __PLAYERS[j] = __NEWPLAYER;
    }


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


// Zeigt eine Option auf der Seite als Daten-Textfeld an
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
// opt: Anzuzeigende Option
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {
// return String mit dem HTML-Code
     if (offsetLeft < 0) {
function getOptionTextarea(opt) {
        offsetLeft = colIdxSort; // ab Sortierspalte
     const __CONFIG = getOptConfig(opt);
     }
    const __NAME = getOptName(opt);
 
     const __VALUE = getOptValue(opt);
     for (let i = offsetUpper; i < rows.length - offsetLower - 1; i++) {
     const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
        if (rows[i].cells[colIdxSort].textContent !== rows[i + 1].cells[colIdxSort].textContent) {
    const __SUBMIT = getValue(__CONFIG.Submit, "");
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
    const __ONSUBMIT = ((__SUBMIT.length > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
                rows[i].cells[j].style.borderBottom = borderString;
    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 ];
// Klasse ColumnManager *****************************************************************
}


// Zeigt eine Option auf der Seite als Button an
function ColumnManager(optSet) {
// opt: Anzuzeigende Option
    this.geb = getOptValue(optSet.zeigeGeb);
// return String mit dem HTML-Code
    this.tal = getOptValue(optSet.zeigeTal);
function getOptionButton(opt) {
    this.quo = getOptValue(optSet.zeigeQuote);
     const __CONFIG = getOptConfig(opt);
    this.aufw = getOptValue(optSet.zeigeAufw);
     const __NAME = getOptName(opt);
    this.alter = getOptValue(optSet.zeigeAlter);
     const __VALUE = getOptValue(opt, false);
    this.skill = getOptValue(optSet.zeigeSkill);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
    this.pos = getOptValue(optSet.zeigePosition);
     const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    this.opti = ((getOptValue(optSet.anzahlOpti) >= 1) && (getOptValue(optSet.anzahlOpti) <= 6));
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
    this.mw = ((getOptValue(optSet.anzahlMW) >= 1) && (getOptValue(optSet.anzahlMW) <= 6));
     this.anzOpti = getOptValue(optSet.anzahlOpti);
     this.anzMw = getOptValue(optSet.anzahlMW);
     this.skillE = getOptValue(optSet.zeigeSkillEnde);
     this.optiE = ((getOptValue(optSet.anzahlOptiEnde) >= 1) && (getOptValue(optSet.anzahlOptiEnde) <= 6));
     this.mwE = ((getOptValue(optSet.anzahlMWEnde) >= 1) && (getOptValue(optSet.anzahlMWEnde) <= 6));
    this.anzOptiE = getOptValue(optSet.anzahlOptiEnde);
     this.anzMwE = getOptValue(optSet.anzahlMWEnde);
    this.kennzE = getOptValue(optSet.kennzeichenEnde);


     return '<label for="' + __NAME + '">' + __FORMLABEL +
     this.toString = function() {
          '</label><input type="button" name="' + __NAME +
        let result = "Skillschnitt\t\t" + this.skill + '\n';
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
        result += "Beste Position\t" + this.pos + '\n';
          __ACTION + '/>';
        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 eine Option auf der Seite an (je nach Typ)
    this.addCell = function(tableRow) {
// opt: Anzuzeigende Option
        tableRow.insertCell(-1);
// return String mit dem HTML-Code
        return tableRow.cells.length - 1;
function getOptionElement(opt) {
     };
    const __CONFIG = getOptConfig(opt);
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
     let element = "";


     if (! __CONFIG.Hidden) {
     this.addAndFillCell = function(tableRow, value, color, digits = 2) {
         switch (__TYPE) {
         if (isFinite(value) && (value !== true) && (value !== false)) {
        case __OPTTYPES.MC : element = getOptionSelect(opt);
            // Zahl einfuegen
                            break;
            if (value < 1000) {
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                // Mit Nachkommastellen darstellen
                                element = getOptionCheckbox(opt);
                tableRow.cells[this.addCell(tableRow)].textContent = parseFloat(value).toFixed(digits);
                            } else {
            } else {
                                element = getOptionRadio(opt);
                // Mit Tausenderpunkten darstellen
                            }
                tableRow.cells[this.addCell(tableRow)].textContent = getNumberString(value.toString());
                            break;
            }
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
         } else {
                            break;
            // String einfuegen
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
            tableRow.cells[this.addCell(tableRow)].textContent = value;
                            break;
         case __OPTTYPES.SI : element = getOptionButton(opt);
                            break;
        default :            break;
         }
         }
        tableRow.cells[tableRow.cells.length - 1].style.color = color;
    };
    this.addTitles = function(headers, titleColor = "#FFFFFF") {
        // Spaltentitel zentrieren
        headers.align = "center";


         if (element.length === 2) {
        // Titel fuer die aktuellen Werte
             element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
         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);
        }
        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);
            }
         }
         }
    }
    return element;
}


// Baut das Benutzermenu auf der Seite auf
        // Titel fuer die Werte mit Ende 18
// optSet: Gesetzte Optionen
        if (this.skillE) {
// optParams: Eventuell notwendige Parameter
            this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
        }
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
        if (this.optiE) {
// 'formWidth': Anzahl der Elemente pro Zeile
            for (let i = 1; i <= this.anzOptiE; i++) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
                this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
// return String mit dem HTML-Code
                if (this.mwE && (this.anzMwE >= i)) {
function getForm(optSet, optParams = { }) {
                     this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    let form = __FORM;
    let count = 0;   // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)
 
    for (let opt in optSet) {
        if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
            const __ELEMENT = getOptionElement(optSet[opt]);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";
 
            if (__ELEMENT.length > 0) {
                if (++count > __FORMBREAK) {
                     if (++column > __FORMWIDTH) {
                        column = 1;
                    }
                 }
                 }
                 if (column === 1) {
            }
                     form += '</tr><tr>';
            if (this.mwE) {
                 for (let i = this.anzOptiE + 1; i <= this.anzMwE; i++) {
                     this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
                 }
                 }
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
            }
        } else if (this.mwE) {
            for (let i = 1; i <= this.anzMwE; i++) {
                this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
             }
             }
         }
         }
     }
     }; // Ende addTitles()
    form += '\n' + __FORMEND;


     return form;
     this.addValues = function(player, playerRow, color = "#FFFFFF") {
}
        const __COLOR = (player.isGoalie ? getColor("TOR") : color);
        const __POS1COLOR = (player.isGoalie ? getColor("TOR") : getColor(player.getPos(1)));


// Fuegt das Script in die Seite ein
        // Aktuelle Werte
// optSet: Gesetzte Optionen
        if (this.tal) {
// optParams: Eventuell notwendige Parameter
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
        }
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
        if (this.quo) {
// return String mit dem HTML-Code fuer das Script
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
function getScript(optSet, optParams = { }) {
        }
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
        if (this.aufw) {
    //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>';
            this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
    //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
        }
    const __SCRIPT = "";
        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(1), __POS1COLOR);
        }
        if (this.opti) {
            for (let i = 1; i <= this.anzOpti; i++) {
                if (player.isGoalie) {
                    if (i === 1) {
                        // TOR-Opti anzeigen
                        this.addAndFillCell(playerRow, player.getOpti("TOR"), getColor("TOR"), 2);
                    } else {
                        // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
                        this.addCell(playerRow);
                    }
                } else {
                    // 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);
                }
            }
        }


    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
        // Werte mit Ende 18
 
        if (this.skillE) {
     return __SCRIPT;
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
}
        }
        if (this.optiE) {
            for (let i = 1; i <= this.anzOptiE; i++) {
                if (player.isGoalie) {
                    if (i === 1) {
                        // TOR-Opti anzeigen
                        this.addAndFillCell(playerRow, player.getOpti("TOR", __TIME.end), getColor("TOR"), 2);
                    } else {
                        // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
                        this.addCell(playerRow);
                    }
                } else {
                    // Feld-Opti anzeigen
                    this.addAndFillCell(playerRow, player.getOpti(player.getPos(i), __TIME.end), getColor(player.getPos(i)), 2);
                }
                if (this.mwE && (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);
                    }
                }
            }
            // 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)
}


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// Klasse PlayerRecord ******************************************************************
// 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);
function PlayerRecord(age, skills, isGoalie) {
     const __SCRIPT = getScript(optSet, optParams);
    // Zu benutzende Marktwertformel
 
     const __MWFORMEL = {
     addForm(anchor, __FORM, __SCRIPT);
        'alt' : 0, // Marktwertformel bis Saison 9 inklusive
}
        'S10' : 1  // Marktwertformel MW5 ab Saison 10
     };


// Informationen zu hinzugefuegten Forms
    this.mwFormel = __MWFORMEL.S10;  // Neue Formel, genauer in initPlayer()
const __FORMS = { };


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
    this.age = age;
// anchor: Element, das als Anker fuer die Anzeige dient
     this.skills = skills;
// form: HTML-Form des Optionsmenu (hinten angefuegt)
     this.isGoalie = isGoalie;
// 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 };
     // 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


     anchor.innerHTML = __REST + script + form;
     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";


// ==================== Ende Abschnitt fuer Optionen ====================
        for (let pos of this.positions) {
            result += "\n\t" + pos + '\t';
            result += this.getOpti(pos).toFixed(2) + '\t';
            result += getNumberString(this.getMarketValue(pos).toString());
        }


// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
        result += "\n\nWerte mit Ende 18\n";
        result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
        result += "Optis und Marktwerte";


// Zeitpunktangaben
        for (let pos of this.positions) {
const __TIME = {
            result += "\n\t" + this.getPos()[i] + '\t';
    'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
    'beg' : 1, // Jugendspieler darf trainieren (wird 13 Jahre alt)
            result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
    'now' : 2,  // Aktueller ZAT
        }
    'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
};


// Funktionen ***************************************************************************
        return result;
    };  // Ende this.toString()


// Erschafft die Spieler-Objekte und fuellt sie mit Werten (reloadData: true = Teamuebersicht, false = Spielereinzelwerte)
    // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, reloadData = false) {
    this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
    const __SAISON = getOptValue(optSet.saison);
        this.zatGeb = gebZAT;
    const __CURRZAT = getOptValue(optSet.aktuellerZat);
        this.zatAge = this.calcZatAge(currZAT);
    const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
        this.talent = tclass;
    const __TCLASSES = getOptValue(optSet.tClasses, []);
        this.aufwert = progresses;
    const __PROGRESSES = getOptValue(optSet.progresses, []);
        this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;
    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++) {
        this.positions = [];
         const __CELLS = playerRows[i].cells;
        // ABW
         const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
        this.positions[0] = [];
         const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
        this.positions[0][0] = "ABW";
         const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
        this.positions[0][1] = this.getOpti("ABW");
         const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);
         // DMI
 
        this.positions[1] = [];
         __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
        this.positions[1][0] = "DMI";
 
         this.positions[1][1] = this.getOpti("DMI");
         if (reloadData) {
         // MIT
            __NEWPLAYER.setZusatz(__ZATAGES[j], __TRAINIERT[j], __POSITIONS[j]);
        this.positions[2] = [];
         }
         this.positions[2][0] = "MIT";
 
         this.positions[2][1] = this.getOpti("MIT");
         __PLAYERS[j] = __NEWPLAYER;
        // 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");


    if (reloadData) {
         // Sortieren
         storePlayerData(__PLAYERS, playerRows, optSet, colIdx, offsetUpper, offsetLower);
         sortPositionArray(this.positions);
    } else {
         calcPlayerData(__PLAYERS, optSet);
    }


    return __PLAYERS;
        // Einzelskills mit Ende 18 berechnen
}
        this.skillsEnd = [];


// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
        const __ADDRATIO = (this.getZatAge(__TIME.end) - this.getZatAge()) / this.getZatAge();
function calcPlayerData(players, optSet) {
    const __ZATAGES = [];
    const __TRAINIERT = [];
    const __POSITIONS = [];


    for (let i = 0; i < players.length; i++) {
        for (let i in this.skills) {
        const __ZUSATZ = players[i].calcZusatz();
            const __SKILL = this.skills[i];
            let progSkill = __SKILL;


        __ZATAGES[i]  = __ZUSATZ.zatAge;
            if (isTrainableSkill(i)) {
        __TRAINIERT[i] = __ZUSATZ.trainiert;
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
        __POSITIONS[i] = __ZUSATZ.bestPos;
                const __ADDSKILL = parseInt((__ADDRATIO * __SKILL).toFixed(0), 10);
    }


    setOpt(optSet.zatAges, __ZATAGES, false);
                progSkill += __ADDSKILL;
    setOpt(optSet.trainiert, __TRAINIERT, false);
            }
    setOpt(optSet.positions, __POSITIONS, false);
}


// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
            this.skillsEnd[i] = Math.min(progSkill, 99);
function storePlayerData(players, playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
        }
    const __BIRTHDAYS = [];
     }; // Ende this.initPlayer()
    const __TCLASSES = [];
     const __PROGRESSES = [];


     for (let i = offsetUpper; i < playerRows.length - offsetLower; i++) {
     this.getGeb = function() {
         const __CELLS = playerRows[i].cells;
         return (this.zatGeb < 0) ? '?' : this.zatGeb;
    };


        __BIRTHDAYS[i - offsetUpper] = getIntFromHTML(__CELLS, colIdx.Geb);
    this.calcZatAge = function(currZAT) {
         __TCLASSES[i - offsetUpper] = getTalentFromHTML(__CELLS, colIdx.Tal);
         if (this.age < 13) {
         __PROGRESSES[i - offsetUpper] = getStringFromHTML(__CELLS, colIdx.Auf);
            return 0; // noch nicht trainiert
    }
         } else {
            let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72; // Basiszeit fuer die Jahre seit Jahrgang 13


    setOpt(optSet.birthdays, __BIRTHDAYS, false);
            if (this.zatGeb < 0) {
    setOpt(optSet.tClasses, __TCLASSES, false);
                return ZATs + currZAT; // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
    setOpt(optSet.progresses, __PROGRESSES, false);
            } else {
}
                return ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
 
// 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 *****************************************************************
    this.getZatAge = function(when = __TIME.now) {
 
        if (when === __TIME.end) {
function ColumnManager(optSet, showCol = undefined) {
            return (18 - 12) * 72 - 1;  // (max.) Trainings-ZATs bis Ende 18
    const __SHOWALL = (showCol === undefined) || (showCol === true);
        } else {
     const __SHOWCOL = getValue(showCol, { });
            return this.zatAge;
        }
    };
 
    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.geb = getValue(__SHOWCOL.zeigeGeb, __SHOWALL) && getOptValue(optSet.zeigeGeb);
     this.getTrainierteSkills = function() {
    this.tal = getValue(__SHOWCOL.zeigeTal, __SHOWALL) && getOptValue(optSet.zeigeTal);
        let sum = 0;
    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() {
        for (let i in this.skills) {
        let result = "Skillschnitt\t\t" + this.skill + '\n';
            if (isTrainableSkill(i)) {
        result += "Beste Position\t" + this.pos + '\n';
                sum += this.skills[i];
        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 sum;
     };
     };


     this.addCell = function(tableRow) {
     this.getAufwertungsSchnitt = function() {
         tableRow.insertCell(-1);
         return parseFloat(this.getTrainierteSkills() / this.getZatAge());
        return tableRow.cells.length - 1;
     };
     };


     this.addAndFillCell = function(tableRow, value, color, digits = 2) {
     this.getPos = function(idx) {
         if (isFinite(value) && (value !== true) && (value !== false)) {
         const __IDXOFFSET = 1;
            // Zahl einfuegen
        return this.positions[idx - __IDXOFFSET][0];
            if (value < 1000) {
    };
                // Mit Nachkommastellen darstellen
 
                tableRow.cells[this.addCell(tableRow)].textContent = parseFloat(value).toFixed(digits);
    this.getTalent = function() {
            } else {
        return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
                // Mit Tausenderpunkten darstellen
    };
                tableRow.cells[this.addCell(tableRow)].textContent = getNumberString(value.toString());
 
            }
    this.getAufwert = function() {
        } else {
         return (this.aufwert.length > 0) ? this.aufwert : "keine";
            // String einfuegen
            tableRow.cells[this.addCell(tableRow)].textContent = value;
         }
        tableRow.cells[tableRow.cells.length - 1].style.color = color;
     };
     };


     this.addTitles = function(headers, titleColor = "#FFFFFF") {
     this.getSkill = function(when = __TIME.now) {
         // Spaltentitel zentrieren
         const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
         headers.align = "center";
         let result = 0;


         // Titel fuer die aktuellen Werte
         for (let skill of __SKILLS) {
        if (this.tal) {
             result += skill;
             this.addAndFillCell(headers, "Talent", titleColor);
         }
         }
         if (this.quo) {
 
            this.addAndFillCell(headers, "Quote", titleColor);
         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];
         }
         }
         if (this.aufw) {
         for (let idx of __IDXSECSKILLS) {
             this.addAndFillCell(headers, "Aufwertung", titleColor);
             sumSecSkills += __SKILLS[idx];
         }
         }
         if (this.geb) {
 
            this.addAndFillCell(headers, "Geb.", titleColor);
         return (5 * sumPriSkills + sumSecSkills) / 27;
         }
    };
        if (this.alter) {
 
            this.addAndFillCell(headers, "Alter", titleColor);
    this.getMarketValue = function(pos, when = __TIME.now) {
        }
         const __AGE = this.getAge(when);
         if (this.skill) {
 
             this.addAndFillCell(headers, "Skill", titleColor);
         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
        if (this.pos) {
         } else {  // MW-Formel ab Saison 10...
            this.addAndFillCell(headers, "Pos", titleColor);
            const __MW5TF = 1.00; // Zwischen 0.97 und 1.03
         }
 
        for (let i = 1; i <= 6; i++) {
             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);
             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
// Funktionen fuer die HTML-Seite *******************************************************
        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") {
// Liest das Alter aus
        const __COLOR = (player.isGoalie ? getColor("TOR") : color);
// return Alter als Zahl
        const __POS1COLOR = getColor(player.getPos());
function getAgeFromHTML(cells, colIdxAge) {
    return parseInt(cells[colIdxAge].textContent, 10);
}


        // Aktuelle Werte
// Liest das Geburtsdatum aus
        if (this.tal) {
// return Geburtsdatum als ZAT
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
function getGebFromHTML(cells, colIdxGeb) {
        }
    const __TEXT = ((cells[colIdxGeb] === undefined) ? '?' : cells[colIdxGeb].textContent);
        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) {
    return parseInt((__TEXT === '?') ? -1 : __TEXT, 10);
                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
// Liest die Talentklasse ("wenig", "normal", "hoch") aus
        if (this.skillE) {
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
function getTalentFromHTML(cells, colIdxTal) {
        }
    const __TEXT = ((cells[colIdxTal] === undefined) ? '?' : cells[colIdxTal].textContent);
        for (let i = 1; i <= 6; i++) {
            const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
            const __COLI = getColor(__POSI);


            if (i <= this.anzOptiE) {
    return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
                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 ******************************************************************
// Liest die Aufwertungen aus
// return Aufwertungen als String
function getAufwertFromHTML(cells, colIdxAuf) {
    const __TEXT = ((cells[colIdxAuf] === undefined) ? '?' : cells[colIdxAuf].textContent);


function PlayerRecord(age, skills, isGoalie) {
    return __TEXT.toString();
    // 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()
// Liest die Einzelskills aus
// return Skills als Array von Zahlen
function getSkillsFromHTML(cells, colIdx) {
    const __RESULT = [];


     this.age = age;
     for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
    this.skills = skills;
        __RESULT[i - colIdx.Einz] = parseInt(cells[i].textContent, 10);
     this.isGoalie = isGoalie;
     }


     // in this.initPlayer() definiert:
     return __RESULT;
    // 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:
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
    // this.trainiert: Anzahl der erfolgreichen Trainingspunkte
// return Angabe, der Spieler Torwart oder Feldspieler ist
     // this.bestPos: erster (bester) Positionstext
function isGoalieFromHTML(cells, colIdxClass) {
     return (cells[colIdxClass].className === "TOR");
}


    this.toString = function() {
// Hilfsfunktionen **********************************************************************
        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) {
// Sortiert das Positionsfeld per BubbleSort
            result += "\n\t" + pos + '\t';
function sortPositionArray(array) {
            result += this.getOpti(pos).toFixed(2) + '\t';
    const __TEMP = [];
            result += getNumberString(this.getMarketValue(pos).toString());
    let transposed = true;
        }
    // TOR soll immer die letzte Position im Feld sein, deshalb - 1
    let length = array.length - 1;


        result += "\n\nWerte mit Ende 18\n";
    while (transposed && (length > 1)) {
        result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
         transposed = false;
         result += "Optis und Marktwerte";
         for (let i = 0; i < length - 1; i++) {
 
             // Vergleich Opti-Werte:
         for (let pos of this.positions) {
            if (array[i][1] < array[i + 1][1]) {
             result += "\n\t" + this.getPos()[i] + '\t';
                // vertauschen
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
                __TEMP[0] = array[i][0];
            result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
                __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--;
    }
}


        return result;
// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
     };  // Ende this.toString()
// 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);


     // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
        return getNumberString(__VORKOMMA) + "," + __NACHKOMMA;
    this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
     } else {
         this.zatGeb = gebZAT;
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
         this.zatAge = this.calcZatAge(currZAT);
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
        this.talent = tclass;
        const __TEMP = reverseString(numberString);
         this.aufwert = progresses;
         let result = "";
         this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;
 
         for (let i = 0; i < __TEMP.length; i++) {
            if ((i > 0) && (i % 3 === 0)) {
                result += ".";
            }
            result += __TEMP.substr(i, 1);
         }
 
         return reverseString(result);
    }
}


        this.positions = [];
// Dreht den uebergebenen String um
        // ABW
function reverseString(string) {
        this.positions[0] = [];
    let result = "";
        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
    for (let i = string.length - 1; i >= 0; i--) {
         sortPositionArray(this.positions);
         result += string.substr(i, 1);
    }


        // Einzelskills mit Ende 18 berechnen
    return result;
        this.skillsEnd = [];
}


        const __ADDRATIO = (this.getZatDone(__TIME.end) - this.getZatDone()) / this.getZatDone();
// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert
 
// Die Indizes gehen von 0 (SCH) bis 16 (EIN)
        for (let i in this.skills) {
function isTrainableSkill(idx) {
            const __SKILL = this.skills[i];
    const __TRAINABLESKILLS = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15];
            let progSkill = __SKILL;
    const __IDX = parseInt(idx, 10);
    let result = false;


            if (isTrainableSkill(i)) {
    for (let idxTrainable of __TRAINABLESKILLS) {
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
        if (__IDX === idxTrainable) {
                const __ADDSKILL = getMulValue(__ADDRATIO, __SKILL, 0, NaN);
            result = true;
 
             break;
                progSkill += __ADDSKILL;
             }
 
            this.skillsEnd[i] = Math.min(progSkill, 99);
         }
         }
     };  // Ende this.initPlayer()
     }


     // Setzt Nebenwerte fuer den Spieler (geht ohne initPlayer())
     return result;
    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())
// Gibt die Indizes der Primaerskills zurueck
     this.calcZusatz = function() {
function getIdxPriSkills(pos) {
         // this.zatAge bereits in initPlayer() berechnet
     switch (pos) {
         this.trainiert = this.getTrainiert(true); // neu berechnet aus Skills
         case "TOR" : return new Array(2, 3, 4, 5);
         this.bestPos = this.getPos(-1); // hier: -1 explizit angeben, da neu ermittelt (this.bestPos noch nicht belegt)
         case "ABW" : return new Array(2, 3, 4, 15);
 
         case "DMI" : return new Array(1, 4, 9, 11);
         return {
        case "MIT" : return new Array(1, 3, 9, 11);
                  zatAge    : this.zatAge,
         case "OMI" : return new Array(1, 5, 9, 11);
                  trainiert : this.trainiert,
         case "STU" : return new Array(0, 2, 3, 5);
                  bestPos  : this.bestPos
        default : return [];
              };
     }
    };
}
 
    this.getGeb = function() {
         return (this.zatGeb < 0) ? '?' : this.zatGeb;
     };


     this.calcZatAge = function(currZAT) {
// Gibt die Indizes der Sekundaerskills zurueck
         let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72; // Basiszeit fuer die Jahre seit Jahrgang 13
function getIdxSecSkills(pos) {
 
     switch (pos) {
         if (this.zatGeb < 0) {
         case "TOR" : return new Array(0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
            return ZATs + currZAT;  // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
         case "ABW" : return new Array(0, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16);
         } else {
        case "DMI" : return new Array(0, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
            return ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
         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 [];
    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) {
// Gibt die zur Position gehoerige Farbe zurueck
        if (this.mwFormel === __MWFORMEL.alt) {
function getColor(pos) {
            return (when === __TIME.end) ? 18 : this.age;
    switch (pos) {
         } else {  // Geburtstage ab Saison 10...
        case "TOR" : return "#FFFF00";
            return (13.00 + this.getZatAge(when) / 72);
        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 "";
    }
}


    this.getTrainiert = function(recalc = false) {
// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
        let sum = 0;


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


        return sum;
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
    };
        'Zeile'  : 0,
 
        'Spalte' : 1,
    this.getAufwertungsSchnitt = function() {
        'start'  : " von ",
         return parseFloat(this.getTrainiert() / this.getZatDone());
         'middle' : "</b><br>",
        'liga'  : ". Liga",
        'land'  : ' ',
        'end'    : "<a href="
     };
     };


    this.getPos = function(idx = undefined) {
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
         const __IDXOFFSET = 1;
         'Zeile'  : 0,
 
        'Spalte' : 0,
         switch (getValue(idx, 0)) {
         'start'  : "<b>",
         case -1 : return (this.bestPos = this.positions[isGoalie ? 5 : 0][0]);
         'middle' : " - ",
         case  0 : return this.bestPos;
         'liga'  : ". Liga",
         default : return this.positions[idx - __IDXOFFSET][0];
         'land'  : 'target="_blank">',
         }
         'end'    : "</a></b>"
     };
     };


    this.getTalent = function() {
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
        return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
// 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 },
     this.getAufwert = function() {
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
        return (this.aufwert.length > 0) ? this.aufwert : "keine";
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);


     this.getSkill = function(when = __TIME.now) {
     let land = (__INDEXLIGA > 0) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
    const __TEAM = (__INDEXMIDDLE > 0) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
        let result = 0;
    let liga = ((__INDEXLIGA > 0) && (__INDEXMIDDLE > 0)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;


         for (let skill of __SKILLS) {
    if (land !== undefined) {
             result += skill;
         if (land.charAt(2) === ' ') {   // Land z.B. hinter "2. Liga A " statt "1. Liga "
             land = land.substr(2);
         }
         }
 
         if (liga !== undefined) {
         return result / __SKILLS.length;
            liga = liga.substring(0, liga.length - land.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) {
         const __INDEXLAND = land.indexOf(__SEARCHLAND);
             sumSecSkills += __SKILLS[idx];
        if (__INDEXLAND > -1) {
             land = land.substr(__INDEXLAND + __SEARCHLAND.length);
         }
         }
    }


         return (5 * sumPriSkills + sumSecSkills) / 27;
    const __RET = {
         'Team' : __TEAM,
        'Liga' : liga,
        'Land' : land,
        'LdNr' : getLandNr(land),
        'LgNr' : getLigaNr(liga)
     };
     };


     this.getMarketValue = function(pos, when = __TIME.now) {
     return __RET;
        const __AGE = this.getAge(when);
}


        if (this.mwFormel === __MWFORMEL.alt) {
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
            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
// url: Adresse der Seite
        } else {  // MW-Formel ab Saison 10...
// return Parameter aus der URL der Seite als Nummer
            const __MW5TF = 1.00; // Zwischen 0.97 und 1.03
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)


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


// Funktionen fuer die HTML-Seite *******************************************************
// 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;


// Liest eine Zahl aus der Spalte einer Zeile der Tabelle aus (z.B. Alter, Geburtsdatum)
    for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
// cells: Die Zellen einer Zeile
        if (__TEXT[i - 1] === "ZAT") {
// colIdxInt: Spaltenindex der gesuchten Werte
            if (__TEXT[i] !== "ist") {
// return Spalteneintrag als Zahl (-1 fuer "keine Zahl", undefined fuer "nicht gefunden")
                ZATNr = parseInt(__TEXT[i], 10);
function getIntFromHTML(cells, colIdxInt) {
    const __CELL = cells[colIdxInt];
    const __TEXT = getValue(__CELL, { }).textContent;
 
    if (__TEXT !== undefined) {
        try {
            const __VALUE = parseInt(__TEXT, 10);
 
            if (! isNaN(__VALUE)) {
                return __VALUE;
             }
             }
         } catch (ex) { }
         }
 
        return -1;
     }
     }


     return undefined;
     return ZATNr;
}
}


// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
// 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) {
// ==================== Hauptprogramm ====================
        try {
            return parseFloat(__TEXT);
        } catch (ex) { }
    }


     return undefined;
// Verarbeitet Ansicht "Haupt" (Managerbuero) zur Ermittlung des aktuellen ZATs
}
function procHaupt() {
     const __TEAMPARAMS = getTeamParamsFromCell(getTable(1), __TEAMSEARCHHAUPT); // Link mit Team, Liga, Land...


// Liest einen String aus der Spalte einer Zeile der Tabelle aus
    buildOptions(__OPTCONFIG, __OPTSET, {
// cells: Die Zellen einer Zeile
                    'teamParams' : __TEAMPARAMS,
// colIdxStr: Spaltenindex der gesuchten Werte
                    'hideMenu'  : true
// return Spalteneintrag als String ("" fuer "nicht gefunden")
                });
function getStringFromHTML(cells, colIdxStr) {
    const __CELL = cells[colIdxStr];
    const __TEXT = getValue(__CELL, { }).textContent;


     return getValue(__TEXT.toString(), "");
     const __NEXTZAT = getZATNrFromCell(getRows(0)[2].cells[0]);  // "Der naechste ZAT ist ZAT xx und ..."
}
    const __CURRZAT = __NEXTZAT - 1;


// Liest die Talentklasse ("wenig", "normal", "hoch") aus der Spalte einer Zeile der Tabelle aus
    if (__CURRZAT >= 0) {
// cells: Die Zellen einer Zeile
        console.log("Aktueller ZAT: " + __CURRZAT);
// 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);
        setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
    }
}
}


// Liest die Einzelskills aus der Spalte einer Zeile der Tabelle aus
// Verarbeitet Ansicht "Teamuebersicht"
// cells: Die Zellen einer Zeile
function procTeamuebersicht() {
// colIdx: Liste von Spaltenindices der gesuchten Werte mit den Eintraegen
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
// 'Einz' (erste Spalte) und 'Zus' (Spalte hinter dem letzten Eintrag)
     const __ROWOFFSETLOWER = 1;     // Ziehen-Button
// return Skills als Array von Zahlen
function getSkillsFromHTML(cells, colIdx) {
     const __RESULT = [];


     for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
     const __COLUMNINDEX = {
         __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
         'Age'  : 0,
     }
        'Geb'  : 1,
        'Flg'  : 2,
        'Land'  : 3,
        'U'    : 4,
        'Skill' : 5,
        'Tal'  : 6,
        'Akt'  : 7,
        'Auf'  : 8,
        'Zus'  : 9
     };


     return __RESULT;
     const __BIRTHDAYS = [];
}
    const __TCLASSES = [];
    const __PROGRESSES = [];


// Liest aus, ob der Spieler Torwart oder Feldspieler ist
    if (getRows(1) === undefined) {
// return Angabe, der Spieler Torwart oder Feldspieler ist
        console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
function isGoalieFromHTML(cells, colIdxClass) {
    } else if (getTable(1).length < 3) {
     return (cells[colIdxClass].className === "TOR");
        console.log("Ziehen-Seite");
}
     } else {
        buildOptions(__OPTCONFIG, __OPTSET, {
                        'menuAnchor' : getTable(0, "div"),
                        'showForm' : {
                                        'sepStyle'    : true,
                                        'sepColor'    : true,
                                        'sepWidth'    : true,
                                        'aktuellerZat' : true,
                                        'team'        : true,
                                        'reset'        : true,
                                        'showForm'    : true
                                      },
                        'formWidth' : 1
                    });


// Hilfsfunktionen **********************************************************************
         const __ROWS = getRows(1);
 
// 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 "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
        'Zeile'  : 0,
        'Spalte' : 1,
        'start'  : " von ",
        'middle' : "</b><br>",
        'liga'  : ". Liga",
        'land'  : ' ',
        'end'    : "<a href="
    };
 
const __TEAMSEARCHTEAM = {  // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
        'Zeile'  : 0,
        'Spalte' : 0,
        'start'  : "<b>",
        'middle' : " - ",
        'liga'  : ". Liga",
        'land'  : 'target="_blank">',
        'end'    : "</a></b>"
    };
 
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
// cell: Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
// teamSeach: Muster fuer die Suche, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
function getTeamParamsFromTable(table, teamSearch = undefined) {
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
    const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
    const __TEAMCELLSTR  = table.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML;
    const __SEARCHSTART  = __TEAMSEARCH.start;
    const __SEARCHMIDDLE = __TEAMSEARCH.middle;
    const __SEARCHLIGA  = __TEAMSEARCH.liga;
    const __SEARCHLAND  = __TEAMSEARCH.land;
    const __SEARCHEND    = __TEAMSEARCH.end;
    const __INDEXSTART  = __TEAMCELLSTR.indexOf(__SEARCHSTART);
    const __INDEXEND    = __TEAMCELLSTR.indexOf(__SEARCHEND);
 
    let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
    const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
 
    let land = (__INDEXLIGA > 0) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
    const __TEAM = (__INDEXMIDDLE > 0) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
    let liga = ((__INDEXLIGA > 0) && (__INDEXMIDDLE > 0)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;
 
    if (land !== undefined) {
        if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
            land = land.substr(2);
        }
        if (liga !== undefined) {
            liga = liga.substring(0, liga.length - land.length);
        }
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
        if (__INDEXLAND > -1) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
        }
    }
 
    const __RET = {
        'Team' : __TEAM,
        'Liga' : liga,
        'Land' : land,
        'LdNr' : getLandNr(land),
        'LgNr' : getLigaNr(liga)
    };
 
    return __RET;
}
 
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// url: Adresse der Seite
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url) {
    // Variablen zur Identifikation der Seite
    const __SUCH = "page=";
    const __INDEXS = url.lastIndexOf(__SUCH);
    const __HAUPT = url.match(/haupt\.php/);        // Ansicht "Haupt" (Managerbuero)
    const __JU = url.match(/ju\.php/);              // Ansicht "Jugendteam"
    let page = -1;                                  // Seitenindex (Rueckgabewert)
 
    // Wert von page (Seitenindex) ermitteln...
    // Annahme: Entscheidend ist jeweils das letzte Vorkommnis von "page=" und ggf. von '&'
    if (__HAUPT) {
        page = 0;
    } else if (__JU) {
        if (__INDEXS < 0) {
            page = 1;
        } else if (url.indexOf('&', __INDEXS) < 0) {
            // Wert von page setzt sich aus allen Zeichen hinter "page=" zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.length), 10);
        } else {
            // Wert von page setzt sich aus allen Zeichen zwischen "page=" und '&' zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.indexOf('&', __INDEXS)), 10);
        }
    }
 
    return page;
}
 
// Gibt die laufende Nummer des ZATs im Text einer Zelle zurueck
// cell: Tabellenzelle mit der ZAT-Nummer im Text
// return ZAT-Nummer im Text
function getZATNrFromCell(cell) {
    const __TEXT = cell.textContent.split(' ');
    let ZATNr = 0;
 
    for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
        if (__TEXT[i - 1] === "ZAT") {
            if (__TEXT[i] !== "ist") {
                ZATNr = parseInt(__TEXT[i], 10);
            }
        }
    }
 
    return ZATNr;
}
 
// ==================== 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...
        for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
             setOpt(__OPTSET.datenZat, __CURRZAT, false);
             __BIRTHDAYS[i - __ROWOFFSETUPPER] = getGebFromHTML(__ROWS[i].cells, __COLUMNINDEX.Geb);
            __TCLASSES[i - __ROWOFFSETUPPER] = getTalentFromHTML(__ROWS[i].cells, __COLUMNINDEX.Tal);
             __PROGRESSES[i - __ROWOFFSETUPPER] = getAufwertFromHTML(__ROWS[i].cells, __COLUMNINDEX.Auf);
         }
         }
    }
}
// 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++) {
         setOpt(__OPTSET.birthdays, __BIRTHDAYS, false);
            __COLMAN.addValues(__PLAYERS[i], __ROWS[i + __ROWOFFSETUPPER], __TITLECOLOR);
        setOpt(__OPTSET.tclasses, __TCLASSES, false);
         }
         setOpt(__OPTSET.progresses, __PROGRESSES, false);


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


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


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

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

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

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