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.44
// @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 52: Zeile 51:
};
};


const __OPTMEM = {
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
     'normal' : {
const __OPTCONFIG = {
                   'Name'      : "Session",
     'zeigeGeb' : {       // Spaltenauswahl fuer Geburtstage (true = anzeigen, false = nicht anzeigen)
                   'Value'     : sessionStorage,
                   'Name'      : "showBirthday",
                   'Display'  : "sessionStorage",
                   'Type'     : __OPTTYPES.SW,
                   'Prefix'    : 'run'
                   'Default: false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Geb. ein",
                   'Hotkey'    : 'G',
                  'AltLabel'  : "Geb. aus",
                  'AltHotkey' : 'G',
                  'FormLabel' : "Geburtstag"
               },
               },
     'unbegrenzt' : {
     'zeigeAlter' : {       // Spaltenauswahl fuer dezimales Alter (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "Browser",
                   'Name'      : "showAge",
                   'Value'     : localStorage,
                   'Type'     : __OPTTYPES.SW,
                   'Display'  : "localStorage",
                   'Default: true,
                   'Prefix'    : 'run'
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Alter ein",
                   'Hotkey'    : 'A',
                  'AltLabel'  : "Alter aus",
                  'AltHotkey' : 'A',
                  'FormLabel' : "Alter"
               },
               },
    'inaktiv' : {
                  'Name'      : "inaktiv",
                  'Value'    : undefined,
                  'Display'  : "",
                  'Prefix'    : ""
              }
};
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
const __OPTCONFIG = {
     'zeigeTal' : {        // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
     'zeigeTal' : {        // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showTclasses",
                   'Name'      : "showTclasses",
Zeile 108: Zeile 108:
                   '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 141: Zeile 130:
                   'FormLabel' : "Skill"
                   'FormLabel' : "Skill"
               },
               },
    'zeigePosition' : {  // Position anzeigen
     'anzahlOpti' : {      // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
                  'Name'      : "showPos",
                           // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Type'      : __OPTTYPES.SW,
                   'Name'      : "anzOpti",
                  'Default'  : false,
                   'Type'      : __OPTTYPES.MC,
                  'Action'    : __OPTACTION.NXT,
                   'ValType'  : "Number",
                  'Label'    : "Position ein",
                  'Hotkey'    : 'P',
                  'AltLabel'  : "Position aus",
                  'AltHotkey' : 'P',
                  'FormLabel' : "Position"
              },
     'anzahlOpti' : {      // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
                           // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                   'Name'      : "anzOpti",
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : "Number",
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Default'  : 1,
                   'Default'  : 1,
Zeile 282: Zeile 260:
                   'Hotkey'    : 'Z',
                   'Hotkey'    : 'Z',
                   'FormLabel' : "ZAT:|$"
                   'FormLabel' : "ZAT:|$"
              },
    'datenZat' : {        // Stand der Daten zum Team und ZAT
                  'Name'      : "dataZAT",
                  'Type'      : __OPTTYPES.SD,
                  'ValType'  : "Number",
                  'Hidden'    : true,
                  'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'  : undefined,
                  'Action'    : __OPTACTION.SET,
                  'Submit'    : undefined,
                  'Cols'      : 1,
                  'Rows'      : 1,
                  'Replace'  : null,
                  'Space'    : 0,
                  'Label'    : "Daten-ZAT:"
               },
               },
     'birthdays' : {      // Datenspeicher fuer Geburtstage der Jugendspieler
     'birthdays' : {      // Datenspeicher fuer Geburtstage der Jugendspieler
Zeile 315: Zeile 276:
                   '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 306:
                   '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 },
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 2,
                   'Rows'      : 5,
                   '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,
                  'Cols'      : 36,
                  'Rows'     : 2,
                   'Replace'   : null,
                  'Space'    : 0,
                  'Label'    : "Trainiert:"
               },
               },
     'positions' : {       // Datenspeicher fuer optimale Positionen der Jugendspieler
     'showForm' : {       // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "positions",
                   'Name'      : "showForm",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SW,
                   'Hidden'   : false,
                   'FormType' : __OPTTYPES.SI,
                  'Serial'    : true,
                  'AutoReset' : true,
                   '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,
// ==================== Invarianter Abschnitt fuer Optionen ====================
                  'ValType'  : "String",
 
                  'Choice'    : Object.keys(__OPTMEM),
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Speicher: $",
                  'Hotkey'    : 'c',
                  'FormLabel' : "Speicher:|$"
              },
    'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'Name'      : "oldStorage",
                  'Type'      : __OPTTYPES.SD,
                  'AutoReset' : true,
                  'Hidden'    : true
              },
    'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showForm",
                  'Type'      : __OPTTYPES.SW,
                  'FormType'  : __OPTTYPES.SI,
                  'Permanent' : true,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Optionen anzeigen",
                  'Hotkey'    : 'O',
                  'AltLabel'  : "Optionen verbergen",
                  'AltHotkey' : 'O',
                  'FormLabel' : ""
               }
};
 
// ==================== Invarianter Abschnitt fuer Optionen ====================
 
// ==================== Abschnitt fuer diverse Utilities ====================
// ==================== Abschnitt fuer diverse Utilities ====================


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


Zeile 483: Zeile 383:


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


Zeile 566: Zeile 450:
// 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
// session: true = bis Browserende gespeichert (sessionStorage), false = unbegrenzt gespeichert (localStorage)
function runStored(optSet, memory = undefined) {
function runStored(optSet, session = true) {
     const __STORAGE = getMemory(memory);
     const __STORAGE = (session ? sessionStorage : localStorage);
     const __MEMORY = __STORAGE.Value;
     const __CMD = ((__STORAGE !== undefined) ? __STORAGE.getItem('runcmd') : undefined);
    const __RUNPREFIX = __STORAGE.Prefix;


     if (__MEMORY !== undefined) {
     if (__CMD !== undefined) {
         const __GETITEM = function(item) {
         const __KEY = __STORAGE.getItem('runkey');
                              return __MEMORY.getItem(__RUNPREFIX + item);
         let value = __STORAGE.getItem('runval');
                          };
         const __DELITEM = function(item) {
                              return __MEMORY.removeItem(__RUNPREFIX + item);
                          };
        const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);


         if (__CMD !== undefined) {
         try {
             const __KEY = __GETITEM('key');
             value = JSON.parse(value);
             let value = __GETITEM('val');
        } catch (ex) {
             console.error("runStored(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
            // ... meist kann man den String selber aber speichern, daher kein "return"...
        }


            try {
        const __VAL = value;
                value = JSON.parse(value);
            } catch (ex) {
                console.error("runStored(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
                // ... meist kann man den String selber aber speichern, daher kein "return"...
            }


            const __VAL = value;
        switch (__OPTACTION[__CMD]) {
 
        case __OPTACTION.SET : console.log("SET '" + __KEY + "' " + __VAL);
            switch (__OPTACTION[__CMD]) {
                              setStored(__KEY, __VAL, false, false);
            case __OPTACTION.SET : console.log("SET '" + __KEY + "' " + __VAL);
                              break;
                                  setStored(__KEY, __VAL, false, false);
        case __OPTACTION.NXT : console.log("SETNEXT '" + __KEY + "' " + __VAL);
                                  break;
                              //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
            case __OPTACTION.NXT : console.log("SETNEXT '" + __KEY + "' " + __VAL);
                              setStored(__KEY, __VAL, false, false);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
                              break;
                                  setStored(__KEY, __VAL, false, false);
        case __OPTACTION.RST : console.log("RESET");
                                  break;
                              resetOptions(optSet, false);
            case __OPTACTION.RST : console.log("RESET");
                              break;
                                  resetOptions(optSet, false);
        default :              break;
                                  break;
            default :              break;
            }
         }
         }
    }


        __DELITEM('cmd');
    __STORAGE.removeItem('runcmd');
        __DELITEM('key');
    __STORAGE.removeItem('runkey');
        __DELITEM('val');
    __STORAGE.removeItem('runval');
    }
}
}


Zeile 650: Zeile 523:
// 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 551:
// ==================== Ende Abschnitt fuer diverse Utilities ====================
// ==================== Ende Abschnitt fuer diverse Utilities ====================


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


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


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


// Medium fuer die Datenbank (Speicher)
    if (opt) {
let myOptMem = __OPTMEMNORMAL;
        GM_registerMenuCommand(menuOff, funOff, keyOff);
    } else {
        GM_registerMenuCommand(menuOn, funOn, keyOn);
    }
}


// Speicher fuer die DB-Daten
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
const __DBMEM = __OPTMEMNORMAL.Value;
// 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;


// Infos ueber dieses Script-Modul
     for (let value of arr) {
const __DBMOD = { };
        if (value === opt) {
 
            options += " / *" + value + '*';
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
         } else {
const __DBTOC = { };
            options += " / " + value;
 
         }
// Daten zu den Modulen (indiziert durch die Script-Namen)
const __DBDATA = { };
 
// ==================== Abschnitt fuer Speicher ====================
 
// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return memory, falls okay, sonst einen Defaultwert
function getMemory(memory = undefined) {
     return getValue(memory, getValue(myOptMem, __OPTMEMNORMAL));
}
 
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
function canUseMemory(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    let ret = false;
 
    if (__MEMORY !== undefined) {
        const __TESTPREFIX = 'canUseStorageTest';
         const __TESTDATA = Math.random().toString();
        const __TESTITEM = __TESTPREFIX + __TESTDATA;
 
         __MEMORY.setItem(__TESTITEM, __TESTDATA);
        ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
        __MEMORY.removeItem(__TESTITEM);
     }
     }
    console.log(options);


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


// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// opt: Option zur Wahl des Speichers
// opt: Derzeitiger Wert der Option
// return Gesuchter Speicher oder Null-Speicher ('inaktiv')
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
function restoreMemoryByOpt(opt) {
// fun: Funktion zum Setzen des naechsten Wertes
     // Memory Storage fuer vorherige Speicherung...
// key: Hotkey zum Setzen des naechsten Wertes im Menu
     const __STORAGE = loadOption(getOpt(opt), true);
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (default: sichtbar)
 
// serial: Serialization fuer komplexe Daten
    return __OPTMEM[getValue(__STORAGE, __MEMNORMAL)];
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);


// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
     console.log(__OPTIONS);
// opt: Option zur Wahl des Speichers
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
function startMemoryByOpt(opt, saveOpt = undefined) {
     // Memory Storage fuer naechste Speicherung...
    let storage = getOptValue(opt, __MEMNORMAL);
    let optMem = __OPTMEM[storage];


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


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


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


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


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


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


// ==================== Abschnitt fuer das Benutzermenu ====================
    if (config.Serial || config.Hidden) {
        config.HiddenMenu = true;
    }


// Zeigt den Eintrag im Menu einer Option
     return value;
// opt: Derzeitiger Wert der Option
}
// menuOn: Text zum Setzen im Menu
// funOn: Funktion zum Setzen
// keyOn: Hotkey zum Setzen im Menu
// menuOff: Text zum Ausschalten im Menu
// funOff: Funktion zum Ausschalten
// keyOff: Hotkey zum Ausschalten im Menu
function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
     const __ON  = (opt ? '*' : "");
    const __OFF = (opt ? "" : '*');


    console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
// Initialisiert die Menue-Funktion einer Option
 
// optAction: Typ der Funktion
     if (opt) {
// item: Key der Option
         GM_registerMenuCommand(menuOff, funOff, keyOff);
// optSet: Platz fuer die gesetzten Optionen (und Config)
    } else {
// return Funktion fuer die Option
         GM_registerMenuCommand(menuOn, funOn, keyOn);
function initOptAction(optAction, item = undefined, optSet = undefined) {
    var fun;
 
     if (optAction !== undefined) {
         const __CONFIG = getOptConfig(getOptByName(optSet, item));
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);
 
        switch (optAction) {
        case __OPTACTION.SET : fun = function() {
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
        case __OPTACTION.NXT : fun = function() {
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
         case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :              break;
        }
     }
     }
    return fun;
}
}


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


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


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


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


     console.log(__OPTIONS);
     return optSet;
}


    if (! hidden) {
// Setzt eine Option auf einen vorgegebenen Wert
        GM_registerMenuCommand(__MENU, fun, key);
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
    }
// opt: Config und vorheriger Value der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setOpt(opt, value, reload = false) {
    return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
}
}


// Zeigt den Eintrag im Menu einer Option
// Ermittelt die naechste moegliche Option
// opt: Config und Value der Option
// opt: Config und Value der Option
function registerOption(opt) {
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
function getNextOpt(opt, value = undefined) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);
    switch (__CONFIG.Type) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
    }


     if (! __CONFIG.HiddenMenu) {
     return __VALUE;
        switch (__CONFIG.Type) {
}
        case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
 
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
// Setzt die naechste moegliche Option
                            break;
// opt: Config und Value der Option
        case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
// value: Default fuer ggfs. zu setzenden Wert
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
// reload: Seite mit neuem Wert neu laden
                            break;
// return Gesetzter Wert
        case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
function setNextOpt(opt, value = undefined, reload = true) {
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
    const __CONFIG = getOptConfig(opt);
                            break;
    const __VALUE = getOptValue(opt, value);
        case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
 
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
     return setOpt(opt, getNextOpt(opt, __VALUE), reload);
                            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 ====================
// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setOptByName(optSet, item, value, reload = false) {
    const __OPT = getOptByName(optSet, item);


// Initialisiert die gesetzten Option
    return setOpt(__OPT, value, reload);
// config: Konfiguration der Option
}
// return Initialwert der gesetzten Option
function initOptValue(config) {
    let value = config.Default;  // Standard


    switch (config.Type) {
// Ermittelt die naechste moegliche Option (Version mit Key)
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
// opt: Config und Value der Option
                            value = config.Choice[0];
// optSet: Platz fuer die gesetzten Optionen (und Config)
                        }
// item: Key der Option
                        break;
// value: Ggfs. zu setzender Wert
    case __OPTTYPES.SW : break;
// return Zu setzender Wert
    case __OPTTYPES.TF : break;
function getNextOptByName(optSet, item, value = undefined) {
    case __OPTTYPES.SD : config.Serial = true;
     const __OPT = getOptByName(optSet, item);
                        break;
    case __OPTTYPES.SI : break;
     default :            break;
    }


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


// Initialisiert die Menue-Funktion einer Option
// Setzt die naechste moegliche Option (Version mit Key)
// optAction: Typ der Funktion
// opt: Config und Value der Option
// 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: Ggfs. 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 setNextOptByName(optSet, item, value = undefined, reload = true) {
     const __OPT = getOptByName(optSet, item);
 
    return setNextOpt(__OPT, value, reload);
}


    if (optAction !== undefined) {
// Baut das Benutzermenu auf
        const __CONFIG = getOptConfig(getOptByName(optSet, item));
// optSet: Gesetzte Optionen
        const __RELOAD = ((__CONFIG !== undefined) ? __CONFIG.ActionReload : false);
function buildMenu(optSet) {
    console.log("buildMenu()");


        switch (optAction) {
    for (let opt in optSet) {
        case __OPTACTION.SET : fun = function() {
         registerOption(optSet[opt]);
                                      return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
        case __OPTACTION.NXT : fun = function() {
                                      return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                  };
                              break;
         case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :              break;
        }
     }
     }
    return fun;
}
}


// Initialisiert die gesetzten Optionen
// 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 initOptions(optConfig, optSet = undefined) {
function loadOption(opt, force = false) {
     var value;
     const __CONFIG = getOptConfig(opt);


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


    for (let opt in optConfig) {
// Laedt die (ueber Menu) gesetzten Optionen
        const __CONFIG = optConfig[opt];
// optSet: Set mit den Optionen
        const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
 
// return Set mit den geladenen Optionen
        optSet[opt] = {
function loadOptions(optSet, force = false) {
            'Config'    : __CONFIG,
     for (let opt in optSet) {
            'Value'     : initOptValue(__CONFIG),
        loadOption(optSet[opt], force);
            'SetValue'  : undefined,
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
        };
     }
     }


Zeile 1.004: Zeile 851:
}
}


// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// optConfig: Konfiguration der Optionen
// opt: Gesetzte Option
// optSet: Platz fuer die gesetzten Optionen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// return Gefuelltes Objekt mit den gesetzten Optionen
// reset: Setzt bei Erfolg auf Initialwert der Option
function startOptions(optConfig, optSet = undefined) {
function deleteOption(opt, force = false, reset = true) {
     optSet = initOptions(optConfig, optSet);
     const __CONFIG = getOptConfig(opt);


     // Memory Storage fuer vorherige Speicherung...
     if (force || ! __CONFIG.Permanent) {
    myOptMem = restoreMemoryByOpt(optSet.oldStorage);
        GM_deleteValue(getOptName(opt));


    runStored(optSet);
        if (reset) {
    loadOptions(optSet);
            setOptValue(opt, initOptValue(__CONFIG));
 
        }
    // Memory Storage fuer naechste Speicherung...
     }
    myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage);
 
    initScriptDB(optSet);
 
     return optSet;
}
}


// Installiert die Visualisierung und Steuerung der Optionen
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// optSet: Platz fuer die gesetzten Optionen
// optSet: Gesetzte Optionen
// 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 deleteOptions(optSet, force = false, reset = true) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     for (let opt in optSet) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
         deleteOption(optSet[opt], force, reset);
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     updateScriptDB(optSet);
 
    if (! optParams.hideMenu) {
         buildMenu(optSet);
    }
 
    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
        buildForm(optParams.menuAnchor, optSet, optParams);
     }
     }
}
}


// Setzt eine Option auf einen vorgegebenen Wert
// 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
// return Umbenannte Option
// return Gesetzter Wert
function renameOption(opt, name, reload = false) {
function setOpt(opt, value, reload = false) {
     const __NAME = getOptName(opt);
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
}


// Ermittelt die naechste moegliche Option
    if (__NAME !== name) {
// opt: Config und Value der Option
        deleteOption(opt, true, ! reload);
// 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) {
        setOptName(opt, name);
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
    }


     return __VALUE;
        if (reload) {
            loadOption(opt);
        }
    }
 
     return opt;
}
}


// Setzt die naechste moegliche Option
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// opt: Config und Value der Option
// reload: Seite mit "Werkseinstellungen" neu laden
// value: Default fuer ggfs. zu setzenden Wert
// optSet: Gesetzte Optionen
// 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, true);
     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)
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
// item: Key der Option
const __OPTSET = { };
// value: (Bei allen Typen) Zu setzender Wert
 
// reload: Seite mit neuem Wert neu laden
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// return Gesetzter Wert
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined };
function setOptByName(optSet, item, value, reload = false) {
    const __OPT = getOptByName(optSet, item);


    return setOpt(__OPT, value, reload);
// Behandelt die Optionen und laedt das Benutzermenu
}
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// 'teamParams': Getrennte "ligaSize"-Option wird genutzt, hier: __MYTEAM mit 'Land' 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


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


     return getNextOpt(__OPT, value);
     runStored(optSet, true);
}
    loadOptions(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
        console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
// reload: Seite mit neuem Wert neu laden
        // ... und abspeichern...
// return Gesetzter Wert
        setOpt(optSet.team, __MYTEAM, false);
function setNextOptByName(optSet, item, value = undefined, reload = true) {
    } else {
    const __OPT = getOptByName(optSet, item);
        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;
            console.log("Gespeichert: " + JSON.stringify(__MYTEAM));
        } else {
            console.error("Unbekannt: " + JSON.stringify(__TEAM));
        }
    }


// Baut das Benutzermenu auf
    if (__MYTEAM.Land !== undefined) {
// optSet: Gesetzte Optionen
        // Prefix fuer die Optionen "birthdays", "tclasses" und "progresses"...
function buildMenu(optSet) {
        renameOption(optSet.birthdays, __MYTEAM.Land + getOptName(optSet.birthdays), true);
    console.log("buildMenu()");
        renameOption(optSet.tclasses, __MYTEAM.Land + getOptName(optSet.tclasses), true);
        renameOption(optSet.progresses, __MYTEAM.Land + getOptName(optSet.progresses), true);
        // ... und nachladen...
        loadOption(optSet.birthdays, true);
        loadOption(optSet.tclasses, true);
        loadOption(optSet.progresses, true);
    }


     for (let opt in optSet) {
     if (! optParams.hideMenu) {
         registerOption(optSet[opt]);
         buildMenu(optSet);
     }
     }
}


// Laedt eine (ueber Menu) gesetzte Option
    if (optParams.menuAnchor !== undefined) {
// opt: Zu ladende Option
        buildForm(optParams.menuAnchor, optSet, optParams);
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
     }
// return Gesetzter Wert der gelandenen Option
function loadOption(opt, force = false) {
     const __CONFIG = getOptConfig(opt);


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


// Laedt die (ueber Menu) gesetzten Optionen
// ==================== Abschnitt fuer diverse Utilities ====================
// optSet: Set mit den Optionen
 
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// return Set mit den geladenen Optionen
// form: <form>...</form>
function loadOptions(optSet, force = false) {
// props: Map von name:value-Paaren
     for (let opt in optSet) {
// type: Typ der Input-Felder (Default: unsichtbare Daten)
         loadOption(optSet[opt], force);
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = "hidden") {
     for (let fieldName in props) {
         let field = form[fieldName];
        if (! field) {
            field = document.createElement("input");
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
        }
        field.value = props[fieldName];
     }
     }


     return optSet;
     return form;
}
}


// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// opt: Gesetzte Option
// form: <form>...</form>
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// props: Map von name:value-Paaren
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Ergaenztes Form-Konstrukt
function deleteOption(opt, force = false, reset = true) {
function addHiddenField(form, props) {
     const __CONFIG = getOptConfig(opt);
     return addInputField(form, props, "hidden");
 
    if (force || ! __CONFIG.Permanent) {
        const __NAME = getOptName(opt);
 
        console.log("DELETE " + __NAME);
 
        GM_deleteValue(__NAME);
 
        if (reset) {
            setOptValue(opt, initOptValue(__CONFIG));
        }
    }
}
}


// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// optSet: Gesetzte Optionen
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// type: Name des Events, z.B. "click"
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// callback: Funktion als Reaktion
// reset: Setzt bei Erfolg auf Initialwert der Option
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
// return false bei Misserfolg
     const __DELETEALL = (optSelect === undefined) || (optSelect === true);
function addEvent(obj, type, callback, capture = false) {
     const __OPTSELECT = getValue(optSelect, { });
     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);
 
            if (__RELOAD) {
                // ... und nachladen...
                loadOption(__OPT, __FORCE);
            }
        }
    }
}
}


// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// Helferfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// optSet: Gesetzte Optionen
// item: Name des betroffenen Items
// reload: Seite mit "Werkseinstellungen" neu laden
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
function resetOptions(optSet, reload = true) {
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
// return Angabe, ob das Item sichtbar sein soll
    deleteOptions(optSet, true, false, ! reload);
function checkVisible(item, showList, hideList = undefined) {
    let show = true;


     if (reload) {
     if (showList !== undefined) {
         // ... und Seite neu laden (mit "Werkseinstellungen")...
         show = (showList[item] === true);  // gesetzt und true
         window.location.reload();
    }
    if (hideList !== undefined) {
         if (hideList[item] === true) {  // gesetzt und true
            show = false; // NICHT anzeigen
        }
     }
     }
    return show;
}
}


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


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


// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
const __MYTEAM = { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 };
// 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;


// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
    return __ROWS;
const __DATAOPTS = {
}
                      'datenZat'  : true,
                      'birthdays'  : true,
                      'tClasses'  : true,
                      'progresses' : true,
                      'zatAges'    : true,
                      'trainiert'  : true,
                      'positions'  : true
                  };


// Behandelt die Optionen und laedt das Benutzermenu
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// 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);
// Liefert den Funktionsaufruf zur Option als String
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// serial: Serialization fuer String-Werte (Select, Textarea)
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined) {
     const __CONFIG = getOptConfig(opt);
    const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
    const __NAMSTR = "'" + getOptName(opt) + "'";
    const __THISVAL = ((__CONFIG.ValType === "String") ? "'\\x22' + this.value + '\\x22'" : "this.value");
    const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
    const __VALSTR = ((value !== undefined) ? JSON.stringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
    const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);


     if (__TEAMPARAMS !== undefined) {
     if (__ACTION !== undefined) {
         __MYTEAM.Team = __TEAMPARAMS.Team;
         switch (__ACTION) {
         __MYTEAM.Liga = __TEAMPARAMS.Liga;
         case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
        __MYTEAM.Land = __TEAMPARAMS.Land;
                              return "(sessionStorage.setItem('runcmd', 'SET'), sessionStorage.setItem('runkey', " + __NAMSTR + "), sessionStorage.setItem('runval', " + __VALSTR + "), window.location.reload())";
        __MYTEAM.LdNr = __TEAMPARAMS.LdNr;
         case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
        __MYTEAM.LgNr = __TEAMPARAMS.LgNr;
                              return "(sessionStorage.setItem('runcmd', 'NXT'), sessionStorage.setItem('runkey', " + __NAMSTR + "), sessionStorage.setItem('runval', " + __VALSTR + "), window.location.reload())";
        console.log("Ermittelt: " + JSON.stringify(__MYTEAM));
         case __OPTACTION.RST : //return "doActionRst()";
        // ... und abspeichern...
                              return "(sessionStorage.setItem('runcmd', 'RST'), window.location.reload())";
        setOpt(optSet.team, __MYTEAM, false);
        default :              break;
    } else {
         }
         const __TEAM = getOptValue(optSet.team);  // Gespeicherte Parameter
 
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
            __MYTEAM.Team = __TEAM.Team;
            __MYTEAM.Liga = __TEAM.Liga;
            __MYTEAM.Land = __TEAM.Land;
            __MYTEAM.LdNr = __TEAM.LdNr;
            __MYTEAM.LgNr = __TEAM.LgNr;
            console.log("Gespeichert: " + JSON.stringify(__MYTEAM));
         } else {
            console.error("Unbekannt: " + JSON.stringify(__TEAM));
         }
     }
     }


     if (__MYTEAM.LdNr !== undefined) {
     return 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)
// return String mit dem (reinen) Funktionsaufruf
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined) {
     const __ACTION = getFormAction(opt, isAlt, value, serial);


     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>';
 
    return [ __ELEMENTON, __ELEMENTOFF ];
}
}


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// 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 addEvent(obj, type, callback, capture = false) {
     const __VALUE = getOptValue(opt, false);
     if (obj.addEventListener) {
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
        return obj.addEventListener(type, callback, capture);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     } else if (obj.attachEvent) {
        return obj.attachEvent("on" + type, callback);
     } else {
        console.log("Could not add " + 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: Entfernt eine Reaktion fuer ein Event
// Zeigt eine Option auf der Seite als Daten-Textfeld 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 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 removeEvent(obj, type, callback, capture = false) {
    const __VALUE = getOptValue(opt);
     if (obj.removeEventListener) {
    const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
        return obj.removeEventListener(type, callback, capture);
     const __SUBMIT = getValue(__CONFIG.Submit, "");
     } else if (obj.detachEvent) {
    const __ONSUBMIT = ((__SUBMIT.length > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
        return obj.detachEvent("on" + type, callback);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     } else {
    const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
        console.log("Could not remove " + type + " event:");
     const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
        console.log(callback);
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                          JSON.stringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


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


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// 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 addDocEvent(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 addEvent(__OBJ, type, callback, capture);
     return '<label for="' + __NAME + '">' + __FORMLABEL +
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';
}
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Zeigt eine Option auf der Seite an (je nach Typ)
// 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 getOptionElement(opt) {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
    const __CONFIG = getOptConfig(opt);
// return false bei Misserfolg
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
function removeDocEvent(id, type, callback, capture = false) {
     let element = "";
     const __OBJ = document.getElementById(id);


     return removeEvent(__OBJ, type, callback, capture);
     if (! __CONFIG.Hidden) {
        switch (__TYPE) {
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                            break;
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                                element = getOptionCheckbox(opt);
                            } else {
                                element = getOptionRadio(opt);
                            }
                            break;
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                            break;
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
                            break;
        case __OPTTYPES.SI : element = getOptionButton(opt);
                            break;
        default :            break;
        }
 
        if (element.length === 2) {
            element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
        }
    }
 
    return element;
}
}


// Hilfsfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// Baut das Benutzermenu auf der Seite auf
// item: Name des betroffenen Items
// optSet: Gesetzte Optionen
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// optParams: Eventuell notwendige Parameter
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// return Angabe, ob das Item sichtbar sein soll
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
function checkVisible(item, showList, hideList = undefined) {
// 'formWidth': Anzahl der Elemente pro Zeile
     let show = true;
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return String mit dem HTML-Code
function getForm(optSet, optParams = { }) {
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    let form = __FORM;
    let count = 0;  // Bisher angezeigte Optionen
     let column = 0; // Spalte der letzten Option (1-basierend)


     if (showList !== undefined) {
     for (let opt in optSet) {
         show = (showList[item] === true);  // gesetzt und true
         if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
    }
            const __ELEMENT = getOptionElement(optSet[opt]);
    if (hideList !== undefined) {
             const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";
        if (hideList[item] === true) {  // gesetzt und true
             show = false; // NICHT anzeigen
        }
    }


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


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// Fuegt das Script in die Seite ein
// 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) {
// return String mit dem HTML-Code fuer das Script
     const __TAGS = document.getElementsByName(name);
function getScript(optSet, optParams = { }) {
     const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
     //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
     const __SCRIPT = "";
 
    //window.eval('function activateMenu() { console.log("TADAAA!"); }');


     return __TABLE;
     return __SCRIPT;
}
}


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// index: Laufende Nummer des Elements (0-based)
// anchor: Element, das als Anker fuer die Anzeige dient
// tag: Tag des Elements ("table")
// optSet: Gesetzte Optionen
// doc: Dokument (document)
// optParams: Eventuell notwendige Parameter
// return Gesuchtes Element oder undefined (falls nicht gefunden)
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
function getTable(index, tag = "table", doc = document) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __TAGS = document.getElementsByTagName(tag);
// 'formWidth': Anzahl der Elemente pro Zeile
    const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function buildForm(anchor, optSet, optParams = { }) {
     console.log("buildForm()");


     return __TABLE;
     const __FORM = getForm(optSet, optParams);
}
    const __SCRIPT = getScript(optSet, optParams);


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
     addForm(anchor, __FORM, __SCRIPT);
// index: Laufende Nummer des Elements (0-based)
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getRows(index, doc = document) {
     const __TABLE = getTable(index, "table", doc);
    const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
 
    return __ROWS;
}
}


// ==================== Abschnitt fuer Optionen auf der Seite ====================
// Informationen zu hinzugefuegten Forms
const __FORMS = { };


// Liefert den 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
// form: HTML-Form des Optionsmenu (hinten angefuegt)
// value: Ggfs. zu setzender Wert
// script: Script mit Reaktionen
// serial: Serialization fuer String-Werte (Select, Textarea)
function addForm(anchor, form = "", script = "") {
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
     const __OLDFORM = __FORMS[anchor];
// return String mit dem (reinen) Funktionsaufruf
     const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
     const __STORAGE = getMemory(memory);
     const __MEMORY = __STORAGE.Value;
    const __MEMSTR = __STORAGE.Display;
    const __RUNPREFIX = __STORAGE.Prefix;


     if (__MEMORY !== undefined) {
     __FORMS[anchor] = { 'Script' : script, 'Form' : form };
        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) {
    anchor.innerHTML = __REST + script + form;
            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
// ==================== Ende Abschnitt fuer Optionen ====================
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = bis Browserende gespeichert (sessionStorage), __OPTMEM.unbegrenzt = unbegrenzt gespeichert (localStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {
    const __ACTION = getFormAction(opt, isAlt, value, serial, memory);


    return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
}


// Zeigt eine Option auf der Seite als Auswahlbox an
// Zeitpunktangaben
// opt: Anzuzeigende Option
const __TIME = {
// return String mit dem HTML-Code
     'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
function getOptionSelect(opt) {
     'beg' : 1, // Jugendspieler darf trainieren (wird 13 Jahre alt)
    const __CONFIG = getOptConfig(opt);
     'now' : 2,  // Aktueller ZAT
     const __NAME = getOptName(opt);
     'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
     const __VALUE = getOptValue(opt);
};
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


    for (let value of __CONFIG.Choice) {
// Funktionen ***************************************************************************
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';


    return __LABEL.replace('$', element);
// Erschafft die Spieler-Objekte und fuellt sie mit Werten
}
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
 
     const __SAISON = getOptValue(optSet.saison);
// Zeigt eine Option auf der Seite als Radiobutton an
     const __CURRZAT = getOptValue(optSet.aktuellerZat);
// opt: Anzuzeigende Option
     const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
// return String mit dem HTML-Code
     const __TCLASSES = getOptValue(optSet.tclasses, []);
function getOptionRadio(opt) {
     const __PROGRESSES = getOptValue(optSet.progresses, []);
     const __CONFIG = getOptConfig(opt);
     const __PLAYERS = [];
     const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt, false);
     const __ACTION = getFormActionEvent(opt, false, true, "click", false);
     const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
     const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'ON" value="1"' +
                        (__VALUE ? ' CHECKED' : __ACTION) +
                        ' /><label for="' + __NAME + 'ON">' +
                        __CONFIG.Label + '</label>';
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';


     return [ __ELEMENTON, __ELEMENTOFF ];
     for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
}
        const __CELLS = playerRows[i].cells;
        const __AGE = getAgeFromHTML(__CELLS, colIdx.Age);
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
        const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);


// Zeigt eine Option auf der Seite als Checkbox an
        __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
// opt: Anzuzeigende Option
        __PLAYERS[j] = __NEWPLAYER;
// return String mit dem HTML-Code
    }
function getOptionCheckbox(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     return '<input type="checkbox" name="' + __NAME +
     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 ];
}
}


// Zeigt eine Option auf der Seite als Button an
// Klasse ColumnManager *****************************************************************
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionButton(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
    const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);


     return '<label for="' + __NAME + '">' + __FORMLABEL +
function ColumnManager(optSet) {
          '</label><input type="button" name="' + __NAME +
    this.geb = getOptValue(optSet.zeigeGeb);
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
    this.tal = getOptValue(optSet.zeigeTal);
          __ACTION + '/>';
    this.quo = getOptValue(optSet.zeigeQuote);
}
    this.aufw = getOptValue(optSet.zeigeAufw);
    this.alter = getOptValue(optSet.zeigeAlter);
    this.skill = getOptValue(optSet.zeigeSkill);
    this.pos = getOptValue(optSet.zeigePosition);
     this.opti = ((getOptValue(optSet.anzahlOpti) >= 1) && (getOptValue(optSet.anzahlOpti) <= 6));
    this.mw = ((getOptValue(optSet.anzahlMW) >= 1) && (getOptValue(optSet.anzahlMW) <= 6));
    this.anzOpti = getOptValue(optSet.anzahlOpti);
    this.anzMw = getOptValue(optSet.anzahlMW);
    this.skillE = getOptValue(optSet.zeigeSkillEnde);
    this.optiE = ((getOptValue(optSet.anzahlOptiEnde) >= 1) && (getOptValue(optSet.anzahlOptiEnde) <= 6));
    this.mwE = ((getOptValue(optSet.anzahlMWEnde) >= 1) && (getOptValue(optSet.anzahlMWEnde) <= 6));
    this.anzOptiE = getOptValue(optSet.anzahlOptiEnde);
    this.anzMwE = getOptValue(optSet.anzahlMWEnde);
    this.kennzE = getOptValue(optSet.kennzeichenEnde);


// Zeigt eine Option auf der Seite an (je nach Typ)
    this.toString = function() {
// opt: Anzuzeigende Option
        let result = "Skillschnitt\t\t" + this.skill + '\n';
// return String mit dem HTML-Code
        result += "Beste Position\t" + this.pos + '\n';
function getOptionElement(opt) {
        result += "Optis\t\t\t" + this.opti + " (" + this.anzOpti + ")\n";
    const __CONFIG = getOptConfig(opt);
        result += "Marktwerte\t\t" + this.mw + " (" + this.anzMw + ")\n";
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
        result += "Skillschnitt Ende\t" + this.skillE + '\n';
    let element = "";
        result += "Optis Ende\t\t" + this.optiE + " (" + this.anzOptiE + ")\n";
        result += "Marktwerte Ende\t" + this.mwE + " (" + this.anzMwE + ")\n";


    if (! __CONFIG.Hidden) {
         return result;
         switch (__TYPE) {
    };
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                            break;
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                                element = getOptionCheckbox(opt);
                            } else {
                                element = getOptionRadio(opt);
                            }
                            break;
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                            break;
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
                            break;
        case __OPTTYPES.SI : element = getOptionButton(opt);
                            break;
        default :            break;
        }


         if (element.length === 2) {
    this.addCell = function(tableRow) {
             element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
         tableRow.insertCell(-1);
        return tableRow.cells.length - 1;
    };
 
    this.addAndFillCell = function(tableRow, value, color, digits = 2) {
        if (isFinite(value) && (value !== true) && (value !== false)) {
             // Zahl einfuegen
            if (value < 1000) {
                // Mit Nachkommastellen darstellen
                tableRow.cells[this.addCell(tableRow)].textContent = parseFloat(value).toFixed(digits);
            } else {
                // Mit Tausenderpunkten darstellen
                tableRow.cells[this.addCell(tableRow)].textContent = getNumberString(value.toString());
            }
        } else {
            // String einfuegen
            tableRow.cells[this.addCell(tableRow)].textContent = value;
         }
         }
     }
        tableRow.cells[tableRow.cells.length - 1].style.color = color;
     };


     return element;
     this.addTitles = function(headers, titleColor = "#FFFFFF") {
}
        // Spaltentitel zentrieren
        headers.align = "center";


// Baut das Benutzermenu auf der Seite auf
        // Titel fuer die aktuellen Werte
// optSet: Gesetzte Optionen
        if (this.tal) {
// optParams: Eventuell notwendige Parameter
            this.addAndFillCell(headers, "Talent", 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.quo) {
// 'formWidth': Anzahl der Elemente pro Zeile
            this.addAndFillCell(headers, "Quote", titleColor);
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
        }
// return String mit dem HTML-Code
        if (this.aufw) {
function getForm(optSet, optParams = { }) {
            this.addAndFillCell(headers, "Aufwertung", titleColor);
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
        }
    const __FORMEND = '</tr></tbody></table></form>';
        if (this.geb) {
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
            this.addAndFillCell(headers, "Geb.", titleColor);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
        }
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
        if (this.alter) {
    let form = __FORM;
            this.addAndFillCell(headers, "Alter", titleColor);
    let count = 0;  // Bisher angezeigte Optionen
        }
    let column = 0;  // Spalte der letzten Option (1-basierend)
         if (this.skill) {
 
             this.addAndFillCell(headers, "Skill", titleColor);
    for (let opt in optSet) {
        }
         if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
        if (this.pos) {
             const __ELEMENT = getOptionElement(optSet[opt]);
             this.addAndFillCell(headers, "Pos", titleColor);
             const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";
        }
 
        if (this.opti) {
            if (__ELEMENT.length > 0) {
            for (let i = 1; i <= this.anzOpti; i++) {
                if (++count > __FORMBREAK) {
                this.addAndFillCell(headers, "Opti " + i, titleColor);
                    if (++column > __FORMWIDTH) {
                if (this.mw && (this.anzMw >= i)) {
                        column = 1;
                    this.addAndFillCell(headers, "MW " + i, titleColor);
                    }
                 }
                 }
                 if (column === 1) {
            }
                     form += '</tr><tr>';
            if (this.mw) {
                 for (let i = this.anzOpti + 1; i <= this.anzMw; i++) {
                     // Mehr MW- als Opti-Spalten
                    this.addAndFillCell(headers, "MW " + i, titleColor);
                 }
                 }
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
            }
        } else if (this.mw) {
            // Keine Opti-, dafuer MW-Spalten
            for (let i = 1; i <= this.anzMw; i++) {
                this.addAndFillCell(headers, "MW " + i, titleColor);
             }
             }
         }
         }
    }
    form += '\n' + __FORMEND;


    return form;
        // Titel fuer die Werte mit Ende 18
}
        if (this.skillE) {
 
            this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
// Fuegt das Script in die Seite ein
        }
// optSet: Gesetzte Optionen
        if (this.optiE) {
// optParams: Eventuell notwendige Parameter
            for (let i = 1; i <= this.anzOptiE; i++) {
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
                this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
                if (this.mwE && (this.anzMwE >= i)) {
// return String mit dem HTML-Code fuer das Script
                    this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
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>';
            if (this.mwE) {
     //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
                for (let i = this.anzOptiE + 1; i <= this.anzMwE; i++) {
    const __SCRIPT = "";
                    this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
                }
            }
        } else if (this.mwE) {
            for (let i = 1; i <= this.anzMwE; i++) {
                this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
            }
        }
     };  // Ende addTitles()


     //window.eval('function activateMenu() { console.log("TADAAA!"); }');
     this.addValues = function(player, playerRow, color = "#FFFFFF") {
        const __COLOR = (player.isGoalie ? getColor("TOR") : color);
        const __POS1COLOR = (player.isGoalie ? getColor("TOR") : getColor(player.getPos(1)));


    return __SCRIPT;
        // Aktuelle Werte
}
        if (this.tal) {
 
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        }
// anchor: Element, das als Anker fuer die Anzeige dient
        if (this.quo) {
// optSet: Gesetzte Optionen
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
// optParams: Eventuell notwendige Parameter
        }
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
        if (this.aufw) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
            this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
// 'formWidth': Anzahl der Elemente pro Zeile
        }
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
        if (this.geb) {
function buildForm(anchor, optSet, optParams = { }) {
            this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
    console.log("buildForm()");
        }
 
        if (this.alter) {
    const __FORM = getForm(optSet, optParams);
            this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
    const __SCRIPT = getScript(optSet, optParams);
        }
 
        if (this.skill) {
    addForm(anchor, __FORM, __SCRIPT);
            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);
                }
            }
        }


// Informationen zu hinzugefuegten Forms
        // Werte mit Ende 18
const __FORMS = { };
        if (this.skillE) {
 
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
        }
// anchor: Element, das als Anker fuer die Anzeige dient
        if (this.optiE) {
// form: HTML-Form des Optionsmenu (hinten angefuegt)
            for (let i = 1; i <= this.anzOptiE; i++) {
// script: Script mit Reaktionen
                if (player.isGoalie) {
function addForm(anchor, form = "", script = "") {
                    if (i === 1) {
    const __OLDFORM = __FORMS[anchor];
                        // TOR-Opti anzeigen
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                        this.addAndFillCell(playerRow, player.getOpti("TOR", __TIME.end), getColor("TOR"), 2);
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
                    } 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)
}


    __FORMS[anchor] = { 'Script' : script, 'Form' : form };
// Klasse PlayerRecord ******************************************************************


     anchor.innerHTML = __REST + script + form;
function PlayerRecord(age, skills, isGoalie) {
}
    // Zu benutzende Marktwertformel
     const __MWFORMEL = {
        'alt' : 0,  // Marktwertformel bis Saison 9 inklusive
        'S10' : 1  // Marktwertformel MW5 ab Saison 10
    };


// ==================== Ende Abschnitt fuer Optionen ====================
    this.mwFormel = __MWFORMEL.S10;  // Neue Formel, genauer in initPlayer()


// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
    this.age = age;
    this.skills = skills;
    this.isGoalie = isGoalie;


// Zeitpunktangaben
    // in this.initPlayer() definiert:
const __TIME = {
     // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
     'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
    // this.zatAge: Bisherige erfolgte Trainings-ZATs
     'beg' : 1,  // Jugendspieler darf trainieren (wird 13 Jahre alt)
    // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
     'now' : 2, // Aktueller ZAT
     // this.aufwert: Aufwertungsstring
     'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
     // 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


// Funktionen ***************************************************************************
    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";


// Erschafft die Spieler-Objekte und fuellt sie mit Werten (reloadData: true = Teamuebersicht, false = Spielereinzelwerte)
        for (let pos of this.positions) {
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, reloadData = false) {
            result += "\n\t" + pos + '\t';
    const __SAISON = getOptValue(optSet.saison);
            result += this.getOpti(pos).toFixed(2) + '\t';
    const __CURRZAT = getOptValue(optSet.aktuellerZat);
            result += getNumberString(this.getMarketValue(pos).toString());
    const __BIRTHDAYS = getOptValue(optSet.birthdays, []);
        }
    const __TCLASSES = getOptValue(optSet.tClasses, []);
    const __PROGRESSES = getOptValue(optSet.progresses, []);
    const __ZATAGES = getOptValue(optSet.zatAges, []);
    const __TRAINIERT = getOptValue(optSet.trainiert, []);
    const __POSITIONS = getOptValue(optSet.positions, []);
    const __PLAYERS = [];


    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
        result += "\n\nWerte mit Ende 18\n";
         const __CELLS = playerRows[i].cells;
         result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
        const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
         result += "Optis und Marktwerte";
        const __SKILLS = getSkillsFromHTML(__CELLS, colIdx);
         const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
        const __NEWPLAYER = new PlayerRecord(__AGE, __SKILLS, __ISGOALIE);


         __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
         for (let pos of this.positions) {
 
            result += "\n\t" + this.getPos()[i] + '\t';
        if (reloadData) {
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
             __NEWPLAYER.setZusatz(__ZATAGES[j], __TRAINIERT[j], __POSITIONS[j]);
             result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
         }
         }


         __PLAYERS[j] = __NEWPLAYER;
         return result;
     }
     };  // Ende this.toString()


     if (reloadData) {
     // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
         storePlayerData(__PLAYERS, playerRows, optSet, colIdx, offsetUpper, offsetLower);
    this.initPlayer = function(saison, currZAT, gebZAT, tclass, progresses) {
    } else {
         this.zatGeb = gebZAT;
         calcPlayerData(__PLAYERS, optSet);
        this.zatAge = this.calcZatAge(currZAT);
    }
        this.talent = tclass;
        this.aufwert = progresses;
         this.mwFormel = (saison < 10) ? __MWFORMEL.alt : __MWFORMEL.S10;


    return __PLAYERS;
        this.positions = [];
}
        // ABW
        this.positions[0] = [];
        this.positions[0][0] = "ABW";
        this.positions[0][1] = this.getOpti("ABW");
        // DMI
        this.positions[1] = [];
        this.positions[1][0] = "DMI";
        this.positions[1][1] = this.getOpti("DMI");
        // MIT
        this.positions[2] = [];
        this.positions[2][0] = "MIT";
        this.positions[2][1] = this.getOpti("MIT");
        // OMI
        this.positions[3] = [];
        this.positions[3][0] = "OMI";
        this.positions[3][1] = this.getOpti("OMI");
        // STU
        this.positions[4] = [];
        this.positions[4][0] = "STU";
        this.positions[4][1] = this.getOpti("STU");
        // TOR
        this.positions[5] = [];
        this.positions[5][0] = "TOR";
        this.positions[5][1] = this.getOpti("TOR");


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


    for (let i = 0; i < players.length; i++) {
        // Einzelskills mit Ende 18 berechnen
         const __ZUSATZ = players[i].calcZusatz();
         this.skillsEnd = [];


         __ZATAGES[i]  = __ZUSATZ.zatAge;
         const __ADDRATIO = (this.getZatAge(__TIME.end) - this.getZatAge()) / this.getZatAge();
        __TRAINIERT[i] = __ZUSATZ.trainiert;
        __POSITIONS[i] = __ZUSATZ.bestPos;
    }


    setOpt(optSet.zatAges, __ZATAGES, false);
        for (let i in this.skills) {
    setOpt(optSet.trainiert, __TRAINIERT, false);
            const __SKILL = this.skills[i];
    setOpt(optSet.positions, __POSITIONS, false);
            let progSkill = __SKILL;
}


// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
            if (isTrainableSkill(i)) {
function storePlayerData(players, playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0) {
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
    const __BIRTHDAYS = [];
                const __ADDSKILL = parseInt((__ADDRATIO * __SKILL).toFixed(0), 10);
    const __TCLASSES = [];
    const __PROGRESSES = [];


    for (let i = offsetUpper; i < playerRows.length - offsetLower; i++) {
                progSkill += __ADDSKILL;
        const __CELLS = playerRows[i].cells;
            }


        __BIRTHDAYS[i - offsetUpper] = getIntFromHTML(__CELLS, colIdx.Geb);
            this.skillsEnd[i] = Math.min(progSkill, 99);
        __TCLASSES[i - offsetUpper] = getTalentFromHTML(__CELLS, colIdx.Tal);
         }
         __PROGRESSES[i - offsetUpper] = getStringFromHTML(__CELLS, colIdx.Auf);
     };  // Ende this.iniPlayer()
     }


     setOpt(optSet.birthdays, __BIRTHDAYS, false);
     this.getGeb = function() {
    setOpt(optSet.tClasses, __TCLASSES, false);
        return (this.zatGeb < 0) ? '?' : this.zatGeb;
     setOpt(optSet.progresses, __PROGRESSES, false);
     };
}


// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
    this.calcZatAge = function(currZAT) {
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0) {
        if (this.age < 13) {
    if (offsetLeft < 0) {
            return 0;  // noch nicht trainiert
         offsetLeft = colIdxSort;  // ab Sortierspalte
         } else {
    }
            let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72;  // Basiszeit fuer die Jahre seit Jahrgang 13


    for (let i = offsetUpper; i < rows.length - offsetLower - 1; i++) {
            if (this.zatGeb < 0) {
        if (rows[i].cells[colIdxSort].textContent !== rows[i + 1].cells[colIdxSort].textContent) {
                return ZATs + currZAT; // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
            } else {
                rows[i].cells[j].style.borderBottom = borderString;
                return ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
             }
             }
         }
         }
     }
     };
}


// Klasse ColumnManager *****************************************************************
    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;
        }
    };


function ColumnManager(optSet, showCol = undefined) {
    this.getAge = function(when = __TIME.now) {
    const __SHOWALL = (showCol === undefined) || (showCol === true);
        if (this.mwFormel === __MWFORMEL.alt) {
     const __SHOWCOL = getValue(showCol, { });
            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());
            }
         } else {
            // String einfuegen
            tableRow.cells[this.addCell(tableRow)].textContent = value;
        }
        tableRow.cells[tableRow.cells.length - 1].style.color = color;
     };
     };


     this.addTitles = function(headers, titleColor = "#FFFFFF") {
     this.getAufwert = function() {
         // Spaltentitel zentrieren
         return (this.aufwert.length > 0) ? this.aufwert : "keine";
        headers.align = "center";
    };


        // Titel fuer die aktuellen Werte
    this.getSkill = function(when = __TIME.now) {
         if (this.tal) {
         const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
            this.addAndFillCell(headers, "Talent", titleColor);
         let result = 0;
         }
 
         if (this.quo) {
         for (let skill of __SKILLS) {
             this.addAndFillCell(headers, "Quote", titleColor);
             result += skill;
         }
         }
         if (this.aufw) {
 
            this.addAndFillCell(headers, "Aufwertung", 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.geb) {
         for (let idx of __IDXSECSKILLS) {
            this.addAndFillCell(headers, "Geb.", titleColor);
             sumSecSkills += __SKILLS[idx];
        }
        if (this.alter) {
            this.addAndFillCell(headers, "Alter", titleColor);
        }
        if (this.skill) {
            this.addAndFillCell(headers, "Skill", titleColor);
        }
        if (this.pos) {
            this.addAndFillCell(headers, "Pos", titleColor);
        }
         for (let i = 1; i <= 6; i++) {
             if (i <= this.anzOpti) {
                this.addAndFillCell(headers, "Opti " + i, titleColor);
            }
            if (i <= this.anzMw) {
                this.addAndFillCell(headers, "MW " + i, titleColor);
            }
         }
         }


         // Titel fuer die Werte mit Ende 18
         return (5 * sumPriSkills + sumSecSkills) / 27;
        if (this.skillE) {
    };
            this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
 
        }
    this.getMarketValue = function(pos, when = __TIME.now) {
        for (let i = 1; i <= 6; i++) {
        const __AGE = this.getAge(when);
            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") {
        if (this.mwFormel === __MWFORMEL.alt) {
        const __COLOR = (player.isGoalie ? getColor("TOR") : color);
            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
         const __POS1COLOR = getColor(player.getPos());
         } else {  // MW-Formel ab Saison 10...
            const __MW5TF = 1.00; // Zwischen 0.97 und 1.03


        // Aktuelle Werte
            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 (this.tal) {
            this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
         }
         }
        if (this.quo) {
    };
            this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, 2);
}
        }
        if (this.aufw) {
            this.addAndFillCell(playerRow, player.getAufwert(), __COLOR);
        }
        if (this.geb) {
            this.addAndFillCell(playerRow, player.getGeb(), __COLOR, 0);
        }
        if (this.alter) {
            this.addAndFillCell(playerRow, player.getAge(), __COLOR, 2);
        }
        if (this.skill) {
            this.addAndFillCell(playerRow, player.getSkill(), __COLOR, 2);
        }
        if (this.pos) {
            this.addAndFillCell(playerRow, player.getPos(), __POS1COLOR);
        }
        for (let i = 1; i <= 6; i++) {
            const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
            const __COLI = getColor(__POSI);


            if (i <= this.anzOpti) {
// Funktionen fuer die HTML-Seite *******************************************************
                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 das Alter aus
        if (this.skillE) {
// return Alter als Zahl
            this.addAndFillCell(playerRow, player.getSkill(__TIME.end), __COLOR, 2);
function getAgeFromHTML(cells, colIdxAge) {
        }
    return parseInt(cells[colIdxAge].textContent, 10);
        for (let i = 1; i <= 6; i++) {
}
            const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
 
            const __COLI = getColor(__POSI);
// Liest das Geburtsdatum aus
// return Geburtsdatum als ZAT
function getGebFromHTML(cells, colIdxGeb) {
    const __TEXT = ((cells[colIdxGeb] === undefined) ? '?' : cells[colIdxGeb].textContent);


            if (i <= this.anzOptiE) {
    return parseInt((__TEXT === '?') ? -1 : __TEXT, 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 Talentklasse ("wenig", "normal", "hoch") aus
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
function getTalentFromHTML(cells, colIdxTal) {
    const __TEXT = ((cells[colIdxTal] === undefined) ? '?' : cells[colIdxTal].textContent);


function PlayerRecord(age, skills, isGoalie) {
    return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
    // 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 Aufwertungen aus
// return Aufwertungen als String
function getAufwertFromHTML(cells, colIdxAuf) {
    const __TEXT = ((cells[colIdxAuf] === undefined) ? '?' : cells[colIdxAuf].textContent);


     this.age = age;
     return __TEXT.toString();
    this.skills = skills;
}
    this.isGoalie = isGoalie;


    // in this.initPlayer() definiert:
// Liest die Einzelskills aus
    // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
// return Skills als Array von Zahlen
     // this.zatAge: Bisherige erfolgte Trainings-ZATs
function getSkillsFromHTML(cells, colIdx) {
     // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
     const __RESULT = [];
    // this.aufwert: Aufwertungsstring
 
    // this.mwFormel: Benutzte MW-Formel, siehe __MWFORMEL
     for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
    // this.positions[][]: Positionstext und Opti; TOR-Index ist 5
        __RESULT[i - colIdx.Einz] = parseInt(cells[i].textContent, 10);
     // this.skillsEnd[]: Berechnet aus this.skills, this.age und aktuellerZat
     }


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


    this.toString = function() {
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
        let result = "Alter\t\t" + this.age + "\n\n";
// return Angabe, der Spieler Torwart oder Feldspieler ist
        result += "Aktuelle Werte\n";
function isGoalieFromHTML(cells, colIdxClass) {
        result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
    return (cells[colIdxClass].className === "TOR");
        result += "Optis und Marktwerte";
}


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


        result += "\n\nWerte mit Ende 18\n";
// Sortiert das Positionsfeld per BubbleSort
        result += "Skillschnitt\t" + this.getSkill(__TIME.end).toFixed(2) + '\n';
function sortPositionArray(array) {
        result += "Optis und Marktwerte";
    const __TEMP = [];
    let transposed = true;
    // TOR soll immer die letzte Position im Feld sein, deshalb - 1
    let length = array.length - 1;


         for (let pos of this.positions) {
    while (transposed && (length > 1)) {
             result += "\n\t" + this.getPos()[i] + '\t';
        transposed = false;
            result += this.getOpti(pos, __TIME.end).toFixed(2) + '\t';
         for (let i = 0; i < length - 1; i++) {
            result += getNumberString(this.getMarketValue(pos, __TIME.end).toString());
             // 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--;
    }
}


        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;


         this.positions = [];
         for (let i = 0; i < __TEMP.length; i++) {
        // ABW
            if ((i > 0) && (i % 3 === 0)) {
        this.positions[0] = [];
                result += ".";
        this.positions[0][0] = "ABW";
            }
        this.positions[0][1] = this.getOpti("ABW");
            result += __TEMP.substr(i, 1);
        // DMI
         }
        this.positions[1] = [];
        this.positions[1][0] = "DMI";
        this.positions[1][1] = this.getOpti("DMI");
        // MIT
        this.positions[2] = [];
        this.positions[2][0] = "MIT";
        this.positions[2][1] = this.getOpti("MIT");
        // OMI
        this.positions[3] = [];
        this.positions[3][0] = "OMI";
        this.positions[3][1] = this.getOpti("OMI");
        // STU
        this.positions[4] = [];
        this.positions[4][0] = "STU";
        this.positions[4][1] = this.getOpti("STU");
         // TOR
        this.positions[5] = [];
        this.positions[5][0] = "TOR";
        this.positions[5][1] = this.getOpti("TOR");


         // Sortieren
         return reverseString(result);
        sortPositionArray(this.positions);
    }
}


        // Einzelskills mit Ende 18 berechnen
// Dreht den uebergebenen String um
        this.skillsEnd = [];
function reverseString(string) {
    let result = "";


        const __ADDRATIO = (this.getZatDone(__TIME.end) - this.getZatDone()) / this.getZatDone();
    for (let i = string.length - 1; i >= 0; i--) {
        result += string.substr(i, 1);
    }


        for (let i in this.skills) {
    return result;
            const __SKILL = this.skills[i];
}
            let progSkill = __SKILL;


            if (isTrainableSkill(i)) {
// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert
                // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
// Die Indizes gehen von 0 (SCH) bis 16 (EIN)
                const __ADDSKILL = getMulValue(__ADDRATIO, __SKILL, 0, NaN);
function isTrainableSkill(idx) {
    const __TRAINABLESKILLS = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15];
    const __IDX = parseInt(idx, 10);
    let result = false;


                progSkill += __ADDSKILL;
    for (let idxTrainable of __TRAINABLESKILLS) {
             }
        if (__IDX === idxTrainable) {
 
             result = true;
             this.skillsEnd[i] = Math.min(progSkill, 99);
             break;
         }
         }
     };  // 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() {
// Gibt die Indizes der Sekundaerskills zurueck
         return (this.zatGeb < 0) ? '?' : this.zatGeb;
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 [];
     }
}


    this.calcZatAge = function(currZAT) {
// Gibt die zur Position gehoerige Farbe zurueck
        let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72; // Basiszeit fuer die Jahre seit Jahrgang 13
function getColor(pos) {
 
    switch (pos) {
         if (this.zatGeb < 0) {
        case "TOR" : return "#FFFF00";
            return ZATs + currZAT; // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
         case "ABW" : return "#00FF00";
         } else {
         case "DMI" : return "#3366FF";
            return ZATs + currZAT - this.zatGeb; // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
         case "MIT" : return "#66FFFF";
         }
         case "OMI" : return "#FF66FF";
    };
         case "STU" : return "#FF0000";
 
         case "LEI" : return "#FFFFFF";
    this.getZatAge = function(when = __TIME.now) {
         default : return "";
         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) {
// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
        if (this.mwFormel === __MWFORMEL.alt) {
            return (when === __TIME.end) ? 18 : this.age;
        } else {  // Geburtstage ab Saison 10...
            return (13.00 + this.getZatAge(when) / 72);
        }
    };


    this.getTrainiert = function(recalc = false) {
// ==================== Abschnitt fuer sonstige Parameter ====================
        let sum = 0;


        if (recalc || (this.trainiert === undefined)) {
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
            for (let i in this.skills) {
// url: Adresse der Seite
                if (isTrainableSkill(i)) {
// return Parameter aus der URL der Seite als Nummer
                    sum += this.skills[i];
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 {
         } else {
             sum += this.trainiert;
             // Wert von page setzt sich aus allen Zeichen zwischen "page=" und '&' zusammen
            page = parseInt(url.substring(__INDEXS + __SUCH.length, url.indexOf('&', __INDEXS)), 10);
         }
         }
    }


        return sum;
    return page;
    };
}


    this.getAufwertungsSchnitt = function() {
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
        return parseFloat(this.getTrainiert() / this.getZatDone());
// cell: Tabellenzelle mit den Parametern zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
     };
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND" },
//        z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine" }
function getTeamParamsFromCell(cell) {
    const __SEARCHSTART = " von ";
    const __SEARCHMIDDLE = "</b><br>";
    const __SEARCHLIGA = ". Liga ";
    const __SEARCHEND = "<a href=";
    const __INDEXSTART = cell.innerHTML.indexOf(__SEARCHSTART);
    const __INDEXEND = cell.innerHTML.indexOf(__SEARCHEND);
 
    let teamParams = cell.innerHTML.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
     const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);


     this.getPos = function(idx = undefined) {
     let land = (__INDEXLIGA > 0) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
        const __IDXOFFSET = 1;
    const __TEAM = (__INDEXMIDDLE > 0) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
    let liga = ((__INDEXLIGA > 0) && (__INDEXMIDDLE > 0)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;


         switch (getValue(idx, 0)) {
    if (land !== undefined) {
        case -1 : return (this.bestPos = this.positions[isGoalie ? 5 : 0][0]);
         if (land.charAt(1) === ' ') {   // Land z.B. hinter "2. Liga A " statt "1. Liga "
         case  0 : return this.bestPos;
            land = land.substr(2);
        default : return this.positions[idx - __IDXOFFSET][0];
         }
        if (liga !== undefined) {
            liga = liga.substring(0, liga.length - land.length - 1);
         }
         }
     };
     }


     this.getTalent = function() {
     const __RET = {
         return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
         'Team' : __TEAM,
        'Liga' : liga,
        'Land' : land
     };
     };


     this.getAufwert = function() {
     return __RET;
        return (this.aufwert.length > 0) ? this.aufwert : "keine";
}
    };


    this.getSkill = function(when = __TIME.now) {
// Gibt die laufende Nummer des ZATs im Text einer Zelle zurueck
        const __SKILLS = (when === __TIME.end) ? this.skillsEnd : this.skills;
// cell: Tabellenzelle mit der ZAT-Nummer im Text
        let result = 0;
// return ZAT-Nummer im Text
function getZATNrFromCell(cell) {
    const __TEXT = cell.textContent.split(' ');
    let ZATNr = 0;


        for (let skill of __SKILLS) {
    for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
             result += skill;
        if (__TEXT[i - 1] === "ZAT") {
             if (__TEXT[i] !== "ist") {
                ZATNr = parseInt(__TEXT[i], 10);
            }
         }
         }
    }


        return result / __SKILLS.length;
    return ZATNr;
    };
}


    this.getOpti = function(pos, when = __TIME.now) {
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
        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) {
// ==================== Hauptprogramm ====================
            sumPriSkills += __SKILLS[idx];
        }
        for (let idx of __IDXSECSKILLS) {
            sumSecSkills += __SKILLS[idx];
        }


        return (5 * sumPriSkills + sumSecSkills) / 27;
// Verarbeitet Ansicht "Haupt" (Managerbuero) zur Ermittlung des aktuellen ZATs
     };
function procHaupt() {
     const __TEAMPARAMS = getTeamParamsFromCell(getRows(1)[0].cells[1]); // Link mit Team, Liga, Land...


     this.getMarketValue = function(pos, when = __TIME.now) {
     buildOptions(__OPTCONFIG, __OPTSET, {
        const __AGE = this.getAge(when);
                    'teamParams' : __TEAMPARAMS,
                    'hideMenu'  : true
                });


        if (this.mwFormel === __MWFORMEL.alt) {
    const __NEXTZAT = getZATNrFromCell(getRows(0)[2].cells[0]);  // "Der naechste ZAT ist ZAT xx und ..."
            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
    const __CURRZAT = __NEXTZAT - 1;
        } else { // MW-Formel ab Saison 10...
            const __MW5TF = 1.00; // Zwischen 0.97 und 1.03


            return Math.round(Math.pow(1 + this.getSkill(when)/100, 5.65) * Math.pow(1 + this.getOpti(pos, when)/100, 8.1) * Math.pow(1 + (100 - __AGE)/49, 10) * __MW5TF);
    if (__CURRZAT >= 0) {
        }
        console.log("Aktueller ZAT: " + __CURRZAT);
    };
}


// Funktionen fuer die HTML-Seite *******************************************************
        setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
 
// Liest eine Zahl aus der Spalte einer Zeile der Tabelle aus (z.B. Alter, Geburtsdatum)
// cells: Die Zellen einer Zeile
// colIdxInt: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Zahl (-1 fuer "keine Zahl", undefined fuer "nicht gefunden")
function getIntFromHTML(cells, colIdxInt) {
    const __CELL = cells[colIdxInt];
    const __TEXT = getValue(__CELL, { }).textContent;
 
    if (__TEXT !== undefined) {
        try {
            const __VALUE = parseInt(__TEXT, 10);
 
            if (! isNaN(__VALUE)) {
                return __VALUE;
            }
        } catch (ex) { }
 
        return -1;
     }
     }
    return undefined;
}
}


// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus
// Verarbeitet Ansicht "Teamuebersicht"
// cells: Die Zellen einer Zeile
function procTeamuebersicht() {
// colIdxInt: Spaltenindex der gesuchten Werte
     const __ROWOFFSETUPPER = 1;     // Header-Zeile
// return Spalteneintrag als Dezimalzahl (undefined fuer "keine Zahl" oder "nicht gefunden")
     const __ROWOFFSETLOWER = 1;    // Ziehen-Button
function getFloatFromHTML(cells, colIdxFloat) {
     const __CELL = cells[colIdxFloat];
     const __TEXT = getValue(__CELL, { }).textContent;
 
     if (__TEXT !== undefined) {
        try {
            return parseFloat(__TEXT);
        } catch (ex) { }
    }


     return undefined;
     const __COLUMNINDEX = {
}
        'Age'  : 0,
        'Geb'  : 1,
        'Flg'  : 2,
        'Land'  : 3,
        'U'    : 4,
        'Skill' : 5,
        'Tal'  : 6,
        'Akt'  : 7,
        'Auf'  : 8,
        'Zus'  : 9
    };


// Liest einen String aus der Spalte einer Zeile der Tabelle aus
    const __BIRTHDAYS = [];
// cells: Die Zellen einer Zeile
     const __TCLASSES = [];
// colIdxStr: Spaltenindex der gesuchten Werte
     const __PROGRESSES = [];
// return Spalteneintrag als String ("" fuer "nicht gefunden")
function getStringFromHTML(cells, colIdxStr) {
     const __CELL = cells[colIdxStr];
     const __TEXT = getValue(__CELL, { }).textContent;


     return getValue(__TEXT.toString(), "");
     if (getRows(1) === undefined) {
}
        console.log("Diese Seite ist ohne Team nicht verf\xFCgbar!");
 
    } else if (getTable(1).length < 3) {
// Liest die Talentklasse ("wenig", "normal", "hoch") aus der Spalte einer Zeile der Tabelle aus
        console.log("Ziehen-Seite");
// cells: Die Zellen einer Zeile
    } else {
// colIdxStr: Spaltenindex der gesuchten Werte
        buildOptions(__OPTCONFIG, __OPTSET, {
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
                        'menuAnchor' : getTable(0, "div"),
function getTalentFromHTML(cells, colIdxTal) {
                        'showForm' : {
     const __TEXT = getStringFromHTML(cells, colIdxTal);
                                        'sepStyle'    : true,
                                        'sepColor'    : true,
                                        'sepWidth'    : true,
                                        'aktuellerZat' : true,
                                        'team'        : true,
                                        'reset'        : true,
                                        'showForm'     : true
                                      },
                        'formWidth' : 1
                    });


    return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
         const __ROWS = getRows(1);
}
 
// Liest die Einzelskills aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdx: Liste von Spaltenindices der gesuchten Werte mit den Eintraegen
// 'Einz' (erste Spalte) und 'Zus' (Spalte hinter dem letzten Eintrag)
// return Skills als Array von Zahlen
function getSkillsFromHTML(cells, colIdx) {
    const __RESULT = [];
 
    for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
        __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
    }
 
    return __RESULT;
}
 
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
// return Angabe, der Spieler Torwart oder Feldspieler ist
function isGoalieFromHTML(cells, colIdxClass) {
    return (cells[colIdxClass].className === "TOR");
}
 
// Hilfsfunktionen **********************************************************************
 
// Sortiert das Positionsfeld per BubbleSort
function sortPositionArray(array) {
    const __TEMP = [];
    let transposed = true;
    // TOR soll immer die letzte Position im Feld sein, deshalb - 1
    let length = array.length - 1;
 
    while (transposed && (length > 1)) {
        transposed = false;
        for (let i = 0; i < length - 1; i++) {
            // Vergleich Opti-Werte:
            if (array[i][1] < array[i + 1][1]) {
                // vertauschen
                __TEMP[0] = array[i][0];
                __TEMP[1] = array[i][1];
                array[i][0] = array[i + 1][0];
                array[i][1] = array[i + 1][1];
                array[i + 1][0] = __TEMP[0];
                array[i + 1][1] = __TEMP[1];
                transposed = true;
            }
        }
        length--;
    }
}
 
// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
function getNumberString(numberString) {
    if (numberString.lastIndexOf(".") !== -1) {
        // Zahl enthaelt Dezimalpunkt
        const __VORKOMMA = numberString.substring(0, numberString.lastIndexOf("."));
        const __NACHKOMMA = numberString.substring(numberString.lastIndexOf(".") + 1, numberString.length);
 
        return getNumberString(__VORKOMMA) + "," + __NACHKOMMA;
    } else {
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
        const __TEMP = reverseString(numberString);
        let result = "";
 
        for (let i = 0; i < __TEMP.length; i++) {
            if ((i > 0) && (i % 3 === 0)) {
                result += ".";
            }
            result += __TEMP.substr(i, 1);
        }
 
        return reverseString(result);
    }
}
 
// Dreht den uebergebenen String um
function reverseString(string) {
    let result = "";
 
    for (let i = string.length - 1; i >= 0; i--) {
        result += string.substr(i, 1);
    }
 
    return result;
}
 
// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert
// Die Indizes gehen von 0 (SCH) bis 16 (EIN)
function isTrainableSkill(idx) {
    const __TRAINABLESKILLS = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15];
    const __IDX = parseInt(idx, 10);
    let result = false;
 
    for (let idxTrainable of __TRAINABLESKILLS) {
        if (__IDX === idxTrainable) {
            result = true;
            break;
        }
    }
 
    return result;
}
 
// Gibt die Indizes der Primaerskills zurueck
function getIdxPriSkills(pos) {
    switch (pos) {
        case "TOR" : return new Array(2, 3, 4, 5);
        case "ABW" : return new Array(2, 3, 4, 15);
        case "DMI" : return new Array(1, 4, 9, 11);
        case "MIT" : return new Array(1, 3, 9, 11);
        case "OMI" : return new Array(1, 5, 9, 11);
        case "STU" : return new Array(0, 2, 3, 5);
        default : return [];
    }
}
 
// Gibt die Indizes der Sekundaerskills zurueck
function getIdxSecSkills(pos) {
    switch (pos) {
        case "TOR" : return new Array(0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
        case "ABW" : return new Array(0, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16);
        case "DMI" : return new Array(0, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
        case "MIT" : return new Array(0, 2, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16);
        case "OMI" : return new Array(0, 2, 3, 4, 6, 7, 8, 10, 12, 13, 14, 15, 16);
        case "STU" : return new Array(1, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
        default : return [];
    }
}
 
// Gibt die zur Position gehoerige Farbe zurueck
function getColor(pos) {
    switch (pos) {
        case "TOR" : return "#FFFF00";
        case "ABW" : return "#00FF00";
        case "DMI" : return "#3366FF";
        case "MIT" : return "#66FFFF";
        case "OMI" : return "#FF66FF";
        case "STU" : return "#FF0000";
        case "LEI" : return "#FFFFFF";
        default : return "";
    }
}
 
// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
 
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
 
const __GAMETYPES = {    // "Blind FSS gesucht!"
        'unbekannt'  : -1,
        "reserviert" :  0,
        "Frei"      :  0,
        "spielfrei"  :  0,
        "Friendly"  :  1,
        "Liga"      :  2,
        "LP"        :  3,
        "OSEQ"      :  4,
        "OSE"        :  5,
         "OSCQ"      :  6,
        "OSC"        :  7
    };
 
const __LIGANRN = {
        'unbekannt'  :  0,
        '1. Liga'    :  1,
        '2. Liga A'  :  2,
        '2. Liga B'  :  3,
        '3. Liga A'  :  4,
        '3. Liga B'  :  5,
        '3. Liga C'  :  6,
        '3. Liga D'  :  7
    };
 
const __LANDNRN = {
        'unbekannt'              :  0,
        'Albanien'              :  45,
        'Andorra'                :  95,
        'Armenien'              :  83,
        'Aserbaidschan'          : 104,
        'Belgien'                :  12,
        'Bosnien-Herzegowina'    :  66,
        'Bulgarien'              :  42,
        'D\xE4nemark'            :  8,
        'Deutschland'            :  6,
        'England'                :  1,
        'Estland'                :  57,
        'Far\xF6er'              :  68,
        'Finnland'              :  40,
        'Frankreich'            :  32,
        'Georgien'              :  49,
        'Griechenland'          :  30,
        'Irland'                :  5,
        'Island'                :  29,
        'Israel'                :  23,
        'Italien'                :  10,
        'Kasachstan'            : 105,
        'Kroatien'              :  24,
        'Lettland'              :  97,
        'Liechtenstein'          :  92,
        'Litauen'                :  72,
        'Luxemburg'              :  93,
        'Malta'                  :  69,
        'Mazedonien'            :  86,
        'Moldawien'              :  87,
        'Niederlande'            :  11,
        'Nordirland'            :  4,
        'Norwegen'              :  9,
        '\xD6sterreich'          :  14,
        'Polen'                  :  25,
        'Portugal'              :  17,
        'Rum\xE4nien'            :  28,
        'Russland'              :  19,
        'San Marino'            :  98,
        'Schottland'            :  2,
        'Schweden'              :  27,
        'Schweiz'                :  37,
        'Serbien und Montenegro' :  41,
        'Slowakei'              :  70,
        'Slowenien'              :  21,
        'Spanien'                :  13,
        'Tschechien'            :  18,
        'T\xFCrkei'              :  39,
        'Ukraine'                :  20,
        'Ungarn'                :  26,
        'Wales'                  :  3,
        'Weissrussland'          :  71,
        'Zypern'                :  38
    };
 
// ==================== Abschnitt fuer Daten des Spielplans ====================
 
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// gameType: Name des Wettbewerbs eines Spiels
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
function getGameTypeID(gameType) {
    return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);
}
 
// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
// land: Name des Landes
// return OS2-ID des Landes, 0 fuer ungueltig
function getLandNr(land) {
    return getValue(__LANDNRN[land], __LANDNRN.unbekannt);
}
 
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
// land: Name der Liga
// return OS2-ID der Liga, 0 fuer ungueltig
function getLigaNr(liga) {
    return getValue(__LIGANRN[liga], __LIGANRN.unbekannt);
}
 
// ==================== Abschnitt fuer sonstige Parameter ====================
 
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<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) {
        for (let i = __ROWOFFSETUPPER; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
        console.log("Aktueller ZAT: " + __CURRZAT);
            __BIRTHDAYS[i - __ROWOFFSETUPPER] = getGebFromHTML(__ROWS[i].cells, __COLUMNINDEX.Geb);
 
             __TCLASSES[i - __ROWOFFSETUPPER] = getTalentFromHTML(__ROWS[i].cells, __COLUMNINDEX.Tal);
        // Neuen aktuellen ZAT speichern...
             __PROGRESSES[i - __ROWOFFSETUPPER] = getAufwertFromHTML(__ROWS[i].cells, __COLUMNINDEX.Auf);
        setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
 
        if (__CURRZAT !== __DATAZAT) {
            console.log(__DATAZAT + " => " + __CURRZAT);
 
             // ... und ZAT-bezogene Daten als veraltet markieren
            deleteOptions(__OPTSET, __DATAOPTS, true, true);
 
             // Neuen Daten-ZAT speichern...
            setOpt(__OPTSET.datenZat, __CURRZAT, false);
         }
         }
    }
}
// Verarbeitet Ansicht "Teamuebersicht"
function procTeamuebersicht() {
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
    const __ROWOFFSETLOWER = 1;    // Ziehen-Button
    const __COLUMNINDEX = {
        'Age'  : 0,
        'Geb'  : 1,
        'Flg'  : 2,
        'Land'  : 3,
        'U'    : 4,
        'Skill' : 5,
        'Tal'  : 6,
        'Akt'  : 7,
        'Auf'  : 8,
        'Zus'  : 9
    };


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


         // Format der Trennlinie zwischen den Monaten...
         // Format der Trennlinie zwischen den Monaten...
Zeile 2.903: Zeile 2.316:
         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.326:
         __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)