Bearbeiten von „OS2.jugend

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

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

Aktuelle Version Dein Text
Zeile 1: Zeile 1:
[[Kategorie:Greasemonkey]]
[[Kategorie:Greasemonkey]]
[[Kategorie:Greasemonkey WE]]
{| style="background-color:white; font-size:11px; float: right; margin:3px 3px 3px 10px; border:1px solid #999; border-color: #9C1818; border-collapse:collapse;" width=500 cellpadding=3 cellspacing=0
{| style="background-color:white; font-size:11px; float: right; margin:3px 3px 3px 10px; border:1px solid #999; border-color: #9C1818; border-collapse:collapse;" width=500 cellpadding=3 cellspacing=0
| colspan="2" style="padding:0.3em; background-color:#9C1818; font-size: 18px; color:#FFFFFF" align=center| '''OS2.jugend'''
| colspan="2" style="padding:0.3em; background-color:#9C1818; font-size: 18px; color:#FFFFFF" align=center| '''OS2.jugend'''
Zeile 8: Zeile 7:
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Version'''
| '''Version'''
| '''0.73 (WebExtensions)'''
| '''0.52'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Autor'''
| '''Autor'''
Zeile 27: Zeile 26:
| '''ju.php?page=2'''
| '''ju.php?page=2'''
| '''Jugendteam-Spielereinzelwerte'''
| '''Jugendteam-Spielereinzelwerte'''
|-
| '''ju.php?page=3'''
| '''Jugendteam-Opt. Skill'''
|-
| '''ju.php?page=4'''
| '''Jugendteam-Optionen'''
|}
|}
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Funktionalität'''
| '''Funktionalität'''
| '''Trennstriche zwischen den Jahrgängen'''<br> '''Aktueller Skill, Opti und MW'''<br> '''Prognose von Opti und MW für Ende Jahrgang 18'''<br> '''Optionen und Menu'''<br> '''Neue Marktwertformel'''<br> '''Automatische Ermittlung des ZATs'''<br> '''Hidden-Optionen und Datenspeicher'''<br> '''Geburtstage und dezimales Alter'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Zusatzspalten Talent/Quote/Aufw./Geb./Alter'''<br> '''Zusatzspalten Quote/Alter/Pos in der Übersicht'''<br> '''Zusatzspalten Alter ersetzen/Aufwertungen kurz+TOR'''<br> '''Zusatzspalten fix/tr./%H/%N/Prios jetzt und Ende'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis'''<br> '''Qualitätsbalken'''<br> '''Markierung der Primärskills bei Einzelwerten und Aufwertungen'''<br> '''Beachtung von Jugendförderung und Doppelpositionen'''<br> '''Warnung vor Ende 18 in letzter Periode und mehr am letzten ZAT'''<br> '''Reguläre Ausdrücke im @include'''<br> '''Neues Design und Seite "Opt. Skill"'''<br> '''Gruppierung nach Jahrgängen U13 bis U18 (per Option)'''<br> '''Warnung vor Ende 18 auch im Managerbüro (per Option)'''<br> '''Warnung vor Sperre des Ziehens im Falle eines Aufstiegs'''<br> '''Neuer Jahrgang U19'''
| '''Trennstriche zwischen den Jahrgängen'''<br> '''Aktueller Skill, Opti und MW'''<br> '''Prognose von Opti und MW für Ende Jahrgang 18'''<br> '''Optionen und Menu'''<br> '''Neue Marktwertformel'''<br> '''Automatische Ermittlung des ZATs'''<br> '''Hidden-Optionen und Datenspeicher'''<br> '''Geburtstage und dezimales Alter'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Zusatzspalten Talent/Quote/Aufw./Geb./Alter'''<br> '''Zusatzspalten Quote/Alter/Pos in der Übersicht'''<br> '''Zusatzspalten Alter ersetzen/Aufwertungen kurz+TOR'''<br> '''Zusatzspalten fix/tr./%H/%N/Prios jetzt und Ende'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis'''<br> '''Qualitätsbalken'''<br> '''Markierung der Primärskills bei Einzelwerten und Aufwertungen'''<br> '''Beachtung von Jugendförderung und Doppelpositionen'''<br> '''Warnung vor Ende 18 in letzer Periode und mehr am letzten ZAT'''<br> '''Reguläre Ausdrücke im @include'''


|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
Zeile 42: Zeile 35:
| '''{{REVISIONDAY}}.{{REVISIONMONTH}}.{{REVISIONYEAR}}'''
| '''{{REVISIONDAY}}.{{REVISIONMONTH}}.{{REVISIONYEAR}}'''
|}
|}
== [https://github.com/Eselce/OS2.scripts/blob/master/OS2.jugend.user.js Quellcode] [https://eselce.github.io/OS2.scripts/OS2.jugend.user.js INSTALLATION] ==
<pre>
<pre>
// ==UserScript==
// ==UserScript==
// @name        OS2.jugend
// @name        OS2.jugend
// @namespace    http://os.ongapo.com/
// @namespace    http://os.ongapo.com/
// @version      0.73
// @version      0.52
// @copyright    2013+
// @copyright    2013+
// @author      Sven Loges (SLC) / Andreas Eckes (Strindheim BK)
// @author      Andreas Eckes (Strindheim BK) / Sven Loges (SLC)
// @description  Jugendteam-Script fuer Online Soccer 2.0
// @description  Jugendteam-Script fuer Online Soccer 2.0
// @include      /^https?://(www\.)?(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/haupt\.php(\?changetosecond=\w+(&\S+)*)?$/
// @include      /^https?://(www\.|)(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/haupt\.php(\?changetosecond=\w+|)$/
// @include      /^https?://(www\.)?(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/ju\.php(\?page=\d+(&\S+)*)?$/
// @include      /^https?://(www\.|)(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/ju\.php(\?page=\d+|)$/
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.deleteValue
// @grant        GM.registerMenuCommand
// @grant        GM.info
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @grant        GM_getValue
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_setValue
Zeile 67: Zeile 52:
// ==/UserScript==
// ==/UserScript==


// ECMAScript 6:
// ECMAScript 6: Erlaubt 'const', 'let', ...
/* jshint esnext: true */
/* jshint esnext: true */
/* jshint moz: true */
/* jshint moz: true */
Zeile 114: Zeile 99:
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
const __OPTCONFIG = {
const __OPTCONFIG = {
     'ersetzeSkills' : {   // Auswahl fuer prognostizierte Einzelskills mit Ende 18 statt der aktuellen (true = Ende 18, false = aktuell)
     'zeigeBalken' : {     // Spaltenauswahl fuer den Guetebalken des Talents (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "substSkills",
                   'Name'      : "showRatioBar",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Einzelwerte Ende",
                  'Hotkey'    : 'E',
                  'AltLabel'  : "Einzelwerte aktuell",
                  'AltHotkey' : 'k',
                  'FormLabel' : "Prognose Einzelwerte"
              },
    'zeigeJahrgang' : {  // Auswahl, ob ueber jedem Jahrgang die Ueberschriften gezeigt werden sollen oder alles in einem Block (true = Jahrgaenge, false = ein Block)
                   'Name'      : "showGroupTitle",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Jahrgangs\xFCberschriften",
                   'Label'    : "Balken ein",
                   'Hotkey'    : 'J',
                   'Hotkey'    : 'B',
                   'AltLabel'  : "Nur Trennlinie benutzen",
                   'AltLabel'  : "Balken aus",
                   'AltHotkey' : 'j',
                   'AltHotkey' : 'B',
                   'FormLabel' : "Jahrg\xE4nge gruppieren"
                   'FormLabel' : "Balken"
               },
               },
     'zeigeUxx' : {        // Auswahl, ob in der Ueberschrift ueber jedem Jahrgang zusaetzlich zur Saison noch der Jahrgang in der Form 'Uxx' angegeben wird
     'zeigeTal' : {        // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showUxx",
                   'Name'      : "showTclasses",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Jahrg\xE4nge anzeigen",
                   'Label'    : "Talent ein",
                   'Hotkey'    : 'U',
                   'Hotkey'    : 'T',
                   'AltLabel'  : "Nur Saisons anzeigen",
                   'AltLabel'  : "Talent aus",
                   'AltHotkey' : 'u',
                   'AltHotkey' : 'T',
                   'FormLabel' : "Jahrg\xE4nge U13 bis U19"
                   'FormLabel' : "Talent"
               },
               },
     'zeigeWarnung' : {   // Auswahl, ob eine Warnung erscheint, wenn Talente gezogen werden sollten
     'zeigeQuote' : {     // Spaltenauswahl fuer Aufwertungsschnitt (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showWarning",
                   'Name'      : "showRatio",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ziehwarnung ein",
                   'Label'    : "Quote ein",
                   'Hotkey'    : 'Z',
                   'Hotkey'    : 'T',
                   'AltLabel'  : "Ziehwarnung aus",
                   'AltLabel'  : "Quote aus",
                   'AltHotkey' : 'Z',
                   'AltHotkey' : 'T',
                   'FormLabel' : "Ziehwarnung"
                   'FormLabel' : "Quote"
               },
               },
     'zeigeWarnungMonat' : { // Auswahl, ob eine Warnung erscheint, wenn zum naechsten Abrechnungs-ZAT Talente gezogen werden sollten
     'zeigeGeb' : {       // Spaltenauswahl fuer Geburtstage (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showWarningMonth",
                   'Name'      : "showBirthday",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ziehwarnung Monat ein",
                   'Label'    : "Geburtstag ein",
                   'Hotkey'    : 'Z',
                   'Hotkey'    : 'G',
                   'AltLabel'  : "Ziehwarnung Monat aus",
                   'AltLabel'  : "Geburtstag aus",
                   'AltHotkey' : 'Z',
                   'AltHotkey' : 'G',
                   'FormLabel' : "Ziehwarnung Monat"
                   'FormLabel' : "Geburtstag"
               },
               },
     'zeigeWarnungHome' : { // Auswahl, ob eine Meldung im Managerbuero erscheint, wenn Talente gezogen werden sollten
     'zeigeAlter' : {     // Spaltenauswahl fuer dezimales Alter (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showWarningHome",
                   'Name'      : "showAge",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ziehwarnung B\xFCro ein",
                   'Label'    : "Alter ein",
                   'Hotkey'    : 'z',
                   'Hotkey'    : 'A',
                   'AltLabel'  : "Ziehwarnung B\xFCro aus",
                   'AltLabel'  : "Alter aus",
                   'AltHotkey' : 'z',
                   'AltHotkey' : 'A',
                   'FormLabel' : "Ziehwarnung B\xFCro"
                   'FormLabel' : "Alter"
               },
               },
     'zeigeWarnungDialog' : { // Auswahl, ob die Meldung im Managerbuero als Dialog erscheinen soll
     'ersetzeAlter' : {   // Spaltenauswahl fuer dezimales Alter statt ganzzahligen Alters (true = Dezimalbruch, false = ganzzahlig)
                   'Name'      : "showWarningDialog",
                   'Name'      : "substAge",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : false,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ziehwarnung B\xFCro als Dialog",
                   'Label'    : "Alter dezimal",
                   'Hotkey'    : 'z',
                   'Hotkey'    : 'd',
                   'AltLabel'  : "Ziehwarnung B\xFCro als Textmeldung",
                   'AltLabel'  : "Alter ganzzahlig",
                   'AltHotkey' : 'z',
                   'AltHotkey' : 'g',
                   'FormLabel' : "Ziehwarnung B\xFCro Dialog"
                   'FormLabel' : "Alter ersetzen"
               },
               },
     'zeigeWarnungAufstieg' : { // Auswahl, ob eine Warnung in der Uebersicht erscheint, wenn Talente nach Aufstieg nicht mehr gezogen werden koennen
     'zeigeAufw' : {       // Spaltenauswahl fuer Aufwertungen (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showWarningAufstieg",
                   'Name'      : "showProgresses",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ziehwarnung Aufstieg ein",
                   'Label'    : "Aufwertungen ein",
                   'Hotkey'    : 'ä',
                   'Hotkey'    : 'W',
                   'AltLabel'  : "Ziehwarnung Aufstieg aus",
                   'AltLabel'  : "Aufwertungen aus",
                   'AltHotkey' : 'ä',
                   'AltHotkey' : 'W',
                   'FormLabel' : "Ziehwarnung Aufstieg"
                   'FormLabel' : "Aufwertungen"
               },
               },
     'zeigeWarnungLegende' : { // Auswahl, ob eine extra Meldung in Teamuebersicht erscheint, die dort als Legende dient
     'shortAufw' : {       // Abgekuerzte Aufwertungsanzeige
                   'Name'      : "showWarningLegende",
                   'Name'      : "shortProgresses",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ziehwarnung Legende ein",
                   'Label'    : "Aufwertungen kurz",
                   'Hotkey'    : 'L',
                   'Hotkey'    : 'A',
                   'AltLabel'  : "Ziehwarnung Legende aus",
                   'AltLabel'  : "Aufwertungen lang",
                   'AltHotkey' : 'L',
                   'AltHotkey' : 'A',
                   'FormLabel' : "Ziehwarnung Legende"
                   'FormLabel' : "Kurze Aufwertungen"
               },
               },
     'zeigeBalken' : {     // Spaltenauswahl fuer den Qualitaetsbalken des Talents (true = anzeigen, false = nicht anzeigen)
     'zeigeFixSkills' : { // Spaltenauswahl fuer die Summe der Fixskills (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showRatioBar",
                   'Name'      : "showFixSkills",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Balken Qualit\xE4t ein",
                   'Label'    : "Fixskills ein",
                   'Hotkey'    : 'B',
                   'Hotkey'    : 'F',
                   'AltLabel'  : "Balken Qualit\xE4t aus",
                   'AltLabel'  : "Fixskills aus",
                   'AltHotkey' : 'B',
                   'AltHotkey' : 'F',
                   'FormLabel' : "Balken Qualit\xE4t"
                   'FormLabel' : "Fixskills"
               },
               },
     'absBalken' : {       // Spaltenauswahl fuer den Guetebalken des Talents absolut statt nach Foerderung (true = absolut, false = relativ nach Foerderung)
     'zeigeTrainiert' : { // Spaltenauswahl fuer die aktuellen trainierten Skills (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "absBar",
                   'Name'      : "showTrainiert",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Balken absolut",
                   'Label'    : "Trainiert ein",
                  'Hotkey'    : 'u',
                  'AltLabel'  : "Balken nach F\xF6rderung",
                  'AltHotkey' : 'u',
                  'FormLabel' : "Balken 100%"
              },
    'zeigeId' : {        // Spaltenauswahl fuer Identifizierungsmerkmale der Jugendspieler (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showFingerprints",
                  'Type'      : __OPTTYPES.SW,
                  'Hidden'    : true,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Identifikation ein",
                   'Hotkey'    : 'T',
                   'Hotkey'    : 'T',
                   'AltLabel'  : "Identifikation aus",
                   'AltLabel'  : "Trainiert aus",
                   'AltHotkey' : 'T',
                   'AltHotkey' : 'T',
                   'FormLabel' : "Identifikation"
                   'FormLabel' : "Trainiert"
               },
               },
     'zeigeTal' : {       // Spaltenauswahl fuer Talente (true = anzeigen, false = nicht anzeigen)
     'zeigeAnteilPri' : { // Spaltenauswahl fuer den prozentualen Anteil der aktuellen Hauptskills (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showTclasses",
                   'Name'      : "showAnteilPri",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Talent ein",
                   'Label'    : "Anteil Hauptskills ein",
                   'Hotkey'    : 'T',
                   'Hotkey'    : 'H',
                   'AltLabel'  : "Talent aus",
                   'AltLabel'  : "Anteil Hauptskills aus",
                   'AltHotkey' : 'T',
                   'AltHotkey' : 'H',
                   'FormLabel' : "Talent"
                   'FormLabel' : "Anteil Hauptskills"
               },
               },
     'zeigeQuote' : {     // Spaltenauswahl fuer Aufwertungsschnitt (true = anzeigen, false = nicht anzeigen)
     'zeigeAnteilSec' : { // Spaltenauswahl fuer den prozentualen Anteil der aktuellen Nebenskills (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showRatio",
                   'Name'      : "showAnteilSec",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Quote ein",
                   'Label'    : "Anteil Nebenskills ein",
                   'Hotkey'    : 'T',
                   'Hotkey'    : 'N',
                   'AltLabel'  : "Quote aus",
                   'AltLabel'  : "Anteil Nebenskills aus",
                   'AltHotkey' : 'T',
                   'AltHotkey' : 'N',
                   'FormLabel' : "Quote"
                   'FormLabel' : "Anteil Nebenskills"
               },
               },
     'zeigeGeb' : {       // Spaltenauswahl fuer Geburtstage (true = anzeigen, false = nicht anzeigen)
     'zeigePrios' : {     // Spaltenauswahl fuer den Schnitt der Hauptskills (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showBirthday",
                   'Name'      : "showPrios",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : false,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Geburtstag ein",
                   'Label'    : "Prios ein",
                   'Hotkey'    : 'G',
                   'Hotkey'    : 'r',
                   'AltLabel'  : "Geburtstag aus",
                   'AltLabel'  : "Prios aus",
                   'AltHotkey' : 'G',
                   'AltHotkey' : 'r',
                   'FormLabel' : "Geburtstag"
                   'FormLabel' : "Prios"
               },
               },
     'zeigeAlter' : {      // Spaltenauswahl fuer dezimales Alter (true = anzeigen, false = nicht anzeigen)
     'zeigeSkill' : {      // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showAge",
                   'Name'      : "showSkill",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Alter ein",
                   'Label'    : "Skill ein",
                   'Hotkey'    : 'A',
                   'Hotkey'    : 'S',
                   'AltLabel'  : "Alter aus",
                   'AltLabel'  : "Skill aus",
                   'AltHotkey' : 'A',
                   'AltHotkey' : 'S',
                   'FormLabel' : "Alter"
                   'FormLabel' : "Skill"
               },
               },
     'ersetzeAlter' : {   // Spaltenauswahl fuer dezimales Alter statt ganzzahligen Alters (true = Dezimalbruch, false = ganzzahlig)
     'zeigePosition' : {   // Position anzeigen
                   'Name'      : "substAge",
                   'Name'      : "showPos",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Alter dezimal",
                   'Label'    : "Position ein",
                   'Hotkey'    : 'd',
                   'Hotkey'    : 'P',
                   'AltLabel'  : "Alter ganzzahlig",
                   'AltLabel'  : "Position aus",
                   'AltHotkey' : 'g',
                   'AltHotkey' : 'P',
                   'FormLabel' : "Alter ersetzen"
                   'FormLabel' : "Position"
               },
               },
     'zeigeAufw' : {       // Spaltenauswahl fuer Aufwertungen (true = anzeigen, false = nicht anzeigen)
     'anzahlOpti' : {     // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
                   'Name'      : "showProgresses",
                          // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                   'Type'      : __OPTTYPES.SW,
                   'Name'      : "anzOpti",
                   'Default'  : true,
                   'Type'      : __OPTTYPES.MC,
                  'ValType'  : "Number",
                  'SelValue'  : false,
                  'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Default'  : 1,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Aufwertungen ein",
                   'Label'    : "Opti: beste $",
                   'Hotkey'    : 'W',
                   'Hotkey'    : 'O',
                   'AltLabel' : "Aufwertungen aus",
                   'FormLabel' : "Opti:|beste $"
                  'AltHotkey' : 'W',
                  'FormLabel' : "Aufwertungen"
               },
               },
     'shortAufw' : {       // Abgekuerzte Aufwertungsanzeige
     'anzahlMW' : {       // Gibt die Anzahl der MW-Spalten an / 1: nur hoechsten MW, 2: die beiden hoechsten, ..., 6: Alle inklusive TOR
                   'Name'      : "shortProgresses",
                          // Bei Torhuetern wird immer nur der TOR-MW angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                   'Type'      : __OPTTYPES.SW,
                   'Name'      : "anzMW",
                   'Default'  : true,
                   'Type'      : __OPTTYPES.MC,
                  'ValType'  : "Number",
                  'SelValue'  : false,
                  'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Default'  : 1,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Aufwertungen kurz",
                   'Label'    : "MW: beste $",
                   'Hotkey'    : 'A',
                   'Hotkey'    : 'M',
                   'AltLabel' : "Aufwertungen lang",
                   'FormLabel' : "MW:|beste $"
                  'AltHotkey' : 'A',
                  'FormLabel' : "Kurze Aufwertungen"
               },
               },
     'zeigeZatDone' : {   // Spaltenauswahl fuer die Anzahl der bisherigen Trainings-ZATs (true = anzeigen, false = nicht anzeigen)
     'ersetzeSkills' : {   // Auswahl fuer prognostizierte Einzelskills mit Ende 18 statt der aktuellen (true = Ende 18, false = aktuell)
                   'Name'      : "showFixZatDone",
                   'Name'      : "substSkills",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : false,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Trainings-ZATs ein",
                   'Label'    : "Skills Ende",
                   'Hotkey'    : 'Z',
                   'Hotkey'    : 'k',
                   'AltLabel'  : "Trainings-ZATs aus",
                   'AltLabel'  : "Skills aktuell",
                   'AltHotkey' : 'Z',
                   'AltHotkey' : 'k',
                   'FormLabel' : "Trainings-ZATs"
                   'FormLabel' : "Einzelwerte ersetzen \u03A9"
               },
               },
     'zeigeZatLeft' : {   // Spaltenauswahl fuer die Anzahl der Rest-ZATs bis Ende 18 (true = anzeigen, false = nicht anzeigen)
     'zeigeTrainiertEnde' : { // Spaltenauswahl fuer die trainierten Skills mit Ende 18 (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showFixZatLeft",
                   'Name'      : "showTrainiertEnde",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Rest-ZATs ein",
                  'Hotkey'    : 'R',
                  'AltLabel'  : "Rest-ZATs aus",
                  'AltHotkey' : 'R',
                  'FormLabel' : "Rest-ZATs"
              },
    'zeigeFixSkills' : {  // Spaltenauswahl fuer die Summe der Fixskills (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showFixSkills",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Fixskills ein",
                   'Label'    : "Trainiert Ende ein",
                   'Hotkey'    : 'F',
                   'Hotkey'    : 'n',
                   'AltLabel'  : "Fixskills aus",
                   'AltLabel'  : "Trainiert Ende aus",
                   'AltHotkey' : 'F',
                   'AltHotkey' : 'n',
                   'FormLabel' : "Fixskills"
                   'FormLabel' : "Trainiert \u03A9"
               },
               },
     'zeigeTrainiert' : {  // Spaltenauswahl fuer die aktuellen trainierten Skills (true = anzeigen, false = nicht anzeigen)
     'zeigeAnteilPriEnde' : {  // Spaltenauswahl fuer den prozentualen Anteil der Hauptskills mit Ende 18 (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showTrainiert",
                   'Name'      : "showAnteilPriEnde",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Trainiert ein",
                   'Label'    : "Anteil Hauptskills Ende ein",
                   'Hotkey'    : 'T',
                   'Hotkey'    : 'u',
                   'AltLabel'  : "Trainiert aus",
                   'AltLabel'  : "Anteil Hauptskills Ende aus",
                   'AltHotkey' : 'T',
                   'AltHotkey' : 'u',
                   'FormLabel' : "Trainiert"
                   'FormLabel' : "Anteil Hauptskills \u03A9"
               },
               },
     'zeigeAnteilPri' : {  // Spaltenauswahl fuer den prozentualen Anteil der aktuellen Hauptskills (true = anzeigen, false = nicht anzeigen)
     'zeigeAnteilSecEnde' : {  // Spaltenauswahl fuer den prozentualen Anteil der Nebenskills mit Ende 18 (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showAnteilPri",
                   'Name'      : "showAnteilSecEnde",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : false,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Anteil Hauptskills ein",
                   'Label'    : "Anteil Nebenskills Ende ein",
                   'Hotkey'    : 'H',
                   'Hotkey'    : 'b',
                   'AltLabel'  : "Anteil Hauptskills aus",
                   'AltLabel'  : "Anteil Nebenskills Ende aus",
                   'AltHotkey' : 'H',
                   'AltHotkey' : 'b',
                   'FormLabel' : "Anteil Hauptskills"
                   'FormLabel' : "Anteil Nebenskills \u03A9"
               },
               },
     'zeigeAnteilSec' : {  // Spaltenauswahl fuer den prozentualen Anteil der aktuellen Nebenskills (true = anzeigen, false = nicht anzeigen)
     'zeigePriosEnde' : {  // Spaltenauswahl fuer den Schnitt der Hauptskills (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showAnteilSec",
                   'Name'      : "showPriosEnde",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : false,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Anteil Nebenskills ein",
                  'Hotkey'    : 'N',
                  'AltLabel'  : "Anteil Nebenskills aus",
                  'AltHotkey' : 'N',
                  'FormLabel' : "Anteil Nebenskills"
              },
    'zeigePrios' : {      // Spaltenauswahl fuer den Schnitt der Hauptskills (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showPrios",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Prios ein",
                   'Label'    : "Prios Ende ein",
                   'Hotkey'    : 'r',
                   'Hotkey'    : 'o',
                   'AltLabel'  : "Prios aus",
                   'AltLabel'  : "Prios Ende aus",
                   'AltHotkey' : 'r',
                   'AltHotkey' : 'o',
                   'FormLabel' : "Prios"
                   'FormLabel' : "Prios \u03A9"
               },
               },
     'zeigeSkill' : {     // Spaltenauswahl fuer die aktuellen Werte (true = anzeigen, false = nicht anzeigen)
     'zeigeSkillEnde' : { // Spaltenauswahl fuer die Werte mit Ende 18 (true = anzeigen, false = nicht anzeigen)
                   'Name'      : "showSkill",
                   'Name'      : "showSkillEnde",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
                   'Default'  : true,
                   'Default'  : true,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Skill ein",
                   'Label'    : "Skill Ende ein",
                   'Hotkey'    : 'S',
                   'Hotkey'    : 'i',
                   'AltLabel'  : "Skill aus",
                   'AltLabel'  : "Skill Ende aus",
                   'AltHotkey' : 'S',
                   'AltHotkey' : 'i',
                   'FormLabel' : "Skill"
                   'FormLabel' : "Skill \u03A9"
               },
               },
     'zeigePosition' : {   // Position anzeigen
     'anzahlOptiEnde' : { // Spaltenauswahl fuer die Werte mit Ende 18:
                  'Name'      : "showPos",
                          // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
                  'Type'      : __OPTTYPES.SW,
                           // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Default'  : false,
                   'Name'      : "anzOptiEnde",
                  'Action'    : __OPTACTION.NXT,
                   'Type'      : __OPTTYPES.MC,
                  'Label'    : "Position ein",
                   'ValType'  : "Number",
                  'Hotkey'    : 'P',
                   'SelValue'  : false,
                  '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',
                   'SelValue'  : false,
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Default'  : 1,
                   'Default'  : 1,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Opti: beste $",
                   'Label'    : "Opti Ende: beste $",
                   'Hotkey'    : 'O',
                   'Hotkey'    : 't',
                   'FormLabel' : "Opti:|beste $"
                   'FormLabel' : "Opti \u03A9:|beste $"
               },
               },
     'anzahlMW' : {       // Gibt die Anzahl der MW-Spalten an / 1: nur hoechsten MW, 2: die beiden hoechsten, ..., 6: Alle inklusive TOR
     'anzahlMWEnde' : {   // Spaltenauswahl fuer die Werte mit Ende 18:
                          // Gibt die Anzahl der MW-Spalten an / 1: nur hoechsten MW, 2: die beiden hoechsten, ..., 6: Alle inklusive TOR
                           // Bei Torhuetern wird immer nur der TOR-MW angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                           // Bei Torhuetern wird immer nur der TOR-MW angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                   'Name'      : "anzMW",
                   'Name'      : "anzMWEnde",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                   'SelValue'  : false,
                   'SelValue'  : false,
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Default'  : 1,
                   'Default'  : 1,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "MW: beste $",
                   'Label'    : "MW Ende: beste $",
                   'Hotkey'    : 'M',
                   'Hotkey'    : 'W',
                   'FormLabel' : "MW:|beste $"
                   'FormLabel' : "MW \u03A9:|beste $"
               },
               },
     'zeigeTrainiertEnde' : {  // Spaltenauswahl fuer die trainierten Skills mit Ende 18 (true = anzeigen, false = nicht anzeigen)
     'kennzeichenEnde' : {  // Markierung fuer Ende 18
                   'Name'      : "showTrainiertEnde",
                   'Name'      : "charEnde",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.MC,
                   'Default'  : true,
                   'ValType: "String",
                  'FreeValue' : true,
                  'MinChoice' : 0,
                  'Choice'    : [ " \u03A9", " 18" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Trainiert Ende ein",
                   'Label'    : "Ende: $",
                   'Hotkey'    : 'n',
                   'Hotkey'    : 'E',
                   'AltLabel' : "Trainiert Ende aus",
                   'FormLabel' : "Ende 18:|$"
                  'AltHotkey' : 'n',
                  'FormLabel' : "Trainiert \u03A9"
               },
               },
     'zeigeAnteilPriEnde' : { // Spaltenauswahl fuer den prozentualen Anteil der Hauptskills mit Ende 18 (true = anzeigen, false = nicht anzeigen)
     'sepStyle' : {       // Stil der Trennlinie
                   'Name'      : "showAnteilPriEnde",
                   'Name'      : "sepStyle",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.MC,
                   'Default'  : false,
                   'ValType'  : "String",
                  'Choice'    : [ "solid", "hidden", "dotted", "dashed", "double", "groove", "ridge",
                                  "inset", "outset", "none" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Anteil Hauptskills Ende ein",
                   'Label'    : "Stil: $",
                   'Hotkey'    : 'u',
                   'Hotkey'    : 'l',
                   'AltLabel' : "Anteil Hauptskills Ende aus",
                   'FormLabel' : "Stil:|$"
                  'AltHotkey' : 'u',
                  'FormLabel' : "Anteil Hauptskills \u03A9"
               },
               },
     'zeigeAnteilSecEnde' : { // Spaltenauswahl fuer den prozentualen Anteil der Nebenskills mit Ende 18 (true = anzeigen, false = nicht anzeigen)
     'sepColor' : {       // Farbe der Trennlinie
                   'Name'      : "showAnteilSecEnde",
                   'Name'      : "sepColor",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.MC,
                   'Default'  : false,
                   'ValType'  : "String",
                  'FreeValue' : true,
                  'Choice'    : [ "white", "yellow", "black", "blue", "cyan", "gold", "grey", "green",
                                  "lime", "magenta", "maroon", "navy", "olive", "orange", "purple",
                                  "red", "teal", "transparent" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Anteil Nebenskills Ende ein",
                   'Label'    : "Farbe: $",
                   'Hotkey'    : 'b',
                   'Hotkey'    : 'F',
                   'AltLabel' : "Anteil Nebenskills Ende aus",
                   'FormLabel' : "Farbe:|$"
                  'AltHotkey' : 'b',
                  'FormLabel' : "Anteil Nebenskills \u03A9"
               },
               },
     'zeigePriosEnde' : { // Spaltenauswahl fuer den Schnitt der Hauptskills (true = anzeigen, false = nicht anzeigen)
     'sepWidth' : {       // Dicke der Trennlinie
                   'Name'      : "showPriosEnde",
                   'Name'      : "sepWidth",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.MC,
                   'Default'  : true,
                   'ValType: "String",
                  'FreeValue' : true,
                  'Choice'    : [ "thin", "medium", "thick" ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Prios Ende ein",
                   'Label'    : "Dicke: $",
                   'Hotkey'    : 'o',
                   'Hotkey'    : 'D',
                   'AltLabel' : "Prios Ende aus",
                   'FormLabel' : "Dicke:|$"
                  'AltHotkey' : 'o',
                  'FormLabel' : "Prios \u03A9"
               },
               },
     'zeigeSkillEnde' : { // Spaltenauswahl fuer die Werte mit Ende 18 (true = anzeigen, false = nicht anzeigen)
     'saison' : {         // Laufende Saison
                   'Name'      : "showSkillEnde",
                   'Name'      : "saison",
                  'Type'      : __OPTTYPES.SW,
                  'Default'  : true,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Skill Ende ein",
                  'Hotkey'    : 'i',
                  'AltLabel'  : "Skill Ende aus",
                  'AltHotkey' : 'i',
                  'FormLabel' : "Skill \u03A9"
              },
    'anzahlOptiEnde' : {  // Spaltenauswahl fuer die Werte mit Ende 18:
                          // Gibt die Anzahl der Opti-Spalten an / 1: nur bester Opti, 2: die beiden besten, ..., 6: Alle inklusive TOR
                          // Bei Torhuetern wird immer nur der TOR-Opti angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                  'Name'      : "anzOptiEnde",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                  'FreeValue' : true,
                   'SelValue'  : false,
                   'SelValue'  : false,
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Choice'    : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
                   'Default'  : 1,
                   'Default'  : 10,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Opti Ende: beste $",
                   'Label'    : "Saison: $",
                   'Hotkey'    : 't',
                   'Hotkey'    : 'a',
                   'FormLabel' : "Opti \u03A9:|beste $"
                   'FormLabel' : "Saison:|$"
               },
               },
     'anzahlMWEnde' : {    // Spaltenauswahl fuer die Werte mit Ende 18:
     'aktuellerZat' : {    // Laufender ZAT
                          // Gibt die Anzahl der MW-Spalten an / 1: nur hoechsten MW, 2: die beiden hoechsten, ..., 6: Alle inklusive TOR
                   'Name'      : "currZAT",
                          // Bei Torhuetern wird immer nur der TOR-MW angezeigt / Werte < 1 oder > 6 schalten die Anzeige aus
                   'Name'      : "anzMWEnde",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'Number',
                   'ValType'  : "Number",
                  'Permanent' : true,
                   'SelValue'  : false,
                   'SelValue'  : false,
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6 ],
                   'Choice'    : [ 0, 1, 2, 3, 4, 5, 6,  7,  8,  9, 10, 11,
                  'Default'  : 1,
                                  12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
                                  24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
                                  36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
                                  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
                                  60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71 ],
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "MW Ende: beste $",
                   'Label'    : "ZAT: $",
                   'Hotkey'    : 'W',
                   'Hotkey'    : 'Z',
                   'FormLabel' : "MW \u03A9:|beste $"
                   '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:"
               },
               },
     'kennzeichenEnde' : { // Markierung fuer Ende 18
     'foerderung' : {     // Jugendfoerderung
                   'Name'      : "charEnde",
                   'Name'      : "donation",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.MC,
                   'ValType'  : 'String',
                   'ValType'  : "Number",
                   'FreeValue' : true,
                   'Permanent' : true,
                   'MinChoice' : 0,
                   'SelValue' : false,
                   'Choice'    : [ " \u03A9", " 18" ],
                   'Choice'    : [ 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000,
                                  5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000 ],
                  'Default'  : 10000,
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Ende: $",
                   'Label'    : "F\xF6rderung: $",
                  'Hotkey'    : 'E',
                  'FormLabel' : "Ende 18:|$"
              },
    'sepStyle' : {        // Stil der Trennlinie
                  'Name'      : "sepStyle",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'String',
                  'Choice'    : [ 'solid', 'hidden', 'dotted', 'dashed', 'double', 'groove', 'ridge',
                                  'inset', 'outset', 'none' ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Stil: $",
                  'Hotkey'    : 'l',
                  'FormLabel' : "Stil:|$"
              },
    'sepColor' : {        // Farbe der Trennlinie
                  'Name'      : "sepColor",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'String',
                  'FreeValue' : true,
                  'Choice'    : [ 'white', 'yellow', 'black', 'blue', 'cyan', 'gold', 'grey', 'green',
                                  'lime', 'magenta', 'maroon', 'navy', 'olive', 'orange', 'purple',
                                  'red', 'teal', 'transparent' ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Farbe: $",
                   'Hotkey'    : 'F',
                   'Hotkey'    : 'F',
                   'FormLabel' : "Farbe:|$"
                   'FormLabel' : "F\xF6rderung:|$"
               },
               },
     'sepWidth' : {       // Dicke der Trennlinie
     'team' : {           // Datenspeicher fuer Daten des Erst- bzw. Zweitteams
                   'Name'      : "sepWidth",
                   'Name'      : "team",
                   'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.SD,
                   'ValType'   : 'String',
                   'Hidden'   : false,
                   'FreeValue' : true,
                  'Serial'   : true,
                   'Choice'   : [ 'thin', 'medium', 'thick' ],
                   'Permanent' : true,
                   'Action'    : __OPTACTION.NXT,
                   'Default'  : undefined,  // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 }
                   'Label'     : "Dicke: $",
                   'Submit'    : undefined,
                   'Hotkey'   : 'D',
                   'Cols'     : 36,
                   'FormLabel' : "Dicke:|$"
                  'Rows'      : 6,
                   'Replace'   : null,
                  'Space'     : 1,
                   'Label'     : "Verein:"
               },
               },
     'saison' : {         // Laufende Saison
     'birthdays' : {       // Datenspeicher fuer Geburtstage der Jugendspieler
                  'Name'      : "saison",
                   'Name'      : "birthdays",
                  'Type'      : __OPTTYPES.MC,
                   'Type'      : __OPTTYPES.SD,
                  'ValType'  : 'Number',
                  'FreeValue' : true,
                  'SelValue'  : false,
                  'Choice'    : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ],
                  'Default'  : 16,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Saison: $",
                  'Hotkey'    : 'a',
                  'FormLabel' : "Saison:|$"
              },
    'aktuellerZat' : {    // Laufender ZAT
                  'Name'      : "currZAT",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'Number',
                  'Permanent' : true,
                  'SelValue'  : false,
                  'Choice'    : [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
                                  12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
                                  24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
                                  36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
                                  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
                                  60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
                                  72 ],
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "ZAT: $",
                  'Hotkey'    : 'Z',
                  'FormLabel' : "ZAT:|$"
              },
    'datenZat' : {        // Stand der Daten zum Team und ZAT
                   'Name'      : "dataZAT",
                   'Type'      : __OPTTYPES.SD,
                  'ValType'  : 'Number',
                   'Hidden'    : true,
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : undefined,
                   'Default'  : [],
                  'Action'    : __OPTACTION.SET,
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 1,
                   'Cols'      : 36,
                   'Rows'      : 1,
                   'Rows'      : 2,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Daten-ZAT:"
                   'Label'    : "Geburtstage:"
               },
               },
     'oldDatenZat' : {     // Stand der Daten zum Team und ZAT
     'tClasses' : {       // Datenspeicher fuer Talente der Jugendspieler (-1=wenig, 0=normal, +1=hoch)
                   'Name'      : "oldDataZAT",
                   'Name'      : "tClasses",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                  'ValType'  : 'Number',
                   'Hidden'    : true,
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : undefined,
                   'Default'  : [],
                  'Action'    : __OPTACTION.SET,
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 1,
                   'Cols'      : 36,
                   'Rows'      : 1,
                   'Rows'      : 2,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Vorheriger Daten-ZAT:"
                   'Label'    : "Talente:"
               },
               },
     'foerderung' : {      // Jugendfoerderung
     'progresses' : {      // Datenspeicher fuer Aufwertungen der Jugendspieler (als Strings)
                  'Name'      : "donation",
                   'Name'      : "progresses",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'Number',
                  'Permanent' : true,
                  'SelValue'  : false,
                  'Choice'    : [ 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000,
                                  5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000 ],
                  'Default'  : 10000,
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "F\xF6rderung: $",
                  'Hotkey'    : 'F',
                  'FormLabel' : "F\xF6rderung:|$"
              },
    'team' : {            // Datenspeicher fuer Daten des Erst- bzw. Zweitteams
                   'Name'      : "team",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : false,
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                  'AutoReset' : true,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : undefined, // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 }
                   'Default'  : [],
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 6,
                   'Rows'      : 7,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 1,
                   'Space'    : 0,
                   'Label'    : "Verein:"
                   'Label'    : "Aufwertungen:"
               },
               },
     'fingerprints' : {   // Datenspeicher fuer Identifizierungsmerkmale der Jugendspieler
     'zatAges' : {         // Datenspeicher fuer (gebrochene) Alter der Jugendspieler
                   'Name'      : "fingerprints",
                   'Name'      : "zatAges",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : true,
                   'Hidden'    : true,
Zeile 682: Zeile 561:
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 6,
                   'Rows'      : 2,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Identifikation:"
                   'Label'    : "ZAT-Alter:"
               },
               },
     'birthdays' : {      // Datenspeicher fuer Geburtstage der Jugendspieler
     'trainiert' : {      // Datenspeicher fuer Trainingsquoten der Jugendspieler
                   'Name'      : "birthdays",
                   'Name'      : "numProgresses",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : true,
                   'Hidden'    : true,
Zeile 700: Zeile 579:
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Geburtstage:"
                   'Label'    : "Trainiert:"
               },
               },
     'tClasses' : {       // Datenspeicher fuer Talente der Jugendspieler (-1=wenig, 0=normal, +1=hoch)
     'positions' : {       // Datenspeicher fuer optimale Positionen der Jugendspieler
                   'Name'      : "tClasses",
                   'Name'      : "positions",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : true,
                   'Hidden'    : true,
Zeile 712: Zeile 591:
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 2,
                   'Rows'      : 3,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Talente:"
                   'Label'    : "Positionen:"
               },
               },
     'progresses' : {     // Datenspeicher fuer Aufwertungen der Jugendspieler (als Strings)
     'skills' : {         // Datenspeicher fuer aktuelle Einzelskills der Jugendspieler
                   'Name'      : "progresses",
                   'Name'      : "skills",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : true,
                   'Hidden'    : true,
Zeile 727: Zeile 606:
                   'Submit'    : undefined,
                   'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 7,
                   'Rows'      : 20,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 0,
                   'Label'    : "Aufwertungen:"
                   'Label'    : "Skills:"
               },
               },
     'ziehAnz' : {         // Datenspeicher fuer Anzahl zu ziehender Jugendspieler bis zur naechsten Abrechnung
     'hauptLS' : {       // Option 'ligaSize' aus Modul 'OS2.haupt', hier als 'hauptLS'
                   'Name'      : "drawCounts",
                  'Shared'    : { /*'namespace' : "http://os.ongapo.com/",*/ 'module' : "OS2.haupt", 'item' : 'ligaSize' },
                  'Hidden'    : true,
                  'FormLabel' : "Liga:|$er (haupt)"
              },
    'hauptZat' : {        // Option 'datenZat' aus Modul 'OS2.haupt', hier als 'hauptZat'
                  'Shared'    : { /*'namespace' : "http://os.ongapo.com/",*/ 'module' : "OS2.haupt", 'item' : 'datenZat' },
                  'Hidden'    : true,
                  'Cols'      : 36,
                   'Rows'      : 6,
                  'Label'    : "ZAT:"
              },
    'haupt' : {          // Alle Optionen des Moduls 'OS2.haupt'
                  'Shared'    : { 'module' : "OS2.haupt", 'item' : '$' },
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : true,
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                  'AutoReset' : false,
                   'Cols'      : 36,
                  'Permanent' : true,
                   'Rows'      : 6,
                  'Default'  : [],
                  'Submit'    : undefined,
                   'Cols'      : 25,
                   'Rows'      : 1,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 4,
                   'Label'    : "Zu ziehen:"
                   'Label'    : "Haupt:"
              },
              },
     'ziehAnzAufstieg' : { // Datenspeicher fuer Anzahl zu ziehender Jugendspieler bis zur naechsten Abrechnung im Falle eines Aufstiegs
     'data' : {           // Optionen aller Module
                   'Name'      : "drawCountsAufstieg",
                   'Shared'    : { 'module' : '$' },
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'Number',
                  'Hidden'    : true,
                  'AutoReset' : false,
                  'Permanent' : true,
                  'FreeValue' : true,
                  'SelValue'  : false,
                  'Choice'    : [ 0, 1, 2, 3, 4, 5 ],
                  'Default'   : 0,
                  'Action'   : __OPTACTION.NXT,
                  'Label'    : "Zu ziehen bei Aufstieg: $",
                  'Hotkey'    : 'z',
                  'FormLabel' : "Zu ziehen bei Aufstieg:|$"
              },
    'zatAges' : {        // Datenspeicher fuer (gebrochene) Alter der Jugendspieler
                  'Name'      : "zatAges",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'    : true,
                   'Hidden'    : true,
                   'Serial'    : true,
                   'Serial'    : true,
                  'AutoReset' : true,
                  'Permanent' : true,
                  'Default'  : [],
                  'Submit'    : undefined,
                   'Cols'      : 36,
                   'Cols'      : 36,
                   'Rows'      : 2,
                   'Rows'      : 6,
                   'Replace'  : null,
                   'Replace'  : null,
                   'Space'    : 0,
                   'Space'    : 4,
                   'Label'    : "ZAT-Alter:"
                   'Label'    : "Data:"
              },
    'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
                  'Name'      : "reset",
                  'Type'      : __OPTTYPES.SI,
                  'Action'    : __OPTACTION.RST,
                  'Label'    : "Standard-Optionen",
                  'Hotkey'    : 'r',
                  'FormLabel' : ""
              },
    'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                  'Name'      : "storage",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : "String",
                  'Choice'    : Object.keys(__OPTMEM),
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Speicher: $",
                  'Hotkey'    : 'c',
                  'FormLabel' : "Speicher:|$"
               },
               },
     'trainiert' : {       // Datenspeicher fuer Trainingsquoten der Jugendspieler
     'oldStorage' : {     // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                   'Name'      : "numProgresses",
                   'Name'      : "oldStorage",
                   'Type'      : __OPTTYPES.SD,
                   'Type'      : __OPTTYPES.SD,
                   'Hidden'   : true,
                   'PreInit'   : true,
                  'Serial'    : true,
                   'AutoReset' : true,
                   'AutoReset' : true,
                  'Hidden'    : true
              },
    'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'Name'      : "showForm",
                  'Type'      : __OPTTYPES.SW,
                  'FormType'  : __OPTTYPES.SI,
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : [],
                   'Default'  : false,
                   'Submit'    : undefined,
                   'Action'    : __OPTACTION.NXT,
                  'Cols'      : 36,
                   'Label'    : "Optionen anzeigen",
                  'Rows'      : 2,
                   'Hotkey'    : 'O',
                  'Replace'  : null,
                   'AltLabel' : "Optionen verbergen",
                  'Space'    : 0,
                   'AltHotkey' : 'O',
                   'Label'    : "Trainiert:"
                   'FormLabel' : ""
              },
               }
    'positions' : {      // Datenspeicher fuer optimale Positionen der Jugendspieler
};
                   'Name'      : "positions",
 
                  'Type'      : __OPTTYPES.SD,
// ==================== Invarianter Abschnitt fuer Optionen ====================
                  'Hidden'    : true,
 
                  'Serial'   : true,
// Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist)
                   'AutoReset' : true,
if (Function.prototype.name === undefined) {
                   'Permanent' : true,
    Object.defineProperty(Function.prototype, 'name', {
                  'Default'   : [],
            get : function() {
                   'Submit'    : undefined,
                      return /function ([^(\s]*)/.exec(this.toString())[1];
                  'Cols'      : 36,
                  }
                  'Rows'      : 3,
        });
                  'Replace'  : null,
}
                  'Space'     : 0,
 
                  'Label'    : "Positionen:"
// Ein Satz von Logfunktionen, die je nach Loglevel zur Verfuegung stehen. Aufruf: __LOG[level](text)
               },
const __LOG = {
    'skills' : {          // Datenspeicher fuer aktuelle Einzelskills der Jugendspieler
                  'logFun'   : [
                  'Name'      : "skills",
                                  console.error,  // [0] Alert
                  'Type'      : __OPTTYPES.SD,
                                  console.error, // [1] Error
                  'Hidden'    : true,
                                  console.log,    // [2] Log: Release
                  'Serial'    : true,
                                  console.log,    // [3] Log: Info
                  'AutoReset' : true,
                                  console.log,   // [4] Log: Debug
                  'Permanent' : true,
                                  console.log,    // [5] Log: Verbose
                  'Default'  : [],
                                  console.log    // [6] Log: Very verbose
                  'Submit'    : undefined,
                              ],
                  'Cols'      : 36,
                  'init'    : function(win, logLevel = 1) {
                  'Rows'      : 20,
                                  for (level = 0; level < this.logFun.length; level++) {
                  'Replace'  : null,
                                      this[level] = ((level > logLevel) ? function() { } : this.logFun[level]);
                  'Space'    : 0,
                                  }
                  'Label'     : "Skills:"
                              },
              },
                  'changed' : function(oldVal, newVal) {
    'hauptLS' : {        // Option 'ligaSize' aus Modul 'OS2.haupt', hier als 'hauptLS'
                                  const __OLDVAL = safeStringify(oldVal);
                  'Shared'    : { /*'namespace' : "http://os.ongapo.com/",*/ 'module' : "OS2.haupt", 'item' : 'ligaSize' },
                                  const __NEWVAL = safeStringify(newVal);
                  'Hidden'   : true,
 
                  'FormLabel' : "Liga:|$er (haupt)"
                                  return ((__OLDVAL !== __NEWVAL) ? __OLDVAL + " => " : "") + __NEWVAL;
              },
                              }
    'hauptZat' : {        // Option 'datenZat' aus Modul 'OS2.haupt', hier als 'hauptZat'
              };
                  'Shared'   : { /*'namespace' : "http://os.ongapo.com/",*/ 'module' : "OS2.haupt", 'item' : 'datenZat' },
 
                  'Hidden'    : true,
__LOG.init(window, __LOGLEVEL);
                  'Cols'      : 36,
 
                  'Rows'      : 6,
// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster mit der Meldung
                  'Label'    : "ZAT:"
// label: Eine Ueberschrift
              },
// message: Der Meldungs-Text
    'haupt' : {           // Alle Optionen des Moduls 'OS2.haupt'
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben
                  'Shared'    : { 'module' : "OS2.haupt", 'item' : '$' },
function showAlert(label, message, data = undefined) {
                  'Type'      : __OPTTYPES.SD,
    __LOG[1](label + ": " + message);
                  'Hidden'    : true,
 
                  'Serial'    : true,
    if (data !== undefined) {
                  'Cols'     : 36,
        __LOG[2](data);
                  'Rows'      : 6,
     }
                  'Replace'  : null,
 
                  'Space'    : 4,
     alert(label + "\n\n" + message);
                  'Label'    : "Haupt:"
}
              },
 
    'data' : {            // Optionen aller Module
// ==================== Abschnitt fuer Klasse Class ====================
                  'Shared'    : { 'module' : '$' },
 
                  'Type'      : __OPTTYPES.SD,
function Class(className, baseClass, initFun) {
                  'Hidden'    : true,
    'use strict';
                  'Serial'    : true,
 
                  'Cols'      : 36,
     try {
                  'Rows'      : 6,
        const __BASE = ((baseClass !== undefined) ? baseClass : Object);
                  'Replace'  : null,
        const __BASEPROTO = (__BASE ? __BASE.prototype : undefined);
                  'Space'     : 4,
         const __BASECLASS = (__BASEPROTO ? __BASEPROTO.__class : undefined);
                  'Label'     : "Data:"
 
              },
        this.className = (className || '?');
    'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
        this.baseClass = __BASECLASS;
                  'FormPrio'  : undefined,
        Object.setConst(this, 'baseProto', __BASEPROTO, false);
                  'Name'      : "reset",
 
                  'Type'     : __OPTTYPES.SI,
        if (! initFun) {
                  'Action'    : __OPTACTION.RST,
            const __BASEINIT = (__BASECLASS || { }).init;
                  'Label'     : "Standard-Optionen",
 
                  'Hotkey'    : 'r',
            if (__BASEINIT) {
                  'FormLabel' : ""
                initFun = function() {
              },
                              // Basisklassen-Init aufrufen...
    'storage' : {         // Browserspeicher fuer die Klicks auf Optionen
                              return __BASEINIT.call(this, arguments);
                  'FormPrio'  : undefined,
                          };
                  'Name'     : "storage",
            } else {
                  'Type'      : __OPTTYPES.MC,
                initFun = function() {
                  'ValType'  : 'String',
                              // Basisklassen-Init fehlt (und Basisklasse ist nicht Object)...
                  'Choice'    : Object.keys(__OPTMEM),
                              return false;
                  'Action'   : __OPTACTION.NXT,
                          };
                  'Label'    : "Speicher: $",
            }
                  'Hotkey'    : 'c',
        }
                  'FormLabel' : "Speicher:|$"
              },
    'oldStorage' : {     // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                  'Name'      : "oldStorage",
                  'Type'      : __OPTTYPES.SD,
                  'PreInit'  : true,
                  'AutoReset' : true,
                  'Hidden'    : true
              },
    'showForm' : {       // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'FormPrio'  : 1,
                  'Name'      : "showForm",
                  'Type'      : __OPTTYPES.SW,
                  'FormType'  : __OPTTYPES.SI,
                  'Permanent' : true,
                  'Default'  : false,
                  'Title'    : "$V Optionen",
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Optionen anzeigen",
                  'Hotkey'    : 'O',
                  'AltTitle'  : "$V schlie\xDFen",
                  'AltLabel'  : "Optionen verbergen",
                  'AltHotkey' : 'O',
                  'FormLabel' : ""
              }
};


// ==================== Invarianter Abschnitt fuer Optionen ====================
        console.assert((__BASE === null) || ((typeof __BASE) === 'function'), "No function:", __BASE);
        console.assert((typeof initFun) === 'function', "No function:", initFun);


// Ein Satz von Logfunktionen, die je nach Loglevel zur Verfuegung stehen. Aufruf: __LOG[level](text)
        this.init = initFun;
const __LOG = {
    } catch (ex) {
                  'logFun'   : [
        showAlert('[' + ex.lineNumber + "] Error in Class " + className, ex.message, ex);
                                    console.error,  // [0] Alert
    }
                                    console.error,  // [1] Error
}
                                    console.log,   // [2] Log: Release
                                    console.log,    // [3] Log: Info
                                    console.log,    // [4] Log: Debug
                                    console.log,    // [5] Log: Verbose
                                    console.log,    // [6] Log: Very verbose
                                    console.warn    // [7] Log: Testing
                                ],
                  'init'      : function(win, logLevel = 1) {
                                    for (let level = 0; level < this.logFun.length; level++) {
                                        this[level] = ((level > logLevel) ? function() { } : this.logFun[level]);
                                    }
                                },
                  'stringify' : safeStringify,      // JSON.stringify
                  'changed'  : function(oldVal, newVal) {
                                    const __OLDVAL = this.stringify(oldVal);
                                    const __NEWVAL = this.stringify(newVal);


                                    return ((__OLDVAL !== __NEWVAL) ? __OLDVAL + " => " : "") + __NEWVAL;
Class.define = function(subClass, baseClass, members = undefined, initFun = undefined, createProto = true) {
                                }
        return (subClass.prototype = subClass.subclass(baseClass, members, initFun, createProto));
              };
    };


__LOG.init(window, __LOGLEVEL);
Object.setConst = function(obj, item, value, config) {
        return Object.defineProperty(obj, item, {
                        enumerable  : false,
                        configurable : (config || true),
                        writable    : false,
                        value        : value
                    });
    };


// Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist)
Object.setConst(Object.prototype, 'subclass', function(baseClass, members, initFun, createProto) {
if (Function.prototype.name === undefined) {
         'use strict';
    Object.defineProperty(Function.prototype, 'name', {
            get : function() {
                      return /function ([^(\s]*)/.exec(this.toString())[1];
                  }
         });
}


// Ergaenzung fuer Strings: Links oder rechts auffuellen nach Vorlage
        try {
// padStr: Vorlage, z.B. "00" fuer zweistellige Zahlen
            const __MEMBERS = (members || { });
// padLeft: true = rechtsbuendig, false = linksbuendig
            const __CREATEPROTO = ((createProto === undefined) ? true : createProto);
// clip: Abschneiden, falls zu lang
// return Rechts- oder linksbuendiger String, der so lang ist wie die Vorlage
String.prototype.pad = function(padStr, padLeft = true, clip = false) {
    const __LEN = ((clip || (padStr.length > this.length)) ? padStr.length : this.length);


    return (padLeft ? String(padStr + this).slice(- __LEN) : String(this + padStr).slice(0, __LEN));
            console.assert((typeof this) === 'function');
};
            console.assert((typeof __MEMBERS) === 'object');


// Ersetzt in einem String {0}, {1}, ... durch die entsprechenden Parameter
            const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
// arguments: Parameter, die fuer {0}, {1}, ... eingesetzt werden sollen
            const __PROTO = (__CREATEPROTO ? Object.create(__CLASS.baseProto) : this.prototype);
// return Resultierender String
String.prototype.format = function() {
    const __ARGS = arguments;
    return this.replace(/{(\d+)}/g, function(match, argIdx) {
                                        const __ARG = __ARGS[argIdx];
                                        return ((__ARG !== undefined) ? __ARG : match);
                                    });
};


// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster mit der Meldung
            for (let item in __MEMBERS) {
// label: Eine Ueberschrift
                if ((item !== '__name') && (item !== '__init')) {
// message: Der Meldungs-Text
                    Object.setConst(__PROTO, item, __MEMBERS[item]);
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben
                }
// return Liefert die Parameter zurueck
            }
function showAlert(label, message, data = undefined) {
    __LOG[0](label + ": " + message);


    if (data !== undefined) {
            Object.setConst(__PROTO, '__class', __CLASS, ! __CREATEPROTO);
        __LOG[2](data);
    }


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


     return arguments;
Class.define(Object, null, {
}
                    '__init'      : function() {
                                        // Oberstes Basisklassen-Init...
                                        return true;
                                    },
                    'getClass'     : function() {
                                        return this.__class;
                                    },
                    'getClassName' : function() {
                                        const __CLASS = this.getClass();


// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster
                                        return (__CLASS ? __CLASS.getName() : undefined);
// mit der Meldung zu einer Exception oder einer Fehlermeldung
                                    },
// label: Eine Ueberschrift
                    'setConst'    : function(item, value, config = undefined) {
// ex: Exception oder sonstiges Fehlerobjekt
                                        return Object.setConst(this, item, value, config);
// return Liefert die showAlert()-Parameter zurueck
                                    }
function showException(label, ex) {
                }, undefined, false);
    if (ex && ex.message) {  // Exception
        showAlert(label, ex.message, ex);
    } else {  // sonstiger Fehler
        showAlert(label, ex);
    }
}


// Standard-Callback-Funktion fuer onRejected, also abgefangener Fehler
Class.define(Function, Object);
// in einer Promise bei Exceptions oder Fehler bzw. Rejections
// error: Parameter von reject() im Promise-Objekt, der von Promise.catch() erhalten wurde
// return Liefert die showAlert()-Parameter zurueck
function defaultCatch(error) {
    try {
        const __LABEL = `[${error.lineNumber}] ${__DBMOD.Name}`;


        if (error && error.message) {  // Exception
Class.define(Class, Object, {
            return showException(__LABEL, error.message, error);
                    'getName'      : function() {
        } else {
                                        return this.className;
            return showException(__LABEL, error);
                                    }
        }
                } );
    } catch (ex) {
 
        return showException(`[${ex.lineNumber}] ${__DBMOD.Name}`, ex.message, ex);
// ==================== Ende Abschnitt fuer Klasse Class ====================
    }
}


// ==================== Abschnitt fuer Klasse Class ====================
// ==================== Abschnitt fuer Klasse Delims ====================


function Class(className, baseClass, initFun) {
// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von Pfaden
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
// back: (Optional) Name des relativen Vaterverzeichnisses
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function Delims(delim, back, root, home) {
     'use strict';
     'use strict';


     try {
     if ((typeof delim) === 'object') {
         const __BASE = ((baseClass !== undefined) ? baseClass : Object);
         // Erster Parameter ist Objekt mit den Properties...
         const __BASEPROTO = (__BASE ? __BASE.prototype : undefined);
        if (back === undefined) {
         const __BASECLASS = (__BASEPROTO ? __BASEPROTO.__class : undefined);
            back = delim.back;
        }
        if (root === undefined) {
            root = delim.root;
         }
        if (home === undefined) {
            home = delim.home;
         }
        delim = delim.delim;
    }


        this.className = (className || '?');
    this.setDelim(delim);
        this.baseClass = __BASECLASS;
    this.setBack(back);
        Object.setConst(this, 'baseProto', __BASEPROTO, false);
    this.setRoot(root);
    this.setHome(home);
}


        if (! initFun) {
Class.define(Delims, Object, {
            const __BASEINIT = (__BASECLASS || { }).init;
              'setDelim'      : function(delim = undefined) {
                                    this.delim = delim;
                                },
              'setBack'        : function(back = undefined) {
                                    this.back = back;
                                },
              'setRoot'        : function(root = undefined) {
                                    this.root = root;
                                },
              'setHome'        : function(home = undefined) {
                                    this.home = home;
                                }
          } );


            if (__BASEINIT) {
// ==================== Ende Abschnitt fuer Klasse Delims ====================
                initFun = function() {
                              // Basisklassen-Init aufrufen...
                              return __BASEINIT.call(this, arguments);
                          };
            } else {
                initFun = function() {
                              // Basisklassen-Init fehlt (und Basisklasse ist nicht Object)...
                              return false;
                          };
            }
        }


        console.assert((__BASE === null) || ((typeof __BASE) === 'function'), "No function:", __BASE);
// ==================== Abschnitt fuer Klasse UriDelims ====================
        console.assert((typeof initFun) === 'function', "Not a function:", initFun);


        this.init = initFun;
// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von URIs
    } catch (ex) {
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
        showAlert('[' + ex.lineNumber + "] Error in Class " + className, ex.message, ex);
// back: (Optional) Name des relativen Vaterverzeichnisses
    }
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
}
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
 
// scheme: (Optional) Trennzeichen fuer den Schema-/Protokollnamen vorne
Class.define = function(subClass, baseClass, members = undefined, initFun = undefined, createProto = true) {
// host: (Optional) Prefix fuer Hostnamen hinter dem Scheme-Trenner
        return (subClass.prototype = subClass.subclass(baseClass, members, initFun, createProto));
// port: (Optional) Trennzeichen vor der Portangabe, falls vorhanden
     };
// query: (Optional) Trennzeichen fuer die Query-Parameter hinter dem Pfad
// parSep: (Optional) Trennzeichen zwischen je zwei Parametern
// parAss: (Optional) Trennzwischen zwischen Key und Value
// node: (Optional) Trennzeichen fuer den Knotennamen hinten (Fragment, Kapitel)
function UriDelims(delim, back, root, home, scheme, host, port, query, qrySep, qryAss, node) {
     'use strict';


Object.setConst = function(obj, item, value, config) {
    if ((typeof delim) === 'object') {
         return Object.defineProperty(obj, item, {
        // Erster Parameter ist Objekt mit den Properties...
                        enumerable  : false,
        if (scheme === undefined) {
                        configurable : (config || true),
            scheme = delim.scheme;
                        writable    : false,
        }
                        value        : value
        if (host === undefined) {
                    });
            host = delim.host;
     };
         }
        if (port === undefined) {
            port = delim.port;
        }
        if (query === undefined) {
            query = delim.query;
        }
        if (qrySep === undefined) {
            qrySep = delim.qrySep;
        }
        if (qryAss === undefined) {
            qryAss = delim.qryAss;
        }
        if (node === undefined) {
            node = delim.node;
        }
     }


Object.setConst(Object.prototype, 'subclass', function(baseClass, members, initFun, createProto) {
    Delims.call(this, delim, back, root, home);
        'use strict';


        try {
    this.setScheme(scheme);
            const __MEMBERS = (members || { });
    this.setHost(host);
            const __CREATEPROTO = ((createProto === undefined) ? true : createProto);
    this.setPort(port);
    this.setQuery(query);
    this.setQrySep(qrySep);
    this.setQryAss(qryAss);
    this.setNode(node);
}


            console.assert((typeof this) === 'function', "Not a function:", this);
Class.define(UriDelims, Delims, {
            console.assert((typeof __MEMBERS) === 'object', "Not an object:", __MEMBERS);
              'setScheme'      : function(scheme = undefined) {
                                    this.scheme = scheme;
                                },
              'setHost'        : function(host = undefined) {
                                    this.host = host;
                                },
              'setPort'        : function(port = undefined) {
                                    this.port = port;
                                },
              'setQuery'       : function(query = undefined) {
                                    this.query = query;
                                },
              'setQrySep'      : function(qrySep = undefined) {
                                    this.qrySep = qrySep;
                                },
              'setQryAss'      : function(qryAss = undefined) {
                                    this.qryAss = qryAss;
                                },
              'setNode'       : function(node = undefined) {
                                    this.node = node;
                                }
          } );


            const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
// ==================== Ende Abschnitt fuer Klasse UriDelims ====================
            const __PROTO = (__CREATEPROTO ? Object.create(__CLASS.baseProto) : this.prototype);


            for (let item in __MEMBERS) {
// ==================== Abschnitt fuer Klasse Path ====================
                if ((item !== '__name') && (item !== '__init')) {
                    Object.setConst(__PROTO, item, __MEMBERS[item]);
                }
            }


            Object.setConst(__PROTO, '__class', __CLASS, ! __CREATEPROTO);
// Basisklasse fuer die Verwaltung eines Pfades
 
// homePath: Absoluter Startpfad als String
            return __PROTO;
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
        } catch (ex) {
// 'delim': Trennzeichen zwischen zwei Ebenen
            showAlert('[' + ex.lineNumber + "] Error in subclassing", ex.message, ex);
// 'back': Name des relativen Vaterverzeichnisses
        }
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
     }, false);
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function Path(homePath, delims) {
    'use strict';
 
    this.dirs = [];
    this.setDelims(delims);
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });
 
     this.home();
}


Class.define(Object, null, {
Class.define(Path, Object, {
                    '__init'       : function() {
                  'root'           : function() {
                                         // Oberstes Basisklassen-Init...
                                         this.dirs.splice(0, this.dirs.length);
                                         return true;
                                    },
                  'home'          : function() {
                                         this.dirs = this.homeDirs.slice();
                                     },
                                     },
                    'getClass'     : function() {
                  'up'             : function() {
                                         return this.__class;
                                         this.dirs.pop();
                                     },
                                     },
                    'getClassName' : function() {
                  'down'           : function(subDir) {
                                         const __CLASS = this.getClass();
                                         this.dirs.push(subDir);
 
                                        return (__CLASS ? __CLASS.getName() : undefined);
                                     },
                                     },
                    'setConst'     : function(item, value, config = undefined) {
                  'setDelims'      : function(delims = undefined) {
                                         return Object.setConst(this, item, value, config);
                                        this.setConst('delims', new Delims(delims));
                                     }
                                    },
                }, undefined, false);
                  'setDelim'      : function(delim = undefined) {
 
                                        this.delims.setDelim(delim || '/');
Class.define(Function, Object);
                                    },
                  'setBackDelim'  : function(backDelim = undefined) {
                                        this.delims.setBack(backDelim || "..");
                                    },
                  'setRootDelim'  : function(rootDelim = undefined) {
                                         this.delims.setRoot(rootDelim || "");
                                    },
                  'setHomeDelim'  : function(homeDelim = undefined) {
                                        this.delims.setHome(homeDelim || '~');
                                    },
                  'setSchemeDelim' : function(schemeDelim = undefined) {
                                        this.delims.setScheme(schemeDelim || ':');
                                    },
                  'setHostDelim'  : function(hostDelim = undefined) {
                                        this.delims.setHost(hostDelim || '//');
                                    },
                  'setPortDelim'  : function(portDelim = undefined) {
                                        this.delims.setHost(portDelim || ':');
                                     },
                  'setQueryDelim'  : function(queryDelim = undefined) {
                                        this.delims.setQuery(queryDelim || '?');
                                    },
                  'setParSepDelim' : function(parSepDelim = undefined) {
                                        this.delims.setParSep(parSepDelim || '&');
                                    },
                  'setParAssDelim' : function(parAssDelim = undefined) {
                                        this.delims.setParAss(parAssDelim || '=');
                                    },
                  'setNodeDelim'  : function(nodeDelim = undefined) {
                                        this.delims.setNode(nodeDelim || '#');
                                    },
                  'getLeaf'        : function(dirs = undefined) {
                                        const __DIRS = (dirs || this.dirs);


Class.define(Class, Object, {
                                        return ((__DIRS && __DIRS.length) ? __DIRS.slice(-1)[0] : "");
                    'getName'     : function() {
                                    },
                                         return this.className;
                  'getPath'        : function(dirs = undefined, delims = undefined) {
                                        const __DELIMS = new Delims(delims);
                                        const __DELIM = (__DELIMS.delim || this.delims.delim);
                                        const __ROOTDELIM = ((__DELIMS.root !== undefined) ? __DELIMS.root : this.delims.root);
                                        const __DIRS = (dirs || this.dirs);
 
                                        return __ROOTDELIM + __DELIM + __DIRS.join(__DELIM);
                                    },
                  'getDirs'       : function(path = undefined, delims = undefined) {
                                        const __DELIMS = new Delims(delims);
                                        const __DELIM = (__DELIMS.delim || this.delims.delim);
                                        const __ROOTDELIM = ((__DELIMS.root !== undefined) ? __DELIMS.root : this.delims.root);
                                        const __HOMEDELIM = ((__DELIMS.home !== undefined) ? __DELIMS.home : this.delims.home);
                                        const __DIRS = (path ? path.split(__DELIM) : []);
                                        const __FIRST = __DIRS[0];
 
                                        if (__FIRST && (__FIRST !== __ROOTDELIM) && (__FIRST !== __HOMEDELIM)) {
                                            showAlert("Kein absoluter Pfad", this.getPath(__DIRS), this);
                                        }
 
                                         return __DIRS.slice(1);
                                     }
                                     }
                 });
                 } );


// ==================== Ende Abschnitt fuer Klasse Class ====================
// ==================== Ende Abschnitt fuer Klasse Path ====================


// ==================== Abschnitt fuer Klasse Delims ====================
// ==================== Abschnitt fuer Klasse URI ====================


// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von Pfaden
// Basisklasse fuer die Verwaltung einer URI/URL
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
// homePath: Absoluter Startpfad als String
// back: (Optional) Name des relativen Vaterverzeichnisses
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'delim': Trennzeichen zwischen zwei Ebenen
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
// 'back': Name des relativen Vaterverzeichnisses
function Delims(delim, back, root, home) {
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function URI(homePath, delims) {
     'use strict';
     'use strict';


     if ((typeof delim) === 'object') {
     Path.call(this);
        // Erster Parameter ist Objekt mit den Properties...
 
        if (back === undefined) {
    const __HOSTPORT = this.getHostPort(homePath);
            back = delim.back;
        }
        if (root === undefined) {
            root = delim.root;
        }
        if (home === undefined) {
            home = delim.home;
        }
        delim = delim.delim;
    }


     this.setDelim(delim);
     this.scheme = this.getSchemePrefix(homePath);
     this.setBack(back);
     this.host = __HOSTPORT.host;
     this.setRoot(root);
    this.port = this.parseValue(__HOSTPORT.port);
     this.setHome(home);
     this.query = this.parseQuery(this.getQueryString(homePath));
}
     this.node = this.getNodeSuffix(homePath);


Class.define(Delims, Object, {
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });
              'setDelim'      : function(delim = undefined) {
                                    this.delim = delim;
                                },
              'setBack'        : function(back = undefined) {
                                    this.back = back;
                                },
              'setRoot'        : function(root = undefined) {
                                    this.root = root;
                                },
              'setHome'       : function(home = undefined) {
                                    this.home = home;
                                }
          });


// ==================== Ende Abschnitt fuer Klasse Delims ====================
    this.home();
}


// ==================== Abschnitt fuer Klasse UriDelims ====================
Class.define(URI, Path, {
 
              'setDelims'        : function() {
// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von URIs
                                        this.setConst('delims', new UriDelims('/', "..", "", '~', ':', "//", ':', '?', '&', '=', '#'));
// delim: Trennzeichen zwischen zwei Ebenen (oder Objekt/Delims mit entsprechenden Properties)
                                    },
// back: (Optional) Name des relativen Vaterverzeichnisses
              'setSchemeDelim'    : function(schemeDelim = undefined) {
// root: (Optional) Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
                                        this.delims.setScheme(schemeDelim || ':');
// home: (Optional) Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
                                    },
// scheme: (Optional) Trennzeichen fuer den Schema-/Protokollnamen vorne
              'setQueryDelim'    : function(queryDelim = undefined) {
// host: (Optional) Prefix fuer Hostnamen hinter dem Scheme-Trenner
                                        this.delims.setQuery(queryDelim || '?');
// port: (Optional) Trennzeichen vor der Portangabe, falls vorhanden
                                    },
// query: (Optional) Trennzeichen fuer die Query-Parameter hinter dem Pfad
              'setParSepDelim'    : function(parSepDelim = undefined) {
// parSep: (Optional) Trennzeichen zwischen je zwei Parametern
                                        this.delims.setParSep(parSepDelim || '&');
// parAss: (Optional) Trennzwischen zwischen Key und Value
                                    },
// node: (Optional) Trennzeichen fuer den Knotennamen hinten (Fragment, Kapitel)
              'setParAssDelim'    : function(parAssDelim = undefined) {
function UriDelims(delim, back, root, home, scheme, host, port, query, qrySep, qryAss, node) {
                                        this.delims.setParAss(parAssDelim || '=');
    'use strict';
                                    },
              'setNodeDelim'      : function(nodeDelim = undefined) {
                                        this.delims.setNode(nodeDelim || '#');
                                    },
              'getServerPath'    : function(path = undefined) {
                                        return this.stripHostPort(this.stripQueryString(this.stripNodeSuffix(this.stripSchemePrefix(path))));
                                    },
              'getHostPort'      : function(path = undefined) {
                                        const __HOSTDELIM = this.delims.host;
                                        const __PORTDELIM = this.delims.port;
                                        const __ROOTDELIM = this.delims.root + this.delims.delim;
                                        const __NOSCHEME = this.stripSchemePrefix(path);
                                        const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1);
                                        const __PATH = (~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME;
                                        const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
                                        const __HOSTPORT = (~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined;
                                        const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1);
                                        const __HOST = (~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT;
                                        const __PORT = (~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined;


    if ((typeof delim) === 'object') {
                                        return {
        // Erster Parameter ist Objekt mit den Properties...
                                                    'host' : __HOST,
        if (scheme === undefined) {
                                                    'port' : __PORT
            scheme = delim.scheme;
                                                };
        }
                                    },
        if (host === undefined) {
              'stripHostPort'    : function(path = undefined) {
            host = delim.host;
                                        const __HOSTDELIM = this.delims.host;
        }
                                        const __ROOTDELIM = this.delims.root + this.delims.delim;
        if (port === undefined) {
                                        const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1);
            port = delim.port;
                                        const __PATH = (~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path;
        }
                                        const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
        if (query === undefined) {
 
            query = delim.query;
                                        return (~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH;
        }
                                    },
        if (qrySep === undefined) {
              'getSchemePrefix'  : function(path = undefined) {
            qrySep = delim.qrySep;
                                        const __SCHEMEDELIM = this.delims.scheme;
        }
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);
        if (qryAss === undefined) {
            qryAss = delim.qryAss;
        }
        if (node === undefined) {
            node = delim.node;
        }
    }


    Delims.call(this, delim, back, root, home);
                                        return (~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined;
                                    },
              'stripSchemePrefix' : function(path = undefined) {
                                        const __SCHEMEDELIM = this.delims.scheme;
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


    this.setScheme(scheme);
                                        return (~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path;
    this.setHost(host);
                                    },
     this.setPort(port);
              'getNodeSuffix'     : function(path = undefined) {
    this.setQuery(query);
                                        const __NODEDELIM = this.delims.node;
    this.setQrySep(qrySep);
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
    this.setQryAss(qryAss);
    this.setNode(node);
}


Class.define(UriDelims, Delims, {
                                        return (~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined;
              'setScheme'      : function(scheme = undefined) {
                                     },
                                    this.scheme = scheme;
              'stripNodeSuffix'   : function(path = undefined) {
                                },
                                        const __NODEDELIM = this.delims.node;
              'setHost'        : function(host = undefined) {
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                                    this.host = host;
                                },
              'setPort'        : function(port = undefined) {
                                     this.port = port;
                                },
              'setQuery'       : function(query = undefined) {
                                    this.query = query;
                                },
              'setQrySep'      : function(qrySep = undefined) {
                                    this.qrySep = qrySep;
                                },
              'setQryAss'      : function(qryAss = undefined) {
                                    this.qryAss = qryAss;
                                },
              'setNode'        : function(node = undefined) {
                                    this.node = node;
                                }
          });


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


// ==================== Abschnitt fuer Klasse Path ====================
                                        return (~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined;
                                    },
              'stripQueryString'  : function(path = undefined) {
                                        const __QUERYDELIM = this.delims.query;
                                        const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);


// Basisklasse fuer die Verwaltung eines Pfades
                                        return (~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path;
// homePath: Absoluter Startpfad als String
                                    },
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
              'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
// 'delim': Trennzeichen zwischen zwei Ebenen
                                        const __PARAMS = [];
// 'back': Name des relativen Vaterverzeichnisses
 
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
                                        for (let param in params) {
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
                                            __PARAMS.push(param + assign + formatFun(params[param]));
function Path(homePath, delims) {
                                        }
    'use strict';
 
                                        return __PARAMS.join(delim);
                                    },
              'parseParams'       : function(params, parseFun, delim = ' ', assign = '=') {
                                        const __RET = { };
 
                                        if (params) {
                                            const __PARAMS = params.split(delim);
 
                                            for (let index = 0; index < __PARAMS.length; index++) {
                                                const __PARAM = __PARAMS[index];


    this.dirs = [];
                                                if (__PARAM) {
    this.setDelims(delims);
                                                    const __INDEX = __PARAM.indexOf(assign);
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });
                                                    const __KEY = (~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM;
                                                    const __VAL = (~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true;


    this.home();
                                                    __RET[__KEY] = __VAL;
}
                                                }
                                            }
                                        }


Class.define(Path, Object, {
                                         return __RET;
                  'root'          : function() {
                                         this.dirs.splice(0, this.dirs.length);
                                     },
                                     },
                  'home'           : function() {
              'rawValue'         : function(value) {
                                         this.dirs = this.homeDirs.slice();
                                         return value;
                                     },
                                     },
                  'up'             : function() {
              'parseValue'       : function(value) {
                                         this.dirs.pop();
                                         const __VALUE = Number(value);
 
                                        if (__VALUE == value) {  // schwacher Vergleich true, also Number
                                            return __VALUE;
                                        } else {
                                            const __LOWER = (value ? value.toLowerCase() : undefined);
 
                                            if ((__LOWER === "true") || (__LOWER === "false")) {
                                                return (value === "true");
                                            }
                                        }
 
                                        return value;
                                     },
                                     },
                  'down'           : function(subDir) {
              'getQuery'         : function(delims = { }) {
                                         this.dirs.push(subDir);
                                         const __QRYSEP = ((delims.qrySep !== undefined) ? delims.qrySep : this.delims.qrySep);
                                        const __QRYASS = ((delims.qryAss !== undefined) ? delims.qryAss : this.delims.qryAss);
 
                                        return this.formatParams(this.query, this.rawValue, __QRYSEP, __QRYASS);
                                     },
                                     },
                  'setDelims'     : function(delims = undefined) {
              'parseQuery'       : function(path = undefined, delims = { }) {
                                         this.setConst('delims', new Delims(delims));
                                         const __QRYSEP = ((delims.qrySep !== undefined) ? delims.qrySep : this.delims.qrySep);
                                        const __QRYASS = ((delims.qryAss !== undefined) ? delims.qryAss : this.delims.qryAss);
 
                                        return this.parseParams(path, this.parseValue, __QRYSEP, __QRYASS);
                                     },
                                     },
                  'setDelim'       : function(delim = undefined) {
              'setQuery'         : function(query) {
                                         this.delims.setDelim(delim || '/');
                                         this.query = query;
                                     },
                                     },
                  'setBackDelim'   : function(backDelim = undefined) {
              'setQueryPar'       : function(key, value) {
                                         this.delims.setBack(backDelim || "..");
                                         this.query[key] = value;
                                     },
                                     },
                  'setRootDelim'   : function(rootDelim = undefined) {
              'getQueryPar'       : function(key) {
                                         this.delims.setRoot(rootDelim || "");
                                         return this.query[key];
                                     },
                                     },
                  'setHomeDelim'   : function(homeDelim = undefined) {
              'getPath'           : function(dirs = undefined, delims = undefined) {
                                         this.delims.setHome(homeDelim || '~');
                                         const __DELIMS = new UriDelims(delims);
                                    },
                                        const __SCHEMEDELIM = ((__DELIMS.scheme !== undefined) ? __DELIMS.scheme : this.delims.scheme);
                  'setSchemeDelim' : function(schemeDelim = undefined) {
                                        const __HOSTDELIM = ((__DELIMS.host !== undefined) ? __DELIMS.host : this.delims.host);
                                        this.delims.setScheme(schemeDelim || ':');
                                        const __PORTDELIM = ((__DELIMS.port !== undefined) ? __DELIMS.port : this.delims.port);
                                    },
                                        const __QUERYDELIM = ((__DELIMS.query !== undefined) ? __DELIMS.query : this.delims.query);
                  'setHostDelim'  : function(hostDelim = undefined) {
                                        const __NODEDELIM = ((__DELIMS.node !== undefined) ? __DELIMS.node : this.delims.node);
                                        this.delims.setHost(hostDelim || '//');
                                        const __SCHEMENAME = this.scheme;
                                    },
                                        const __SCHEME = (__SCHEMENAME ? __SCHEMENAME + __SCHEMEDELIM : "");
                  'setPortDelim'  : function(portDelim = undefined) {
                                         const __HOSTNAME = this.host;
                                        this.delims.setHost(portDelim || ':');
                                        const __HOST = (__HOSTNAME ? __HOSTDELIM + __HOSTNAME : "");
                                    },
                                        const __PORTNR = this.port;
                  'setQueryDelim'  : function(queryDelim = undefined) {
                                        const __PORT = ((__HOSTNAME && __PORTNR) ? __PORTDELIM + __PORTNR : "");
                                        this.delims.setQuery(queryDelim || '?');
                                         const __QUERYSTR = this.getQuery();
                                    },
                                        const __QUERY = (__QUERYSTR ? __QUERYDELIM + __QUERYSTR : "");
                  'setParSepDelim' : function(parSepDelim = undefined) {
                                         const __NODENAME = this.node;
                                        this.delims.setParSep(parSepDelim || '&');
                                        const __NODE = (__NODENAME ? __NODEDELIM + __NODENAME : "");
                                    },
                  'setParAssDelim' : function(parAssDelim = undefined) {
                                         this.delims.setParAss(parAssDelim || '=');
                                    },
                  'setNodeDelim'  : function(nodeDelim = undefined) {
                                         this.delims.setNode(nodeDelim || '#');
                                    },
                  'getLeaf'        : function(dirs = undefined) {
                                         const __DIRS = (dirs || this.dirs);


                                         return ((__DIRS && __DIRS.length) ? __DIRS.slice(-1)[0] : "");
                                         return __SCHEME + __HOST + __PORT + Path.prototype.getPath.call(this, dirs, delims) + __QUERY + __NODE;
                                     },
                                     },
                  'getPath'       : function(dirs = undefined, delims = undefined) {
              'getDirs'           : function(path = undefined, delims = undefined) {
                                         const __DELIMS = new Delims(delims);
                                         const __PATH = this.getServerPath(path);
                                        const __DELIM = (__DELIMS.delim || this.delims.delim);
                                        const __ROOTDELIM = ((__DELIMS.root !== undefined) ? __DELIMS.root : this.delims.root);
                                        const __DIRS = (dirs || this.dirs);


                                         return __ROOTDELIM + __DELIM + __DIRS.join(__DELIM);
                                         return Path.prototype.getDirs.call(this, __PATH);
                                    },
                  'getDirs'        : function(path = undefined, delims = undefined) {
                                        const __DELIMS = new Delims(delims);
                                        const __DELIM = (__DELIMS.delim || this.delims.delim);
                                        const __ROOTDELIM = ((__DELIMS.root !== undefined) ? __DELIMS.root : this.delims.root);
                                        const __HOMEDELIM = ((__DELIMS.home !== undefined) ? __DELIMS.home : this.delims.home);
                                        const __DIRS = (path ? path.split(__DELIM) : []);
                                        const __FIRST = __DIRS[0];
 
                                        if (__FIRST && (__FIRST !== __ROOTDELIM) && (__FIRST !== __HOMEDELIM)) {
                                            showAlert("Kein absoluter Pfad", this.getPath(__DIRS), this);
                                        }
 
                                        return __DIRS.slice(1);
                                     }
                                     }
                });
          } );


// ==================== Ende Abschnitt fuer Klasse Path ====================
// ==================== Ende Abschnitt fuer Klasse URI ====================


// ==================== Abschnitt fuer Klasse URI ====================
// ==================== Abschnitt fuer Klasse Directory ====================


// Basisklasse fuer die Verwaltung einer URI/URL
// Basisklasse fuer eine Verzeichnisstruktur
// homePath: Absoluter Startpfad als String
// homePath: Absoluter Startpfad als String
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
Zeile 1.353: Zeile 1.288:
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function URI(homePath, delims) {
function Directory(homePath, delims) {
     'use strict';
     'use strict';


     Path.call(this);
     Path.call(this, homePath, delims);
}


    const __HOSTPORT = this.getHostPort(homePath);
Class.define(Directory, Path, {
 
                    'chDir' : function(subDir = undefined) {
    this.scheme = this.getSchemePrefix(homePath);
                                  if (subDir === undefined) {
    this.host = __HOSTPORT.host;
                                      this.root();
    this.port = this.parseValue(__HOSTPORT.port);
                                  } else if ((typeof subDir) === 'object') {
    this.query = this.parseQuery(this.getQueryString(homePath));
                                      for (let sub of subDir) {
    this.node = this.getNodeSuffix(homePath);
                                          this.chDir(sub);
                                      }
                                  } else {
                                      if (subDir === this.delims.home) {
                                          this.home();
                                      } else if (subDir === this.delims.back) {
                                          this.up();
                                      } else {
                                          this.down(subDir);
                                      }
                                  }
                              },
                    'pwd'  : function() {
                                  return this.getPath();
                              }
                } );
 
// ==================== Ende Abschnitt fuer Klasse Directory ====================
 
// ==================== Abschnitt fuer Klasse ObjRef ====================
 
// Basisklasse fuer eine Objekt-Referenz
function ObjRef(rootObj) {
    'use strict';


     this.homeDirs = this.getDirs(homePath, { 'home' : "" });
     Directory.call(this, undefined, new Delims('/', "..", '/', '~'));


     this.home();
     this.setConst('rootObj', rootObj); // Wichtig: Verweis nicht verfolgen! Gefahr durch Zyklen!
}
}


Class.define(URI, Path, {
Class.define(ObjRef, Directory, {
              'setDelims'         : function() {
                    'valueOf' : function() {
                                        this.setConst('delims', new UriDelims('/', "..", "", '~', ':', "//", ':', '?', '&', '=', '#'));
                                    let ret = this.rootObj;
                                    },
 
              'setSchemeDelim'    : function(schemeDelim = undefined) {
                                    for (let name of this.dirs) {
                                        this.delims.setScheme(schemeDelim || ':');
                                        if (ret === undefined) {
                                    },
                                            break;
              'setQueryDelim'    : function(queryDelim = undefined) {
                                        }
                                        this.delims.setQuery(queryDelim || '?');
                                        ret = ret[name];
                                    },
                                    }
              'setParSepDelim'    : function(parSepDelim = undefined) {
                                        this.delims.setParSep(parSepDelim || '&');
                                    },
              'setParAssDelim'    : function(parAssDelim = undefined) {
                                        this.delims.setParAss(parAssDelim || '=');
                                    },
              'setNodeDelim'      : function(nodeDelim = undefined) {
                                        this.delims.setNode(nodeDelim || '#');
                                    },
              'getServerPath'    : function(path = undefined) {
                                        return this.stripHostPort(this.stripQueryString(this.stripNodeSuffix(this.stripSchemePrefix(path))));
                                    },
              'getHostPort'      : function(path = undefined) {
                                        const __HOSTDELIM = this.delims.host;
                                        const __PORTDELIM = this.delims.port;
                                        const __ROOTDELIM = this.delims.root + this.delims.delim;
                                        const __NOSCHEME = this.stripSchemePrefix(path);
                                        const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1);
                                        const __PATH = ((~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME);
                                        const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);
                                        const __HOSTPORT = ((~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined);
                                        const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1);
                                        const __HOST = ((~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT);
                                        const __PORT = ((~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined);


                                        return {
                                    return ret;
                                                    'host' : __HOST,
                                }
                                                    'port' : __PORT
                } );
                                                };
                                    },
              'stripHostPort'    : function(path = undefined) {
                                        const __HOSTDELIM = this.delims.host;
                                        const __ROOTDELIM = this.delims.root + this.delims.delim;
                                        const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1);
                                        const __PATH = ((~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path);
                                        const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1);


                                        return ((~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH);
// ==================== Ende Abschnitt fuer Klasse ObjRef ====================
                                    },
              'getSchemePrefix'  : function(path = undefined) {
                                        const __SCHEMEDELIM = this.delims.scheme;
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);


                                        return ((~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined);
// ==================== Abschnitt fuer diverse Utilities ====================
                                    },
 
              'stripSchemePrefix' : function(path = undefined) {
// Gibt einen Wert zurueck. Ist dieser nicht definiert oder null, wird ein Alternativwert geliefert
                                        const __SCHEMEDELIM = this.delims.scheme;
// value: Ein Wert. Ist dieser nicht undefined oder null, wird er zurueckgeliefert (oder retValue)
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// retValue: Falls definiert, Rueckgabe-Wert fuer den Fall, dass value nicht undefined oder null ist
// return Der Wert. Sind weder value noch defValue definiert, dann undefined
function getValue(value, defValue = undefined, retValue = undefined) {
    return ((value === undefined) || (value === null)) ? defValue : (retValue === undefined) ? value : retValue;
}


                                        return ((~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path);
// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert
                                    },
// value: Ein Wert. Ist dieser definiet und in den Grenzen, wird er zurueckgeliefert
              'getNodeSuffix'    : function(path = undefined) {
// minValue: Untere Grenze fuer den Wert, falls angegeben
                                        const __NODEDELIM = this.delims.node;
// minValue: Obere Grenze fuer den Wert, falls angegeben
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist oder der Wert ausserhalb liegt
// return Der Wert. Sind weder value (in den Grenzen) noch defValue definiert, dann undefined
function getValueIn(value, minValue = undefined, maxValue = undefined, defValue = undefined) {
    const __VALUE = getValue(value, defValue);


                                        return ((~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined);
    if ((minValue !== undefined) && (__VALUE < minValue)) {
                                    },
        return defValue;
              'stripNodeSuffix'  : function(path = undefined) {
    }
                                        const __NODEDELIM = this.delims.node;
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
        return defValue;
    }


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


                                        return ((~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined);
// Ermittelt den naechsten Wert aus einer Array-Liste
                                    },
// arr: Array-Liste mit den moeglichen Werte
              'stripQueryString'  : function(path = undefined) {
// value: Vorher gesetzter Wert
                                        const __QUERYDELIM = this.delims.query;
// return Naechster Wert in der Array-Liste
                                        const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);
function getNextValue(arr, value) {
    const __POS = arr.indexOf(value) + 1;


                                        return ((~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path);
    return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
                                    },
}
              'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
                                        const __PARAMS = [];


                                        for (let param in params) {
// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
                                            __PARAMS.push(param + assign + formatFun(params[param]));
// 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;


                                        return __PARAMS.join(delim);
    if ((valueA !== undefined) && (valueB !== undefined)) {
                                    },
        product = parseFloat(valueA) * parseFloat(valueB);
              'parseParams'      : function(params, parseFun, delim = ' ', assign = '=') {
    }
                                        const __RET = { };


                                        if (params) {
    if (isNaN(product)) {
                                            const __PARAMS = params.split(delim);
        product = defValue;
    }


                                            for (let index = 0; index < __PARAMS.length; index++) {
    return parseFloat(product.toFixed(digits));
                                                const __PARAM = __PARAMS[index];
}


                                                if (__PARAM) {
// Ueberprueft, ob ein Objekt einer bestimmten Klasse angehoert (ggfs. per Vererbung)
                                                    const __INDEX = __PARAM.indexOf(assign);
// obj: Ein (generisches) Objekt
                                                    const __KEY = ((~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM);
// base: Eine Objektklasse (Konstruktor-Funktion)
                                                    const __VAL = ((~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true);
// return true, wenn der Prototyp rekursiv gefunden werden konnte
 
function instanceOf(obj, base) {
                                                    __RET[__KEY] = __VAL;
    while (obj !== null) {
                                                }
        if (obj === base.prototype)
                                            }
            return true;
                                        }
        if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
            return (base.prototype === XML.prototype);
        }
        obj = Object.getPrototypeOf(obj);
    }


                                        return __RET;
    return false;
                                    },
}
              'rawValue'          : function(value) {
                                        return value;
                                    },
              'parseValue'        : function(value) {
                                        const __VALUE = Number(value);


                                        if (__VALUE == value) // schwacher Vergleich true, also Number
// Liefert alle Basisklassen des Objekts (inkl. Vererbung)
                                            return __VALUE;
// obj: Ein (generisches) Objekt
                                        } else {
// return true, wenn der Prototyp rekursiv gefunden werden konnte
                                            const __LOWER = (value ? value.toLowerCase() : undefined);
function getPrototypes(obj) {
    let ret = [];


                                            if ((__LOWER === 'true') || (__LOWER === 'false')) {
    while (obj !== null) {
                                                return (value === 'true');
        const __PROTO = Object.getPrototypeOf(obj);
                                            }
                                        }


                                        return value;
        ret.push(__PROTO);
                                    },
        if ((typeof obj) === 'xml') {  // Sonderfall mit Selbstbezug
              'getQuery'          : function(delims = { }) {
            break;
                                        const __QRYSEP = ((delims.qrySep !== undefined) ? delims.qrySep : this.delims.qrySep);
        }
                                        const __QRYASS = ((delims.qryAss !== undefined) ? delims.qryAss : this.delims.qryAss);
        obj = __PROTO;
    }


                                        return this.formatParams(this.query, this.rawValue, __QRYSEP, __QRYASS);
    return ret;
                                    },
}
              'parseQuery'        : function(path = undefined, delims = { }) {
                                        const __QRYSEP = ((delims.qrySep !== undefined) ? delims.qrySep : this.delims.qrySep);
                                        const __QRYASS = ((delims.qryAss !== undefined) ? delims.qryAss : this.delims.qryAss);


                                        return this.parseParams(path, this.parseValue, __QRYSEP, __QRYASS);
// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)
                                    },
// obj: Ein (generisches) Objekt
              'setQuery'          : function(query) {
// return Array von Items (Property-Namen)
                                        this.query = query;
function getAllProperties(obj) {
                                    },
    let ret = [];
              'setQueryPar'      : function(key, value) {
                                        this.query[key] = value;
                                    },
              'getQueryPar'      : function(key) {
                                        return this.query[key];
                                    },
              'getPath'          : function(dirs = undefined, delims = undefined) {
                                        const __DELIMS = new UriDelims(delims);
                                        const __SCHEMEDELIM = ((__DELIMS.scheme !== undefined) ? __DELIMS.scheme : this.delims.scheme);
                                        const __HOSTDELIM = ((__DELIMS.host !== undefined) ? __DELIMS.host : this.delims.host);
                                        const __PORTDELIM = ((__DELIMS.port !== undefined) ? __DELIMS.port : this.delims.port);
                                        const __QUERYDELIM = ((__DELIMS.query !== undefined) ? __DELIMS.query : this.delims.query);
                                        const __NODEDELIM = ((__DELIMS.node !== undefined) ? __DELIMS.node : this.delims.node);
                                        const __SCHEMENAME = this.scheme;
                                        const __SCHEME = (__SCHEMENAME ? __SCHEMENAME + __SCHEMEDELIM : "");
                                        const __HOSTNAME = this.host;
                                        const __HOST = (__HOSTNAME ? __HOSTDELIM + __HOSTNAME : "");
                                        const __PORTNR = this.port;
                                        const __PORT = ((__HOSTNAME && __PORTNR) ? __PORTDELIM + __PORTNR : "");
                                        const __QUERYSTR = this.getQuery();
                                        const __QUERY = (__QUERYSTR ? __QUERYDELIM + __QUERYSTR : "");
                                        const __NODENAME = this.node;
                                        const __NODE = (__NODENAME ? __NODEDELIM + __NODENAME : "");


                                        return __SCHEME + __HOST + __PORT + Path.prototype.getPath.call(this, dirs, delims) + __QUERY + __NODE;
    for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
                                    },
      ret = ret.concat(Object.getOwnPropertyNames(o));
              'getDirs'          : function(path = undefined, delims = undefined) {
    }
                                        const __PATH = this.getServerPath(path);


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


// ==================== Ende Abschnitt fuer Klasse URI ====================
// Ueberpruefung, ob ein Item aktiv ist oder nicht
// item: Name des betroffenen Items
// inList: Checkliste der inkludierten Items (Positivliste, true fuer aktiv)
// exList: Checkliste der exkludierten Items (Negativliste, true fuer inaktiv)
// return Angabe, ob das Item aktiv ist
function checkItem(item, inList = undefined, exList = undefined) {
    let active = true;


// ==================== Abschnitt fuer Klasse Directory ====================
    if (inList !== undefined) {
        active = (inList[item] === true);  // gesetzt und true
    }
    if (exList !== undefined) {
        if (exList[item] === true) {  // gesetzt und true
            active = false;  // NICHT anzeigen
        }
    }


// Basisklasse fuer eine Verzeichnisstruktur
     return active;
// homePath: Absoluter Startpfad als String
// delims: Objekt mit Trennern und Symbolen als Properties (oder Delims-Objekt)
// 'delim': Trennzeichen zwischen zwei Ebenen
// 'back': Name des relativen Vaterverzeichnisses
// 'root': Kennung vor dem ersten Trenner am Anfang eines absoluten Pfads
// 'home': Kennung vor dem ersten Trenner am Anfang eines Pfads relativ zu Home
function Directory(homePath, delims) {
     'use strict';
 
    Path.call(this, homePath, delims);
}
}


Class.define(Directory, Path, {
// Fuegt Properties zu einem Objekt hinzu, die in einem zweiten stehen. Doppelte Werte werden ueberschrieben
                    'chDir' : function(subDir = undefined) {
// data: Objekt, dem Daten hinzugefuegt werden
                                  if (subDir === undefined) {
// addData: Objekt, das zusaetzliche Properties enthaelt
                                      this.root();
// addList: Checkliste der zu setzenden Items (true fuer kopieren), falls angegeben
                                  } else if ((typeof subDir) === 'object') {
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
                                      for (let sub of subDir) {
// return Das gemergete Objekt mit allen Properties
                                          this.chDir(sub);
function addProps(data, addData, addList = undefined, ignList = undefined) {
                                      }
    for (let item in getValue(addData, { })) {
                                  } else {
        if (checkItem(item, addList, ignList)) {
                                      if (subDir === this.delims.home) {
            data[item] = addData[item];
                                          this.home();
        }
                                      } else if (subDir === this.delims.back) {
    }
                                          this.up();
                                      } else {
                                          this.down(subDir);
                                      }
                                  }
                              },
                    'pwd'  : function() {
                                  return this.getPath();
                              }
                });


// ==================== Ende Abschnitt fuer Klasse Directory ====================
    return data;
}


// ==================== Abschnitt fuer Klasse ObjRef ====================
// Gibt den Wert einer Property zurueck. Ist dieser nicht definiert oder null, wird er vorher gesetzt
// obj: Ein Objekt. Ist dieses undefined oder null, wird defValue zurueckgeliefert
// item: Key des Properties
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return Der Wert des Properties. Sind das obj oder das Property und defValue undefined oder null, dann undefined
function getProp(obj, item, defValue = undefined) {
    if ((obj === undefined) || (obj === null)) {
        return defValue;
    }


// Basisklasse fuer eine Objekt-Referenz
     const __PROP = obj[item];
function ObjRef(rootObj) {
     'use strict';


     Directory.call(this, undefined, new Delims('/', "..", '/', '~'));
     if ((__PROP !== undefined) && (__PROP !== null)) {
        return __PROP;
    }


     this.setConst('rootObj', rootObj); // Wichtig: Verweis nicht verfolgen! Gefahr durch Zyklen!
     return (obj[item] = defValue);
}
}


Class.define(ObjRef, Directory, {
// Sicheres obj.valueOf() fuer alle Daten
                    'valueOf' : function() {
// data: Objekt oder Wert
                                    let ret = this.rootObj;
// return Bei Objekten valueOf() oder das Objekt selber, bei Werten der Wert
function valueOf(data) {
    return (((typeof data) === 'object') ? data.valueOf() : data);
}


                                    for (let name of this.dirs) {
// Sicheres JSON.stringify(), das auch mit Zyklen umgehen kann
                                        if (ret === undefined) {
// value: Auszugebene Daten. Siehe JSON.stringify()
                                            break;
// replacer: Elementersetzer. Siehe JSON.stringify()
                                        }
// space: Verschoenerung. Siehe JSON.stringify()
                                        ret = ret[name];
// cycleReplacer: Ersetzer im Falle von Zyklen
                                    }
// return String mit Ausgabe der Objektdaten
function safeStringify(value, replacer = undefined, space = undefined, cycleReplacer = undefined) {
    return JSON.stringify(value, serializer(replacer, cycleReplacer), space);
}


                                    return ret;
// Hilfsfunktion fuer safeStringify(): Kapselt replacer und einen cycleReplacer fuer Zyklen
                                }
// replacer: Elementersetzer. Siehe JSON.stringify()
                });
// cycleReplacer: Ersetzer im Falle von Zyklen
// return Ersetzer-Funktion fuer JSON.stringify(), die beide Ersetzer vereint
function serializer(replacer = undefined, cycleReplacer = undefined) {
    const __STACK = [];
    const __KEYS = [];


// ==================== Ende Abschnitt fuer Klasse ObjRef ====================
    if (! cycleReplacer) {
 
        cycleReplacer = function(key, value) {
// ==================== Abschnitt fuer diverse Utilities ====================
                if (__STACK[0] === value) {
                    return "[~]";
                }
                return "[~." + __KEYS.slice(0, __STACK.indexOf(value)).join('.') + ']';
            };
    }
 
    return function(key, value) {
            if (__STACK.length) {
                const __THISPOS = __STACK.indexOf(this);
 
                if (~ __THISPOS) {
                    __STACK.splice(__THISPOS + 1);
                    __KEYS.splice(__THISPOS, Infinity, key);
                } else {
                    __STACK.push(this);
                    __KEYS.push(key);
                }
                if (~ __STACK.indexOf(value)) {
                    value = cycleReplacer.call(this, key, value);
                }
            } else {
                __STACK.push(value);
            }


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


// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// value: Ein Wert. Ist dieser definiet und in den Grenzen, wird er zurueckgeliefert
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// minValue: Untere Grenze fuer den Wert, falls angegeben
// value: Beliebiger (strukturierter) Wert
// minValue: Obere Grenze fuer den Wert, falls angegeben
// return String-Darstellung des Wertes
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist oder der Wert ausserhalb liegt
function serialize(name, value) {
// return Der Wert. Sind weder value (in den Grenzen) noch defValue definiert, dann undefined
    const __STREAM = (value !== undefined) ? safeStringify(value) : value;
function getValueIn(value, minValue = undefined, maxValue = undefined, defValue = undefined) {
    const __VALUE = getValue(value, defValue);


     if ((minValue !== undefined) && (__VALUE < minValue)) {
     __LOG[4](name + " >> " + __STREAM);
        return defValue;
    }
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
        return defValue;
    }


     return __VALUE;
     GM_setValue(name, __STREAM);
}
 
// Ermittelt den naechsten Wert aus einer Array-Liste
// arr: Array-Liste mit den moeglichen Werte
// value: Vorher gesetzter Wert
// return Naechster Wert in der Array-Liste
function getNextValue(arr, value) {
    const __POS = arr.indexOf(value) + 1;


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


// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
// valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// return Objekt, das unter dem Namen gespeichert war
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
function deserialize(name, defValue = undefined) {
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue
     const __STREAM = GM_getValue(name, defValue);
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {
     let product = defValue;


     if ((valueA !== undefined) && (valueB !== undefined)) {
     __LOG[4](name + " << " + __STREAM);
        product = parseFloat(valueA) * parseFloat(valueB);
    }


     if (isNaN(product)) {
     if ((__STREAM !== undefined) && __STREAM.length) {
         product = defValue;
         try {
            return JSON.parse(__STREAM);
        } catch (ex) {
            __LOG[1](name + ": " + ex.message);
        }
     }
     }


     return parseFloat(product.toFixed(digits));
     return undefined;
}
}


// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck
// Setzt eine Option dauerhaft und laedt die Seite neu
// value: Eine ganze Zahl
// name: Name der Option als Speicherort
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')
// value: Zu setzender Wert
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben
// reload: Seite mit neuem Wert neu laden
function getOrdinal(value, defValue = '*') {
// return Gespeicherter Wert fuer setOptValue()
     return getValue(value, defValue, value + '.');
function setStored(name, value, reload = false, serial = false) {
}
     if (serial) {
        serialize(name, value);
    } else {
        GM_setValue(name, value);
    }


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


     return false;
     return value;
}
}


// Liefert alle Basisklassen des Objekts (inkl. Vererbung)
// Setzt den naechsten Wert aus einer Array-Liste als Option
// obj: Ein (generisches) Objekt
// arr: Array-Liste mit den moeglichen Optionen
// return true, wenn der Prototyp rekursiv gefunden werden konnte
// name: Name der Option als Speicherort
function getPrototypes(obj) {
// value: Vorher gesetzter Wert
     let ret = [];
// reload: Seite mit neuem Wert neu laden
// return Gespeicherter Wert fuer setOptValue()
function setNextStored(arr, name, value, reload = false, serial = false) {
     return setStored(name, getNextValue(arr, value), reload, serial);
}


    while (obj !== null) {
// Fuehrt die in einem Storage gespeicherte Operation aus
        const __PROTO = Object.getPrototypeOf(obj);
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
function getStoredCmds(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];


         ret.push(__PROTO);
    if (__MEMORY !== undefined) {
         if ((typeof obj) === 'xml') { // Sonderfall mit Selbstbezug
         const __GETITEM = function(item) {
             break;
                              return __MEMORY.getItem(__RUNPREFIX + item);
        }
                          };
        obj = __PROTO;
         const __DELITEM = function(item) {
    }
                              return __MEMORY.removeItem(__RUNPREFIX + item);
                          };
        const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);
 
        if (__CMD !== undefined) {
             const __KEY = __GETITEM('key');
            let value = __GETITEM('val');


    return ret;
            try {
}
                value = JSON.parse(value);
            } catch (ex) {
                __LOG[1]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
                // ... meist kann man den String selber aber speichern, daher kein "return"...
            }


// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)
            __STOREDCMDS.push({
// obj: Ein (generisches) Objekt
                                'cmd' : __CMD,
// return Array von Items (Property-Namen)
                                'key' : __KEY,
function getAllProperties(obj) {
                                'val' : value
    let ret = [];
                            });
        }


    for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
        __DELITEM('cmd');
      ret = ret.concat(Object.getOwnPropertyNames(o));
        __DELITEM('key');
        __DELITEM('val');
     }
     }


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


// Ueberpruefung, ob ein Item aktiv ist oder nicht
// Fuehrt die in einem Storage gespeicherte Operation aus
// item: Name des betroffenen Items
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// inList: Checkliste der inkludierten Items (Positivliste, true fuer aktiv)
// optSet: Set mit den Optionen
// exList: Checkliste der exkludierten Items (Negativliste, true fuer inaktiv)
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
// return Angabe, ob das Item aktiv ist
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
function checkItem(item, inList = undefined, exList = undefined) {
// return Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
     let active = true;
function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined) {
    const __BEFORELOAD = getValue(beforeLoad, true);
    const __STOREDCMDS = getValue(storedCmds, []);
    const __LOADEDCMDS = [];
     let invalidated = false;
 
    while (__STOREDCMDS.length) {
        const __STORED = __STOREDCMDS.shift();
        const __CMD = __STORED.cmd;
        const __KEY = __STORED.key;
        const __VAL = __STORED.val;


    if (inList) {
        if (__BEFORELOAD) {
        active = (inList[item] === true);  // gesetzt und true
            if (__STOREDCMDS.length) {
    }
                invalidateOpts(optSet);  // alle Optionen invalidieren
    if (exList) {
                invalidated = true;
         if (exList[item] === true) { // gesetzt und true
            }
             active = false;  // NICHT anzeigen
            switch (__OPTACTION[__CMD]) {
            case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL);
                                  setStored(__KEY, __VAL, false, false);
                                  break;
            case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
                                  setStored(__KEY, __VAL, false, false);
                                  break;
            case __OPTACTION.RST : __LOG[4]("RESET (delayed)");
                                  __LOADEDCMDS.push(__STORED);
                                  break;
            default :              break;
            }
         } else {
            switch (__OPTACTION[__CMD]) {
            case __OPTACTION.SET :
            case __OPTACTION.NXT : __LOG[2]("SET/SETNEXT (undefined)");
                                  break;
             case __OPTACTION.RST : __LOG[4]("RESET");
                                  resetOptions(optSet, false);
                                  loadOptions(optSet);  // Reset auf umbenannte Optionen anwenden!
                                  break;
            default :              break;
            }
         }
         }
     }
     }


     return active;
     return (__LOADEDCMDS.length ? __LOADEDCMDS : undefined);
}
}


// Fuegt Properties zu einem Objekt hinzu, die in einem zweiten stehen. Doppelte Werte werden ueberschrieben
// Gibt eine Option sicher zurueck
// data: Objekt, dem Daten hinzugefuegt werden
// opt: Config und Value der Option, ggfs. undefined
// addData: Objekt, das zusaetzliche Properties enthaelt
// defOpt: Rueckgabewert, falls undefined
// addList: Checkliste der zu setzenden Items (true fuer kopieren), falls angegeben
// return Daten zur Option (oder defOpt)
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
function getOpt(opt, defOpt = { }) {
// return Das gemergete Objekt mit allen Properties
     return getValue(opt, defOpt);
function addProps(data, addData, addList = undefined, ignList = undefined) {
     for (let item in getValue(addData, { })) {
        if (checkItem(item, addList, ignList)) {
            data[item] = addData[item];
        }
    }
 
    return data;
}
}


// Entfernt Properties in einem Objekt
// Gibt eine Option sicher zurueck (Version mit Key)
// data: Objekt, deren Properties bearbeitet werden
// optSet: Platz fuer die gesetzten Optionen (und Config)
// delList: Checkliste der zu loeschenden Items (true fuer loeschen), falls angegeben
// item: Key der Option
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
// defOpt: Rueckgabewert, falls nicht zu finden
// return Das veraenderte Objekt ohne die geloeschten Properties
// return Daten zur Option (oder defOpt)
function delProps(data, delList = undefined, ignList = undefined) {
function getOptByName(optSet, item, defOpt = { }) {
     for (let item in getValue(data, { })) {
     if ((optSet !== undefined) && (item !== undefined)) {
        if (checkItem(item, delList, ignList)) {
        return getOpt(optSet[item], defOpt);
            delete data[item];
    } else {
         }
         return defOpt;
     }
     }
}


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


// Gibt den Wert einer Property zurueck. Ist dieser nicht definiert oder null, wird er vorher gesetzt
// Setzt den Namen einer Option
// obj: Ein Objekt. Ist dieses undefined oder null, wird defValue zurueckgeliefert
// opt: Config und Value der Option
// item: Key des Properties
// name: Zu setzender Name der Option
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// reload: Seite mit neuem Wert neu laden
// return Der Wert des Properties. Sind das obj oder das Property und defValue undefined oder null, dann undefined
// return Gesetzter Name der Option
function getProp(obj, item, defValue = undefined) {
function setOptName(opt, name) {
     if ((obj === undefined) || (obj === null)) {
     const __CONFIG = getOptConfig(opt);
        return defValue;
     const __NAME = getOptName(opt);
     }


     const __PROP = obj[item];
     if (__NAME !== name) {
        __LOG[4]("RENAME " + __NAME + " => " + name);


    if ((__PROP !== undefined) && (__PROP !== null)) {
        __CONFIG.Name = name;
        return __PROP;
     }
     }


     return (obj[item] = defValue);
     return name;
}
}


// Sicheres obj.valueOf() fuer alle Daten
// Gibt den Namen einer Option zurueck
// data: Objekt oder Wert
// opt: Config und Value der Option
// return Bei Objekten valueOf() oder das Objekt selber, bei Werten der Wert
// return Name der Option
function valueOf(data) {
function getOptName(opt) {
     return (((typeof data) === 'object') ? data.valueOf() : data);
     const __CONFIG = getOptConfig(opt);
}
    const __NAME = __CONFIG.Name;


// Sicheres JSON.stringify(), das auch mit Zyklen umgehen kann
    if (! __NAME) {
// value: Auszugebene Daten. Siehe JSON.stringify()
        const __SHARED = __CONFIG.Shared;
// replacer: Elementersetzer. Siehe JSON.stringify()
// space: Verschoenerung. Siehe JSON.stringify()
// cycleReplacer: Ersetzer im Falle von Zyklen
// return String mit Ausgabe der Objektdaten
function safeStringify(value, replacer = undefined, space = undefined, cycleReplacer = undefined) {
    return JSON.stringify(value, serializer(replacer, cycleReplacer), space);
}


// Hilfsfunktion fuer safeStringify(): Kapselt replacer und einen cycleReplacer fuer Zyklen
        if (__SHARED && ! opt.Loaded) {
// replacer: Elementersetzer. Siehe JSON.stringify()
            const __OBJREF = getSharedRef(__SHARED, opt.Item);
// cycleReplacer: Ersetzer im Falle von Zyklen
 
// return Ersetzer-Funktion fuer JSON.stringify(), die beide Ersetzer vereint
            return __OBJREF.getPath();
function serializer(replacer = undefined, cycleReplacer = undefined) {
        }
    const __STACK = [];
    const __KEYS = [];


    if (! cycleReplacer) {
         showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
         cycleReplacer = function(key, value) {
                if (__STACK[0] === value) {
                    return "[~]";
                }
                return "[~." + __KEYS.slice(0, __STACK.indexOf(value)).join('.') + ']';
            };
     }
     }


     return function(key, value) {
     return __NAME;
            if (__STACK.length) {
                const __THISPOS = __STACK.indexOf(this);
 
                if (~ __THISPOS) {
                    __STACK.splice(__THISPOS + 1);
                    __KEYS.splice(__THISPOS, Infinity, key);
                } else {
                    __STACK.push(this);
                    __KEYS.push(key);
                }
                if (~ __STACK.indexOf(value)) {
                    value = cycleReplacer.call(this, key, value);
                }
            } else {
                __STACK.push(value);
            }
 
            return ((! replacer) ? value : replacer.call(this, key, value));
        };
}
}


// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// Setzt den Wert einer Option
// key: Der uebergebene Schluessel
// opt: Config und Value der Option
// value: Der uebergebene Wert
// name: Zu setzender Wert der Option
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
// return Gesetzter Wert
function replaceArraySimple(key, value) {
function setOptValue(opt, value) {
     if (Array.isArray(value)) {
     if (opt !== undefined) {
        return "[ " + value.join(", ") + " ]";
        if (! opt.ReadOnly) {
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));
 
            opt.Value = value;
        }
        return opt.Value;
    } else {
        return undefined;
     }
     }
    return value;
}
}


// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// Gibt den Wert einer Option zurueck
// key: Der uebergebene Schluessel
// opt: Config und Value der Option
// value: Der uebergebene Wert
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
// load: Laedt die Option per loadOption(), falls noetig
function replaceArray(key, value) {
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
     if (Array.isArray(value)) {
// return Gesetzter Wert
        __RET = value.map(function(element) {
function getOptValue(opt, defValue = undefined, load = true, force = false) {
                              return safeStringify(element, replaceArray, 0);
     let value;
                          });


         return __RET;
    if (opt !== undefined) {
         if (load && ! opt.Loaded) {
            value = loadOption(opt, force);
        } else {
            value = opt.Value;
        }
     }
     }


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


// Moegliche einfache Ersetzungen mit '$'...
// ==================== Ende Abschnitt fuer diverse Utilities ====================
let textSubst;


// Substituiert '$'-Parameter in einem Text
// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
// text: Urspruenglicher Text mit '$'-Befehlen
// par1: Der (erste) uebergebene Parameter
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function substParam(text, par1) {
    let ret = getValue(text, "");


    if (! textSubst) {
// Namen des Default-, Temporaer- und Null-Memories...
        textSubst  = {
const __MEMNORMAL  = 'normal';
                'n' : __DBMOD.name,
const __MEMSESSION  = 'begrenzt';
                'v' : __DBMOD.version,
const __MEMINAKTIVE = 'inaktiv';
                'V' : __DBMOD.Name
            };
    }


    for (let ch in textSubst) {
// Definition des Default-, Dauer- und Null-Memories...
        const __SUBST = textSubst[ch];
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
const __OPTMEMSESSION  = __OPTMEM[__MEMSESSION];
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];


        ret = ret.replace('$' + ch, __SUBST);
// Medium fuer die Datenbank (Speicher)
    }
let myOptMem = __OPTMEMNORMAL;
let myOptMemSize;


    return ret.replace('$', par1);
// Infos ueber dieses Script-Modul
}
const __DBMOD = new ScriptModule();


// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
const __DBTOC = { };
// numberString: Dezimalzahl als String
// return Diese Dezimalzahl als String mit Tausender-Trennpunkten und Komma statt Dezimalpunkt
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;
// Daten zu den Modulen (indiziert durch die Script-Namen)
    } else {
const __DBDATA = { };
        // 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++) {
// ==================== Abschnitt fuer Speicher ====================
            if ((i > 0) && (i % 3 === 0)) {
                result += '.';
            }
            result += __TEMP.substr(i, 1);
        }


        return reverseString(result);
// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
     }
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// defMemory: Ersatz-Wert, falls memory undefined. Soll nur memory genutzt werden, dann z.B. null uebergeben!
// return memory, falls okay, sonst einen Defaultwert
function getMemory(memory = undefined, defMemory = getValue(myOptMem, __OPTMEMNORMAL)) {
     return getValue(memory, defMemory);
}
}


// Dreht den uebergebenen String um
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// string: Eine Zeichenkette
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return Dieselbe Zeichenkette rueckwaerts
// return true, wenn der Speichertest erfolgreich war
function reverseString(string) {
function canUseMemory(memory = undefined) {
     let result = "";
    const __STORAGE = getMemory(memory, { });
    const __MEMORY = __STORAGE.Value;
     let ret = false;


     for (let i = string.length - 1; i >= 0; i--) {
     if (__MEMORY !== undefined) {
         result += string.substr(i, 1);
        const __TESTPREFIX = 'canUseStorageTest';
        const __TESTDATA = Math.random().toString();
        const __TESTITEM = __TESTPREFIX + __TESTDATA;
 
        __MEMORY.setItem(__TESTITEM, __TESTDATA);
         ret = (__MEMORY.getItem(__TESTITEM) === __TESTDATA);
        __MEMORY.removeItem(__TESTITEM);
     }
     }


     return result;
    __LOG[2]("canUseStorage(" + __STORAGE.Name + ") = " + ret);
 
     return ret;
}
}


// Speichert einen String/Integer/Boolean-Wert unter einem Namen ab
// Ermittelt die Groesse des benutzten Speichers
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// value: Zu speichernder String/Integer/Boolean-Wert
// return Groesse des genutzten Speichers in Bytes
// return Promise auf ein Objekt, das 'name' und 'value' der Operation enthaelt
function getMemSize(memory = undefined) {
function storeValue(name, value) {
     const __STORAGE = getMemory(memory);
     __LOG[4](name + " >> " + value);
    const __MEMORY = __STORAGE.Value;


     return GM.setValue(name, value).then(voidValue => {
     //getMemUsage(__MEMORY);
            __LOG[5]("OK " + name + " >> " + value);


            return Promise.resolve({
    if (__MEMORY !== undefined) {
                    'name'  : name,
        const __SIZE = safeStringify(__MEMORY).length;
                    'value' : value
 
                });
        __LOG[2]("MEM: " + __SIZE + " bytes");
         }, defaultCatch);
         return __SIZE;
    } else {
        return 0;
    }
}
}


// Holt einen String/Integer/Boolean-Wert unter einem Namen zurueck
// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// out: Logfunktion, etwa console.log
// return Promise fuer den String/Integer/Boolean-Wert, der unter dem Namen gespeichert war
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen)
function summonValue(name, defValue = undefined) {
// name: Name des Objekts
     return GM.getValue(name, defValue).then(value => {
function getMemUsage(value = undefined, out = undefined, depth = -1, name = '$') {
            __LOG[4](name + " << " + value);
     const __OUT = (out || __LOG[4]);


            return Promise.resolve(value);
    if ((typeof value) === 'string') {
         }, ex => {
         const __SIZE = value.length;
            __LOG[0](name + ": " + ex.message);


            return Promise.reject(ex);
        __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.substr(0, 255));
         }, defaultCatch);
    } else if ((typeof value) === 'object') {
}
         if (depth === 0) {
            const __SIZE = safeStringify(value).length;


// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
            __OUT("USAGE: " + name + '\t' + __SIZE);
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
        } else {
// value: Beliebiger (strukturierter) Wert
            depth--;
// return Promise auf ein Objekt, das 'name' und 'value' in der String-Darstellung des Wertes enthaelt
            for (let sub in value) {
function serialize(name, value) {
                getMemUsage(value[sub], __OUT, depth, name + '.' + sub);
    const __STREAM = ((value !== undefined) ? safeStringify(value) : value);
 
    return storeValue(name, __STREAM);
}
 
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
// name: GM.getValue()-Name, unter dem die Daten gespeichert wurden
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// return Promise fuer das Objekt, das unter dem Namen gespeichert war
function deserialize(name, defValue = undefined) {
    return summonValue(name).then(stream => {
            if (stream && stream.length) {
                return JSON.parse(stream);
            } else {
                return defValue;
             }
             }
         });
            getMemUsage(value, __OUT, 0, name);
}
         }
    } else {
      const __DATA = (((typeof value) === 'function') ? "" : '\t' + value);


// Setzt die Seite gemaess der Aenderungen zurueck...
         __OUT("USAGE: " + name + '\t' + (typeof value) + __DATA);
// reload: Seite wird ganz neu geladen
function refreshPage(reload = true) {
    if (reload) {
         __LOG[2]("Seite wird neu geladen...");
        window.location.reload();
     }
     }
}
}


// Setzt eine Option dauerhaft und laedt die Seite neu
// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// name: Name der Option als Speicherort
// opt: Option zur Wahl des Speichers
// value: Zu setzender Wert
// return Gesuchter Speicher oder Null-Speicher ('inaktiv')
// reload: Seite mit neuem Wert neu laden
function restoreMemoryByOpt(opt) {
// serial: Serialization fuer komplexe Daten
    // Memory Storage fuer vorherige Speicherung...
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
    const __STORAGE = getOptValue(opt, __MEMNORMAL, true, true);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gespeicherter Wert fuer setOptValue()
function setStored(name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
    (serial ? serialize(name, value)
            : storeValue(name, value))
                .then(onFulfilled, onRejected)
                .then(() => refreshPage(reload), defaultCatch); // Ende der Kette...


     return value;
     return __OPTMEM[__STORAGE];
}
}


// Setzt den naechsten Wert aus einer Array-Liste als Option
// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
// arr: Array-Liste mit den moeglichen Optionen
// opt: Option zur Wahl des Speichers
// name: Name der Option als Speicherort
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// value: Vorher gesetzter Wert
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
// reload: Seite mit neuem Wert neu laden
function startMemoryByOpt(opt, saveOpt = undefined) {
// serial: Serialization fuer komplexe Daten
    // Memory Storage fuer naechste Speicherung...
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
    let storage = getOptValue(opt, __MEMNORMAL);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
    let optMem = __OPTMEM[storage];
// return Gespeicherter Wert fuer setOptValue()
 
function setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
    if (! canUseMemory(optMem)) {
    return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);
        if (storage !== __MEMINAKTIVE) {
            storage = __MEMINAKTIVE;
            optMem = __OPTMEM[storage];
        }
    }
 
    if (saveOpt !== undefined) {
        setOpt(saveOpt, storage, false);
    }
 
    return optMem;
}
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// ==================== Ende Abschnitt fuer Speicher ====================
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
function getStoredCmds(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];


    if (__MEMORY !== undefined) {
// ==================== Abschnitt fuer die Scriptdatenbank ====================
        const __GETITEM = function(item) {
                              return __MEMORY.getItem(__RUNPREFIX + item);
                          };
        const __DELITEM = function(item) {
                              return __MEMORY.removeItem(__RUNPREFIX + item);
                          };
        const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);


        if (__CMD !== undefined) {
// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
            const __KEY = __GETITEM('key');
// meta: Metadaten des Scripts (Default: GM_info.script)
            let value = __GETITEM('val');
// return Beschreibende Daten fuer __DBMOD
function ScriptModule(meta) {
    'use strict';


            try {
    const __META = getValue(meta, GM_info.script);
                value = JSON.parse(value);
    const __PROPS = {
             } catch (ex) {
                'name'        : true,
                __LOG[0]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");
                'version'    : true,
                // ... meist kann man den String selber aber speichern, daher kein "return"...
                'namespace'  : true,
            }
                'description' : true
             };
 
    const __DBMOD = { };
 
    __LOG[5](__META);
 
    // Infos zu diesem Script...
    addProps(__DBMOD, __META, __PROPS);


            __STOREDCMDS.push({
    // Voller Name fuer die Ausgabe...
                                'cmd' : __CMD,
    Object.defineProperty(__DBMOD, 'Name', {
                                'key' : __KEY,
                    get : function() {
                                'val' : value
                              return this.name + " (" + this.version + ')';
                            });
                          },
        }
                    set : undefined
                });


        __DELITEM('cmd');
    __LOG[2](__DBMOD);
        __DELITEM('key');
        __DELITEM('val');
    }


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


// Fuehrt die in einem Storage gespeicherte Operation aus
Class.define(ScriptModule, Object);
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// optSet: Object mit den Optionen
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {
    const __BEFORELOAD = getValue(beforeLoad, true);
    const __STOREDCMDS = getValue(storedCmds, []);
    const __LOADEDCMDS = [];
    let invalidated = false;


    while (__STOREDCMDS.length) {
// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
        const __STORED = __STOREDCMDS.shift();
// optSet: Gesetzte Optionen (und Config)
        const __CMD = __STORED.cmd;
function initScriptDB(optSet) {
        const __KEY = __STORED.key;
    // Speicher fuer die DB-Daten...
        const __VAL = __STORED.val;
    const __DBMEM = myOptMem.Value;
 
    __DBTOC.versions = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
    __DBTOC.namespaces = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.namespaces')), { });
 
    // Zunaechst den alten Eintrag entfernen...
    delete __DBTOC.versions[__DBMOD.name];
    delete __DBTOC.namespaces[__DBMOD.name];


        if (__BEFORELOAD) {
    if (__DBMEM !== undefined) {
            if (__STOREDCMDS.length) {
        // ... und die Daten der Fremdscripte laden...
                await invalidateOpts(optSet);  // alle Optionen invalidieren
         for (let module in __DBTOC.versions) {
                invalidated = true;
             scriptDB(module, getValue(JSON.parse(__DBMEM.getItem('__DBDATA.' + module)), { }));
            }
            switch (__OPTACTION[__CMD]) {
            case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL);
                                  setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);
                                  break;
            case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL);
                                  //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false, onFulfilled, onRejected);
                                  setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);
                                  break;
            case __OPTACTION.RST : __LOG[4]("RESET (delayed)");
                                  __LOADEDCMDS.push(__STORED);
                                  break;
            default :              break;
            }
         } else {
            switch (__OPTACTION[__CMD]) {
             case __OPTACTION.SET :
            case __OPTACTION.NXT : __LOG[2]("SET/SETNEXT (undefined)");
                                  break;
            case __OPTACTION.RST : __LOG[4]("RESET");
                                  await resetOptions(optSet, false);
                                  await loadOptions(optSet); // Reset auf umbenannte Optionen anwenden!
                                  break;
            default :              break;
            }
         }
         }
     }
     }
    return (__LOADEDCMDS.length ? __LOADEDCMDS : undefined);
}
}


// Gibt eine Option sicher zurueck
// Setzt die Daten dieses Scriptes in der Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// opt: Config und Value der Option, ggfs. undefined
// optSet: Gesetzte Optionen (und Config)
// defOpt: Rueckgabewert, falls undefined
function updateScriptDB(optSet) {
// return Daten zur Option (oder defOpt)
    // Eintrag ins Inhaltsverzeichnis...
function getOpt(opt, defOpt = { }) {
    __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
    return getValue(opt, defOpt);
    __DBTOC.namespaces[__DBMOD.name] = __DBMOD.namespace;
}
 
    // Speicher fuer die DB-Daten...
    const __DBMEM = myOptMem.Value;
 
    if (__DBMEM !== undefined) {
        // Permanente Speicherung der Eintraege...
        __DBMEM.setItem('__DBTOC.versions', safeStringify(__DBTOC.versions));
        __DBMEM.setItem('__DBTOC.namespaces', safeStringify(__DBTOC.namespaces));
        __DBMEM.setItem('__DBDATA.' + __DBMOD.name, safeStringify(optSet));


// Gibt eine Option sicher zurueck (Version mit Key)
        // Aktualisierung der Speichergroesse...
// optSet: Platz fuer die gesetzten Optionen (und Config)
        myOptMemSize = getMemSize(myOptMem);
// item: Key der Option
// defOpt: Rueckgabewert, falls nicht zu finden
// return Daten zur Option (oder defOpt)
function getOptByName(optSet, item, defOpt = { }) {
    if ((optSet !== undefined) && (item !== undefined)) {
        return getOpt(optSet[item], defOpt);
    } else {
        return defOpt;
     }
     }
}


// Gibt die Konfigurationsdaten einer Option zurueck
    // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
// opt: Config und Value der Option
    scriptDB(__DBMOD.name, getValue(optSet, { }));
// defConfig: Rueckgabewert, falls Config nicht zu finden
 
// return Konfigurationsdaten der Option
     __LOG[2](__DBDATA);
function getOptConfig(opt, defConfig = { }) {
     return getValue(getOpt(opt).Config, defConfig);
}
}


// Setzt den Namen einer Option
// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
// opt: Config und Value der Option
// module: Gesetzte Optionen (und Config)
// name: Zu setzender Name der Option
// initValue: Falls angegeben, zugewiesener Startwert
// reload: Seite mit neuem Wert neu laden
// return Daten zu diesem Modul
// return Gesetzter Name der Option
function scriptDB(module, initValue = undefined) {
function setOptName(opt, name) {
     const __NAMESPACE = __DBTOC.namespaces[module];
     const __CONFIG = getOptConfig(opt);
     const __DBMODS = getProp(__DBDATA, __NAMESPACE, { });
     const __NAME = getOptName(opt);


     if (__NAME !== name) {
     if (initValue !== undefined) {
         __LOG[4]("RENAME " + __NAME + " => " + name);
         return (__DBMODS[module] = initValue);
 
    } else {
         __CONFIG.Name = name;
         return getProp(__DBMODS, module, { });
     }
     }
    return name;
}
}


// Gibt den Namen einer Option zurueck
// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
// opt: Config und Value der Option
// return Name der Option
function getOptName(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = __CONFIG.Name;


    if (! __NAME) {
// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
        const __SHARED = __CONFIG.Shared;


        if (__SHARED && ! opt.Loaded) {
// ==================== Abschnitt fuer das Benutzermenu ====================
            const __OBJREF = getSharedRef(__SHARED, opt.Item);


            return __OBJREF.getPath();
// Zeigt den Eintrag im Menu einer Option
        }
// val: Derzeitiger Wert der Option
 
// menuOn: Text zum Setzen im Menu
        showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
// 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(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
    const __ON  = (val ? '*' : "");
    const __OFF = (val ? "" : '*');
 
    __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
 
    if (val) {
        GM_registerMenuCommand(menuOff, funOff, keyOff);
    } else {
        GM_registerMenuCommand(menuOn, funOn, keyOn);
     }
     }
    return __NAME;
}
}


// Setzt den Wert einer Option
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// opt: Config und Value der Option
// val: Derzeitiger Wert der Option
// name: Zu setzender Wert der Option
// arr: Array-Liste mit den moeglichen Optionen
// return Gesetzter Wert
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
function setOptValue(opt, value) {
// fun: Funktion zum Setzen des naechsten Wertes
     if (opt !== undefined) {
// key: Hotkey zum Setzen des naechsten Wertes im Menu
        if (! opt.ReadOnly) {
function registerNextMenuOption(val, arr, menu, fun, key) {
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));
     const __MENU = menu.replace('$', val);
    let options = "OPTION " + __MENU;


             opt.Value = value;
    for (let value of arr) {
        if (value === val) {
            options += " / *" + value + '*';
        } else {
             options += " / " + value;
         }
         }
        return opt.Value;
    } else {
        return undefined;
     }
     }
    __LOG[3](options);
    GM_registerMenuCommand(__MENU, fun, key);
}
}


// Gibt den Wert einer Option zurueck
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// opt: Config und Value der Option
// val: Derzeitiger Wert der Option
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// load: Laedt die Option per loadOption(), falls noetig
// fun: Funktion zum Setzen des naechsten Wertes
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// return Gesetzter Wert
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {
// serial: Serialization fuer komplexe Daten
     let value;
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
    const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
    const __MENU = getValue(menu, "").replace('$', __VALUE);
    const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);
 
     __LOG[hidden ? 4 : 3](__OPTIONS);


     if (opt !== undefined) {
     if (! hidden) {
         if (load && ! opt.Loaded) {
         GM_registerMenuCommand(__MENU, fun, key);
            if (! opt.Promise) {
                loadOption(opt, force);
            }
            if (! asyncLoad) {
                __LOG[0]("Warnung: getOptValue(" + getOptName(opt) + ") fordert zum Nachladen auf, daher nur Default-Wert!");
            }
        } else {
            value = opt.Value;
        }
     }
     }
    return valueOf(getValue(value, defValue));
}
}


// ==================== Ende Abschnitt fuer diverse Utilities ====================
// Zeigt den Eintrag im Menu einer Option
// opt: Config und Value der Option
function registerOption(opt) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt);
    const __LABEL = __CONFIG.Label;
    const __ACTION = opt.Action;
    const __HOTKEY = __CONFIG.Hotkey;
    const __HIDDEN = __CONFIG.HiddenMenu;
    const __SERIAL = __CONFIG.Serial;


// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
    if (! __CONFIG.HiddenMenu) {
 
        switch (__CONFIG.Type) {
// Namen des Default-, Temporaer- und Null-Memories...
        case __OPTTYPES.MC : registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
const __MEMNORMAL  = 'normal';
                            break;
const __MEMSESSION  = 'begrenzt';
        case __OPTTYPES.SW : registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
const __MEMINAKTIVE = 'inaktiv';
                                                __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
                            break;
        case __OPTTYPES.TF : registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                            break;
        case __OPTTYPES.SD : registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
                            break;
        case __OPTTYPES.SI : registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
                            break;
        default :            break;
        }
    } else {
        // Nur Anzeige im Log...
        registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
    }
}


// Definition des Default-, Dauer- und Null-Memories...
// ==================== Ende Abschnitt fuer das Benutzermenu ====================
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
const __OPTMEMSESSION  = __OPTMEM[__MEMSESSION];
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];


// Medium fuer die Datenbank (Speicher)
// Initialisiert die gesetzten Option
let myOptMem = __OPTMEMNORMAL;
// config: Konfiguration der Option
let myOptMemSize;
// setValue: Zu uebernehmender Default-Wert (z.B. der jetzt gesetzte)
// return Initialwert der gesetzten Option
function initOptValue(config, setValue = undefined) {
    let value = getValue(setValue, config.Default); // Standard


// Infos ueber dieses Script-Modul
    if (config.SharedData !== undefined) {
const __DBMOD = new ScriptModule();
        value = config.SharedData;
    }


// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
    switch (config.Type) {
const __DBTOC = { };
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
 
                            value = config.Choice[0];
// Daten zu den Modulen (indiziert durch die Script-Namen)
                        }
const __DBDATA = { };
                        break;
 
    case __OPTTYPES.SW : break;
// ==================== Abschnitt fuer Speicher ====================
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                        break;
    case __OPTTYPES.SI : break;
    default :            break;
    }


// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
     if (config.Serial || config.Hidden) {
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
         config.HiddenMenu = true;
// defMemory: Ersatz-Wert, falls memory undefined. Soll nur memory genutzt werden, dann z.B. null uebergeben!
// return memory, falls okay, sonst einen Defaultwert
function getMemory(memory = undefined, defMemory = getValue(myOptMem, __OPTMEMNORMAL)) {
     return getValue(memory, defMemory);
}
 
// Kompatibilitaetsfunktion: Testet, ob der uebergebene Speicher genutzt werden kann
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return true, wenn der Speichertest erfolgreich war
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);
     }
     }


    __LOG[2]("canUseStorage(" + __STORAGE.Name + ") = " + ret);
     return value;
 
     return ret;
}
}


// Ermittelt die Groesse des benutzten Speichers
// Initialisiert die Menue-Funktion einer Option
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// optAction: Typ der Funktion
// return Groesse des genutzten Speichers in Bytes
// item: Key der Option
function getMemSize(memory = undefined) {
// optSet: Platz fuer die gesetzten Optionen (und Config)
     const __STORAGE = getMemory(memory);
// optConfig: Konfiguration der Option
    const __MEMORY = __STORAGE.Value;
// return Funktion fuer die Option
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
     let fun;


     //getMemUsage(__MEMORY);
     if (optAction !== undefined) {
        const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
        const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);


    if (__MEMORY !== undefined) {
        switch (optAction) {
         const __SIZE = safeStringify(__MEMORY).length;
        case __OPTACTION.SET : fun = function() {
                                      return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD);
                                  };
                              break;
        case __OPTACTION.NXT : fun = function() {
                                      return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice);
                                  };
                              break;
         case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD);
                                  };
                              break;
        default :              break;
        }
    }


        __LOG[2]("MEM: " + __SIZE + " bytes");
     return fun;
        return __SIZE;
     } else {
        return 0;
    }
}
}


// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus
// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
// out: Logfunktion, etwa __LOG[4]
// item: Key der Option
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen)
// return ObjRef, die das Ziel definiert
// name: Name des Objekts
function getSharedRef(shared, item = undefined) {
function getMemUsage(value = undefined, out = undefined, depth = -1, name = '$') {
     if (shared === undefined) {
     const __OUT = (out || __LOG[4]);
        return undefined;
    }


     if ((typeof value) === 'string') {
     const __OBJREF = new ObjRef(__DBDATA);  // Gemeinsame Daten
        const __SIZE = value.length;
    const __PROPS = [ 'namespace', 'module', 'item' ];
    const __DEFAULTS = [ __DBMOD.namespace, __DBMOD.name, item ];


        __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.slice(0, 255));
     for (let stage in __PROPS) {
     } else if ((typeof value) === 'object') {
         const __DEFAULT = __DEFAULTS[stage];
         if (depth === 0) {
        const __PROP = __PROPS[stage];
            const __SIZE = safeStringify(value).length;
        const __NAME = shared[__PROP];


            __OUT("USAGE: " + name + '\t' + __SIZE);
        if (__NAME === '$') {
        } else {
             break;
            depth--;
            for (let sub in value) {
                getMemUsage(value[sub], __OUT, depth, name + '.' + sub);
             }
            getMemUsage(value, __OUT, 0, name);
         }
         }
    } else {
      const __DATA = (((typeof value) === 'function') ? "" : '\t' + value);


         __OUT("USAGE: " + name + '\t' + (typeof value) + __DATA);
         __OBJREF.chDir(getValue(__NAME, __DEFAULT));
     }
     }
    return __OBJREF;
}
}


// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
// opt: Option zur Wahl des Speichers
// optConfig: Konfiguration der Option
// return Promise auf gesuchten Speicher oder Null-Speicher ('inaktiv')
// item: Key der Option
async function restoreMemoryByOpt(opt) {
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
     // Memory Storage fuer vorherige Speicherung...
function getSharedConfig(optConfig, item = undefined) {
     const __STORAGE = await getOptValue(opt, __MEMNORMAL, true, true, true);
     let config = getValue(optConfig, { });
     const __SHARED = config.Shared;


     return __OPTMEM[__STORAGE];
     if (__SHARED !== undefined) {
}
        const __OBJREF = getSharedRef(__SHARED, item); // Gemeinsame Daten


// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
        if (getValue(__SHARED.item, '$') !== '$') { // __REF ist ein Item
// opt: Option zur Wahl des Speichers
            const __REF = valueOf(__OBJREF);
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist
function startMemoryByOpt(opt, saveOpt = undefined, onFulfilled = undefined, onRejected = undefined) {
    // Memory Storage fuer naechste Speicherung...
    let storage = getOptValue(opt, __MEMNORMAL);
    let optMem = __OPTMEM[storage];


    if (! canUseMemory(optMem)) {
            config = { };  // Neu aufbauen...
        if (storage !== __MEMINAKTIVE) {
            addProps(config, getOptConfig(__REF));
            storage = __MEMINAKTIVE;
            addProps(config, optConfig);
             optMem = __OPTMEM[storage];
            config.setConst('SharedData', getOptValue(__REF));
        } else { // __REF enthaelt die Daten selbst
            if (! config.Name) {
                config.Name = __OBJREF.getPath();
             }
            config.setConst('SharedData', __OBJREF); // Achtung: Ggfs. zirkulaer!
         }
         }
     }
     }


    if (saveOpt !== undefined) {
     return config;
        setOpt(saveOpt, storage, false, onFulfilled, onRejected);
    }
 
     return optMem;
}
}


// ==================== Ende Abschnitt fuer Speicher ====================
// Initialisiert die gesetzten Optionen
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
// return Gefuelltes Objekt mit den gesetzten Optionen
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
    let value;


// ==================== Abschnitt fuer die Scriptdatenbank ====================
    if (optSet === undefined) {
        optSet = { };
    }


// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
    for (let opt in optConfig) {
// meta: Metadaten des Scripts (Default: GM.info.script)
        const __OPTCONFIG = optConfig[opt];
// return Beschreibende Daten fuer __DBMOD
        const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
function ScriptModule(meta) {
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);
    'use strict';


    const __META = getValue(meta, GM.info.script);
        if ((preInit === undefined) || (__PREINIT === preInit)) {
    const __PROPS = {
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
                 'name'       : true,
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
                 'version'    : true,
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat? Wurde ggfs. bereits geaendert...
                 'namespace'   : true,
            const __USESTUB = ((preInit === false) && __PREINIT);
                 'description' : true
            const __LOADED = (__USESTUB && optSet[opt].Loaded);
            const __VALUE = (__USESTUB ? optSet[opt].Value : undefined);
 
            optSet[opt] = {
                 'Item'      : opt,
                'Config'    : __CONFIG,
                'Loaded'   : (__ISSHARED || __LOADED),
                 'Value'    : initOptValue(__CONFIG, __VALUE),
                 'SetValue' : __CONFIG.SetValue,
                'ReadOnly'  : (__ISSHARED || __CONFIG.ReadOnly),
                'Action'    : initOptAction(__CONFIG.Action, opt, optSet, __CONFIG),
                 'AltAction' : initOptAction(__ALTACTION, opt, optSet, __CONFIG)
            };
        } else if (preInit) {  // erstmal nur Stub
            optSet[opt] = {
                'Item'      : opt,
                'Config'    : __OPTCONFIG,
                'Loaded'    : false,
                'Value'    : initOptValue(__OPTCONFIG),
                'ReadOnly'  : (__ISSHARED || __OPTCONFIG.ReadOnly)
             };
             };
        }
    }


     const __DBMOD = { };
     return optSet;
}


     __LOG[5](__META);
     // Abhaengigkeiten:
 
    // ================
     // Infos zu diesem Script...
    // initOptions (PreInit):
     addProps(__DBMOD, __META, __PROPS);
    // restoreMemoryByOpt: PreInit oldStorage
 
     // getStoredCmds: restoreMemoryByOpt
     // Voller Name fuer die Ausgabe...
     // runStoredCmds (beforeLoad): getStoredCmds
     Object.defineProperty(__DBMOD, 'Name', {
    // loadOptions (PreInit): PreInit
                    get : function() {
    // startMemoryByOpt: storage oldStorage
                              return this.name + " (" + this.version + ')';
    // initScriptDB: startMemoryByOpt
                          },
    // initOptions (Rest): PreInit
                    set : undefined
     // getMyTeam callback (getOptPrefix): initTeam
                });
     // __MYTEAM (initTeam): initOptions
    // renameOptions: getOptPrefix
    // runStoredCmds (afterLoad): getStoredCmds, renameOptions
    // loadOptions (Rest): PreInit/Rest runStoredCmds
    // updateScriptDB: startMemoryByOpt
    // showOptions: startMemoryByOpt renameOptions
    // buildMenu: showOptions
    // buildForm: showOptions


     __LOG[2](__DBMOD);
// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// return Gefuelltes Objekt mit den gesetzten Optionen
function startOptions(optConfig, optSet = undefined, classification = undefined) {
     optSet = initOptions(optConfig, optSet, true); // PreInit


     return __DBMOD;
     // Memory Storage fuer vorherige Speicherung...
}
    myOptMemSize = getMemSize(myOptMem = restoreMemoryByOpt(optSet.oldStorage));


Class.define(ScriptModule, Object);
    // Zwischengespeicherte Befehle auslesen...
    const __STOREDCMDS = getStoredCmds(myOptMem);


// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
    // ... ermittelte Befehle ausführen...
// optSet: Gesetzte Optionen (und Config)
     const __LOADEDCMDS = runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad
function initScriptDB(optSet) {
    // Speicher fuer die DB-Daten...
     const __DBMEM = myOptMem.Value;


     __DBTOC.versions = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.versions')), { });
     // Bisher noch nicht geladenene Optionen laden...
     __DBTOC.namespaces = getValue((__DBMEM === undefined) ? undefined : JSON.parse(__DBMEM.getItem('__DBTOC.namespaces')), { });
     loadOptions(optSet);


     // Zunaechst den alten Eintrag entfernen...
     // Memory Storage fuer naechste Speicherung...
     delete __DBTOC.versions[__DBMOD.name];
     myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));
    delete __DBTOC.namespaces[__DBMOD.name];


     if (__DBMEM !== undefined) {
     // Globale Daten ermitteln...
        // ... und die Daten der Fremdscripte laden...
    initScriptDB(optSet);
        for (let module in __DBTOC.versions) {
            scriptDB(module, getValue(JSON.parse(__DBMEM.getItem('__DBDATA.' + module)), { }));
        }
    }
}


// Setzt die Daten dieses Scriptes in der Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
    optSet = initOptions(optConfig, optSet, false)// Rest
// optSet: Gesetzte Optionen (und Config)
function updateScriptDB(optSet) {
    // Eintrag ins Inhaltsverzeichnis...
    __DBTOC.versions[__DBMOD.name] = __DBMOD.version;
    __DBTOC.namespaces[__DBMOD.name] = __DBMOD.namespace;


    // Speicher fuer die DB-Daten...
     if (classification !== undefined) {
    const __DBMEM = myOptMem.Value;
         // Umbenennungen durchfuehren...
 
         classification.renameOptions();
     if (__DBMEM !== undefined) {
    }
         // Permanente Speicherung der Eintraege...
         __DBMEM.setItem('__DBTOC.versions', safeStringify(__DBTOC.versions));
        __DBMEM.setItem('__DBTOC.namespaces', safeStringify(__DBTOC.namespaces));
        __DBMEM.setItem('__DBDATA.' + __DBMOD.name, safeStringify(optSet));


        // Aktualisierung der Speichergroesse...
    // ... ermittelte Befehle ausführen...
        myOptMemSize = getMemSize(myOptMem);
    runStoredCmds(__LOADEDCMDS, optSet, false); // Rest
    }


     // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
     // Als globale Daten speichern...
     scriptDB(__DBMOD.name, getValue(optSet, { }));
     updateScriptDB(optSet);


     __LOG[2](__DBDATA);
     return optSet;
}
}


// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
// Installiert die Visualisierung und Steuerung der Optionen
// module: Gesetzte Optionen (und Config)
// optSet: Platz fuer die gesetzten Optionen
// initValue: Falls angegeben, zugewiesener Startwert
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Daten zu diesem Modul
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function scriptDB(module, initValue = undefined) {
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
     const __NAMESPACE = __DBTOC.namespaces[module];
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
     const __DBMODS = getProp(__DBDATA, __NAMESPACE, { });
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     if (! optParams.hideMenu) {
        buildMenu(optSet);
     }


     if (initValue !== undefined) {
     if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
        return (__DBMODS[module] = initValue);
         buildForm(optParams.menuAnchor, optSet, optParams);
    } else {
         return getProp(__DBMODS, module, { });
     }
     }
}
}


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


// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================
// Ermittelt die naechste moegliche Option
// opt: Config und Value der Option
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
function getNextOpt(opt, value = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);


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


// Zeigt den Eintrag im Menu einer Option
    return __VALUE;
// val: Derzeitiger Wert der Option
}
// menuOn: Text zum Setzen im Menu
 
// funOn: Funktion zum Setzen
// Setzt die naechste moegliche Option
// keyOn: Hotkey zum Setzen im Menu
// opt: Config und Value der Option
// menuOff: Text zum Ausschalten im Menu
// value: Default fuer ggfs. zu setzenden Wert
// funOff: Funktion zum Ausschalten
// reload: Seite mit neuem Wert neu laden
// keyOff: Hotkey zum Ausschalten im Menu
// return Gesetzter Wert
// return Promise von GM.registerMenuCommand()
function setNextOpt(opt, value = undefined, reload = false) {
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
     return setOpt(opt, getNextOpt(opt, value), reload);
     const __ON  = (val ? '*' : "");
}
    const __OFF = (val ? "" : '*');


     __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab
// opt: Config und Value der Option
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// return Gesetzter Wert
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3) {
     const __CONFIG = getOptConfig(opt);
    const __CHOICE = __CONFIG.Choice;


     if (val) {
     if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {
        return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);
         return setNextOpt(opt, value, reload);
    } else {
         return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);
     }
     }
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
     const __VALUE = getOptValue(opt, value);
// val: 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
// return Promise von GM.registerMenuCommand()
function registerNextMenuOption(val, arr, menu, fun, key) {
     const __MENU = substParam(menu, val);
    let options = "OPTION " + __MENU;


     for (let value of arr) {
     try {
        if (value === val) {
        const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
            options += " / *" + value + '*';
        let message = "";
 
        if (selValue) {
            for (let index = 0; index < __CHOICE.length; index++) {
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
            }
            message += "\nNummer eingeben:";
         } else {
         } else {
             options += " / " + value;
             message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
         }
         }
    }
    __LOG[3](options);


    return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
        const __ANSWER = prompt(message, __NEXTVAL);
}


// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
        if (__ANSWER) {
// val: Derzeitiger Wert der Option
            const __INDEX = parseInt(__ANSWER, 10) - 1;
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);
// fun: Funktion zum Setzen des naechsten Wertes
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
// serial: Serialization fuer komplexe Daten
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
    const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
    const __MENU = substParam(menu, __VALUE);
    const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);


    __LOG[hidden ? 4 : 3](__OPTIONS);
            if (nextVal === undefined) {
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
                const __CASTVAL = this[__VALTYPE](__ANSWER);


    if (hidden) {
                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
        return Promise.resolve(__VALUE);
                    nextVal = __CASTVAL;
    } else {
                }
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
            }
    }
}


// Zeigt den Eintrag im Menu einer Option
            if (nextVal !== __VALUE) {
// opt: Config und Value der Option
                if (nextVal) {
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
                    return setOpt(opt, nextVal, reload);
function registerOption(opt) {
                }
    const __CONFIG = getOptConfig(opt);
 
    const __VALUE = getOptValue(opt);
                const __LABEL = __CONFIG.Label.replace('$', __VALUE);
    const __LABEL = __CONFIG.Label;
    const __ACTION = opt.Action;
    const __HOTKEY = __CONFIG.Hotkey;
    const __HIDDEN = __CONFIG.HiddenMenu;
    const __SERIAL = __CONFIG.Serial;


    if (! __CONFIG.HiddenMenu) {
                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
        switch (__CONFIG.Type) {
            }
        case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
        case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                      __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
        case __OPTTYPES.TF : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                      __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
        case __OPTTYPES.SD : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
        case __OPTTYPES.SI : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
        default :            return Promise.resolve(__VALUE);
         }
         }
     } else {
     } catch (ex) {
         // Nur Anzeige im Log...
         __LOG[1]("promptNextOpt: " + ex.message);
        return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
     }
     }
    return __VALUE;
}
}


// ==================== 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
}
// setValue: Zu uebernehmender Default-Wert (z.B. der jetzt gesetzte)
// return Initialwert der gesetzten Option
function initOptValue(config, setValue = undefined) {
    let value = getValue(setValue, config.Default); // Standard


    if (config.SharedData !== undefined) {
// Ermittelt die naechste moegliche Option (Version mit Key)
        value = config.SharedData;
// optSet: Platz fuer die gesetzten Optionen (und Config)
    }
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
    const __OPT = getOptByName(optSet, item);


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


    if (config.Serial || config.Hidden) {
// Setzt die naechste moegliche Option (Version mit Key)
        config.HiddenMenu = true;
// optSet: Platz fuer die gesetzten Optionen (und Config)
    }
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Wert
function setNextOptByName(optSet, item, value = undefined, reload = false) {
    const __OPT = getOptByName(optSet, item);


     return value;
     return setNextOpt(__OPT, value, reload);
}
}


// Initialisiert die Menue-Funktion einer Option
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
// optAction: Typ der Funktion
// 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: Default fuer ggfs. zu setzenden Wert
// optConfig: Konfiguration der Option
// reload: Seite mit neuem Wert neu laden
// return Funktion fuer die Option
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
     let fun;
// return Gesetzter Wert
function promptNextOptByName(optSet, item, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3) {
     const __OPT = getOptByName(optSet, item);


     if (optAction !== undefined) {
     return promptNextOpt(__OPT, value, reload, freeValue, selValue, minChoice);
        const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
}
        const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);


        switch (optAction) {
// Baut das Benutzermenu auf
        case __OPTACTION.SET : fun = function() {
// optSet: Gesetzte Optionen
                                      return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);
function buildMenu(optSet) {
                                  };
    __LOG[3]("buildMenu()");
                              break;
 
        case __OPTACTION.NXT : fun = function() {
    for (let opt in optSet) {
                                      return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
         registerOption(optSet[opt]);
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice).catch(defaultCatch);
                                  };
                              break;
        case __OPTACTION.RST : fun = function() {
                                      return resetOptions(optSet, __RELOAD).then(
                                              result => __LOG[3]("RESETTING (" + result + ")..."),
                                              defaultCatch);
                                  };
                              break;
         default :              break;
        }
     }
     }
    return fun;
}
}


// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
// Invalidiert eine (ueber Menu) gesetzte Option
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
// opt: Zu invalidierende Option
// item: Key der Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return ObjRef, die das Ziel definiert
function invalidateOpt(opt, force = false) {
function getSharedRef(shared, item = undefined) {
     if (! opt.ReadOnly) {
     if (shared === undefined) {
         const __CONFIG = getOptConfig(opt);
         return undefined;
    }
 
    const __OBJREF = new ObjRef(__DBDATA);  // Gemeinsame Daten
    const __PROPS = [ 'namespace', 'module', 'item' ];
    const __DEFAULTS = [ __DBMOD.namespace, __DBMOD.name, item ];


    for (let stage in __PROPS) {
         // Wert "ungeladen"...
         const __DEFAULT = __DEFAULTS[stage];
         opt.Loaded = (force || ! __CONFIG.AutoReset);
         const __PROP = __PROPS[stage];
        const __NAME = shared[__PROP];


         if (__NAME === '$') {
         if (opt.Loaded && __CONFIG.AutoReset) {
             break;
             // Nur zuruecksetzen, gilt als geladen...
            setOptValue(opt, initOptValue(__CONFIG));
         }
         }
        __OBJREF.chDir(getValue(__NAME, __DEFAULT));
     }
     }
}


    return __OBJREF;
// Invalidiert die (ueber Menu) gesetzten Optionen
}
// optSet: Set mit den Optionen
 
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
// return Set mit den geladenen Optionen
// optConfig: Konfiguration der Option
function invalidateOpts(optSet, force = false) {
// item: Key der Option
     for (let opt in optSet) {
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
        const __OPT = optSet[opt];
function getSharedConfig(optConfig, item = undefined) {
     let config = getValue(optConfig, { });
    const __SHARED = config.Shared;


    if (__SHARED !== undefined) {
         if (__OPT.Loaded) {
        const __OBJREF = getSharedRef(__SHARED, item);  // Gemeinsame Daten
             invalidateOpt(__OPT, force);
 
         if (getValue(__SHARED.item, '$') !== '$') {  // __OBJREF ist ein Item
            const __REF = valueOf(__OBJREF);
 
            config = { };  // Neu aufbauen...
             addProps(config, getOptConfig(__REF));
            addProps(config, optConfig);
            config.setConst('SharedData', getOptValue(__REF), false);  // Wert muss schon da sein, NICHT nachladen, sonst ggfs. Promise
        } else {  // __OBJREF enthaelt die Daten selbst
            if (! config.Name) {
                config.Name = __OBJREF.getPath();
            }
            config.setConst('SharedData', __OBJREF);  // Achtung: Ggfs. zirkulaer!
         }
         }
     }
     }


     return config;
     return optSet;
}
}


// 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
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
// return Gesetzter Wert der gelandenen Option
// return Gefuelltes Objekt mit den gesetzten Optionen
function loadOption(opt, force = false) {
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __ISSHARED = getValue(__CONFIG.Shared, false, true);
    const __NAME = getOptName(opt);
    const __DEFAULT = getOptValue(opt, undefined, false, false);
     let value;
     let value;


     if (optSet === undefined) {
     if (opt.Loaded && ! __ISSHARED) {
         optSet = { };
         __LOG[1]("Error: Oprion '" + __NAME + "' bereits geladen!");
     }
     }


     for (let opt in optConfig) {
     if (opt.ReadOnly || __ISSHARED) {
         const __OPTCONFIG = optConfig[opt];
         value = __DEFAULT;
         const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
    } else if (! force && __CONFIG.AutoReset) {
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);
        value = initOptValue(__CONFIG);
    } else {
         value = (__CONFIG.Serial ?
                        deserialize(__NAME, __DEFAULT) :
                        GM_getValue(__NAME, __DEFAULT));
    }
 
    __LOG[5]("LOAD " + __NAME + ": " + __LOG.changed(__DEFAULT, value));
 
    // Wert als geladen markieren...
    opt.Loaded = true;
 
    // Wert intern setzen...
    return setOptValue(opt, value);
}


        if ((preInit === undefined) || (__PREINIT === preInit)) {
// Laedt die (ueber Menu) gesetzten Optionen
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
// optSet: Set mit den Optionen
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?
// return Set mit den geladenen Optionen
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);
function loadOptions(optSet, force = false) {
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);
    for (let opt in optSet) {
            const __VALUE = (__LOADED ? optSet[opt].Value : undefined);
        const __OPT = optSet[opt];


            optSet[opt] = {
         if (! __OPT.Loaded) {
                'Item'      : opt,
             loadOption(__OPT, force);
                'Config'    : __CONFIG,
                'Loaded'    : (__ISSHARED || __LOADED),
                'Promise'  : __PROMISE,
                'Value'    : initOptValue(__CONFIG, __VALUE),
                'SetValue'  : __CONFIG.SetValue,
                'ReadOnly'  : (__ISSHARED || __CONFIG.ReadOnly),
                'Action'    : initOptAction(__CONFIG.Action, opt, optSet, __CONFIG),
                'AltAction' : initOptAction(__ALTACTION, opt, optSet, __CONFIG)
            };
         } else if (preInit) { // erstmal nur Stub
             optSet[opt] = {
                'Item'      : opt,
                'Config'    : __OPTCONFIG,
                'Loaded'    : false,
                'Promise'  : undefined,
                'Value'    : initOptValue(__OPTCONFIG),
                'ReadOnly'  : (__ISSHARED || __OPTCONFIG.ReadOnly)
            };
         }
         }
     }
     }
Zeile 2.803: Zeile 2.677:
}
}


    // Abhaengigkeiten:
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
    // ================
// opt: Gesetzte Option
    // initOptions (PreInit):
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
    // restoreMemoryByOpt: PreInit oldStorage
// reset: Setzt bei Erfolg auf Initialwert der Option
    // getStoredCmds: restoreMemoryByOpt
function deleteOption(opt, force = false, reset = true) {
    // runStoredCmds (beforeLoad): getStoredCmds
     const __CONFIG = getOptConfig(opt);
    // loadOptions (PreInit): PreInit
    // startMemoryByOpt: storage oldStorage
    // initScriptDB: startMemoryByOpt
    // initOptions (Rest): PreInit
     // getMyTeam callback (getOptPrefix): initTeam
    // __MYTEAM (initTeam): initOptions
    // renameOptions: getOptPrefix
    // runStoredCmds (afterLoad): getStoredCmds, renameOptions
    // loadOptions (Rest): PreInit/Rest runStoredCmds
    // updateScriptDB: startMemoryByOpt
    // showOptions: startMemoryByOpt renameOptions
    // buildMenu: showOptions
    // buildForm: showOptions


// Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start
    if (force || ! __CONFIG.Permanent) {
// optConfig: Konfiguration der Optionen
        const __NAME = getOptName(opt);
// optSet: Platz fuer die gesetzten Optionen
// return Promise auf gefuelltes Objekt mit den gesetzten Optionen
async function startOptions(optConfig, optSet = undefined, classification = undefined) {
    optSet = initOptions(optConfig, optSet, true); // PreInit


    // Memory Storage fuer vorherige Speicherung...
        __LOG[4]("DELETE " + __NAME);
    myOptMemSize = getMemSize(myOptMem = await restoreMemoryByOpt(optSet.oldStorage));


    // Zwischengespeicherte Befehle auslesen...
        GM_deleteValue(__NAME);
    const __STOREDCMDS = getStoredCmds(myOptMem);


    // ... ermittelte Befehle ausfuehren...
        if (reset) {
    const __LOADEDCMDS = await runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad
            setOptValue(opt, initOptValue(__CONFIG));
        }
    }
}


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


     // Memory Storage fuer naechste Speicherung...
     for (let opt in optSet) {
    myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));
        if (getValue(__OPTSELECT[opt], __DELETEALL)) {
            deleteOption(optSet[opt], force, reset);
        }
    }
}


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


    optSet = initOptions(optConfig, optSet, false); // Rest
        setOptName(opt, name);


    if (classification !== undefined) {
        if (reload) {
        // Umbenennungen durchfuehren...
            loadOption(opt, force);
        await classification.renameOptions();
        }
     }
     }


     // ... ermittelte Befehle ausfuehren...
     return opt;
    await runStoredCmds(__LOADEDCMDS, optSet, false);  // Rest
}


    // Als globale Daten speichern...
// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
     updateScriptDB(optSet);
// name: Gesetzter Name (Speicheradresse)
// prefix: Prefix, das vorangestellt werden soll
// return Neu zu setzender Name (Speicheradresse)
function prefixName(name, prefix) {
     return (prefix + name);
}


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


// Installiert die Visualisierung und Steuerung der Optionen
// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// optSet: Platz fuer die gesetzten Optionen
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// 'reload': Option nachladen?
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// renameParam: Wird an renameFun uebergeen
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// renameFun: function(name, param) zur Ermittlung des neuen Namens
// 'formWidth': Anzahl der Elemente pro Zeile
// - name: Neu zu setzender Name (Speicheradresse)
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix
// return Liefert die gesetzten Optionen zurueck
function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
     if (renameFun === undefined) {
     if (! optParams.hideMenu) {
         __LOG[1]("RENAME: Illegale Funktion!");
         buildMenu(optSet).then(() => __LOG[3]("Menu OK"));
     }
     }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];
        if (__OPT === undefined) {
            __LOG[1]("RENAME: Option '" + opt + "' nicht gefunden!");
        } else {
            const __NAME = getOptName(__OPT);
            const __NEWNAME = renameFun(__NAME, renameParam);
            const __ISSCALAR = ((typeof __OPTPARAMS) === 'boolean');
            // Laedt die unter dem neuen Namen gespeicherten Daten nach?
            const __RELOAD = (__ISSCALAR ? __OPTPARAMS : __OPTPARAMS.reload);
            // Laedt auch Optionen mit 'AutoReset'-Attribut?
            const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force);


    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
            renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE);
        buildForm(optParams.menuAnchor, optSet, optParams);
        }
     }
     }
}


     return optSet;
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// optSet: Gesetzte Optionen
// reload: Seite mit "Werkseinstellungen" neu laden
function resetOptions(optSet, reload = true) {
     // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
    deleteOptions(optSet, true, false, ! reload);
 
    if (reload) {
        // ... und Seite neu laden (mit "Werkseinstellungen")...
        window.location.reload();
    }
}
}


// Setzt eine Option auf einen vorgegebenen Wert
// ==================== Abschnitt fuer diverse Utilities ====================
// 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
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
function setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
    return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));
}


// Ermittelt die naechste moegliche Option
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// opt: Config und Value der Option
// form: <form>...</form>
// value: Ggfs. zu setzender Wert
// props: Map von name:value-Paaren
// return Zu setzender Wert
// type: Typ der Input-Felder (Default: unsichtbare Daten)
function getNextOpt(opt, value = undefined) {
// return Ergaenztes Form-Konstrukt
     const __CONFIG = getOptConfig(opt);
function addInputField(form, props, type = "hidden") {
    const __VALUE = getOptValue(opt, value);
     for (let fieldName in props) {
 
        let field = form[fieldName];
    switch (__CONFIG.Type) {
        if (! field) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
            field = document.createElement("input");
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
            field.type = type;
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
            field.name = fieldName;
    case __OPTTYPES.SD : return getValue(value, __VALUE);
            form.appendChild(field);
    case __OPTTYPES.SI : break;
        }
    default :            break;
        field.value = props[fieldName];
     }
     }


     return __VALUE;
     return form;
}
}


// Setzt die naechste moegliche Option
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// opt: Config und Value der Option
// form: <form>...</form>
// value: Default fuer ggfs. zu setzenden Wert
// props: Map von name:value-Paaren
// reload: Seite mit neuem Wert neu laden
// return Ergaenztes Form-Konstrukt
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
function addHiddenField(form, props) {
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
     return addInputField(form, props, "hidden");
// return Gesetzter Wert
function setNextOpt(opt, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
     return setOpt(opt, getNextOpt(opt, value), reload, onFulfilled, onRejected);
}
}


// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// opt: Config und Value der Option
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// value: Default fuer ggfs. zu setzenden Wert
// type: Name des Events, z.B. "click"
// reload: Seite mit neuem Wert neu laden
// callback: Funktion als Reaktion
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// return false bei Misserfolg
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
function addEvent(obj, type, callback, capture = false) {
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
    if (obj.addEventListener) {
// return Gesetzter Wert
        return obj.addEventListener(type, callback, capture);
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
    } else if (obj.attachEvent) {
    const __CONFIG = getOptConfig(opt);
        return obj.attachEvent("on" + type, callback);
    const __CHOICE = __CONFIG.Choice;
    } else {
        __LOG[1]("Could not add " + type + " event:");
        __LOG[2](callback);


    if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {
         return false;
         return setNextOpt(opt, value, reload, onFulfilled, onRejected);
     }
     }
}


     const __VALUE = getOptValue(opt, value);
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function removeEvent(obj, type, callback, capture = false) {
     if (obj.removeEventListener) {
        return obj.removeEventListener(type, callback, capture);
    } else if (obj.detachEvent) {
        return obj.detachEvent("on" + type, callback);
    } else {
        __LOG[1]("Could not remove " + type + " event:");
        __LOG[2](callback);


    try {
         return false;
         const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
    }
        let message = "";
}


        if (selValue) {
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
            for (let index = 0; index < __CHOICE.length; index++) {
// id: ID des betroffenen Eingabeelements
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
// type: Name des Events, z.B. "click"
            }
// callback: Funktion als Reaktion
            message += "\nNummer oder Wert eingeben:";
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
        } else {
// return false bei Misserfolg
            message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
function addDocEvent(id, type, callback, capture = false) {
        }
    const __OBJ = document.getElementById(id);


        const __ANSWER = prompt(message, __NEXTVAL);
    return addEvent(__OBJ, type, callback, capture);
}


        if (__ANSWER) {
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
            const __INDEX = parseInt(__ANSWER, 10) - 1;
// id: ID des betroffenen Eingabeelements
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function removeDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);


            if (nextVal === undefined) {
    return removeEvent(__OBJ, type, callback, capture);
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
}
                const __CASTVAL = this[__VALTYPE](__ANSWER);


                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
                    nextVal = __CASTVAL;
// name: Name des Elements (siehe "name=")
                }
// index: Laufende Nummer des Elements (0-based), Default: 0
            }
// doc: Dokument (document)
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
function getElement(name, index = 0, doc = document) {
    const __TAGS = document.getElementsByName(name);
    const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];


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


                const __LABEL = substParam(__CONFIG.Label, __VALUE);
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// index: Laufende Nummer des Elements (0-based)
// tag: Tag des Elements ("table")
// doc: Dokument (document)
// return Gesuchtes Element oder undefined (falls nicht gefunden)
function getTable(index, tag = "table", doc = document) {
    const __TAGS = document.getElementsByTagName(tag);
    const __TABLE = (__TAGS === undefined) ? undefined : __TAGS[index];


                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
     return __TABLE;
            }
        }
    } catch (ex) {
        __LOG[0]("promptNextOpt: " + ex.message);
    }
 
     return __VALUE;
}
}


// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// index: Laufende Nummer des Elements (0-based)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// doc: Dokument (document)
// item: Key der Option
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
// value: (Bei allen Typen) Zu setzender Wert
function getRows(index, doc = document) {
// reload: Seite mit neuem Wert neu laden
    const __TABLE = getTable(index, "table", doc);
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
     const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
function setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
     const __OPT = getOptByName(optSet, item);


     return setOpt(__OPT, value, reload, onFulfilled, onRejected);
     return __ROWS;
}
}


// Ermittelt die naechste moegliche Option (Version mit Key)
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
    const __OPT = getOptByName(optSet, item);


    return getNextOpt(__OPT, value);
// Liefert den Funktionsaufruf zur Option als String
}
// opt: Auszufuehrende Option
 
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// Setzt die naechste moegliche Option (Version mit Key)
// value: Ggfs. zu setzender Wert
// optSet: Platz fuer die gesetzten Optionen (und Config)
// serial: Serialization fuer String-Werte (Select, Textarea)
// item: Key der Option
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// value: Default fuer ggfs. zu setzenden Wert
// return String mit dem (reinen) Funktionsaufruf
// reload: Seite mit neuem Wert neu laden
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
    const __STORAGE = getMemory(memory);
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
    const __MEMORY = __STORAGE.Value;
// return Gesetzter Wert
    const __MEMSTR = __STORAGE.Display;
function setNextOptByName(optSet, item, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
    const __RUNPREFIX = __STORAGE.Prefix;
    const __OPT = getOptByName(optSet, item);
 
    if (__MEMORY !== undefined) {
        const __RELOAD = "window.location.reload()";
        const __SETITEM = function(item, val, quotes = true) {
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                          };
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                          };
        const __CONFIG = getOptConfig(opt);
        const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
        const __THISVAL = ((__CONFIG.ValType === "String") ? "'\\x22' + this.value + '\\x22'" : "this.value");
        const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
        const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);
 
        if (__ACTION !== undefined) {
            switch (__ACTION) {
            case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                  return __SETITEMS('SET', getOptName(opt), __VALSTR);
            case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                  return __SETITEMS('NXT', getOptName(opt), __VALSTR);
            case __OPTACTION.RST : //return "doActionRst()";
                                  return __SETITEMS('RST');
            default :              break;
            }
        }
    }


     return setNextOpt(__OPT, value, reload, onFulfilled, onRejected);
     return undefined;
}
}


// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
// Liefert die Funktionsaufruf zur Option als String
// optSet: Platz fuer die gesetzten Optionen (und Config)
// opt: Auszufuehrende Option
// item: Key der Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Default fuer ggfs. zu setzenden Wert
// value: Ggfs. zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// serial: Serialization fuer String-Werte (Select, Textarea)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// return String mit dem (reinen) Funktionsaufruf
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined, memory = undefined) {
// return Gesetzter Wert
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
function promptNextOptByName(optSet, item, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
     const __OPT = getOptByName(optSet, item);


     return promptNextOpt(__OPT, value, reload, freeValue, selValue, minChoice, onFulfilled, onRejected);
     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
}
}


// Baut das Benutzermenu auf (asynchron im Hintergrund)
// Zeigt eine Option auf der Seite als Auswahlbox an
// optSet: Gesetzte Optionen
// opt: Anzuzeigende Option
// return Promise auf void
// return String mit dem HTML-Code
async function buildMenu(optSet) {
function getOptionSelect(opt) {
     __LOG[3]("buildMenu()");
     const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


     for (let opt in optSet) {
    if (__CONFIG.FreeValue && ! (~ __CONFIG.Choice.indexOf(__VALUE))) {
         await registerOption(optSet[opt]).then(
        element += '\n<option value="' + __VALUE + '" SELECTED>' + __VALUE + '</option>';
                result => __LOG[6](`REGISTEROPTION[${opt}] = ${result}`),
    }
                defaultCatch);
     for (let value of __CONFIG.Choice) {
         element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
     }
     }
    element += '\n</select>';
    return __LABEL.replace('$', element);
}
}


// Invalidiert eine (ueber Menu) gesetzte Option
// Zeigt eine Option auf der Seite als Radiobutton an
// opt: Zu invalidierende Option
// opt: Anzuzeigende Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return String mit dem HTML-Code
// return Promise auf resultierenden Wert
function getOptionRadio(opt) {
function invalidateOpt(opt, force = false) {
     const __CONFIG = getOptConfig(opt);
     return Promise.resolve(opt.Promise).then(value => {
    const __NAME = getOptName(opt);
            if (opt.Loaded && ! opt.ReadOnly) {
    const __VALUE = getOptValue(opt, false);
                const __CONFIG = getOptConfig(opt);
    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>';


                // Wert "ungeladen"...
    return [ __ELEMENTON, __ELEMENTOFF ];
                opt.Loaded = (force || ! __CONFIG.AutoReset);
}


                if (opt.Loaded && __CONFIG.AutoReset) {
// Zeigt eine Option auf der Seite als Checkbox an
                    // Nur zuruecksetzen, gilt als geladen...
// opt: Anzuzeigende Option
                    setOptValue(opt, initOptValue(__CONFIG));
// 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 getOptValue(opt);
    return '<input type="checkbox" name="' + __NAME +
        }, defaultCatch);
          '" id="' + __NAME + '" value="' + __VALUE + '"' +
          (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
          __NAME + '">' + __FORMLABEL + '</label>';
}
}


// Invalidiert die (ueber Menu) gesetzten Optionen
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// optSet: Object mit den Optionen
// opt: Anzuzeigende Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return String mit dem HTML-Code
// return Promise auf Object mit den geladenen Optionen
function getOptionTextarea(opt) {
async function invalidateOpts(optSet, force = false) {
    const __CONFIG = getOptConfig(opt);
     for (let opt in optSet) {
    const __NAME = getOptName(opt);
        const __OPT = optSet[opt];
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
    const __SUBMIT = getValue(__CONFIG.Submit, "");
    //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");
     const __FORMLABEL = 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 + '>' +
                          safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


        await invalidateOpt(__OPT, force);
    return [ __ELEMENTLABEL, __ELEMENTTEXT ];
    }
}


     return optSet;
// Zeigt eine Option auf der Seite als Button an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionButton(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
    const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
    const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
 
     return '<label for="' + __NAME + '">' + __FORMLABEL +
          '</label><input type="button" name="' + __NAME +
          '" id="' + __NAME + '" value="' + __BUTTONLABEL + '"' +
          __ACTION + '/>';
}
}


// Laedt eine (ueber Menu) gesetzte Option
// Zeigt eine Option auf der Seite an (je nach Typ)
// opt: Zu ladende Option
// opt: Anzuzeigende Option
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return String mit dem HTML-Code
// return Promise auf gesetzten Wert der gelandenen Option
function getOptionElement(opt) {
function loadOption(opt, force = false) {
     const __CONFIG = getOptConfig(opt);
     if (! opt.Promise) {
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
        const __CONFIG = getOptConfig(opt);
    let element = "";
        const __ISSHARED = getValue(__CONFIG.Shared, false, true);
        const __NAME = getOptName(opt);
        const __DEFAULT = getOptValue(opt, undefined, false, false, false);
        let value;


         if (opt.Loaded && ! __ISSHARED) {
    if (! __CONFIG.Hidden) {
            const __ERROR = "Error: Oprion '" + __NAME + "' bereits geladen!";
        switch (__TYPE) {
 
        case __OPTTYPES.MC : element = getOptionSelect(opt);
            __LOG[0](__MESSAGE);
                            break;
 
         case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
            return Promise.reject(__MESSAGE);
                                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 (opt.ReadOnly || __ISSHARED) {
         if (element.length === 2) {
            value = __DEFAULT;
             element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
        } else if (! force && __CONFIG.AutoReset) {
            value = initOptValue(__CONFIG);
        } else {
             value = (__CONFIG.Serial ?
                            deserialize(__NAME, __DEFAULT) :
                            GM.getValue(__NAME, __DEFAULT));
         }
         }
    }


        opt.Promise = Promise.resolve(value).then(value => {
     return element;
                // Paranoide Sicherheitsabfrage (das sollte nie passieren!)...
                if (opt.Loaded || ! opt.Promise) {
                    showAlert("Error", "Unerwarteter Widerspruch zwischen opt.Loaded und opt.Promise", safeStringify(opt));
                }
                __LOG[5]("LOAD " + __NAME + ": " + __LOG.changed(__DEFAULT, value));
 
                // Wert intern setzen...
                const __VAL = setOptValue(opt, value);
 
                // Wert als geladen markieren...
                opt.Promise = undefined;
                opt.Loaded = true;
 
                return __VAL;
            }, defaultCatch);
    }
 
     return opt.Promise;
}
}


// Laedt die (ueber Menu) gesetzten Optionen
// Baut das Benutzermenu auf der Seite auf
// optSet: Object mit den Optionen
// optSet: Gesetzte Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// optParams: Eventuell notwendige Parameter
// return Array mit Promises neuer Ladevorgaenge (fuer Objekte mit 'name' und 'value')
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
function loadOptions(optSet, force = false) {
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
     const __PROMISES = [];
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return String mit dem HTML-Code
function getForm(optSet, optParams = { }) {
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    let form = __FORM;
     let count = 0;  // Bisher angezeigte Optionen
    let column = 0; // Spalte der letzten Option (1-basierend)


     for (let opt in optSet) {
     for (let opt in optSet) {
         const __OPT = optSet[opt];
         if (checkItem(opt, __SHOWFORM, optParams.hideForm)) {
            const __ELEMENT = getOptionElement(optSet[opt]);
            const __TDOPT = (~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"';


        if (! __OPT.Loaded) {
            if (__ELEMENT) {
            const __PROMISE = loadOption(__OPT, force).then(value => {
                if (++count > __FORMBREAK) {
                     __LOG[5]("LOADED " + opt + " << " + value);
                     if (++column > __FORMWIDTH) {
 
                        column = 1;
                    return Promise.resolve({
                    }
                            'name' : opt,
                }
                            'value' : value
                if (column === 1) {
                        });
                    form += '</tr><tr>';
                 }, defaultCatch);
                }
 
                 form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
             __PROMISES.push(__PROMISE);
             }
         }
         }
     }
     }
    form += '\n' + __FORMEND;


     return Promise.all(__PROMISES);
     return form;
}
}


// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// Fuegt das Script in die Seite ein
// opt: Gesetzte Option
// optSet: Gesetzte Optionen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// optParams: Eventuell notwendige Parameter
// reset: Setzt bei Erfolg auf Initialwert der Option (auch fuer nicht 'AutoReset')
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// return Promise von GM.deleteValue() (oder void)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
function deleteOption(opt, force = false, reset = true) {
// return String mit dem HTML-Code fuer das Script
     const __CONFIG = getOptConfig(opt);
function getScript(optSet, optParams = { }) {
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
     //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
    const __SCRIPT = "";


     if (force || ! __CONFIG.Permanent) {
     //window.eval('function activateMenu() { console.log("TADAAA!"); }');
        const __NAME = getOptName(opt);


        __LOG[4]("DELETE " + __NAME);
     return __SCRIPT;
 
        return GM.deleteValue(__NAME).then(voidValue => {
                if (reset || __CONFIG.AutoReset) {
                    setOptValue(opt, initOptValue(__CONFIG));
                }
            }, defaultCatch);
    }
 
     return Promise.resolve();
}
}


// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// anchor: Element, das als Anker fuer die Anzeige dient
// optSet: Gesetzte Optionen
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// optParams: Eventuell notwendige Parameter
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// reset: Setzt bei Erfolg auf Initialwert der Option
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// return Promise auf diesen Vorgang
// 'formWidth': Anzahl der Elemente pro Zeile
async function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
     const __DELETEALL = ((optSelect === undefined) || (optSelect === true));
function buildForm(anchor, optSet, optParams = { }) {
    const __OPTSELECT = getValue(optSelect, { });
     __LOG[3]("buildForm()");


     for (let opt in optSet) {
     const __FORM = getForm(optSet, optParams);
        if (getValue(__OPTSELECT[opt], __DELETEALL)) {
    const __SCRIPT = getScript(optSet, optParams);
            await deleteOption(optSet[opt], force, reset);
        }
    }


     return Promise.resolve();
     addForm(anchor, __FORM, __SCRIPT);
}
}


// Benennt eine Option um und laedt sie ggfs. nach
// Informationen zu hinzugefuegten Forms
// opt: Gesetzte Option
const __FORMS = { };
// name: Neu zu setzender Name (Speicheradresse)
 
// reload: Wert nachladen statt beizubehalten
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// anchor: Element, das als Anker fuer die Anzeige dient
// return Promise auf umbenannte Option
// form: HTML-Form des Optionsmenu (hinten angefuegt)
async function renameOption(opt, name, reload = false, force = false) {
// script: Script mit Reaktionen
     const __NAME = getOptName(opt);
function addForm(anchor, form = "", script = "") {
     const __OLDFORM = __FORMS[anchor];
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);


     if (__NAME !== name) {
     __FORMS[anchor] = {
        await deleteOption(opt, true, ! reload);
                          'Script' : script,
                          'Form'  : form
                      };


        setOptName(opt, name);
    anchor.innerHTML = __REST + script + form;
}


        await invalidateOpt(opt, opt.Loaded);
// ==================== Abschnitt fuer Klasse Classification ====================


        if (reload) {
// Basisklasse fuer eine Klassifikation der Optionen nach Kriterium (z.B. Erst- und Zweitteam oder Fremdteam)
            opt.Loaded = false;
function Classification() {
    'use strict';


            await loadOption(opt, force);
    this.renameFun = prefixName;
        }
    //this.renameParamFun = undefined;
     }
     this.optSet = undefined;
 
     this.optSelect = { };
     return Promise.resolve(opt);
}
}


// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
Class.define(Classification, Object, {
// name: Gesetzter Name (Speicheradresse)
                    'renameOptions' : function() {
// prefix: Prefix, das vorangestellt werden soll
                                          const __PARAM = this.renameParamFun();
// return Neu zu setzender Name (Speicheradresse)
function prefixName(name, prefix) {
    return (prefix + name);
}


// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
                                          if (__PARAM !== undefined) {
// name: Gesetzter Name (Speicheradresse)
                                              // Klassifizierte Optionen umbenennen...
// postfix: Postfix, das angehaengt werden soll
                                              renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun);
// return Neu zu setzender Name (Speicheradresse)
                                          }
function postfixName(name, postfix) {
                                      },
    return (name + postfix);
                    'deleteOptions' : function() {
}
                                          return deleteOptions(this.optSet, this.optSelect, true, true);
                                      }
                } );
 
// ==================== Ende Abschnitt fuer Klasse Classification ====================
 
// ==================== Abschnitt fuer Klasse TeamClassification ====================


// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// Klasse fuer die Klassifikation der Optionen nach Team (Erst- und Zweitteam oder Fremdteam)
// optSet: Gesetzte Optionen
function TeamClassification() {
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
     'use strict';
// 'reload': Option nachladen?
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// renameParam: Wird an renameFun uebergeen
// renameFun: function(name, param) zur Ermittlung des neuen Namens
// - name: Neu zu setzender Name (Speicheradresse)
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix
// return Promise auf diesen Vorgang
async function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
     if (renameFun === undefined) {
        __LOG[0]("RENAME: Illegale Funktion!");
    }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];


        if (__OPT === undefined) {
    Classification.call(this);
            __LOG[0]("RENAME: Option '" + opt + "' nicht gefunden!");
        } else {
            const __NAME = getOptName(__OPT);
            const __NEWNAME = renameFun(__NAME, renameParam);
            const __ISSCALAR = ((typeof __OPTPARAMS) === 'boolean');
            // Laedt die unter dem neuen Namen gespeicherten Daten nach?
            const __RELOAD = (__ISSCALAR ? __OPTPARAMS : __OPTPARAMS.reload);
            // Laedt auch Optionen mit 'AutoReset'-Attribut?
            const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force);


            await renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE);
    this.team = undefined;
        }
     this.teamParams = undefined;
     }
}
}


// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
Class.define(TeamClassification, Classification, {
// optSet: Gesetzte Optionen
                    'renameParamFun' : function() {
// reload: Seite mit "Werkseinstellungen" neu laden
                                          const __MYTEAM = (this.team = getMyTeam(this.optSet, this.teamParams, this.team));
// return Promise auf diesen Vorgang
 
async function resetOptions(optSet, reload = true) {
                                          if (__MYTEAM.LdNr) {
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
                                              // Prefix fuer die Optionen mit gesonderten Behandlung...
    await deleteOptions(optSet, true, false, ! reload);
                                              return __MYTEAM.LdNr.toString() + '.' + __MYTEAM.LgNr.toString() + ':';
                                          } else {
                                              return undefined;
                                          }
                                      }
                } );


    // ... und ggfs. Seite neu laden (mit "Werkseinstellungen")...
// ==================== Ende Abschnitt fuer Klasse TeamClassification ====================
    refreshPage(reload);
}


// ==================== Abschnitt fuer diverse Utilities ====================
// ==================== Abschnitt fuer Klasse Team ====================


// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// Klasse fuer Teamdaten
// form: <form>...</form>
function Team(team, land, liga) {
// props: Map von name:value-Paaren
     'use strict';
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = 'hidden') {
     for (let fieldName in props) {
        let field = form[fieldName];
        if (! field) {
            field = document.createElement('input');
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
        }
        field.value = props[fieldName];
    }


     return form;
     this.Team = team;
    this.Land = land;
    this.Liga = liga;
    this.LdNr = getLandNr(land);
    this.LgNr = getLigaNr(liga);
}
}


// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
Class.define(Team, Object, {
// form: <form>...</form>
                    '__TEAMITEMS' : {  // Items, die in Team als Teamdaten gesetzt werden...
// props: Map von name:value-Paaren
                                        'Team' : true,
// return Ergaenztes Form-Konstrukt
                                        'Liga' : true,
function addHiddenField(form, props) {
                                        'Land' : true,
    return addInputField(form, props, 'hidden');
                                        'LdNr' : true,
}
                                        'LgNr' : true
                                    }
                } );


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// ==================== Ende Abschnitt fuer Klasse Team ====================
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function addEvent(obj, type, callback, capture = false) {
    if (obj.addEventListener) {
        return obj.addEventListener(type, callback, capture);
    } else if (obj.attachEvent) {
        return obj.attachEvent('on' + type, callback);
    } else {
        __LOG[0]("Could not add " + type + " event:");
        __LOG[2](callback);


        return false;
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
    }
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
const __OPTSET = { };
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function removeEvent(obj, type, callback, capture = false) {
    if (obj.removeEventListener) {
        return obj.removeEventListener(type, callback, capture);
    } else if (obj.detachEvent) {
        return obj.detachEvent('on' + type, callback);
    } else {
        __LOG[0]("Could not remove " + type + " event:");
        __LOG[2](callback);


        return false;
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
    }
const __TEAMCLASS = new TeamClassification();
}


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
// id: ID des betroffenen Eingabeelements
__TEAMCLASS.optSelect = {
// type: Name des Events, z.B. "click"
                      'datenZat'  : true,
// callback: Funktion als Reaktion
                      'birthdays'  : true,
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
                      'tClasses'  : true,
// return false bei Misserfolg
                      'progresses' : true,
function addDocEvent(id, type, callback, capture = false) {
                      'zatAges'    : true,
     const __OBJ = document.getElementById(id);
                      'trainiert'  : true,
                      'positions'  : true,
                      'skills'     : true
                  };


     return addEvent(__OBJ, type, callback, capture);
// Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option
}
// optSet: Platz fuer die gesetzten Optionen
// teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'LdNr' und 'LgNr')
// myTeam: Objekt fuer die Teamdaten
// return Die Teamdaten oder undefined bei Fehler
function getMyTeam(optSet = undefined, teamParams = undefined, myTeam = new Team()) {
     if (teamParams !== undefined) {
        addProps(myTeam, teamParams, myTeam.__TEAMITEMS);
        __LOG[2]("Ermittelt: " + safeStringify(myTeam));
        // ... und abspeichern...
        setOpt(optSet.team, myTeam, false);
    } else {
        const __TEAM = getOptValue(optSet.team);  // Gespeicherte Parameter


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
// id: ID des betroffenen Eingabeelements
            addProps(myTeam, __TEAM, myTeam.__TEAMITEMS);
// type: Name des Events, z.B. "click"
            __LOG[2]("Gespeichert: " + safeStringify(myTeam));
// callback: Funktion als Reaktion
        } else {
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
            __LOG[1]("Unbekannt: " + safeStringify(__TEAM));
// return false bei Misserfolg
        }
function removeDocEvent(id, type, callback, capture = false) {
     }
     const __OBJ = document.getElementById(id);


     return removeEvent(__OBJ, type, callback, capture);
     return myTeam;
}
}


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// Behandelt die Optionen und laedt das Benutzermenu
// name: Name des Elements (siehe "name=")
// optConfig: Konfiguration der Optionen
// index: Laufende Nummer des Elements (0-based), Default: 0
// optSet: Platz fuer die gesetzten Optionen
// doc: Dokument (document)
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function getElement(name, index = 0, doc = document) {
// 'teamParams': Getrennte Daten-Option wird genutzt, hier: Team() mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
     const __TAGS = doc.getElementsByName(name);
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
     const __TABLE = (__TAGS ? __TAGS[index] : undefined);
// '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 }) {
     // Klassifikation ueber Land und Liga des Teams...
    __TEAMCLASS.optSet = optSet; // Classification mit optSet verknuepfen
     __TEAMCLASS.teamParams = optParams.teamParams; // Ermittelte Parameter


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


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
    showOptions(optSet, optParams);
// index: Laufende Nummer des Elements (0-based)
// tag: Tag des Elements ("table")
// doc: Dokument (document)
// return Gesuchtes Element oder undefined (falls nicht gefunden)
function getTable(index, tag = 'table', doc = document) {
    const __TAGS = doc.getElementsByTagName(tag);
    const __TABLE = (__TAGS ? __TAGS[index] : undefined);


     return __TABLE;
     return optSet;
}
}


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// ==================== Ende Abschnitt fuer Optionen ====================
// name: Name des Tabellen-Elements (siehe "name=")
// index: Laufende Nummer des Tabellen-Elements (0-based), Default: 0
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getElementRows(name, index = 0, doc = document) {
    const __TABLE = getElement(name, index, doc);
    const __ROWS = (__TABLE ? __TABLE.rows : undefined);


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


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


     return __ROWS;
// Erschafft die Spieler-Objekte und fuellt sie mit Werten
// reloadData: true = Teamuebersicht, false = Spielereinzelwerte
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, reloadData = false) {
    storePlayerDataFromHTML(playerRows, optSet, colIdx, offsetUpper, offsetLower, reloadData);
 
    const __SAISON = getOptValue(optSet.saison);
    const __CURRZAT = getOptValue(optSet.aktuellerZat);
    const __DONATION = getOptValue(optSet.foerderung);
    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 __SKILLS = getOptValue(optSet.skills, []);
    const __PLAYERS = [];
 
    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
        const __CELLS = playerRows[i].cells;
        const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
        const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
        const __NEWPLAYER = new PlayerRecord(__AGE, getValue(__SKILLS[j], []), __ISGOALIE);
 
        __NEWPLAYER.initPlayer(__SAISON, __CURRZAT, __DONATION, __BIRTHDAYS[j], __TCLASSES[j], __PROGRESSES[j]);
 
        if (reloadData) {
            __NEWPLAYER.setZusatz(__ZATAGES[j], __TRAINIERT[j], __POSITIONS[j]);
        }
 
        __PLAYERS[j] = __NEWPLAYER;
    }
 
    if (! reloadData) {
        calcPlayerData(__PLAYERS, optSet);
    }
 
     return __PLAYERS;
}
}


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
// id: ID des Tabellen-Elements
function calcPlayerData(players, optSet) {
// doc: Dokument (document)
    const __ZATAGES = [];
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
    const __TRAINIERT = [];
function getRowsById(id, doc = document) {
    const __POSITIONS = [];
    const __TABLE = doc.getElementById(id);
 
    const __ROWS = (__TABLE ? __TABLE.rows : undefined);
    for (let i = 0; i < players.length; i++) {
        const __ZUSATZ = players[i].calcZusatz();
 
        if (__ZUSATZ.zatAge !== undefined) {  // braucht Geburtstag fuer gueltige Werte!
            __ZATAGES[i]    = __ZUSATZ.zatAge;
        }
        __TRAINIERT[i]  = __ZUSATZ.trainiert;
        __POSITIONS[i]  = __ZUSATZ.bestPos;
    }


     return __ROWS;
     setOpt(optSet.zatAges, __ZATAGES, false);
    setOpt(optSet.trainiert, __TRAINIERT, false);
    setOpt(optSet.positions, __POSITIONS, false);
}
}


// ==================== Abschnitt fuer Optionen auf der Seite ====================
// Ermittelt die Werte in den Spieler-Objekten aus den Daten der Seite und speichert diese
// reloadData: true = Teamuebersicht, false = Spielereinzelwerte
function storePlayerDataFromHTML(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, reloadData = false) {
    if (reloadData) {
        const __BIRTHDAYS = [];
        const __TCLASSES = [];
        const __PROGRESSES = [];
 
        for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
            const __CELLS = playerRows[i].cells;


// Liefert den Funktionsaufruf zur Option als String
            __BIRTHDAYS[j] = getIntFromHTML(__CELLS, colIdx.Geb);
// opt: Auszufuehrende Option
            __TCLASSES[j] = getTalentFromHTML(__CELLS, colIdx.Tal);
// isAlt: Angabe, ob AltAction statt Action gemeint ist
            __PROGRESSES[j] = getAufwertFromHTML(__CELLS, colIdx.Auf, getOptValue(optSet.shortAufw, true));
// value: Ggfs. zu setzender Wert
        }
// serial: Serialization fuer String-Werte (Select, Textarea)
        setOpt(optSet.birthdays, __BIRTHDAYS, false);
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
        setOpt(optSet.tClasses, __TCLASSES, false);
// return String mit dem (reinen) Funktionsaufruf
        setOpt(optSet.progresses, __PROGRESSES, false);
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
     } else {
    const __STORAGE = getMemory(memory);
        const __SKILLS = [];
    const __MEMORY = __STORAGE.Value;
     const __MEMSTR = __STORAGE.Display;
    const __RUNPREFIX = __STORAGE.Prefix;


    if (__MEMORY !== undefined) {
         for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++, j++) {
         const __RELOAD = "window.location.reload()";
            const __CELLS = playerRows[i].cells;
        const __SETITEM = function(item, val, quotes = true) {
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                          };
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                          };
        const __CONFIG = getOptConfig(opt);
        const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
        const __THISVAL = ((__CONFIG.ValType === 'String') ? "'\\x22' + this.value + '\\x22'" : "this.value");
        const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
        const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);


        if (__ACTION !== undefined) {
            __SKILLS[j] = getSkillsFromHTML(__CELLS, colIdx);
            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;
            }
         }
         }
        setOpt(optSet.skills, __SKILLS, false);
     }
     }
    return undefined;
}
}


// Liefert die Funktionsaufruf zur Option als String
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
// opt: Auszufuehrende Option
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0, formatFun = sameValue) {
// isAlt: Angabe, ob AltAction statt Action gemeint ist
     if (offsetLeft < 0) {
// value: Ggfs. zu setzender Wert
        offsetLeft = colIdxSort; // ab Sortierspalte
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
    }
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormActionEvent(opt, isAlt = false, value = undefined, type = 'click', serial = undefined, memory = undefined) {
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);


     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
     for (let i = offsetUpper, newVal, oldVal = formatFun(rows[i].cells[colIdxSort].textContent); i < rows.length - offsetLower - 1; i++, oldVal = newVal) {
        newVal = formatFun(rows[i + 1].cells[colIdxSort].textContent);
        if (newVal !== oldVal) {
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
                rows[i].cells[j].style.borderBottom = borderString;
            }
        }
    }
}
}


// Hilfsfunktion: Wendet eine Konvertierung auf jede "Zeile" innerhalb eines Textes an
// Klasse ColumnManager *****************************************************************
// text: Urspruenglicher Text
// convFun: function(line, index, arr): Konvertiert line in "Zeile" line des Arrays arr
// separator: Zeilentrenner im Text (Default: '\n')
// thisArg: optionaler this-Parameter fuer die Konvertierung
// limit: optionale Begrenzung der Zeilen
// return String mit dem neuen Text
function eachLine(text, convFun, separator = '\n', thisArg = undefined, limit = undefined) {
    const __ARR = text.split(separator, limit);
    const __RES = __ARR.map(convFun, thisArg);


     return __RES.join(separator);
function ColumnManager(optSet, colIdx, showCol) {
}
    'use strict';
 
     __LOG[3]("ColumnManager()");


// Hilfsfunktion: Ergaenzt einen HTML-Code um einen Titel (ToolTip)
    const __SHOWCOL = getValue(showCol, true);
// html: Urspruenglicher HTML-Code (z.B. ein HTML-Element oder Text)
    const __SHOWALL = ((__SHOWCOL === true) || (__SHOWCOL.Default === true));
// title: Im ToolTip angezeigter Text
// separator: Zeilentrenner im Text (Default: '|')
// limit: optionale Begrenzung der Zeilen
// return String mit dem neuen HTML-Code
function withTitle(html, title, separator = '|', limit = undefined) {
    if (title && title.length) {
        return eachLine(html, line => '<abbr title="' + title + '">' + line + '</abbr>', separator, undefined, limit);
    } else {
        return html;
    }
}


// Hilfsfunktion: Ermittelt einen Label- oder FormLabel-Eintrag (Default)
    const __BIRTHDAYS = getOptValue(optSet.birthdays, []).length;
// label: Config-Eintrag fuer Label oder FormLabel
    const __TCLASSES = getOptValue(optSet.tClasses, []).length;
// defLabel: Ersatzwert, falls label nicht angegeben
     const __PROGRESSES = getOptValue(optSet.progresses, []).length;
// isSelect: Angabe, ob ein Parameter angezeigt wird (Default: false)
// isForm: Angabe, ob ein FormLabel gesucht ist (Default: true)
// return Vollstaendiger Label- oder FormLabel-Eintrag
function formatLabel(label, defLabel = undefined, isSelect = false, isForm = true) {
     const __LABEL = getValue(label, defLabel);


     if (isSelect && __LABEL && (substParam(__LABEL, '_') === __LABEL)) {
     const __ZATAGES = getOptValue(optSet.zatAges, []).length;
        return __LABEL + (isForm ? "|$" : " $");
    const __TRAINIERT = getOptValue(optSet.trainiert, []).length;
     } else {
     const __POSITIONS = getOptValue(optSet.positions, []).length;
        return __LABEL;
    }
}


// Zeigt eine Option auf der Seite als Auswahlbox an
     const __EINZELSKILLS = getOptValue(optSet.skills, []).length;
// opt: Anzuzeigende Option
     const __PROJECTION = (__EINZELSKILLS && __ZATAGES);
// return String mit dem HTML-Code
function getOptionSelect(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, 'change', undefined);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label, true);
     const __TITLE = substParam(getValue(__CONFIG.Title, __CONFIG.Label), __VALUE);
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


     if (__CONFIG.FreeValue && ! (~ __CONFIG.Choice.indexOf(__VALUE))) {
     this.colIdx = colIdx;
        element += '\n<option value="' + __VALUE + '" SELECTED>' + __VALUE + '</option>';
    }
    for (let value of __CONFIG.Choice) {
        element += '\n<option value="' + value + '"' +
                  ((value === __VALUE) ? ' SELECTED' : "") +
                  '>' + value + '</option>';
    }
    element += '\n</select>';


     return withTitle(substParam(__LABEL, element), __TITLE);
     this.bar = (__PROJECTION && getValue(__SHOWCOL.zeigeBalken, __SHOWALL) && getOptValue(optSet.zeigeBalken));
    this.geb = (__BIRTHDAYS && getValue(__SHOWCOL.zeigeGeb, __SHOWALL) && getOptValue(optSet.zeigeGeb));
    this.tal = (__TCLASSES && getValue(__SHOWCOL.zeigeTal, __SHOWALL) && getOptValue(optSet.zeigeTal));
    this.quo = (__ZATAGES && __TRAINIERT && getValue(__SHOWCOL.zeigeQuote, __SHOWALL) && getOptValue(optSet.zeigeQuote));
    this.aufw = (__PROGRESSES && getValue(__SHOWCOL.zeigeAufw, __SHOWALL) && getOptValue(optSet.zeigeAufw));
    this.substAge = (__ZATAGES && getValue(__SHOWCOL.ersetzeAlter, __SHOWALL) && getOptValue(optSet.ersetzeAlter));
    this.alter = (__ZATAGES && getValue(__SHOWCOL.zeigeAlter, __SHOWALL) && getOptValue(optSet.zeigeAlter));
    this.fix = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeFixSkills, __SHOWALL) && getOptValue(optSet.zeigeFixSkills));
    this.tr = (__EINZELSKILLS && __TRAINIERT && getValue(__SHOWCOL.zeigeTrainiert, __SHOWALL) && getOptValue(optSet.zeigeTrainiert));
    this.antHpt = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeAnteilPri, __SHOWALL) && getOptValue(optSet.zeigeAnteilPri));
    this.antNeb = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeAnteilSec, __SHOWALL) && getOptValue(optSet.zeigeAnteilSec));
    this.pri = (__EINZELSKILLS && getValue(__SHOWCOL.zeigePrios, __SHOWALL) && getOptValue(optSet.zeigePrios));
    this.skill = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeSkill, __SHOWALL) && getOptValue(optSet.zeigeSkill));
    this.pos = (__EINZELSKILLS && __POSITIONS && getValue(__SHOWCOL.zeigePosition, __SHOWALL) && getOptValue(optSet.zeigePosition));
    this.anzOpti = ((__EINZELSKILLS && getValue(__SHOWCOL.zeigeOpti, __SHOWALL)) ? getOptValue(optSet.anzahlOpti) : 0);
    this.anzMw =  ((__PROJECTION && getValue(__SHOWCOL.zeigeMW, __SHOWALL)) ? getOptValue(optSet.anzahlMW) : 0);
    this.substSkills = (__PROJECTION && getValue(__SHOWCOL.ersetzeSkills, __SHOWALL) && getOptValue(optSet.ersetzeSkills));
    this.trE = (__PROJECTION && __TRAINIERT && getValue(__SHOWCOL.zeigeTrainiertEnde, __SHOWALL) && getOptValue(optSet.zeigeTrainiertEnde));
    this.antHptE = (__PROJECTION && getValue(__SHOWCOL.zeigeAnteilPriEnde, __SHOWALL) && getOptValue(optSet.zeigeAnteilPriEnde));
    this.antNebE = (__PROJECTION && getValue(__SHOWCOL.zeigeAnteilSecEnde, __SHOWALL) && getOptValue(optSet.zeigeAnteilSecEnde));
    this.priE = (__PROJECTION && getValue(__SHOWCOL.zeigePriosEnde, __SHOWALL) && getOptValue(optSet.zeigePriosEnde));
    this.skillE = (__PROJECTION && getValue(__SHOWCOL.zeigeSkillEnde, __SHOWALL) && getOptValue(optSet.zeigeSkillEnde));
    this.anzOptiE = ((__PROJECTION && getValue(__SHOWCOL.zeigeOptiEnde, __SHOWALL)) ? getOptValue(optSet.anzahlOptiEnde) : 0);
    this.anzMwE = ((__PROJECTION && getValue(__SHOWCOL.zeigeMWEnde, __SHOWALL)) ? getOptValue(optSet.anzahlMWEnde) : 0);
    this.kennzE = getOptValue(optSet.kennzeichenEnde);
}
}


// Zeigt eine Option auf der Seite als Radiobutton an
Class.define(ColumnManager, Object, {
// opt: Anzuzeigende Option
        'toString'       : function() // Bisher nur die noetigsten Parameter ausgegeben...
// return String mit dem HTML-Code
                              let result = "Skillschnitt\t\t" + this.skill + '\n';
function getOptionRadio(opt) {
                              result += "Beste Position\t" + this.pos + '\n';
    const __CONFIG = getOptConfig(opt);
                              result += "Optis\t\t\t" + this.anzOpti + '\n';
    const __NAME = getOptName(opt);
                              result += "Marktwerte\t\t" + this.anzMw + '\n';
    const __VALUE = getOptValue(opt, false);
                              result += "Skillschnitt Ende\t" + this.skillE + '\n';
    const __ACTION = getFormActionEvent(opt, false, true, 'click', false);
                              result += "Optis Ende\t\t" + this.anzOptiE + '\n';
    const __ALTACTION = getFormActionEvent(opt, true, false, 'click', false);
                              result += "Marktwerte Ende\t" + this.anzMwE + '\n';
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel); // nur nutzen, falls angegeben
    const __TITLE = getValue(__CONFIG.Title, '$');
    const __TITLEON = substParam(__TITLE, __CONFIG.Label);
    const __TITLEOFF = substParam(getValue(__CONFIG.AltTitle, __TITLE), __CONFIG.AltLabel);
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'ON" value="1"' +
                        (__VALUE ? ' CHECKED' : __ACTION) +
                        ' /><label for="' + __NAME + 'ON">' +
                        __CONFIG.Label + '</label>';
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                        '" id="' + __NAME + 'OFF" value="0"' +
                        (__VALUE ? __ALTACTION : ' CHECKED') +
                        ' /><label for="' + __NAME + 'OFF">' +
                        __CONFIG.AltLabel + '</label>';
    const __ELEMENT = [
                          withTitle(__FORMLABEL, __VALUE ? __TITLEON : __TITLEOFF),
                          withTitle(__ELEMENTON, __TITLEON),
                          withTitle(__ELEMENTOFF, __TITLEOFF)
                      ];


    return ((__FORMLABEL && __FORMLABEL.length) ? __ELEMENT : __ELEMENT.slice(1, 3));
                              return result;
}
                          },
        'addCell'        : function(tableRow) {
                              tableRow.insertCell(-1);
                              return tableRow.cells.length - 1;
                          },
        'addAndFillCell' : function(tableRow, value, color, align, digits = 2) {
                              let text = value;


// Zeigt eine Option auf der Seite als Checkbox an
                              if (value && isFinite(value) && (value !== true) && (value !== false)) {
// opt: Anzuzeigende Option
                                  // Zahl einfuegen
// return String mit dem HTML-Code
                                  if (value < 1000) {
function getOptionCheckbox(opt) {
                                      // Mit Nachkommastellen darstellen
    const __CONFIG = getOptConfig(opt);
                                      text = parseFloat(value).toFixed(digits);
    const __NAME = getOptName(opt);
                                  } else {
    const __VALUE = getOptValue(opt, false);
                                      // Mit Tausenderpunkten darstellen
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
                                      text = getNumberString(value.toString());
    const __VALUELABEL = (__VALUE ? __CONFIG.Label : getValue(__CONFIG.AltLabel, __CONFIG.Label));
                                  }
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
                              }
    const __TITLE = substParam(getValue(__VALUE ? __CONFIG.Title : getValue(__CONFIG.AltTitle, __CONFIG.Title), '$'), __VALUELABEL);


    return withTitle('<input type="checkbox" name="' + __NAME +
                              // String, Boolean oder Zahl einfuegen...
                    '" id="' + __NAME + '" value="' + __VALUE + '"' +
                              tableRow.cells[this.addCell(tableRow)].innerHTML = text;
                    (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
                              if (color) {
                    __NAME + '">' + __FORMLABEL + '</label>', __TITLE);
                                  tableRow.cells[tableRow.cells.length - 1].style.color = color;
}
                              }
                              if (align) {
                                  tableRow.cells[tableRow.cells.length - 1].align = align;
                              }
                          },
        'addAndBarCell' : function(tableRow, value, scale = 100, offset = 0, width = 100, height = 10) {
                              const __VALUE = ((scale && isFinite(value)) ? (value - offset) * 100 / scale : 0);


// Zeigt eine Option auf der Seite als Daten-Textfeld an
                              // HTML-Code fuer Anteilsbalken einfuegen...
// opt: Anzuzeigende Option
                              tableRow.cells[this.addCell(tableRow)].innerHTML = this.getBarImg(__VALUE, width, height);
// return String mit dem HTML-Code
                              tableRow.cells[tableRow.cells.length - 1].align = 'left';
function getOptionTextarea(opt) {
                          },
    const __CONFIG = getOptConfig(opt);
        'getBarImg'     : function(value, width = 100, height = 10) {
    const __NAME = getOptName(opt);
                              const __VALUE = Math.max(0, Math.min(99, getMulValue(value, 1, 0, 0)));
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, 'submit', undefined);
    const __SUBMIT = getValue(__CONFIG.Submit, "");
    //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
    const __TITLE = substParam(getValue(__CONFIG.Title, '$'), __FORMLABEL);
    const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                          '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                          safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


    return [ withTitle(__ELEMENTLABEL, __TITLE), __ELEMENTTEXT ];
                              // HTML-Code fuer Anteilsbalken...
}
                              return '<img src="images/balken/' + __VALUE + '.GIF" width="' + (__VALUE * width / 100) + '" height=' + height + '>';
                          },
        'addTitles'      : function(headers, titleColor = "#FFFFFF") {
                              // Spaltentitel zentrieren
                              headers.align = "center";


// Zeigt eine Option auf der Seite als Button an
                              // Titel fuer die aktuellen Werte
// opt: Anzuzeigende Option
                              if (this.bar) {
// return String mit dem HTML-Code
                                  this.addAndFillCell(headers, "Qualit\xE4t", titleColor);
function getOptionButton(opt) {
                              }
    const __CONFIG = getOptConfig(opt);
                              if (this.tal) {
    const __NAME = getOptName(opt);
                                  this.addAndFillCell(headers, "Talent", titleColor);
    const __VALUE = getOptValue(opt, false);
                              }
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
                              if (this.quo) {
    const __BUTTONLABEL = (__VALUE ? getValue(__CONFIG.AltLabel, __CONFIG.Label) : __CONFIG.Label);
                                  this.addAndFillCell(headers, "Quote", titleColor);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __BUTTONLABEL);
                              }
    const __BUTTONTITLE = substParam(getValue(__VALUE ? getValue(__CONFIG.AltTitle, __CONFIG.Title) : __CONFIG.Title, '$'), __BUTTONLABEL);
                              if (this.aufw) {
                                  this.addAndFillCell(headers, "Aufwertung", titleColor);
                              }
                              if (this.geb) {
                                  this.addAndFillCell(headers, "Geb.", titleColor);
                              }
                              if (this.alter && ! this.substAge) {
                                  this.addAndFillCell(headers, "Alter", titleColor);
                              }
                              if (this.fix) {
                                  this.addAndFillCell(headers, "fix", titleColor);
                              }
                              if (this.tr) {
                                  this.addAndFillCell(headers, "tr.", titleColor);
                              }
                              if (this.antHpt) {
                                  this.addAndFillCell(headers, "%H", titleColor);
                              }
                              if (this.antNeb) {
                                  this.addAndFillCell(headers, "%N", titleColor);
                              }
                              if (this.pri) {
                                  this.addAndFillCell(headers, "Prios", 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);
                                  }
                              }


    return '<label for="' + __NAME + '">' + __FORMLABEL + '</label>' +
                              // Titel fuer die Werte mit Ende 18
          withTitle('<input type="button" name="" + ' + __NAME +
                              if (this.trE) {
                    '" id="' + __NAME + '" value="' + __BUTTONLABEL +
                                  this.addAndFillCell(headers, "tr." + this.kennzE, titleColor);
                    '"' + __ACTION + '/>', __BUTTONTITLE);
                              }
}
                              if (this.antHptE) {
 
                                  this.addAndFillCell(headers, "%H" + this.kennzE, titleColor);
// Zeigt eine Option auf der Seite an (je nach Typ)
                              }
// opt: Anzuzeigende Option
                              if (this.antNebE) {
// return String mit dem HTML-Code
                                  this.addAndFillCell(headers, "%N" + this.kennzE, titleColor);
function getOptionElement(opt) {
                              }
    const __CONFIG = getOptConfig(opt);
                              if (this.priE) {
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
                                  this.addAndFillCell(headers, "Prios" + this.kennzE, titleColor);
    let element = "";
                              }
                              if (this.skillE) {
                                  this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
                              }
                              for (let i = 1; i <= 6; i++) {
                                  if (i <= this.anzOptiE) {
                                      this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
                                  }
                                  if (i <= this.anzMwE) {
                                      this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
                                  }
                              }
                          },  // Ende addTitles()
        'addValues'      : function(player, playerRow, color = "#FFFFFF") {
                              const __COLALERT = getColor('STU'); // rot
                              const __COLOR = ((player.zatLeft < 1) ? __COLALERT : player.isGoalie ? getColor('TOR') : color);
                              const __POS1COLOR = getColor((player.getPosPercent() > 99.99) ? 'LEI' : player.getPos());


    if (! __CONFIG.Hidden) {
                              // Aktuelle Werte
        switch (__TYPE) {
                              if (this.bar) {
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                                  this.addAndBarCell(playerRow, player.getPrios(player.getPos(), player.__TIME.end), 100, 0, 100, 10);
                            break;
                              }
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                              if (this.tal) {
                                element = getOptionCheckbox(opt);
                                  this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
                            } else {
                              }
                                element = getOptionRadio(opt);
                              if (this.quo) {
                            }
                                  this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, null, 2);
                            break;
                              }
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                              if (this.colIdx.Auf) {
                            break;
                                  convertStringFromHTML(playerRow.cells, this.colIdx.Auf, function(aufwert) {
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
                                                                                              return player.boldPriSkillNames(aufwert);
                            break;
                                                                                          });
        case __OPTTYPES.SI : element = getOptionButton(opt);
                              }
                            break;
                              if (this.aufw) {
        default :            break;
                                  this.addAndFillCell(playerRow, player.boldPriSkillNames(player.getAufwert()), __COLOR, 'left');
        }
                              }
 
                              if (this.geb) {
        if ((typeof element) !== 'string') {
                                  this.addAndFillCell(playerRow, player.getGeb(), __COLOR, null, 0);
            element = '<div>' + Array.from(element).join('<br />') + '</div>';
                              }
        }
                              if (this.substAge) {
    }
                                  convertStringFromHTML(playerRow.cells, this.colIdx.Age, function(unused) {
 
                                                                                              return parseFloat(player.getAge()).toFixed(2);
    return element;
                                                                                          });
}
                              } else if (this.alter) {
 
                                  this.addAndFillCell(playerRow, player.getAge(), __COLOR, null, 2);
// Gruppiert die Daten eines Objects nach einem Kriterium
                              }
// data: Object mit Daten
                              if (player.zatLeft < 6) {  // Abrechnungszeitraum vor dem letztmoeglichen Ziehen...
// byFun: function(val), die das Kriterium ermittelt. Default: value
                                  formatCell(playerRow.cells[this.colIdx.Age], true, __COLALERT, null);
// filterFun: function(key, index, arr), die das Kriterium key im Array arr an der Stelle index vergleicht. Default: Wert identisch
                              }
// sortFun: function(a, b), nach der die Kriterien sortiert werden. Default: Array.sort()
                              if (this.fix) {
// return Neues Object mit Eintraegen der Form <Kriterium> : [ <alle Keys zu diesem Kriterium> ]
                                  this.addAndFillCell(playerRow, player.getFixSkills(), __COLOR, null, 0);
function groupData(data, byFun, filterFun, sortFun) {
                              }
    const __BYFUN = (byFun || (val => val));
                              if (this.tr) {
    const __FILTERFUN = (filterFun || ((key, index, arr) => (arr[index] === key)));
                                  this.addAndFillCell(playerRow, player.getTrainableSkills(), __COLOR, null, 0);
    const __KEYS = Object.keys(data);
                              }
    const __VALS = Object.values(data);
                              if (this.antHpt) {
    const __BYKEYS = __VALS.map(__BYFUN);
                                  this.addAndFillCell(playerRow, player.getPriPercent(player.getPos()), __COLOR, null, 0);
    const __BYKEYSET = new Set(__BYKEYS);
                              }
    const __BYKEYARRAY = [...__BYKEYSET];
                              if (this.antNeb) {
    const __SORTEDKEYS = __BYKEYARRAY.sort(sortFun);
                                  this.addAndFillCell(playerRow, player.getSecPercent(player.getPos()), __COLOR, null, 0);
    const __GROUPEDKEYS = __SORTEDKEYS.map(byVal => __KEYS.filter((key, index, arr) => __FILTERFUN(byVal, index, __BYKEYS)));
                              }
    const __ASSIGN = ((keyArr, valArr) => Object.assign({ }, ...keyArr.map((key, index) => ({ [key] : valArr[index] }))));
                              if (this.pri) {
 
                                  this.addAndFillCell(playerRow, player.getPrios(player.getPos()), __COLOR, null, 1);
    return __ASSIGN(__SORTEDKEYS, __GROUPEDKEYS);
                              }
}
                              if (this.skill) {
 
                                  this.addAndFillCell(playerRow, player.getSkill(), __COLOR, null, 2);
// Baut das Benutzermenu auf der Seite auf
                              }
// optSet: Gesetzte Optionen
                              if (this.pos) {
// optParams: Eventuell notwendige Parameter
                                  this.addAndFillCell(playerRow, player.getPos(), __POS1COLOR);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
                              }
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
                              for (let i = 1; i <= 6; i++) {
// 'formWidth': Anzahl der Elemente pro Zeile
                                  const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
                                  const __COLI = getColor(__POSI);
// return String mit dem HTML-Code
function getForm(optSet, optParams = { }) {
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    const __PRIOOPTS = groupData(optSet, opt => getOptConfig(opt).FormPrio);
    let form = __FORM;
    let count = 0;  // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)


    for (let optKeys of Object.values(__PRIOOPTS)) {
                                  if (i <= this.anzOpti) {
        for (let optKey of optKeys) {
                                      if ((i === 1) || ! player.isGoalie) {
            if (checkItem(optKey, __SHOWFORM, optParams.hideForm)) {
                                          // Opti anzeigen
                const __ELEMENT = getOptionElement(optSet[optKey]);
                                          this.addAndFillCell(playerRow, player.getOpti(__POSI), __COLI, null, 2);
                const __TDOPT = ((~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"');
                                      } else {
 
                                          // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
                if (__ELEMENT) {
                                          this.addCell(playerRow);
                    if (++count > __FORMBREAK) {
                                      }
                        if (++column > __FORMWIDTH) {
                                  }
                            column = 1;
                                  if (i <= this.anzMw) {
                        }
                                      if ((i === 1) || ! player.isGoalie) {
                    }
                                          // MW anzeigen
                    if (column === 1) {
                                          this.addAndFillCell(playerRow, player.getMarketValue(__POSI), __COLI, null, 0);
                        form += '</tr><tr>';
                                      } else {
                    }
                                          // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                    form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
                                          this.addCell(playerRow);
                }
                                      }
            }
                                  }
        }
                              }
    }
    form += '\n' + __FORMEND;


    return form;
                              // Werte mit Ende 18
}
                              if (this.substSkills) {
                                  const __IDXPRI = getIdxPriSkills(player.getPos());
                                  const __OSBLAU = getColor("");


// Fuegt das Script in die Seite ein
                                  convertArrayFromHTML(playerRow.cells, this.colIdx.Einz, player.skillsEnd, function(value, cell, unused, index) {
// optSet: Gesetzte Optionen
                                                                                                                if (~ __IDXPRI.indexOf(index)) {
// optParams: Eventuell notwendige Parameter
                                                                                                                    formatCell(cell, true, __OSBLAU, __POS1COLOR);
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
                                                                                                                }
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
                                                                                                                return value;
// return String mit dem HTML-Code fuer das Script
                                                                                                            });
function getScript(optSet, optParams = { }) {
                              }
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
                              if (this.trE) {
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
                                  this.addAndFillCell(playerRow, player.getTrainableSkills(player.__TIME.end), __COLOR, null, 1);
    //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
                              }
     const __SCRIPT = "";
                              if (this.antHptE) {
                                  this.addAndFillCell(playerRow, player.getPriPercent(player.getPos(), player.__TIME.end), __COLOR, null, 0);
                              }
                              if (this.antNebE) {
                                  this.addAndFillCell(playerRow, player.getSecPercent(player.getPos(), player.__TIME.end), __COLOR, null, 0);
                              }
                              if (this.priE) {
                                  this.addAndFillCell(playerRow, player.getPrios(player.getPos(), player.__TIME.end), __COLOR, null, 1);
                              }
                              if (this.skillE) {
                                  this.addAndFillCell(playerRow, player.getSkill(player.__TIME.end), __COLOR, null, 2);
                              }
                              for (let i = 1; i <= 6; i++) {
                                  const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
                                  const __COLI = getColor(__POSI);
 
                                  if (i <= this.anzOptiE) {
                                      if ((i === 1) || ! player.isGoalie) {
                                          // Opti anzeigen
                                          this.addAndFillCell(playerRow, player.getOpti(__POSI, player.__TIME.end), __COLI, null, 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, player.__TIME.end), __COLI, null, 0);
                                      } else {
                                          // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                                          this.addCell(playerRow);
                                      }
                                  }
                              }
                          }  // Ende addValues(player, playerRow)
     } );
 
// Klasse PlayerRecord ******************************************************************


    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
function PlayerRecord(age, skills, isGoalie) {
    'use strict';


     return __SCRIPT;
     this.mwFormel = this.__MWFORMEL.S10; // Neue Formel, genauer in initPlayer()
}


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
    this.age = age;
// anchor: Element, das als Anker fuer die Anzeige dient
    this.skills = skills;
// optSet: Gesetzte Optionen
    this.isGoalie = isGoalie;
// optParams: Eventuell notwendige Parameter
 
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
    // in this.initPlayer() definiert:
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
    // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
// 'formWidth': Anzahl der Elemente pro Zeile
    // this.zatAge: Bisherige erfolgte Trainings-ZATs
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
    // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
function buildForm(anchor, optSet, optParams = { }) {
    // this.aufwert: Aufwertungsstring
     __LOG[3]("buildForm()");
    // this.mwFormel: Benutzte MW-Formel, siehe __MWFORMEL
    // this.positions[][]: Positionstexte und Optis; TOR-Index ist 5
    // this.skillsEnd[]: Berechnet aus this.skills, this.age und aktuellerZat
    // this.zatLeft: ZATs bis zum Ende 18 (letzte Ziehmoeglichkeit)
     // this.restEnd: Korrekturterm zum Ausgleich von Rundungsfehlern mit Ende 18
    //              (also Skills, die nicht explizit in this.skillsEnd stehen)


     const __FORM = getForm(optSet, optParams);
     // in this.calcZusatz()/setZusatz() definiert:
     const __SCRIPT = getScript(optSet, optParams);
    // this.trainiert: Anzahl der erfolgreichen Trainingspunkte
     // indirekt this.zatAge und this.bestPos


     addForm(anchor, __FORM, __SCRIPT);
     // in this.getPos() definiert:
    // this.bestPos: erster (bester) Positionstext
}
}


// Informationen zu hinzugefuegten Forms
Class.define(PlayerRecord, Object, {
const __FORMS = { };
        '__TIME'                : {  // Zeitpunktangaben
                                      'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
                                      'beg' : 1,  // Jugendspieler darf trainieren (wird 13 Jahre alt)
                                      'now' : 2,  // Aktueller ZAT
                                      'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
                                  },
        '__MWFORMEL'            : {   // Zu benutzende Marktwertformel
                                      'alt' : 0,  // Marktwertformel bis Saison 9 inklusive
                                      'S10' : 1  // Marktwertformel MW5 ab Saison 10
                                  },
        'toString'              : function() {  // Bisher nur die noetigsten Werte ausgegeben...
                                      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";


// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
                                      for (let pos of this.positions) {
// anchor: Element, das als Anker fuer die Anzeige dient
                                          result += "\n\t" + pos + '\t';
// form: HTML-Form des Optionsmenu (hinten angefuegt)
                                          result += this.getOpti(pos).toFixed(2) + '\t';
// script: Script mit Reaktionen
                                          result += getNumberString(this.getMarketValue(pos).toString());
function addForm(anchor, form = "", script = "") {
                                      }
    const __OLDFORM = __FORMS[anchor];
 
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                                      result += "\n\nWerte mit Ende 18\n";
                  anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
                                      result += "Skillschnitt\t" + this.getSkill(this.__TIME.end).toFixed(2) + '\n';
                                      result += "Optis und Marktwerte";


    __FORMS[anchor] = {
                                      for (let pos of this.positions) {
                          'Script' : script,
                                          result += "\n\t" + this.getPos()[i] + '\t';
                          'Form'   : form
                                          result += this.getOpti(pos, this.__TIME.end).toFixed(2) + '\t';
                      };
                                          result += getNumberString(this.getMarketValue(pos, this.__TIME.end).toString());
                                      }


    anchor.innerHTML = __REST + script + form;
                                      return result;
}
                                  },  // Ende this.toString()
        'initPlayer'            : function(saison, currZAT, donation, gebZAT, tclass, progresses) {
                                      // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
                                      this.zatGeb = gebZAT;
                                      this.zatAge = this.calcZatAge(currZAT);
                                      this.talent = tclass;
                                      this.aufwert = progresses;
                                      this.mwFormel = ((saison < 10) ? this.__MWFORMEL.alt : this.__MWFORMEL.S10);


// ==================== Abschnitt fuer Klasse Classification ====================
                                      const __POSREIHEN = [ 'ABW', 'DMI', 'MIT', 'OMI', 'STU', 'TOR' ];
                                      this.positions = [];
                                      for (let index = 0; index < __POSREIHEN.length; index++) {
                                          const __REIHE = __POSREIHEN[index];


// Basisklasse fuer eine Klassifikation der Optionen nach Kriterium (z.B. Erst- und Zweitteam oder Fremdteam)
                                          this.positions[index] = [ __REIHE, this.getOpti(__REIHE) ];
function Classification() {
                                      }
    'use strict';


    this.renameFun = prefixName;
                                      // Sortieren
    //this.renameParamFun = undefined;
                                      sortPositionArray(this.positions);
    this.optSet = undefined;
    this.optSelect = { };
}


Class.define(Classification, Object, {
                                      // Einzelskills mit Ende 18 berechnen
                    'renameOptions' : function() {
                                      this.skillsEnd = [];
                                          const __PARAM = this.renameParamFun();


                                          if (__PARAM !== undefined) {
                                      const __ZATDONE = this.getZatDone();
                                              // Klassifizierte Optionen umbenennen...
                                      const __ZATTOGO = (this.zatLeft = this.getZatDone(this.__TIME.end) - __ZATDONE);
                                              return renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun);
                                      const __ADDRATIO = (__ZATDONE ? __ZATTOGO / __ZATDONE : 0);
                                          } else {
                                       let addSkill = (__ADDRATIO ? __ADDRATIO * this.getTrainiert() : __ZATTOGO * (1 + this.talent / 3.6) * donation / 10000);
                                              return Promise.resolve();
                                          }
                                       },
                    'deleteOptions' : function(ignList) {
                                          const __OPTSELECT = addProps([], this.optSelect, null, ignList);


                                          return deleteOptions(this.optSet, __OPTSELECT, true, true);
                                      for (let i in this.skills) {
                                      }
                                          const __SKILL = this.skills[i];
                });
                                          let progSkill = __SKILL;


// ==================== Ende Abschnitt fuer Klasse Classification ====================
                                          if (isTrainableSkill(i)) {
                                              // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
                                              const __ADDSKILL = Math.min(99 - progSkill, getMulValue(__ADDRATIO, __SKILL, 0, NaN));


// ==================== Abschnitt fuer Klasse TeamClassification ====================
                                              progSkill += __ADDSKILL;
                                              addSkill -= __ADDSKILL;
                                          }


// Klasse fuer die Klassifikation der Optionen nach Team (Erst- und Zweitteam oder Fremdteam)
                                          this.skillsEnd[i] = progSkill;
function TeamClassification() {
                                      }
    'use strict';
                                      this.restEnd = addSkill;
 
                                  },  // Ende this.initPlayer()
    Classification.call(this);
        'setZusatz'            : function(zatAge, trainiert, bestPos) {
                                      // Setzt Nebenwerte fuer den Spieler (geht ohne initPlayer())
                                      this.zatAge = zatAge;
                                      this.trainiert = trainiert;
                                      this.bestPos = bestPos;
                                  },
        'calcZusatz'           : function() {
                                      // Ermittelt Nebenwerte fuer den Spieler und gibt sie alle zurueck (nach initPlayer())
                                      // this.zatAge und this.skills bereits in initPlayer() berechnet
                                      this.trainiert = this.getTrainiert(true);  // neu berechnet aus Skills
                                      let bestPos = this.getPos(-1); // hier: -1 explizit angeben, damit neu ermittelt (falls this.bestPos noch nicht belegt)


     this.team = undefined;
                                      return {
    this.teamParams = undefined;
                                                'zatAge'     : this.zatAge,
}
                                                'trainiert'  : this.trainiert,
                                                'bestPos'    : bestPos
                                            };
                                  },
        'getGeb'                : function() {
                                      return (this.zatGeb < 0) ? '?' : this.zatGeb;
                                  },
        'calcZatAge'            : function(currZAT) {
                                      let zatAge;


Class.define(TeamClassification, Classification, {
                                      if (this.zatGeb !== undefined) {
                    'renameParamFun' : function() {
                                          let ZATs = (this.age - ((currZAT < this.zatGeb) ? 12 : 13)) * 72; // Basiszeit fuer die Jahre seit Jahrgang 13
                                          const __MYTEAM = (this.team = getMyTeam(this.optSet, this.teamParams, this.team));


                                          if (__MYTEAM.LdNr) {
                                          if (this.zatGeb < 0) {
                                              // Prefix fuer die Optionen mit gesonderten Behandlung...
                                              zatAge = ZATs + currZAT;  // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
                                              return __MYTEAM.LdNr.toString() + '.' + __MYTEAM.LgNr.toString() + ':';
                                          } else {
                                          } else {
                                              zatAge = ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
                                              return undefined;
                                          }
                                          }
                                      }
                                      }
                });


// ==================== Ende Abschnitt fuer Klasse TeamClassification ====================
                                      return zatAge;
 
                                  },
// ==================== Abschnitt fuer Klasse Team ====================
        'getZatAge'            : function(when = this.__TIME.now) {
                                      if (when === this.__TIME.end) {
                                          return (18 - 12) * 72 - 1;  // (max.) Trainings-ZATs bis Ende 18
                                      } else {
                                          return this.zatAge;
                                      }
                                  },
        'getZatDone'            : function(when = this.__TIME.now) {
                                      return Math.max(0, this.getZatAge(when));
                                  },
        'getAge'                : function(when = this.__TIME.now) {
                                      if (this.mwFormel === this.__MWFORMEL.alt) {
                                          return (when === this.__TIME.end) ? 18 : this.age;
                                      } else {  // Geburtstage ab Saison 10...
                                          return (13.00 + this.getZatAge(when) / 72);
                                      }
                                  },
        'getTrainiert'          : function(recalc = false) {
                                      if (recalc || (this.trainiert === undefined)) {
                                          return this.getTrainableSkills();
                                      } else {
                                          return this.trainiert;
                                      }
                                  },
        'getAufwertungsSchnitt' : function() {
                                      return parseFloat(this.getTrainiert() / this.getZatDone());
                                  },
        'getPos'                : function(idx = 0) {
                                      const __IDXOFFSET = 1;


// Klasse fuer Teamdaten
                                      switch (idx) {
function Team(team, land, liga) {
                                      case -1 : return (this.bestPos = this.positions[this.isGoalie ? 5 : 0][0]);
    'use strict';
                                      case  0 : return this.bestPos;
                                      default : return this.positions[idx - __IDXOFFSET][0];
                                      }
                                  },
        'getPosPercent'        : function(idx = 0) {
                                      const __IDXOFFSET = 1;
                                      const __OPTI = this.positions[this.isGoalie ? 5 : 0][1];
                                      let optiSec = __OPTI;


    this.Team = team;
                                      switch (idx) {
    this.Land = land;
                                      case -1 : break; // __OPTI
    this.Liga = liga;
                                      case  0 : optiSec = (this.isGoalie ? 0 : this.positions[1][1]); // Backup-Wert (TOR: keiner)
    this.LdNr = getLandNr(land);
                                                break;
    this.LgNr = getLigaNr(liga);
                                      default : optiSec = this.positions[idx - __IDXOFFSET][1];
}
                                      }


Class.define(Team, Object, {
                                      return parseFloat(100 * optiSec / __OPTI);
                    '__TEAMITEMS' : {   // Items, die in Team als Teamdaten gesetzt werden...
                                  },
                                        'Team' : true,
        'getTalent'             : function() {
                                        'Liga' : true,
                                      return (this.talent < 0) ? "wenig" : (this.talent > 0) ? "hoch" : "normal";
                                        'Land' : true,
                                  },
                                        'LdNr' : true,
        'getAufwert'           : function() {
                                        'LgNr' : true
                                      return this.aufwert;
                                    }
                                  },
                });
        'boldPriSkillNames'     : function(text) {
                                      const __PRISKILLNAMES = this.getPriSkillNames();


// ==================== Ende Abschnitt fuer Klasse Team ====================
                                      return (! text) ? text : text.replace(/\w+/g, function(name) {
                                                                                        return ((~ __PRISKILLNAMES.indexOf(name)) ? '<b>' + name + '</b>' : name);
                                                                                    });
                                  },
        'getPriSkillNames'      : function(pos = undefined) {
                                      return getSkillNameArray(getIdxPriSkills(pos ? pos : this.getPos()), this.isGoalie);
                                  },
        'getSkillSum'          : function(when = this.__TIME.now, idxSkills = undefined, restRate = 15) {
                                      let cachedItem;


// ==================== Abschnitt fuer Klasse Verein ====================
                                      if (idxSkills === undefined) {  // Gesamtsumme ueber alle Skills wird gecached...
                                          cachedItem = ((when === this.__TIME.end) ? 'skillSumEnd' : 'skillSum');


// Klasse fuer Vereinsdaten
                                          const __CACHED = this[cachedItem];
function Verein(team, land, liga, id, manager, flags) {
    'use strict';


    Team.call(this, team, land, liga);
                                          if (__CACHED !== undefined) {
                                              return __CACHED;
                                          }


    this.ID = id;
                                          idxSkills = getIdxAllSkills();
    this.Manager = manager;
                                      }
    this.Flags = (flags || []);
}


Class.define(Verein, Team, {
                                      const __SKILLS = ((when === this.__TIME.end) ? this.skillsEnd : this.skills);
                    '__TEAMITEMS' : {  // Items, die in Verein als Teamdaten gesetzt werden...
                                      let sumSkills = (when === this.__TIME.end) ? (restRate / 15) * this.restEnd : 0;
                                        'Team'    : true,
                                        'Liga'    : true,
                                        'Land'    : true,
                                        'LdNr'    : true,
                                        'LgNr'    : true,
                                        'ID'      : true,
                                        'Manager' : true,
                                        'Flags'  : true
                                    }
                });


// ==================== Ende Abschnitt fuer Klasse Verein ====================
                                      for (let idx of idxSkills) {
                                          sumSkills += __SKILLS[idx];
                                      }


// ==================== Spezialisierter Abschnitt fuer Optionen ====================
                                      if (cachedItem !== undefined) {
                                          this[cachedItem] = sumSkills;
                                      }


// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
                                      return sumSkills;
const __OPTSET = { };
                                  },
        'getSkill'              : function(when = this.__TIME.now) {
                                      return this.getSkillSum(when) / 17;
                                  },
        'getOpti'              : function(pos, when = this.__TIME.now) {
                                      const __SUMALLSKILLS = this.getSkillSum(when);
                                      const __SUMPRISKILLS = this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4);


// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
                                      return (4 * __SUMPRISKILLS + __SUMALLSKILLS) / 27;
const __TEAMCLASS = new TeamClassification();
                                  },
        'getPrios'              : function(pos, when = this.__TIME.now) {
                                      return this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4) / 4;
                                  },
        'getPriPercent'        : function(pos, when = this.__TIME.now) {
                                      const __SUMPRISKILLS = this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4);
                                      const __SUMSECSKILLS = this.getSkillSum(when, getIdxSecSkills(pos), 7);


// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
                                      return (100 * __SUMPRISKILLS) / (__SUMPRISKILLS + __SUMSECSKILLS);
__TEAMCLASS.optSelect = {
                                  },
                            'datenZat'        : true,
        'getSecPercent'        : function(pos, when = this.__TIME.now) {
                            'oldDatenZat'    : true,
                                      const __SUMPRISKILLS = this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4);
                            'fingerprints'    : true,
                                      const __SUMSECSKILLS = this.getSkillSum(when, getIdxSecSkills(pos), 7);
                            'birthdays'      : true,
                            'tClasses'        : true,
                            'progresses'      : true,
                            'ziehAnz'        : true,
                            'ziehAnzAufstieg' : true,
                            'zatAges'        : true,
                            'trainiert'      : true,
                            'positions'      : true,
                            'skills'          : true,
                            'foerderung'      : true
                        };


// Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option
                                      return (100 * __SUMSECSKILLS) / (__SUMPRISKILLS + __SUMSECSKILLS);
// optSet: Platz fuer die gesetzten Optionen
                                  },
// teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'LdNr' und 'LgNr')
        'getTrainableSkills'    : function(when = this.__TIME.now) {
// myTeam: Objekt fuer die Teamdaten
                                      return this.getSkillSum(when, getIdxTrainableSkills());
// return Die Teamdaten oder undefined bei Fehler
                                  },
function getMyTeam(optSet = undefined, teamParams = undefined, myTeam = new Team()) {
        'getFixSkills'         : function() {
    if (teamParams !== undefined) {
                                      return this.getSkillSum(this.__TIME.now, getIdxFixSkills());
        addProps(myTeam, teamParams, myTeam.__TEAMITEMS);
                                  },
        __LOG[2]("Ermittelt: " + safeStringify(myTeam));
        'getMarketValue'       : function(pos, when = this.__TIME.now) {
        // ... und abspeichern, falls erweunscht...
                                      const __AGE = this.getAge(when);
        if (optSet && optSet.team) {
 
            setOpt(optSet.team, myTeam, false);
                                      if (this.mwFormel === this.__MWFORMEL.alt) {
        }
                                          return Math.round(Math.pow((1 + this.getSkill(when)/100) * (1 + this.getOpti(pos, when)/100) * (2 - __AGE/100), 10) * 2);   // Alte Formel bis Saison 9
     } else {
                                      } else {  // MW-Formel ab Saison 10...
         const __TEAM = ((optSet && optSet.team) ? getOptValue(optSet.team) : undefined); // Gespeicherte Parameter
                                          const __MW5TF = 1.00;  // Zwischen 0.97 und 1.03
 
                                          return Math.round(Math.pow(1 + this.getSkill(when)/100, 5.65) * Math.pow(1 + this.getOpti(pos, when)/100, 8.1) * Math.pow(1 + (100 - __AGE)/49, 10) * __MW5TF);
                                      }
                                  }
    } );
 
// Funktionen fuer die HTML-Seite *******************************************************
 
// Liest eine Zahl aus der Spalte einer Zeile der Tabelle aus (z.B. Alter, Geburtsdatum)
// cells: Die Zellen einer Zeile
// colIdxInt: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Zahl (-1 fuer "keine Zahl", undefined fuer "nicht gefunden")
function getIntFromHTML(cells, colIdxInt) {
    const __CELL = getValue(cells[colIdxInt], { });
    const __TEXT = __CELL.textContent;
 
     if (__TEXT !== undefined) {
         try {
            const __VALUE = parseInt(__TEXT, 10);
 
            if (! isNaN(__VALUE)) {
                return __VALUE;
            }
        } catch (ex) { }


         if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
         return -1;
            addProps(myTeam, __TEAM, myTeam.__TEAMITEMS);
            __LOG[2]("Gespeichert: " + safeStringify(myTeam));
        } else {
            __LOG[6]("Team nicht ermittelt: " + safeStringify(__TEAM));
        }
     }
     }


     return myTeam;
     return undefined;
}
}


// Behandelt die Optionen und laedt das Benutzermenu
// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus
// optConfig: Konfiguration der Optionen
// cells: Die Zellen einer Zeile
// optSet: Platz fuer die gesetzten Optionen
// colIdxInt: Spaltenindex der gesuchten Werte
// optParams: Eventuell notwendige Parameter zur Initialisierung
// return Spalteneintrag als Dezimalzahl (undefined fuer "keine Zahl" oder "nicht gefunden")
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
function getFloatFromHTML(cells, colIdxFloat) {
// 'teamParams': Getrennte Daten-Option wird genutzt, hier: Team() mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
    const __CELL = getValue(cells[colIdxFloat], { });
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
     const __TEXT = __CELL.textContent;
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return Promise auf gefuelltes Objekt mit den gesetzten Optionen
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
    // Klassifikation ueber Land und Liga des Teams...
    __TEAMCLASS.optSet = optSet; // Classification mit optSet verknuepfen
     __TEAMCLASS.teamParams = optParams.teamParams; // Ermittelte Parameter


     return startOptions(optConfig, optSet, __TEAMCLASS).then(optSet => {
     if (__TEXT !== undefined) {
                    if (optParams.getDonation) {
        try {
                        // Jugendfoerderung aus der Options-HTML-Seite ermitteln...
            return parseFloat(__TEXT);
                        const __BOXDONATION = document.getElementsByTagName('option');
        } catch (ex) { }
                        const __DONATION = getSelectionFromComboBox(__BOXDONATION, 10000, 'Number');
    }


                        __LOG[3]("Jugendf\xF6rderung: " + __DONATION + " Euro");
    return undefined;
}


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


                    return showOptions(optSet, optParams);
    return getValue(__TEXT.toString(), "");
                }, defaultCatch);
}
}


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


// ==================== Abschnitt genereller Code zur Anzeige der Jugend ====================
    return parseInt((__TEXT === "wenig") ? -1 : (__TEXT === "hoch") ? +1 : 0, 10);
}


// Funktionen ***************************************************************************
// 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 = [];


// Erschafft die Spieler-Objekte und fuellt sie mit Werten
    for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
// playerRows: Array von Zeilen mit Array cells (Spielertabelle)
        __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
// optSet: Gesetzte Optionen (und Config)
    }
// colIdx: Liste von Spaltenindices der gesuchten Werte
// offsetUpper: Ignorierte Zeilen oberhalb der Daten
// offsetLower: Ignorierte Zeilen unterhalb der Daten
// page: 1: Teamuebersicht, 2: Spielereinzelwerte, 3: Opt. Skill, 4: Optionen, Default: 0
function init(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, page = 0) {
    storePlayerDataFromHTML(playerRows, optSet, colIdx, offsetUpper, offsetLower, page);


     const __SAISON = getOptValue(optSet.saison);
     return __RESULT;
    const __AKTZAT = getOptValue(optSet.aktuellerZat);
}
    const __GEALTERT = ((__AKTZAT >= 72) ? (getIntFromHTML(playerRows[playerRows.length - offsetLower - 1].cells, colIdx.Age) < 13) : false);
    const __CURRZAT = (__GEALTERT ? 0 : __AKTZAT);
    const __LGNR = __TEAMCLASS.team.LgNr;
    const __KLASSE = (__LGNR > 1) ? (__LGNR > 3) ? 3 : 2 : 1;
    const __DONATION = getOptValue(optSet.foerderung);
    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 __SKILLS = getOptValue(optSet.skills, []);
    const __ISSKILLPAGE = (page === 2);
    const __BASEDATA = [ __BIRTHDAYS, __TCLASSES, __PROGRESSES ];  // fuer initPlayer
    const __DATA = (__ISSKILLPAGE ? [ __SKILLS, __BASEDATA ] : [ __BASEDATA, __SKILLS ]);  // fuer initPlayer: [0] = von HTML-Seite, [1] = aus gespeicherten Daten
    const __IDMAP = getPlayerIdMap(optSet);
    const __CATIDS = __IDMAP.catIds;
    const __PLAYERS = [];


     __LOG[5](__IDMAP);
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
// cells: Die Zellen einer Zeile
// colIdxClass: Spaltenindex einer fuer TOR eingefaerbten Zelle
// return Angabe, der Spieler Torwart oder Feldspieler ist
function isGoalieFromHTML(cells, colIdxClass) {
     return (cells[colIdxClass].className === 'TOR');
}


    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++) {
// Liest einen String aus der Spalte einer Zeile der Tabelle aus, nachdem dieser konvertiert wurde
        const __CELLS = playerRows[i].cells;
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// convertFun: Funktion, die den Wert konvertiert
// return Spalteneintrag als String ("" fuer "nicht gefunden")
function convertStringFromHTML(cells, colIdxStr, convertFun = sameValue) {
    const __CELL = getValue(cells[colIdxStr], { });
    const __TEXT = convertFun(__CELL.textContent, __CELL, colIdxStr, 0);


        if (__CELLS.length > 1) {
    if (__TEXT !== undefined) {
            const __LAND = getStringFromHTML(__CELLS, colIdx.Land);
        __CELL.innerHTML = __TEXT;
            const __AGE = getIntFromHTML(__CELLS, colIdx.Age);
    }
            const __ISGOALIE = isGoalieFromHTML(__CELLS, colIdx.Age);
            const __AKTION = getElementFromHTML(__CELLS, colIdx.Akt);


            const __NEWPLAYER = new PlayerRecord(__LAND, __AGE, __ISGOALIE, __SAISON, __CURRZAT, __DONATION);
    return getValue(__TEXT.toString(), "");
}


            __NEWPLAYER.initPlayer(__DATA[0], j, __ISSKILLPAGE);
// Liest ein Array von String-Werten aus den Spalten ab einer Zeile der Tabelle aus, nachdem diese konvertiert wurden
// cells: Die Zellen einer Zeile
// colIdxArr: Erster Spaltenindex der gesuchten Werte
// arrOrLength: Entweder ein Datenarray zum Fuellen oder die Anzahl der zu lesenden Werte
// convertFun: Funktion, die die Werte konvertiert
// return Array mit Spalteneintraegen als String ("" fuer "nicht gefunden")
function convertArrayFromHTML(cells, colIdxArr, arrOrLength = 1, convertFun = sameValue) {
    const __ARR = ((typeof arrOrSize === 'number') ? { } : arrOrLength);
    const __LENGTH = getValue(__ARR.length, arrOrLength);
    const __RET = [];


            const __IDX = selectPlayerIndex(__NEWPLAYER, j, __CATIDS);
    for (let index = 0, colIdx = colIdxArr; index < __LENGTH; index++, colIdx++) {
        const __CELL = getValue(cells[colIdx], { });
        const __TEXT = convertFun(getValue(__ARR[index], __CELL.textContent), __CELL, colIdx, index);


            __NEWPLAYER.initPlayer(__DATA[1], __IDX, ! __ISSKILLPAGE);
        if (__TEXT !== undefined) {
 
             __CELL.innerHTML = __TEXT;
            __NEWPLAYER.prognoseSkills();
 
            if (! __ISSKILLPAGE) {
                __NEWPLAYER.setZusatz(__ZATAGES[__IDX], __TRAINIERT[__IDX], __POSITIONS[__IDX]);
            }
 
             __NEWPLAYER.createWarnDraw(__AKTION, __KLASSE);
 
            __PLAYERS[j++] = __NEWPLAYER;
         }
         }
    }


    if (__ISSKILLPAGE) {
        __RET.push(getValue(__TEXT, "").toString());
        calcPlayerData(__PLAYERS, optSet);
    } else {
        setPlayerData(__PLAYERS, optSet);
     }
     }


    storePlayerIds(__PLAYERS, optSet);
     return __RET;
 
     return __PLAYERS;
}
}


// Berechnet die Identifikations-IDs (Fingerprints) der Spieler neu und speichert diese
// Konvertiert den Aufwertungstext einer Zelle auf der Jugend-Teamuebersicht
function getPlayerIdMap(optSet) {
// value: Der Inhalt dieser Zeile ("+1 SKI +1 OPT" bzw. "+2 SKI)
    const __FINGERPRINTS = getOptValue(optSet.fingerprints, []);
// cell: Zelle, in der der Text stand (optional)
    const __MAP = {
// return Der konvertierte String ("SKI OPT" bzw. "SKI SKI")
                      'ids'    : { },
function convertAufwertung(value, cell = undefined) {
                      'cats'  : [],
     if (value !== undefined) {
                      'catIds' : { }
        value = value.replace(/\+2 (\w+)/, "$1 $1").replace(/\+1 /g, "");
                  };
     const __IDS = __MAP.ids;
    const __CATS = __MAP.cats;
    const __CATIDS = __MAP.catIds;


    for (let i = 0; i < __FINGERPRINTS.length; i++) {
        if (cell) {
        const __ID = __FINGERPRINTS[i];
            if (cell.className === 'TOR') {
        const __CAT = PlayerRecord.prototype.getCatFromFingerPrint(__ID);
                value = convertGoalieSkill(value);
            }


        if (__ID) {
             cell.align = 'left';
             if (! __CATIDS[__CAT]) {
                __CATIDS[__CAT] = { };
            }
            __IDS[__ID] = i;
            __CATS[i] = __CAT;
            __CATIDS[__CAT][__ID] = i;
         }
         }
     }
     }


     return __MAP;
     return value;
}
}


// Berechnet die Identifikations-IDs (Fingerprints) der Spieler neu und speichert diese
// Formatiert eine Zelle um (mit einfachen Parametern)
// players: Array von PlayerRecord mit den Spielerdaten
// cell: Zu formatierende Zelle
// optSet: Gesetzte Optionen (und Config)
// bold: Inhalt fett darstellen (true = ja, false = nein)
function storePlayerIds(players, optSet) {
// color: Falls angegeben, die Schriftfarbe
     const __FINGERPRINTS = [];
// bgColor: Falls angegeben, die Hintergrundfarbe
 
// return Die formatierte Zelle
    for (let i = 0; i < players.length; i++) {
function formatCell(cell, bold = true, color = undefined, bgColor = undefined) {
        const __PLAYER = players[i];
     if (cell) {
 
        if (bold) {
         if ((__PLAYER.zatGeb !== undefined) && (__PLAYER.talent !== undefined) && (__PLAYER.positions !== undefined)) {
            cell.style.fontWeight = 'bold';
             __FINGERPRINTS[i]  = __PLAYER.getFingerPrint();
        }
         if (color) {
            cell.style.color = color;
        }
        if (bgColor) {
             cell.style.backgroundColor = bgColor;
         }
         }
     }
     }


     setOpt(optSet.fingerprints, __FINGERPRINTS, false);
     return cell;
}
}


// Sucht fuer den Spieler den Eintrag aus catIds heraus und gibt den (geloeschten) Index zurueck
// Konvertiert die allgemeinen Skills in die eines Torwarts
// player: PlayerRecord mit den Daten eines Spielers
// value: Ein Text, der die Skillnamen enthaelt
// index: Position des Spielers im neuen Array von Spielerdaten
// return Der konvertierte String mit Aenderungen (z.B. "FAN" statt "KOB") oder unveraendert
// catIds: PlayerIdMap zum Finden des Spielers ueber die Spielerdaten
function convertGoalieSkill(value) {
// return Original-Index der Daten dieses Spielers im Array von Spielerdaten
     if (value !== undefined) {
function selectPlayerIndex(player, index, catIds) {
         value = value.replace(/\w+/g, getGoalieSkill);
    const __MYCAT = player.getCat();
    const __CATS = catIds[__MYCAT];
    const __ID = player.findInFingerPrints(__CATS);
    let idx = index;
 
     if (__ID !== undefined) {
         idx = __CATS[__ID];
        delete __CATS[__ID];
     }
     }


     return idx;
     return value;
}
}


// Speichtert die abgeleiteten Werte in den Spieler-Objekten
// Konvertiert einen Aufwertungstext fuer einen Skillnamen in den fuer einen Torwart
// players: Array von PlayerRecord mit den Spielerdaten
// name: Allgemeiner Skillname (abgeleitet von den Feldspielern)
// optSet: Gesetzte Optionen (und Config)
// return Der konvertierte String (z.B. "FAN" statt "KOB") oder unveraendert
function setPlayerData(players, optSet) {
function getGoalieSkill(name) {
     const __ZIEHANZAHL = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
     const __GOALIESKILLS = {
    let ziehAnzAufstieg = 0;
                              'SCH' : 'ABS',
    const __ZATAGES = [];
                              'BAK' : 'STS',
    const __TRAINIERT = [];
                              'KOB' : 'FAN',
    const __POSITIONS = [];
                              'ZWK' : 'STB',
                              'DEC' : 'SPL',
                              'GES' : 'REF'
                          };


     for (let i = 0; i < players.length; i++) {
     return getValue(__GOALIESKILLS[name], name);
        const __ZUSATZ = players[i].calcZusatz();
}


        if (__ZUSATZ.zatAge !== undefined) {  // braucht Geburtstag fuer gueltige Werte!
// Liest die Aufwertungen eines Spielers aus und konvertiert je nachdem, ob der Spieler Torwart oder Feldspieler ist
            const __INDEX = players[i].calcZiehIndex(); // Lfd. Nummer des Abrechnungsmonats (0-basiert)
// cells: Die Zellen einer Zeile
// colIdxAuf: Spaltenindex der gesuchten Aufwertungen
// shortForm: true = abgekuerzt, false = Originalform
// return Konvertierte Aufwertungen (kurze oder lange Form, aber in jedem Fall fuer Torwart konvertiert)
function getAufwertFromHTML(cells, colIdxAuf, shortForm = true) {
    const __ISGOALIE = isGoalieFromHTML(cells, colIdxAuf);


            if ((__INDEX >= 0) && (__INDEX < __ZIEHANZAHL.length)) {
    return convertStringFromHTML(cells, colIdxAuf, (shortForm ? convertAufwertung : __ISGOALIE ? convertGoalieSkill : undefined));
                __ZIEHANZAHL[__INDEX]++;
}
            }


            __ZATAGES[i] = __ZUSATZ.zatAge;
// Identitaetsfunktion. Konvertiert nichts, sondern liefert einfach den Wert zurueck
        }
// value: Der uebergebene Wert
        if (players[i].isZiehAufstieg()) {
// return Derselbe Wert
            ziehAnzAufstieg++;
function sameValue(value) {
        }
     return value;
        __TRAINIERT[i] = __ZUSATZ.trainiert;
        __POSITIONS[i] = __ZUSATZ.bestPos;
    }
 
    setOpt(optSet.ziehAnz, __ZIEHANZAHL, false);
    setOpt(optSet.ziehAnzAufstieg, ziehAnzAufstieg, false);
    setOpt(optSet.zatAges, __ZATAGES, false);
    setOpt(optSet.trainiert, __TRAINIERT, false);
     setOpt(optSet.positions, __POSITIONS, false);
}
}


// Berechnet die abgeleiteten Werte in den Spieler-Objekten neu und speichert diese
// Liefert den ganzzeiligen Anteil einer Zahl zurueck, indem alles hinter einem Punkt abgeschnitten wird
// players: Array von PlayerRecord mit den Spielerdaten
// value: Eine uebergebene Dezimalzahl
// optSet: Gesetzte Optionen (und Config)
// return Der ganzzeilige Anteil dieser Zahl
function calcPlayerData(players, optSet) {
function floorValue(value, dot = '.') {
     const __ZATAGES = [];
     const __VALUE = value.toString();
     const __TRAINIERT = [];
     const __INDEXDOT = (__VALUE ? __VALUE.indexOf(dot) : -1);
    const __POSITIONS = [];


     for (let i = 0; i < players.length; i++) {
     return Number((~ __INDEXDOT) ? __VALUE.substring(0, __INDEXDOT) : __VALUE);
        const __ZUSATZ = players[i].calcZusatz();
}


        if (__ZUSATZ.zatAge !== undefined) {  // braucht Geburtstag fuer gueltige Werte!
// Hilfsfunktionen **********************************************************************
            __ZATAGES[i] = __ZUSATZ.zatAge;
        }
        __TRAINIERT[i] = __ZUSATZ.trainiert;
        __POSITIONS[i] = __ZUSATZ.bestPos;
    }


    setOpt(optSet.zatAges, __ZATAGES, false);
// Sortiert das Positionsfeld per BubbleSort
     setOpt(optSet.trainiert, __TRAINIERT, false);
function sortPositionArray(array) {
     setOpt(optSet.positions, __POSITIONS, false);
    const __TEMP = [];
}
    let transposed = true;
     // TOR soll immer die letzte Position im Feld sein, deshalb - 1
     let length = array.length - 1;


// Ermittelt die fuer diese Seite relevanten Werte in den Spieler-Objekten aus den Daten der Seite und speichert diese
    while (transposed && (length > 1)) {
// playerRows: Array von Zeilen mit Array cells (Spielertabelle)
        transposed = false;
// optSet: Gesetzte Optionen (und Config)
        for (let i = 0; i < length - 1; i++) {
// colIdx: Liste von Spaltenindices der gesuchten Werte
            // Vergleich Opti-Werte:
// offsetUpper: Ignorierte Zeilen oberhalb der Daten
            if (array[i][1] < array[i + 1][1]) {
// offsetLower: Ignorierte Zeilen unterhalb der Daten
                // vertauschen
// page: 1: Teamuebersicht, 2: Spielereinzelwerte, 3: Opt. Skill, 4: Optionen, Default: 0
                __TEMP[0] = array[i][0];
function storePlayerDataFromHTML(playerRows, optSet, colIdx, offsetUpper = 1, offsetLower = 0, page = 0) {
                __TEMP[1] = array[i][1];
    const __COLDEFS = [ { }, {
                array[i][0] = array[i + 1][0];
                                'birthdays'  : { 'name' : 'birthdays', 'getFun' : getIntFromHTML, 'params' : [ colIdx.Geb ] },
                array[i][1] = array[i + 1][1];
                                'tClasses'  : { 'name' : 'tClasses', 'getFun' : getTalentFromHTML, 'params' : [ colIdx.Tal ] },
                array[i + 1][0] = __TEMP[0];
                                'progresses' : { 'name' : 'progresses', 'getFun' : getAufwertFromHTML, 'params' : [ colIdx.Auf, getOptValue(optSet.shortAufw, true) ] }
                 array[i + 1][1] = __TEMP[1];
                            }, {
                transposed = true;
                                'skills'    : { 'name' : 'skills', 'getFun' : getSkillsFromHTML, 'params' : [ colIdx ]}
                            } ][getValueIn(page, 1, 2, 0)];
 
    return storePlayerDataColsFromHTML(playerRows, optSet, __COLDEFS, offsetUpper, offsetLower);
}
 
// Ermittelt bestimmte Werte in den Spieler-Objekten aus den Daten der Seite und speichert diese
// playerRows: Array von Zeilen mit Array cells (Spielertabelle)
// optSet: Gesetzte Optionen (und Config)
// colDefs: Informationen zu ausgewaehlten Datenspalten
// offsetUpper: Ignorierte Zeilen oberhalb der Daten
// offsetLower: Ignorierte Zeilen unterhalb der Daten
function storePlayerDataColsFromHTML(playerRows, optSet, colDefs, offsetUpper = 1, offsetLower = 0) {
    const __DATA = { };
 
    for (let key in colDefs) {
        __DATA[key] = [];
    }
 
    for (let i = offsetUpper, j = 0; i < playerRows.length - offsetLower; i++) {
        const __CELLS = playerRows[i].cells;
 
        if (__CELLS.length > 1) {
            for (let key in colDefs) {
                const __COLDEF = colDefs[key];
 
                 __DATA[key][j] = __COLDEF.getFun(__CELLS, ...__COLDEF.params);
             }
             }
            j++;
         }
         }
        length--;
     }
     }
}


     for (let key in colDefs) {
// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
         const __COLDEF = colDefs[key];
// 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);


         __LOG[7]('Schreibe ' + __COLDEF.name + ': ' + __DATA[key]);
         return getNumberString(__VORKOMMA) + "," + __NACHKOMMA;
 
     } else {
        setOpt(optSet[__COLDEF.name], __DATA[key], false);
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
     }
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
}
        const __TEMP = reverseString(numberString);
 
         let result = "";
// Trennt die Gruppen (z.B. Jahrgaenge) mit Linien
function separateGroups(rows, borderString, colIdxSort = 0, offsetUpper = 1, offsetLower = 0, offsetLeft = -1, offsetRight = 0, formatFun = sameValue) {
    if (offsetLeft < 0) {
         offsetLeft = colIdxSort; // ab Sortierspalte
    }


    for (let i = offsetUpper, newVal, oldVal = formatFun((rows[i].cells[colIdxSort] || { }).textContent); i < rows.length - offsetLower - 1; i++, oldVal = newVal) {
        for (let i = 0; i < __TEMP.length; i++) {
        newVal = formatFun((rows[i + 1].cells[colIdxSort] || { }).textContent);
            if ((i > 0) && (i % 3 === 0)) {
        if (newVal !== oldVal) {
                 result += ".";
            for (let j = offsetLeft; j < rows[i].cells.length - offsetRight; j++) {
                 rows[i].cells[j].style.borderBottom = borderString;
             }
             }
            result += __TEMP.substr(i, 1);
         }
         }
        return reverseString(result);
     }
     }
}
}


// Klasse ColumnManager *****************************************************************
// Dreht den uebergebenen String um
function reverseString(string) {
    let result = "";


function ColumnManager(optSet, colIdx, showCol) {
    for (let i = string.length - 1; i >= 0; i--) {
     'use strict';
        result += string.substr(i, 1);
     }


     __LOG[3]("ColumnManager()");
     return result;
}


     const __SHOWCOL = getValue(showCol, true);
// Schaut nach, ob der uebergebene Index zu einem trainierbaren Skill gehoert
     const __SHOWALL = ((__SHOWCOL === true) || (__SHOWCOL.Default === true));
// Die Indizes gehen von 0 (SCH) bis 16 (EIN)
function isTrainableSkill(idx) {
     const __TRAINABLESKILLS = getIdxTrainableSkills();
     const __IDX = parseInt(idx, 10);
    let result = false;


     const __BIRTHDAYS = getOptValue(optSet.birthdays, []).length;
     for (let idxTrainable of __TRAINABLESKILLS) {
    const __TCLASSES = getOptValue(optSet.tClasses, []).length;
        if (__IDX === idxTrainable) {
     const __PROGRESSES = getOptValue(optSet.progresses, []).length;
            result = true;
            break;
        }
     }


     const __ZATAGES = getOptValue(optSet.zatAges, []).length;
     return result;
     const __TRAINIERT = getOptValue(optSet.trainiert, []).length;
}
     const __POSITIONS = getOptValue(optSet.positions, []).length;
 
// Gibt alle Skill-Namen zurueck
function getAllSkillNames(isGoalie = false) {
     if (isGoalie) {
        return [ 'ABS', 'STS', 'FAN', 'STB', 'SPL', 'REF', 'FUQ', 'ERF', 'AGG', 'PAS', 'AUS', 'UEB', 'WID', 'SEL', 'DIS', 'ZUV', 'EIN' ];
     } else {
        return [ 'SCH', 'BAK', 'KOB', 'ZWK', 'DEC', 'GES', 'FUQ', 'ERF', 'AGG', 'PAS', 'AUS', 'UEB', 'WID', 'SEL', 'DIS', 'ZUV', 'EIN' ];
    }
}


    const __EINZELSKILLS = getOptValue(optSet.skills, []).length;
// Gibt den Skill-Namen zu einem Index zurueck
     const __PROJECTION = (__EINZELSKILLS && __ZATAGES);
function getSkillName(idx, isGoalie = false) {
     const __ALLNAMES = getAllSkillNames(isGoalie);


     this.colIdx = colIdx;
     return __ALLNAMES[idx];
}


     this.saison = getOptValue(optSet.saison);
// Gibt den Skill-Namen zu einem Index-Array zurueck
    this.gt = getOptValue(optSet.zeigeJahrgang);
function getSkillNameArray(idxArr, isGoalie = false) {
    this.gtUxx = getOptValue(optSet.zeigeUxx);
     return (idxArr ? idxArr.map(function(item) {
                                    return getSkillName(item, isGoalie);
                                }) : idxArr);
}


    this.fpId = (__BIRTHDAYS && __TCLASSES && __POSITIONS && getValue(__SHOWCOL.zeigeId, __SHOWALL) && getOptValue(optSet.zeigeId));
// Gibt die Indizes aller Skills zurueck
    this.warn = (__ZATAGES && getValue(__SHOWCOL.zeigeWarnung, __SHOWALL) && getOptValue(optSet.zeigeWarnung));
function getIdxAllSkills() {
     this.warnMonth = (__ZATAGES && getValue(__SHOWCOL.zeigeWarnungMonat, __SHOWALL) && getOptValue(optSet.zeigeWarnungMonat));
     return [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ];
    this.warnHome = (__ZATAGES && getValue(__SHOWCOL.zeigeWarnungHome, __SHOWALL) && getOptValue(optSet.zeigeWarnungHome));
    this.warnDialog = (__ZATAGES && getValue(__SHOWCOL.zeigeWarnungDialog, __SHOWALL) && getOptValue(optSet.zeigeWarnungDialog));
    this.warnAufstieg = (__ZATAGES && getValue(__SHOWCOL.zeigeWarnungAufstieg, __SHOWALL) && getOptValue(optSet.zeigeWarnungAufstieg));
    this.warnLegende = (__ZATAGES && getValue(__SHOWCOL.zeigeWarnungLegende, __SHOWALL) && getOptValue(optSet.zeigeWarnungLegende));
    this.bar = (__PROJECTION && getValue(__SHOWCOL.zeigeBalken, __SHOWALL) && getOptValue(optSet.zeigeBalken));
    this.barAbs = getOptValue(optSet.absBalken);
    this.donor = getOptValue(optSet.foerderung);
    this.geb = (__BIRTHDAYS && getValue(__SHOWCOL.zeigeGeb, __SHOWALL) && getOptValue(optSet.zeigeGeb));
    this.tal = (__TCLASSES && getValue(__SHOWCOL.zeigeTal, __SHOWALL) && getOptValue(optSet.zeigeTal));
    this.quo = (__ZATAGES && __TRAINIERT && getValue(__SHOWCOL.zeigeQuote, __SHOWALL) && getOptValue(optSet.zeigeQuote));
    this.aufw = (__PROGRESSES && getValue(__SHOWCOL.zeigeAufw, __SHOWALL) && getOptValue(optSet.zeigeAufw));
    this.substAge = (__ZATAGES && getValue(__SHOWCOL.ersetzeAlter, __SHOWALL) && getOptValue(optSet.ersetzeAlter));
    this.alter = (__ZATAGES && getValue(__SHOWCOL.zeigeAlter, __SHOWALL) && getOptValue(optSet.zeigeAlter));
    this.fix = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeFixSkills, __SHOWALL) && getOptValue(optSet.zeigeFixSkills));
    this.tr = (__EINZELSKILLS && __TRAINIERT && getValue(__SHOWCOL.zeigeTrainiert, __SHOWALL) && getOptValue(optSet.zeigeTrainiert));
    this.zat = (__ZATAGES && getValue(__SHOWCOL.zeigeZatDone, __SHOWALL) && getOptValue(optSet.zeigeZatDone));
    this.antHpt = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeAnteilPri, __SHOWALL) && getOptValue(optSet.zeigeAnteilPri));
    this.antNeb = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeAnteilSec, __SHOWALL) && getOptValue(optSet.zeigeAnteilSec));
    this.pri = (__EINZELSKILLS && getValue(__SHOWCOL.zeigePrios, __SHOWALL) && getOptValue(optSet.zeigePrios));
    this.skill = (__EINZELSKILLS && getValue(__SHOWCOL.zeigeSkill, __SHOWALL) && getOptValue(optSet.zeigeSkill));
    this.pos = (__EINZELSKILLS && __POSITIONS && getValue(__SHOWCOL.zeigePosition, __SHOWALL) && getOptValue(optSet.zeigePosition));
    this.anzOpti = ((__EINZELSKILLS && getValue(__SHOWCOL.zeigeOpti, __SHOWALL)) ? getOptValue(optSet.anzahlOpti) : 0);
    this.anzMw =  ((__PROJECTION && getValue(__SHOWCOL.zeigeMW, __SHOWALL)) ? getOptValue(optSet.anzahlMW) : 0);
    this.substSkills = (__PROJECTION && getValue(__SHOWCOL.ersetzeSkills, __SHOWALL) && getOptValue(optSet.ersetzeSkills));
    this.trE = (__PROJECTION && __TRAINIERT && getValue(__SHOWCOL.zeigeTrainiertEnde, __SHOWALL) && getOptValue(optSet.zeigeTrainiertEnde));
    this.zatE = (__ZATAGES && getValue(__SHOWCOL.zeigeZatLeft, __SHOWALL) && getOptValue(optSet.zeigeZatLeft));
    this.antHptE = (__PROJECTION && getValue(__SHOWCOL.zeigeAnteilPriEnde, __SHOWALL) && getOptValue(optSet.zeigeAnteilPriEnde));
    this.antNebE = (__PROJECTION && getValue(__SHOWCOL.zeigeAnteilSecEnde, __SHOWALL) && getOptValue(optSet.zeigeAnteilSecEnde));
    this.priE = (__PROJECTION && getValue(__SHOWCOL.zeigePriosEnde, __SHOWALL) && getOptValue(optSet.zeigePriosEnde));
    this.skillE = (__PROJECTION && getValue(__SHOWCOL.zeigeSkillEnde, __SHOWALL) && getOptValue(optSet.zeigeSkillEnde));
    this.anzOptiE = ((__PROJECTION && getValue(__SHOWCOL.zeigeOptiEnde, __SHOWALL)) ? getOptValue(optSet.anzahlOptiEnde) : 0);
    this.anzMwE = ((__PROJECTION && getValue(__SHOWCOL.zeigeMWEnde, __SHOWALL)) ? getOptValue(optSet.anzahlMWEnde) : 0);
    this.kennzE = getOptValue(optSet.kennzeichenEnde);
}
}


Class.define(ColumnManager, Object, {
// Gibt die Indizes der trainierbaren Skills zurueck
        'toString'      : function() { // Bisher nur die noetigsten Parameter ausgegeben...
function getIdxTrainableSkills() {
                              let result = "Skillschnitt\t\t" + this.skill + '\n';
    return [ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15 ];
                              result += "Beste Position\t" + this.pos + '\n';
}
                              result += "Optis\t\t\t" + this.anzOpti + '\n';
                              result += "Marktwerte\t\t" + this.anzMw + '\n';
                              result += "Skillschnitt Ende\t" + this.skillE + '\n';
                              result += "Optis Ende\t\t" + this.anzOptiE + '\n';
                              result += "Marktwerte Ende\t" + this.anzMwE + '\n';


                              return result;
// Gibt die Indizes der Fixskills zurueck
                          },
function getIdxFixSkills() {
        'addCell'        : function(tableRow) {
    return [ 6, 7, 12, 13, 14, 16 ];
                              return tableRow.insertCell(-1);
}
                          },
        'addAndFillCell' : function(tableRow, value, color, align, digits = 2) {
                              let text = value;


                              if ((value || (value === 0)) && isFinite(value) && (value !== true) && (value !== false)) {
// Gibt die Indizes der Primaerskills zurueck
                                  // Zahl einfuegen
function getIdxPriSkills(pos) {
                                  if (value < 1000) {
    switch (pos) {
                                      // Mit Nachkommastellen darstellen
        case 'TOR' : return [ 2, 3, 4, 5 ];
                                      text = parseFloat(value).toFixed(digits);
        case 'ABW' : return [ 2, 3, 4, 15 ];
                                  } else {
        case 'DMI' : return [ 1, 4, 9, 11 ];
                                      // Mit Tausenderpunkten darstellen
        case 'MIT' : return [ 1, 3, 9, 11 ];
                                      text = getNumberString(value.toString());
        case 'OMI' : return [ 1, 5, 9, 11 ];
                                  }
        case 'STU' : return [ 0, 2, 3, 5 ];
                              }
        default :    return [];
    }
}


                              // String, Boolean oder Zahl einfuegen...
// Gibt die Indizes der (trainierbaren) Sekundaerskills zurueck
                              const __CELL = this.addCell(tableRow);
function getIdxSecSkills(pos) {
    switch (pos) {
        case 'TOR' : return [ 0, 1, 8, 9, 10, 11, 15 ];
        case 'ABW' : return [ 0, 1, 5, 8, 9, 10, 11 ];
        case 'DMI' : return [ 0, 2, 3, 5, 8, 10, 15 ];
        case 'MIT' : return [ 0, 2, 4, 5, 8, 10, 15 ];
        case 'OMI' : return [ 0, 2, 3, 4, 8, 10, 15 ];
        case 'STU' : return [ 1, 4, 8, 9, 10, 11, 15 ];
        default :    return [];
    }
}


                              __CELL.innerHTML = text;
// Gibt die zur Position gehoerige Farbe zurueck
                              if (color) {
function getColor(pos) {
                                  __CELL.style.color = color;
    switch (pos) {
                              }
        case 'TOR' : return "#FFFF00";
                              if (align) {
        case 'ABW' : return "#00FF00";
                                  __CELL.align = align;
        case 'DMI' : return "#3366FF";
                              }
        case 'MIT' : return "#66FFFF";
 
        case 'OMI' : return "#FF66FF";
                              return __CELL;
         case 'STU' : return "#FF0000";
                          },
        case 'LEI' : return "#FFFFFF";
         'addAndBarCell' : function(tableRow, value, scale = 100, offset = 0, width = 100, height = 10, zoom = 100) {
        case "" :    return "#111166";  // osBlau
                              const __VALUE = ((scale && isFinite(value)) ? ((value - offset) / Math.max(1, scale - offset) * 100) : 0);
        default :    return "";
 
    }
                              // HTML-Code fuer Anteilsbalken einfuegen...
}
                              const __CELL = this.addCell(tableRow);


                              __CELL.innerHTML = this.getBarImg(__VALUE, width, height, zoom);
// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
                              __CELL.align = 'left';


                              return __CELL;
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
                          },
        'getBarImg'      : function(value, width = 100, height = 10, zoom = 100) {
                              const __IMAGE = Math.min(99, Math.max(0, getMulValue(value, 1, 0, 0)));
                              const __LENGTH = getMulValue(width / 100, getMulValue(zoom / 100, value, 0, 0), 0, 0);
                              const __WIDTH = Math.min(width, __LENGTH);
                              const __HEIGHT = Math.max(3, getMulValue(zoom / 100, height * (__LENGTH / __WIDTH), 0, 0));


                              // HTML-Code fuer Anteilsbalken...
const __GAMETYPES = {    // "Blind FSS gesucht!"
                              return '<img src="images/balken/' + __IMAGE + '.GIF" width="' + __WIDTH + '" height=' + __HEIGHT + '>';
        'unbekannt'  : -1,
                          },
        'reserviert' :  0,
         'addTitles'     : function(headers, titleColor = "#FFFFFF") {
        'Frei'      :  0,
                              // Spaltentitel zentrieren
        'spielfrei'  :  0,
                              headers.align = "center";
        'Friendly'   :  1,
        'Liga'       :  2,
        'LP'         :  3,
        'OSEQ'       :  4,
        'OSE'        :  5,
         'OSCQ'       : 6,
        'OSC'        :  7
    };


                              // Titel fuer die aktuellen Werte
const __LIGANRN = {
                              if (this.fpId) {
        'unbekannt'  :  0,
                                  this.addAndFillCell(headers, "Identifikation", titleColor);
        '1. Liga'    :  1,
                              }
        '2. Liga A'  :  2,
                              if (this.bar) {
        '2. Liga B'  :  3,
                                  this.addAndFillCell(headers, "Qualit\xE4t", titleColor);
        '3. Liga A'  :  4,
                              }
        '3. Liga B'  :  5,
                              if (this.tal) {
        '3. Liga C'  :  6,
                                  this.addAndFillCell(headers, "Talent", titleColor);
        '3. Liga D'  :  7
                              }
    };
                              if (this.quo) {
                                  this.addAndFillCell(headers, "Quote", titleColor);
                              }
                              if (this.aufw) {
                                  this.addAndFillCell(headers, "Aufwertung", titleColor);
                              }
                              if (this.geb) {
                                  this.addAndFillCell(headers, "Geb.", titleColor);
                              }
                              if (this.alter && ! this.substAge) {
                                  this.addAndFillCell(headers, "Alter", titleColor);
                              }
                              if (this.fix) {
                                  this.addAndFillCell(headers, "fix", titleColor);
                              }
                              if (this.tr) {
                                  this.addAndFillCell(headers, "tr.", titleColor);
                              }
                              if (this.zat) {
                                  this.addAndFillCell(headers, "ZAT", titleColor);
                              }
                              if (this.antHpt) {
                                  this.addAndFillCell(headers, "%H", titleColor);
                              }
                              if (this.antNeb) {
                                  this.addAndFillCell(headers, "%N", titleColor);
                              }
                              if (this.pri) {
                                  this.addAndFillCell(headers, "Prios", 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
const __LANDNRN = {
                              if (this.trE) {
        'unbekannt'              :  0,
                                  this.addAndFillCell(headers, "tr." + this.kennzE, titleColor);
        'Albanien'              :  45,
                              }
        'Andorra'                :  95,
                              if (this.zatE) {
        'Armenien'              :  83,
                                  this.addAndFillCell(headers, "ZAT" + this.kennzE, titleColor);
        'Aserbaidschan'          : 104,
                              }
        'Belgien'                :  12,
                              if (this.antHptE) {
        'Bosnien-Herzegowina'    :  66,
                                  this.addAndFillCell(headers, "%H" + this.kennzE, titleColor);
        'Bulgarien'              :  42,
                              }
        'D\xE4nemark'            :  8,
                              if (this.antNebE) {
        'Deutschland'            :  6,
                                  this.addAndFillCell(headers, "%N" + this.kennzE, titleColor);
        'England'                :  1,
                              }
        'Estland'                :  57,
                              if (this.priE) {
        'Far\xF6er'              :  68,
                                  this.addAndFillCell(headers, "Prios" + this.kennzE, titleColor);
        'Finnland'              :  40,
                              }
        'Frankreich'            :  32,
                              if (this.skillE) {
        'Georgien'              :  49,
                                  this.addAndFillCell(headers, "Skill" + this.kennzE, titleColor);
        'Griechenland'          :  30,
                              }
        'Irland'                :  5,
                              for (let i = 1; i <= 6; i++) {
        'Island'                :  29,
                                  if (i <= this.anzOptiE) {
        'Israel'                :  23,
                                      this.addAndFillCell(headers, "Opti " + i + this.kennzE, titleColor);
        'Italien'                :  10,
                                  }
        'Kasachstan'            : 105,
                                  if (i <= this.anzMwE) {
        'Kroatien'              :  24,
                                      this.addAndFillCell(headers, "MW " + i + this.kennzE, titleColor);
        'Lettland'              :  97,
                                  }
        'Liechtenstein'          :  92,
                              }
        'Litauen'                :  72,
                          }// Ende addTitles()
        'Luxemburg'              :  93,
         'addValues'     : function(player, playerRow, color = "#FFFFFF") {
        'Malta'                  :  69,
                              // Warnlevel des Spielers anpassen...
        'Mazedonien'            :  86,
                              const __WARNDRAW = player.warnDraw || player.warnDrawAufstieg || __NOWARNDRAW;
        'Moldawien'              :  87,
                              __WARNDRAW.setWarn(this.warn, this.warnMonth, this.warnAufstieg);
        'Niederlande'            :  11,
        'Nordirland'            :  4,
        'Norwegen'              :  9,
        '\xD6sterreich'          :  14,
        'Polen'                  :  25,
        'Portugal'              :  17,
        'Rum\xE4nien'            :  28,
        'Russland'              :  19,
        'San Marino'            :  98,
        'Schottland'            :  2,
        'Schweden'              :  27,
        'Schweiz'                :  37,
        'Serbien und Montenegro' :  41,
        'Slowakei'              :  70,
        'Slowenien'              :  21,
        'Spanien'                : 13,
         'Tschechien'             : 18,
        'T\xFCrkei'              :  39,
        'Ukraine'                :  20,
        'Ungarn'                :  26,
        'Wales'                  :  3,
        'Weissrussland'          :  71,
        'Zypern'                :  38
    };


                              const __IDXPRI = getIdxPriSkills(player.getPos());
// ==================== Abschnitt fuer Daten des Spielplans ====================
                              const __COLOR = __WARNDRAW.getColor(player.isGoalie ? getColor('TOR') : color); // Angepasst an Ziehwarnung
                              const __POS1COLOR = getColor((player.getPosPercent() > 99.99) ? 'LEI' : player.getPos());
                              const __OSBLAU = getColor("");


                              // Aktuelle Werte
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
                              if (this.fpId) {
// gameType: Name des Wettbewerbs eines Spiels
                                  this.addAndFillCell(playerRow, player.getFingerPrint(), __COLOR);
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig
                              }
function getGameTypeID(gameType) {
                              if (this.bar) {
    return getValue(__GAMETYPES[gameType], __GAMETYPES.unbekannt);
                                  const __VALUE = player.getPrios(player.getPos(), player.__TIME.end);
}
                                  const __SCALE = (this.barAbs ? 100 : (this.donor / 125));
                                  const __OFFSET = (this.barAbs ? 0 : Math.pow(__SCALE / 20, 2));
                                  const __ZOOM = 50 + __SCALE / 2;


                                  this.addAndBarCell(playerRow, __VALUE, __SCALE, __OFFSET, 100, 10, __ZOOM);
// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
                              }
// land: Name des Landes
                              if (this.tal) {
// return OS2-ID des Landes, 0 fuer ungueltig
                                  this.addAndFillCell(playerRow, player.getTalent(), __COLOR);
function getLandNr(land) {
                              }
    return getValue(__LANDNRN[land], __LANDNRN.unbekannt);
                              if (this.quo) {
}
                                  this.addAndFillCell(playerRow, player.getAufwertungsSchnitt(), __COLOR, null, 2);
 
                              }
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
                              if (this.colIdx.Auf) {
// land: Name der Liga
                                  convertStringFromHTML(playerRow.cells, this.colIdx.Auf, function(aufwert) {
// return OS2-ID der Liga, 0 fuer ungueltig
                                                                                              return player.boldPriSkillNames(aufwert);
function getLigaNr(liga) {
                                                                                          });
    return getValue(__LIGANRN[liga], __LIGANRN.unbekannt);
                              }
}
                              if (this.aufw) {
 
                                  this.addAndFillCell(playerRow, player.boldPriSkillNames(player.getAufwert()), __COLOR, 'left');
// ==================== Abschnitt fuer sonstige Parameter ====================
                              }
 
                              if (this.geb) {
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
                                  this.addAndFillCell(playerRow, player.getGeb(), __COLOR, null, 0);
        'Zeile'  : 0,
                              }
        'Spalte' : 1,
                              if (this.substAge) {
        'start'  : " von ",
                                  convertStringFromHTML(playerRow.cells, this.colIdx.Age, function(unused) {
        'middle' : "</b><br>",
                                                                                              return parseFloat(player.getAge()).toFixed(2);
        'liga'  : ". Liga",
                                                                                          });
        'land'  : ' ',
                              } else if (this.alter) {
        'end'    : "<a href="
                                  this.addAndFillCell(playerRow, player.getAge(), __COLOR, null, 2);
    };
                              }
 
                              if (__WARNDRAW.monthDraw()) {  // Abrechnungszeitraum vor dem letztmoeglichen Ziehen...
const __TEAMSEARCHTEAM = { // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>"
                                  formatCell(playerRow.cells[this.colIdx.Age], true, __WARNDRAW.colAlert, null, 1.0);
        'Zeile'  : 0,
                              }
        'Spalte' : 0,
                              if (this.fix) {
        'start'  : "<b>",
                                  this.addAndFillCell(playerRow, player.getFixSkills(), __COLOR, null, 0);
        'middle' : " - ",
                              }
        'liga'  : ". Liga",
                              if (this.tr) {
        'land'  : 'target="_blank">',
                                  this.addAndFillCell(playerRow, player.getTrainableSkills(), __COLOR, null, 0);
        'end'    : "</a></b>"
                              }
    };
                              if (this.zat) {
                                  this.addAndFillCell(playerRow, player.getZatDone(), __COLOR, null, 0);
                              }
                              if (this.antHpt) {
                                  this.addAndFillCell(playerRow, player.getPriPercent(player.getPos()), __COLOR, null, 0);
                              }
                              if (this.antNeb) {
                                  this.addAndFillCell(playerRow, player.getSecPercent(player.getPos()), __COLOR, null, 0);
                              }
                              if (this.pri) {
                                  this.addAndFillCell(playerRow, player.getPrios(player.getPos()), __COLOR, null, 1);
                              }
                              if (this.skill) {
                                  this.addAndFillCell(playerRow, player.getSkill(), __COLOR, null, 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) {
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam)
                                      if ((i === 1) || ! player.isGoalie) {
// cell: Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]"
                                          // Opti anzeigen
// teamSeach: Muster fuer die Suche, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt
                                          this.addAndFillCell(playerRow, player.getOpti(__POSI), __COLI, null, 2);
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER },
                                      } else {
//       z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 }
                                          // TOR, aber nicht bester Opti -> nur Zelle hinzufuegen
function getTeamParamsFromTable(table, teamSearch = undefined) {
                                          this.addCell(playerRow);
    const __TEAMSEARCH  = getValue(teamSearch, __TEAMSEARCHHAUPT);
                                      }
    const __TEAMCELLROW  = getValue(__TEAMSEARCH.Zeile, 0);
                                  }
    const __TEAMCELLCOL  = getValue(__TEAMSEARCH.Spalte, 0);
                                  if (i <= this.anzMw) {
    const __TEAMCELLSTR  = (table === undefined) ? "" : table.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML;
                                      if ((i === 1) || ! player.isGoalie) {
    const __SEARCHSTART  = __TEAMSEARCH.start;
                                          // MW anzeigen
    const __SEARCHMIDDLE = __TEAMSEARCH.middle;
                                          this.addAndFillCell(playerRow, player.getMarketValue(__POSI), __COLI, null, 0);
    const __SEARCHLIGA  = __TEAMSEARCH.liga;
                                      } else {
    const __SEARCHLAND  = __TEAMSEARCH.land;
                                          // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
    const __SEARCHEND    = __TEAMSEARCH.end;
                                          this.addCell(playerRow);
    const __INDEXSTART  = __TEAMCELLSTR.indexOf(__SEARCHSTART);
                                      }
    const __INDEXEND    = __TEAMCELLSTR.indexOf(__SEARCHEND);
                                  }
 
                              }
    let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND);
    const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA);
    const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE);
 
    let land = (~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined;
    const __TEAMNAME = (~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined;
    let liga = ((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined;


                              // Einzelwerte mit Ende 18
    if (land !== undefined) {
                              if (this.colIdx.Einz) {
        if (land.charAt(2) === ' ') {   // Land z.B. hinter "2. Liga A " statt "1. Liga "
                                  if (this.substSkills) {
            land = land.substr(2);
                                      convertArrayFromHTML(playerRow.cells, this.colIdx.Einz, player.skillsEnd, function(value, cell, unused, index) {
        }
                                                                                                                    if (~ __IDXPRI.indexOf(index)) {
        if (liga !== undefined) {
                                                                                                                        formatCell(cell, true, __OSBLAU, __POS1COLOR, 1.0);
            liga = liga.substring(0, liga.length - land.length);
                                                                                                                    }
        }
                                                                                                                    return value;
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
                                                                                                                });
        if (~ __INDEXLAND) {
                                  } else {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
                                      convertArrayFromHTML(playerRow.cells, this.colIdx.Einz, player.skills.length, function(value, cell, unused, index) {
        }
                                                                                                                        if (~ __IDXPRI.indexOf(index)) {
    }
                                                                                                                            formatCell(cell, true, __POS1COLOR, null, 1.0);
                                                                                                                        }
                                                                                                                        return value;
                                                                                                                    });
                                  }
                              }
                              if (this.trE) {
                                  this.addAndFillCell(playerRow, player.getTrainableSkills(player.__TIME.end), __COLOR, null, 1);
                              }
                              if (this.zatE) {
                                  this.addAndFillCell(playerRow, player.getZatLeft(), __COLOR, null, 0);
                              }
                              if (this.antHptE) {
                                  this.addAndFillCell(playerRow, player.getPriPercent(player.getPos(), player.__TIME.end), __COLOR, null, 0);
                              }
                              if (this.antNebE) {
                                  this.addAndFillCell(playerRow, player.getSecPercent(player.getPos(), player.__TIME.end), __COLOR, null, 0);
                              }
                              if (this.priE) {
                                  this.addAndFillCell(playerRow, player.getPrios(player.getPos(), player.__TIME.end), __COLOR, null, 1);
                              }
                              if (this.skillE) {
                                  this.addAndFillCell(playerRow, player.getSkill(player.__TIME.end), __COLOR, null, 2);
                              }
                              for (let i = 1; i <= 6; i++) {
                                  const __POSI = ((i === 1) ? player.getPos() : player.getPos(i));
                                  const __COLI = getColor(__POSI);


                                  if (i <= this.anzOptiE) {
    const __TEAM = new Team(__TEAMNAME, land, liga);
                                      if ((i === 1) || ! player.isGoalie) {
                                          // Opti anzeigen
                                          this.addAndFillCell(playerRow, player.getOpti(__POSI, player.__TIME.end), __COLI, null, 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, player.__TIME.end), __COLI, null, 0);
                                      } else {
                                          // TOR, aber nicht bester MW -> nur Zelle hinzufuegen
                                          this.addCell(playerRow);
                                      }
                                  }
                              }
                          },  // Ende addValues(player, playerRow)
        'setGroupTitle'  : function(tableRow) {
                              if (this.gtUxx) {
                                  const __CELL = tableRow.cells[0];
                                  const __SAI = __CELL.innerHTML.match(/Saison (\d+)/)[1];
                                  const __JG = 13 + this.saison - __SAI;


                                  __CELL.innerHTML = __CELL.innerHTML.replace('Jahrgang', 'U' + __JG + ' - $&');
    return __TEAM;
                              }
}


                              tableRow.style.display = (this.gt ? '' : 'none');
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
                          }  // Ende setGroupTitle(tableRow)
// url: Adresse der Seite
     });
// leafs: Liste von Filenamen mit der Default-Seitennummer (falls Query-Parameter nicht gefunden)
// item: Query-Parameter, der die Nummer der Unterseite angibt
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url, leafs, item = "page") {
     const __URI = new URI(url);
    const __LEAF = __URI.getLeaf();


// Klasse PlayerRecord ******************************************************************
    for (let leaf in leafs) {
        if (__LEAF === leaf) {
            const __DEFAULT = leafs[leaf];


function PlayerRecord(land, age, isGoalie, saison, currZAT, donation) {
            return getValue(__URI.getQueryPar(item), __DEFAULT);
     'use strict';
        }
     }


     this.land = land;
     return -1;
    this.age = age;
}
    this.isGoalie = isGoalie;


    this.saison = saison;
// Gibt die laufende Nummer des ZATs im Text einer Zelle zurueck
    this.currZAT = currZAT;
// cell: Tabellenzelle mit der ZAT-Nummer im Text
    this.donation = donation;
// return ZAT-Nummer im Text
     this.mwFormel = ((this.saison < 10) ? this.__MWFORMEL.alt : this.__MWFORMEL.S10);
function getZATNrFromCell(cell) {
     const __TEXT = ((cell === undefined) ? [] : cell.textContent.split(' '));
    let ZATNr = 0;


     // in new PlayerRecord() definiert:
     for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
    // this.land: TLA des Geburtslandes
        if (__TEXT[i - 1] === "ZAT") {
    // this.age: Ganzzahliges Alter des Spielers
            if (__TEXT[i] !== "ist") {
    // this.isGoalie: Angabe, ob es ein TOR ist
                ZATNr = parseInt(__TEXT[i], 10);
    // this.mwFormel: Benutzte MW-Formel, siehe __MWFORMEL
            }
     // this.donation: Jugendfoerderungsbetrag in Euro
        }
     }


     // in this.initPlayer() definiert:
     return ZATNr;
    // this.zatGeb: ZAT, an dem der Spieler Geburtstag hat, -1 fuer "noch nicht zugewiesen", also '?'
}
    // this.zatAge: Bisherige erfolgte Trainings-ZATs
    // this.birth: Universell eindeutige Nummer des Geburtstags-ZATs des Spielers
    // this.talent: Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
    // this.aufwert: Aufwertungsstring


    // in this.calcSkills() definiert:
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
    // this.positions[][]: Positionstexte und Optis; TOR-Index ist 5
    // this.skills[]: Einzelskills
    // this.skillsEnd[]: Berechnet aus this.skills, this.age und aktuellerZat
    // this.zatLeft: ZATs bis zum Ende 18 (letzte Ziehmoeglichkeit)
    // this.restEnd: Korrekturterm zum Ausgleich von Rundungsfehlern mit Ende 18
    //              (also Skills, die nicht explizit in this.skillsEnd stehen)


    // in this.calcZusatz()/setZusatz() definiert:
// ==================== Hauptprogramm ====================
    // this.trainiert: Anzahl der erfolgreichen Trainingspunkte
    // indirekt this.zatAge und this.bestPos


    // in this.createWarnDraw() definiert:
// Verarbeitet Ansicht "Haupt" (Managerbuero) zur Ermittlung des aktuellen ZATs
     // this.warnDraw: Behandlung von Warnungen Ende 18
function procHaupt() {
    // this.warnDrawAufstieg: Behandlung von Warnungen bei Aufstieg
     const __TEAMPARAMS = getTeamParamsFromTable(getTable(1), __TEAMSEARCHHAUPT);  // Link mit Team, Liga, Land...


     // in this.getPos() definiert:
     buildOptions(__OPTCONFIG, __OPTSET, {
    // this.bestPos: erster (bester) Positionstext
                    'teamParams' : __TEAMPARAMS,
}
                    'hideMenu'  : true
                });


Class.define(PlayerRecord, Object, {
    const __ZATCELL = getProp(getProp(getRows(0), 2), 'cells', { })[0];
        '__TIME'                : {  // Zeitpunktangaben
    const __NEXTZAT = getZATNrFromCell(__ZATCELL); // "Der naechste ZAT ist ZAT xx und ..."
                                      'cre' : 0,  // Jugendspieler angelegt (mit 12 Jahren)
    const __CURRZAT = __NEXTZAT - 1;
                                      'beg' : 1, // Jugendspieler darf trainieren (wird 13 Jahre alt)
    const __DATAZAT = getOptValue(__OPTSET.datenZat);
                                      'now' : 2, // Aktueller ZAT
                                      'end' : 3  // Jugendspieler wird Ende 18 gezogen (Geb. - 1 bzw. ZAT 71 fuer '?')
                                  },
        '__MWFORMEL'            : {   // Zu benutzende Marktwertformel
                                      'alt' : 0,  // Marktwertformel bis Saison 9 inklusive
                                      'S10' : 1  // Marktwertformel MW5 ab Saison 10
                                  },
        '__MAXPRISKILLS'        : 4 * 99,
        'toString'              : function() { // Bisher nur die noetigsten Werte ausgegeben...
                                      let result = "Alter\t\t" + this.age + "\n\n";
                                      result += "Aktuelle Werte\n";
                                      result += "Skillschnitt\t" + this.getSkill().toFixed(2) + '\n';
                                      result += "Optis und Marktwerte";


                                      for (let pos of this.positions) {
    if (__CURRZAT >= 0) {
                                          result += "\n\t" + pos + '\t';
        __LOG[2]("Aktueller ZAT: " + __CURRZAT);
                                          result += this.getOpti(pos).toFixed(2) + '\t';
                                          result += getNumberString(this.getMarketValue(pos).toString());
                                      }


                                      result += "\n\nWerte mit Ende 18\n";
        // Neuen aktuellen ZAT speichern...
                                      result += "Skillschnitt\t" + this.getSkill(this.__TIME.end).toFixed(2) + '\n';
        setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
                                      result += "Optis und Marktwerte";


                                      for (let pos of this.positions) {
        if (__CURRZAT !== __DATAZAT) {
                                          result += "\n\t" + this.getPos()[i] + '\t';
            __LOG[2](__LOG.changed(__DATAZAT, __CURRZAT));
                                          result += this.getOpti(pos, this.__TIME.end).toFixed(2) + '\t';
                                          result += getNumberString(this.getMarketValue(pos, this.__TIME.end).toString());
                                      }


                                      return result;
            // ... und ZAT-bezogene Daten als veraltet markieren
                                  },  // Ende this.toString()
            __TEAMCLASS.deleteOptions();
        'initPlayer'            : function(data, index, isSkillData = false) {  // isSkillData: true = Skilldaten, false = Basiswerte (Geb., Talent, Aufwertungen) oder keine
 
                                      if (data !== undefined) {
            // Neuen Daten-ZAT speichern...
                                          if (isSkillData) {
            setOpt(__OPTSET.datenZat, __CURRZAT, false);
                                              this.setSkills(data[index]);
        }
                                          } else if (data.length >= 2){
    }
                                              this.setGeb(data[0][index]);
}
                                              this.talent = data[1][index];
                                              this.aufwert = data[2][index];
                                          } else {
                                              // keine Daten
                                          }
                                      }
                                  },  // Ende this.initPlayer()
        'createWarnDraw'        : function(ziehmich = null, klasse = 1) {  // ziehmich: input Element zum Ziehen; klasse: Spielklasse 1, 2, 3
                                      // Objekte fuer die Verwaltung der Ziehwarnungen...
                                      this.warnDraw = undefined;
                                      this.warnDrawAufstieg = undefined;
                                      if (ziehmich) {
                                          const __LASTZAT = this.currZAT + this.getZatLeft();


                                          if (__LASTZAT < 72) {  // U19
// Verarbeitet Ansicht "Teamuebersicht"
                                              this.warnDraw = new WarnDrawPlayer(this, getColor('STU'));  // rot
function procTeamuebersicht() {
                                              __LOG[4](this.getAge().toFixed(2), "rot");
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
                                          } else if (__LASTZAT < Math.max(2, klasse) * 72) { // Rest bis inkl. U18 (Liga 1 und 2) bzw. U17 (Liga 3)
    const __ROWOFFSETLOWER = 1;     // Ziehen-Button
                                              // do nothing
                                          } else if (__LASTZAT < (klasse + 1) * 72) {  // U17/U16 je nach Liga 2/3
                                              this.warnDrawAufstieg = new WarnDrawPlayer(this, getColor('OMI'));  // magenta
                                              this.warnDrawAufstieg.setAufstieg();
                                              __LOG[4](this.getAge().toFixed(2), "magenta");
                                          }
                                      }
                                  },  // Ende this.createWarnDraw()
        'setSkills'            : function(skills) {
                                      // Berechnet die Opti-Werte, sortiert das Positionsfeld und berechnet die Einzelskills mit Ende 18
                                    this.skills = skills;


                                      const __POSREIHEN = [ 'ABW', 'DMI', 'MIT', 'OMI', 'STU', 'TOR' ];
    const __COLUMNINDEX = {
                                      this.positions = [];
            'Age'  : 0,
                                      for (let index = 0; index < __POSREIHEN.length; index++) {
            'Geb'   : 1,
                                          const __REIHE = __POSREIHEN[index];
            'Flg'   : 2,
            'Land' : 3,
            'U'    : 4,
            'Skill' : 5,
            'Tal'   : 6,
            'Akt'   : 7,
            'Auf'  : 8,
            'Zus'  : 9
        };


                                          this.positions[index] = [ __REIHE, this.getOpti(__REIHE) ];
    if (getElement('transfer') !== undefined) {
                                      }
        __LOG[2]("Ziehen-Seite");
    } else if (getRows(1) === undefined) {
        __LOG[2]("Diese Seite ist ohne Team nicht verf\xFCgbar!");
    } else {
        buildOptions(__OPTCONFIG, __OPTSET, {
                        'menuAnchor' : getTable(0, "div"),
                        'showForm'  : {
                                            'kennzeichenEnde'    : true,
                                            'shortAufw'          : true,
                                            'sepStyle'          : true,
                                            'sepColor'          : true,
                                            'sepWidth'          : true,
                                            'saison'            : true,
                                            'aktuellerZat'      : true,
                                            'foerderung'        : true,
                                            'team'              : true,
                                            'zeigeBalken'        : true,
                                            'ersetzeAlter'      : true,
                                            'zeigeAlter'        : true,
                                            'zeigeQuote'        : true,
                                            'zeigePosition'      : true,
                                            'zeigeFixSkills'    : true,
                                            'zeigeTrainiert'    : true,
                                            'zeigeAnteilPri'    : true,
                                            'zeigeAnteilSec'    : true,
                                            'zeigePrios'        : true,
                                            'zeigeSkill'        : true,
                                            'anzahlOpti'        : true,
                                            'anzahlMW'          : true,
                                            'zeigeTrainiertEnde' : true,
                                            'zeigeAnteilPriEnde' : true,
                                            'zeigeAnteilSecEnde' : true,
                                            'zeigePriosEnde'    : true,
                                            'zeigeSkillEnde'    : true,
                                            'anzahlOptiEnde'    : true,
                                            'anzahlMWEnde'      : true,
                                            'zatAges'            : true,
                                            'trainiert'          : true,
                                            'positions'          : true,
                                            'skills'            : true,
                                            'reset'              : true,
                                            'showForm'          : true
                                        },
                        'formWidth'  : 1
                    });


                                      // Sortieren
        const __ROWS = getRows(1);
                                      sortPositionArray(this.positions);
        const __HEADERS = __ROWS[0];
                                  },  // Ende this.setSkills()
         const __TITLECOLOR = getColor('LEI')// "#FFFFFF"
         'prognoseSkills'       : function() {
                                      // Einzelskills mit Ende 18 berechnen
                                      this.skillsEnd = [];


                                      const __ZATDONE = this.getZatDone();
        const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, true);
                                      const __ZATTOGO = this.getZatLeft();
        const __COLMAN = new ColumnManager(__OPTSET, __COLUMNINDEX, {
                                      const __ADDRATIO = (__ZATDONE ? __ZATTOGO / __ZATDONE : 0);
                                            'Default'            : true,
 
                                            'ersetzeSkills'     : false,
                                      let addSkill = __ZATTOGO * this.getAufwertungsSchnitt();
                                            'zeigeGeb'           : false,
 
                                            'zeigeSkill'         : false,
                                      for (let i in this.skills) {
                                            'zeigeTal'           : false,
                                          const __SKILL = this.skills[i];
                                            'zeigeAufw'         : false
                                          let progSkill = __SKILL;
                                        });
 
                                          if (isTrainableSkill(i)) {
                                              // Auf ganze Zahl runden und parseInt(), da das sonst irgendwie als String interpretiert wird
                                              const __ADDSKILL = Math.min(99 - progSkill, getMulValue(__ADDRATIO, __SKILL, 0, NaN));
 
                                              progSkill += __ADDSKILL;
                                              addSkill -= __ADDSKILL;
                                          }
 
                                          this.skillsEnd[i] = progSkill;
                                      }
                                      this.restEnd = addSkill;
                                  },  // Ende this.prognoseSkills()
        'setZusatz'            : function(zatAge, trainiert, bestPos) {
                                      // Setzt Nebenwerte fuer den Spieler (geht ohne initPlayer())
                                      if (zatAge !== undefined) {
                                          this.zatAge = zatAge;
                                      }
                                      this.trainiert = trainiert;
                                      this.bestPos = bestPos;
                                  },
        'calcZusatz'            : function() {
                                      // Ermittelt Nebenwerte fuer den Spieler und gibt sie alle zurueck (nach initPlayer())
                                      // this.zatAge und this.skills bereits in initPlayer() berechnet
                                      this.trainiert = this.getTrainiert(true);  // neu berechnet aus Skills
                                      let bestPos = this.getPos(-1);  // hier: -1 explizit angeben, damit neu ermittelt (falls this.bestPos noch nicht belegt)
 
                                      return {
                                                'zatAge'     : this.zatAge,
                                                'trainiert' : this.trainiert,
                                                'bestPos'   : bestPos
                                            };
                                  },
        'getGeb'                : function() {
                                      return (this.zatGeb < 0) ? '?' : this.zatGeb;
                                  },
        'setGeb'               : function(gebZAT) {
                                      this.zatGeb = gebZAT;
                                      this.zatAge = this.calcZatAge(this.currZAT);
                                      this.birth = (36 + this.saison) * 72 + this.currZAT - this.zatAge;
                                  },
        'calcZatAge'            : function(currZAT) {
                                      let zatAge;


                                      if (this.zatGeb !== undefined) {
         __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
                                          let ZATs = 72 * (this.age - ((currZAT < this.zatGeb) ? 12 : 13));  // Basiszeit fuer die Jahre seit Jahrgang 13
 
                                          if (this.zatGeb < 0) {
                                              zatAge = ZATs + currZAT;  // Zaehlung begann Anfang der Saison (und der Geburtstag wird erst nach dem Ziehen bestimmt)
                                          } else {
                                              zatAge = ZATs + currZAT - this.zatGeb;  // Verschiebung relativ zum Geburtstag (von -zatGeb, ..., 0, ..., 71 - zatGeb)
                                          }
                                      }
 
                                      return zatAge;
                                  },
        'getZatAge'            : function(when = this.__TIME.now) {
                                      if (when === this.__TIME.end) {
                                          return (18 - 12) * 72 - 1;  // (max.) Trainings-ZATs bis Ende 18
                                      } else if (this.zatAge !== undefined) {
                                          return this.zatAge;
                                      } else {
                                          __LOG[4]("Empty getZatAge()");
 
                                          return NaN;
                                      }
                                  },
        'getZatDone'            : function(when = this.__TIME.now) {
                                      return Math.max(0, this.getZatAge(when));
                                  },
        'getZatLeft'            : function(when = this.__TIME.now) {
                                      if (this.zatLeft === undefined) {
                                          this.zatLeft = this.getZatDone(this.__TIME.end) - this.getZatDone(when);
                                      }
 
                                      return this.zatLeft;
                                  },
        'calcZiehIndex'        : function() {
                                      //const __RESTZAT = this.getZatAge(this.__TIME.end) - this.getZatAge() + this.currZAT;
                                      //const __INDEX = parseInt(__RESTZAT / 6 + 1) - 1;  // Lfd. Nummer des Abrechnungsmonats (0-basiert)
 
                                      return (this.warnDraw && this.warnDraw.calcZiehIndex(this.currZAT));
                                  },
        'isZiehAufstieg'        : function() {
                                      return (this.warnDrawAufstieg && this.warnDrawAufstieg.isZiehAufstieg());
                                  },
        'getAge'                : function(when = this.__TIME.now) {
                                      if (this.mwFormel === this.__MWFORMEL.alt) {
                                          return (when === this.__TIME.end) ? 18 : this.age;
                                      } else {  // Geburtstage ab Saison 10...
                                          return (13.00 + this.getZatAge(when) / 72);
                                      }
                                  },
        'getTrainiert'          : function(recalc = false) {
                                      if (recalc || (this.trainiert === undefined)) {
                                          this.trainiert = this.getTrainableSkills();
                                      }
 
                                      return this.trainiert;
                                  },
        'getAufwertungsSchnitt' : function() {
                                      const __ZATDONE = this.getZatDone();
 
                                      if (__ZATDONE) {
                                          return parseFloat(this.getTrainiert() / __ZATDONE);
                                      } else {
                                          // Je nach Talentklasse mittlerer Aufwertungsschnitt aller Talente der Klasse
                                          // (gewichtet nach Verteilung der Talentstufen in dieser Talentklasse)
                                          return (1 + (this.talent / 3.6)) * (this.donation / 10000);
                                      }
                                  },
        'getPos'                : function(idx = 0) {
                                      const __IDXOFFSET = 1;
 
                                      switch (idx) {
                                      case -1 : return (this.bestPos = this.positions[this.isGoalie ? 5 : 0][0]);
                                      case  0 : return this.bestPos;
                                      default : return this.positions[idx - __IDXOFFSET][0];
                                      }
                                  },
        'getPosPercent'        : function(idx = 0) {
                                      const __IDXOFFSET = 1;
                                      const __OPTI = this.positions[this.isGoalie ? 5 : 0][1];
                                      let optiSec = __OPTI;
 
                                      switch (idx) {
                                      case -1 : break;  // __OPTI
                                      case  0 : optiSec = (this.isGoalie ? 0 : this.positions[1][1]);  // Backup-Wert (TOR: keiner)
                                                break;
                                      default : optiSec = this.positions[idx - __IDXOFFSET][1];
                                      }
 
                                      return parseFloat(100 * optiSec / __OPTI);
                                  },
        'getTalent'            : function() {
                                      return (this.talent < 0) ? 'wenig' : (this.talent > 0) ? 'hoch' : 'normal';
                                  },
        'getAufwert'            : function() {
                                      return this.aufwert;
                                  },
        'boldPriSkillNames'    : function(text) {
                                      const __PRISKILLNAMES = this.getPriSkillNames();
 
                                      return (! text) ? text : text.replace(/\w+/g, function(name) {
                                                                                        return ((~ __PRISKILLNAMES.indexOf(name)) ? '<b>' + name + '</b>' : name);
                                                                                    });
                                  },
        'getPriSkillNames'      : function(pos = undefined) {
                                      return getSkillNameArray(getIdxPriSkills(pos ? pos : this.getPos()), this.isGoalie);
                                  },
        'getSkillSum'          : function(when = this.__TIME.now, idxSkills = undefined, restRate = 15) {
                                      let cachedItem;
 
                                      if (idxSkills === undefined) {  // Gesamtsumme ueber alle Skills wird gecached...
                                          cachedItem = ((when === this.__TIME.end) ? 'skillSumEnd' : 'skillSum');
 
                                          const __CACHED = this[cachedItem];
 
                                          if (__CACHED !== undefined) {
                                              return __CACHED;
                                          }
 
                                          idxSkills = getIdxAllSkills();
                                      }
 
                                      const __SKILLS = ((when === this.__TIME.end) ? this.skillsEnd : this.skills);
                                      let sumSkills = ((when === this.__TIME.end) ? (restRate / 15) * this.restEnd : 0);
 
                                      if (__SKILLS) {
                                          for (let idx of idxSkills) {
                                              sumSkills += __SKILLS[idx];
                                          }
                                      }
 
                                      if (cachedItem !== undefined) {
                                          this[cachedItem] = sumSkills;
                                      }
 
                                      return sumSkills;
                                  },
        'getSkill'              : function(when = this.__TIME.now) {
                                      return this.getSkillSum(when) / 17;
                                  },
        'getOpti'              : function(pos, when = this.__TIME.now) {
                                      const __SUMALLSKILLS = this.getSkillSum(when);
                                      const __SUMPRISKILLS = this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4);
                                      const __OVERFLOW = Math.max(0, __SUMPRISKILLS - this.__MAXPRISKILLS);
/*if (this.zatGeb === 24) {
    console.error("__OVERFLOW = " + __OVERFLOW);
    console.error("__SUMALLSKILLS = " + __SUMALLSKILLS);
    console.error("__SUMPRISKILLS = " + __SUMPRISKILLS);
    console.error("getOpti(" + pos + ") = " + ((4 * (__SUMPRISKILLS - __OVERFLOW) + __SUMALLSKILLS) / 27));
}*/
                                      return (4 * (__SUMPRISKILLS - __OVERFLOW) + __SUMALLSKILLS) / 27;
                                  },
        'getPrios'              : function(pos, when = this.__TIME.now) {
                                      return Math.min(this.__MAXPRISKILLS, this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4)) / 4;
                                  },
        'getPriPercent'        : function(pos, when = this.__TIME.now) {
                                      const __SUMPRISKILLS = this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4);
                                      const __SUMSECSKILLS = this.getSkillSum(when, getIdxSecSkills(pos), 7);
                                      const __OVERFLOW = Math.max(0, __SUMPRISKILLS - this.__MAXPRISKILLS);
 
                                      return (100 * (__SUMPRISKILLS - __OVERFLOW)) / (__SUMPRISKILLS + __SUMSECSKILLS);
                                  },
        'getSecPercent'        : function(pos, when = this.__TIME.now) {
                                      const __SUMPRISKILLS = this.getSkillSum(when, getIdxPriSkills(pos), 2 * 4);
                                      const __SUMSECSKILLS = this.getSkillSum(when, getIdxSecSkills(pos), 7);
                                      const __OVERFLOW = Math.max(0, __SUMPRISKILLS - this.__MAXPRISKILLS);
 
                                      return (100 * (__SUMSECSKILLS + __OVERFLOW)) / (__SUMPRISKILLS + __SUMSECSKILLS);
                                  },
        'getTrainableSkills'    : function(when = this.__TIME.now) {
                                      return this.getSkillSum(when, getIdxTrainableSkills());
                                  },
        'getFixSkills'          : function() {
                                      return this.getSkillSum(this.__TIME.now, getIdxFixSkills());
                                  },
        'getMarketValue'        : function(pos, when = this.__TIME.now) {
                                      const __AGE = this.getAge(when);
 
                                      if (this.mwFormel === this.__MWFORMEL.alt) {
                                          return Math.round(Math.pow((1 + this.getSkill(when)/100) * (1 + this.getOpti(pos, when)/100) * (2 - __AGE/100), 10) * 2);    // Alte Formel bis Saison 9
                                      } else {  // MW-Formel ab Saison 10...
                                          const __MW5TF = 1.00;  // Zwischen 0.97 und 1.03
 
                                          return Math.round(Math.pow(1 + this.getSkill(when)/100, 5.65) * Math.pow(1 + this.getOpti(pos, when)/100, 8.1) * Math.pow(1 + (100 - __AGE)/49, 10) * __MW5TF);
                                      }
                                  },
        'getFingerPrint'        : function() {
                                      // Jeweils gleichbreite Werte: (Alter/Geb.=>Monat), Land, Talent ('-', '=', '+')...
                                      const __BASEPART = padNumber(this.birth / 6, 3) + padLeft(this.land, -3);
                                      const __TALENT = '-=+'[this.talent + 1];
 
                                      if (this.skills === undefined) {
                                          return __BASEPART + getValue(__TALENT, "");
                                      } else {
                                          const __SKILLS = this.skills;
                                          const __FIXSKILLS = getIdxFixSkills().slice(-4);  // ohne die Nullen aus FUQ und ERF
                                          const __FIXSKILLSTR = __FIXSKILLS.map(function(idx) {
                                                                                    return padNumber(__SKILLS[idx], -2);
                                                                                }).join("");
 
                                          // Jeweils gleichbreite Werte: Zusaetzlich vier der sechs Fixskills...
                                          return (__BASEPART + getValue(__TALENT, '?') + __FIXSKILLSTR);
                                      }
                                  },
        'isFingerPrint'        : function(fpA, fpB) {
                                      if (fpA && fpB) {
                                          if (fpA === fpB) {
                                              return true;  // voellig identisch
                                          } else if (this.isBaseFingerPrint(fpA, fpB)) {
                                              return 1;  // schwaches true
                                          }
                                      }
 
                                      return false;
                                  },
        'isBaseFingerPrint'    : function(fpA, fpB) {
                                      if (fpA && fpB) {
                                          if (this.getBaseFingerPrint(fpA) === this.getBaseFingerPrint(fpB)) {
                                              // Base ist identisch...
                                              if ((getValue(fpA[6], '?') === '?') || (getValue(fpB[6], '?') === '?') || (fpA[6] === fpB[6])) {
                                                  // ... und auch das Talent-Zeichen ist leer oder '?'...
                                                  return true;
                                              }
                                          }
                                      }
 
                                      return false;
                                  },
        'getBaseFingerPrint'    : function(fingerprint) {
                                      return (fingerprint ? fingerprint.slice(0, 6) : undefined);
                                  },
        'getCatFromFingerPrint' : function(fingerprint) {
                                      return (fingerprint ? floorValue((fingerprint.slice(0, 3) - 1) / 12) : undefined);
                                  },
        'getCat'                : function() {
                                      return (this.birth ? floorValue((this.birth - 1) / 72) : undefined);
                                  },
        'findInFingerPrints'    : function(fingerprints) {
                                      const __MYFINGERPRINT = this.getFingerPrint();  // ggfs. unvollstaendiger Fingerprint
                                      const __MYCAT = this.getCat();
                                      const __RET = [];
 
                                      if (__MYCAT !== undefined) {
                                          for (let id in fingerprints) {
                                              const __CAT = this.getCatFromFingerPrint(id);
 
                                              if (__CAT === __MYCAT) {
                                                  if (this.isFingerPrint(id, __MYFINGERPRINT)) {
                                                      __RET.push(id);
                                                      break;  // erster Treffer zaehlt
                                                  }
                                              }
                                          }
                                      }
 
                                      return ((__RET.length === 1) ? __RET[0] : undefined);
                                  }
    });
 
// Klasse WarnDrawPlayer *****************************************************************
 
function WarnDrawPlayer(player, alertColor) {
    'use strict';
 
    this.player = player;
 
    if (this.player !== undefined) {
        // Default Warnlevel...
        this.setZatLeft(player.getZatLeft());
        this.currZAT = player.currZAT;
        this.setWarn(true, true, true);
        this.colAlert = alertColor || this.alertColor();
    } else {
        // Kein Warnlevel...
        this.setZatLeft(undefined);
        this.currZAT = undefined;
        this.setWarn(false, false, false);
        this.colAlert = undefined;
    }
}
 
Class.define(WarnDrawPlayer, Object, {
        '__MONATEBISABR'    : 1,
        '__ZATWARNVORLAUF'  : 1,
        '__ZATMONATVORLAUF' : 6,
        'setZatLeft'        : function(zatLeft) {
                                  this.zatLeft = zatLeft;
                              },
        'setWarn'          : function(warn, warnMonth, warnAufstieg) {
                                  this.warn = (this.aufstieg ? warnAufstieg : warn);
                                  this.warnMonth = warnMonth;
                              },
        'alertColor'        : function() {
                                  return getColor('STU');  // rot
                              },
        'getColor'          : function(color) {
                                  return ((this.mustDraw() && this.colAlert) ? this.colAlert : color);
                              },
        'calcZiehIndex'    : function(currZAT) {
                                  const __RESTZAT = this.zatLeft + currZAT;
                                  const __INDEX = parseInt(__RESTZAT / 6 + 1) - this.__MONATEBISABR;  // Lfd. Nummer des Abrechnungsmonats (0-basiert)
 
                                  return __INDEX;
                              },
        'isZiehAufstieg'    : function() {
                                  return this.aufstieg;
                              },
        'setAufstieg'      : function() {
                                  this.aufstieg = true;
 
                                  if (this.isZiehAufstieg()) {
                                      this.setZatLeft(72 - this.currZAT - this.__ZATWARNVORLAUF);
                                  }
 
                                  return this.zatLeft;
                              },
        'mustDraw'          : function() {
                                  return ((this.warn || this.warnMonth) && (this.zatLeft < this.__ZATWARNVORLAUF));
                              },
        'monthDraw'        : function() {
                                  return (this.mustDraw() || (this.warn && (this.aufstieg || this.warnMonth) && (this.zatLeft < this.__ZATMONATVORLAUF)));  // Abrechnungszeitraum vor dem letztmoeglichen Ziehen...
                              }
    });
 
const __NOWARNDRAW = new WarnDrawPlayer(undefined, undefined);  // inaktives Objekt
 
// Klasse WarnDrawMessage *****************************************************************
 
function WarnDrawMessage(optSet, currZAT) {
    'use strict';
 
    this.optSet = optSet;
 
    this.warn = getOptValue(this.optSet.zeigeWarnung, true);
    this.warnMonth = getOptValue(this.optSet.zeigeWarnungMonat, true);
    this.warnHome = getOptValue(this.optSet.zeigeWarnungHome, true);
    this.warnDialog = getOptValue(this.optSet.zeigeWarnungDialog, false);
    this.warnAufstieg = getOptValue(this.optSet.zeigeWarnungAufstieg, true);
    this.warnLegende = getOptValue(this.optSet.zeigeWarnungLegende, true);
 
    this.out = {
                  'supertag' : true,
                  'top'      : true,
                  'link'    : true,
                  'label'    : true,
                  'bottom'  : true
              };
 
    this.setOptionHome();
 
    this.startMessage(currZAT);
}
 
Class.define(WarnDrawMessage, Object, {
        '__ZATWARNVORLAUF'  : 1,
        '__ZATMONATVORLAUF' : 6,
        'startMessage'      : function(currZAT) {
                                  this.setZat(currZAT);
                                  this.createMessage();
                              },
        'setZat'            : function(currZAT) {
                                  this.currZAT = currZAT;
 
                                  if (currZAT === undefined) {
                                      this.abrZAT = undefined;
                                      this.rest  = undefined;
                                      this.anzahl = undefined;
                                  } else {
                                      this.configureZat();
                                  }
                              },
        'setOptionHome'    : function() {
                                  this.warnOption = this.hasHome();
                              },
        'setOptionLegende'  : function() {
                                  this.warnOption = this.hasLegende();
                              },
        'configureZat'      : function() {
                                  const __ZIEHANZAHL = getOptValue(this.optSet.ziehAnz, []);
                                  const __INDEX = parseInt(this.currZAT / 6);
 
                                  this.abrZAT = (__INDEX + 1) * 6;
                                  this.rest  = 5 - (this.currZAT % 6);
                                  this.anzahl = __ZIEHANZAHL[__INDEX];
                              },
        'getTextMessage'    : function() {
                                  return "ZAT " + this.abrZAT + ' ' + ((this.anzahl > 1) ? "m\xFCssen " + this.anzahl : "muss einer") +
                                        " deiner Jugendspieler in das Profiteam \xFCbernommen werden, ansonsten verschwinde" + ((this.anzahl > 1) ? "n sie" : "t er") + '!';
                              },
        'createMessage'    : function() {
                                  this.label = undefined;
                                  this.when = undefined;
                                  this.text = undefined;
 
                                  if (this.hasHome() || this.hasLegende() || this.hasDialog()) {
                                      if (this.anzahl > 0) {
                                          this.text = this.getTextMessage();
 
                                          if (this.warnMonth && (this.rest > 0)) {
                                              this.label = "Warnung";
                                              this.when = "Bis zur n\xE4chsten Abrechnung am ";
                                          } else if ((this.warn || this.warnMonth) && (this.rest === 0)) {
                                              this.label = "LETZTE WARNUNG VOR DER ABRECHNUNG";
                                              this.when = "Bis zum n\xE4chsten ";
                                          }
                                      }
                                  }
                              },
        'hasMessage'        : function() {
                                  return !! this.when;
                              },
        'hasHome'          : function() {
                                  return this.warnHome;
                              },
        'hasLegende'        : function() {
                                  return this.warnLegende;
                              },
        'hasOption'        : function() {
                                  return this.warnOption;
                              },
        'hasDialog'        : function() {
                                  return this.warnDialog;
                              },
        'showMessage'      : function(anchor, tag, appendFind = true) {  // appendFind: true = append, false = insertBefore, "..." search string = insert at find position
                                  let ret = (anchor || { }).innerHTML;
 
                                  if (this.hasMessage()) {
                                      if (this.hasOption()) {
                                          const __OLDHTML = ret;
                                          const __HTML = this.getHTML(tag);
 
                                          if ((typeof appendFind) === 'string') {
                                              const __INDEX = __OLDHTML.indexOf(appendFind);
                                              const __POS = (~ __INDEX) ? __INDEX : __OLDHTML.length;
 
                                              ret = __OLDHTML.substring(0, __POS) + __HTML + __OLDHTML.substring(__POS);
                                          } else if (appendFind) {
                                              ret = __OLDHTML + __HTML;
                                          } else {
                                              ret = __HTML + __OLDHTML;
                                          }
 
                                          anchor.innerHTML = ret;
                                      }
                                  }
 
                                  return ret;
                              },
        'showDialog'        : function(dlgFun) {
                                  if (this.hasMessage()) {
                                      if (this.hasDialog() && (this.rest === 0)) {
                                          dlgFun(this.label, this.when + this.text);
                                      }
                                  }
                              },
        'tagText'          : function(tag, text) {
                                  return ((tag !== undefined) ? this.getOpeningTag(tag) + text + this.getClosingTag(tag) : text);
                              },
        'tagParagraph'      : function(tag, text) {
                                  return this.tagText(tag, this.tagText(this.getSubTag(tag), text));
                              },
        'getSubTag'        : function(tag) {
                                  return ((tag === 'tr') ? 'td' + this.getColorTD() : ((tag === 'p') ? this.getColorTag() : undefined));
                              },
        'getSuperTag'      : function(tag) {
                                  return ((tag === 'p') ? 'div' : undefined);
                              },
        'getOpeningTag'    : function(tag) {
                                  return '<' + tag + '>';
                              },
        'getClosingTag'    : function(tag) {
                                  const __INDEX1 = (tag ? tag.indexOf(' ') : -1);
                                  const __INDEX2 = (tag ? tag.indexOf('=') : -1);
                                  const __INDEX = ((~ __INDEX1) && (~ __INDEX2)) ? Math.min(__INDEX1, __INDEX2) : Math.max(__INDEX1, __INDEX2);
                                  const __TAGNAME = ((~ __INDEX) ? tag.substring(0, __INDEX) : tag);
 
                                  return "</" + __TAGNAME + '>';
                              },
        'getLink'          : function() {
                                  return './ju.php';
                              },
        'getTopHTML'        : function(tag) {
                                  return this.tagParagraph(tag, "&nbsp;");
                              },
        'getBottomHTML'    : function(tag) {
                                  return this.tagParagraph(tag, "&nbsp;");
                              },
        'getColorTag'      : function() {
                                  return "color='red'";  // rot
                              },
        'getColorTD'        : function() {
                                  return " class='STU'";  // rot
                              },
        'getHTML'          : function(tag = 'p') {
                                  return this.tagParagraph((this.out.supertag ? this.getSuperTag(tag) : undefined), (this.out.top ? this.getTopHTML(tag) : "") +
                                        this.tagParagraph(tag, this.tagText('b', this.tagText((this.out.link ? "a href='" + this.getLink() + "'" : undefined),
                                        (this.out.label ? this.label + ": " : "") + this.when + this.text))) + (this.out.bottom ? this.getBottomHTML(tag) : ""));
                              }
    });
 
Object.defineProperty(WarnDrawMessage.prototype, 'innerHTML', {
        get : function() {
                  return this.getHTML('p');
              }
    });
 
// Klasse WarnDrawMessageAufstieg *****************************************************************
 
function WarnDrawMessageAufstieg(optSet, currZAT) {
    'use strict';
 
    WarnDrawMessage.call(this, optSet, currZAT);
 
    this.out.top = false;  // kein Vorschub vor der Zeile
 
    this.warn = (this.warn && this.warnAufstieg);  // kann man ausschalten
    this.startMessage(currZAT);  // 2. Aufruf (zur Korrektur)
}
 
Class.define(WarnDrawMessageAufstieg, WarnDrawMessage, {
        'configureZat'      : function() {
                                  const __ZIEHANZAUFSTIEG = getOptValue(this.optSet.ziehAnzAufstieg, 0);
                                  const __INDEX = parseInt(this.currZAT / 6);
 
                                  this.abrZAT = (__INDEX + 1) * 6;
                                  this.rest  = 5 - (this.currZAT % 6);
                                  this.anzahl = ((this.currZAT + this.__ZATMONATVORLAUF > 72 - this.__ZATWARNVORLAUF) ? __ZIEHANZAUFSTIEG : 0);
 
                                  this.warnDialog = false;    // kein Dialog fuer Aufstiegswarnung
                                  this.warnMonth = this.warn;  // nur im letzten Monat der Saison!
                              },
        'getTextMessage'    : function() {
                                  return "ZAT " + this.abrZAT + " ist im Falle eines Aufstiegs f\xFCr " + ((this.anzahl > 1) ? "" + this.anzahl : "einen") +
                                        " deiner Jugendspieler m\xF6glicherweise die letzte Chance, " + ((this.anzahl > 1) ? " diese noch vor ihrem" : "ihn noch vor seinem") +
                                        " Geburtstag in der n\xE4chsten Saison in das Profiteam zu \xFCbernehmen!";
                              },
        'getColorTag'      : function() {
                                  return "color='magenta'";  // magenta
                              },
        'getColorTD'        : function() {
                                  return " class='OMI'";  // magenta
                              }
    });
 
// Ende Hilfs-Klassen *****************************************************************
 
// Funktionen fuer die HTML-Seite *******************************************************
 
// Liest eine Zahl aus der Spalte einer Zeile der Tabelle aus (z.B. Alter, Geburtsdatum)
// cells: Die Zellen einer Zeile
// colIdxInt: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Zahl (-1 fuer "keine Zahl", undefined fuer "nicht gefunden")
function getIntFromHTML(cells, colIdxInt) {
    const __CELL = getValue(cells[colIdxInt], { });
    const __TEXT = __CELL.textContent;
 
    if (__TEXT !== undefined) {
        try {
            const __VALUE = parseInt(__TEXT, 10);
 
            if (! isNaN(__VALUE)) {
                return __VALUE;
            }
        } catch (ex) { }
 
        return -1;
    }
 
    return undefined;
}
 
// Liest eine Dezimalzahl aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdxInt: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Dezimalzahl (undefined fuer "keine Zahl" oder "nicht gefunden")
function getFloatFromHTML(cells, colIdxFloat) {
    const __CELL = getValue(cells[colIdxFloat], { });
    const __TEXT = __CELL.textContent;
 
    if (__TEXT !== undefined) {
        try {
            return parseFloat(__TEXT);
        } catch (ex) { }
    }
 
    return undefined;
}
 
// Liest einen String aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// return Spalteneintrag als String ("" fuer "nicht gefunden")
function getStringFromHTML(cells, colIdxStr) {
    const __CELL = getValue(cells[colIdxStr], { });
    const __TEXT = __CELL.textContent;
 
    return getValue(__TEXT.toString(), "");
}
 
// Liest ein erstes Element aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// return Spalteneintrag als Element (null fuer "nicht gefunden")
function getElementFromHTML(cells, colIdxStr) {
    const __CELL = getValue(cells[colIdxStr], { });
 
    return __CELL.firstElementChild;
}
 
// Liest die Talentklasse ("wenig", "normal", "hoch") aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// return Talent als Zahl (-1=wenig, 0=normal, +1=hoch)
function getTalentFromHTML(cells, colIdxTal) {
    const __TEXT = getStringFromHTML(cells, colIdxTal);
 
    return parseInt((__TEXT === 'wenig') ? -1 : (__TEXT === 'hoch') ? +1 : 0, 10);
}
 
// Liest die Einzelskills aus der Spalte einer Zeile der Tabelle aus
// cells: Die Zellen einer Zeile
// colIdx: Liste von Spaltenindices der gesuchten Werte mit den Eintraegen
// 'Einz' (erste Spalte) und 'Zus' (Spalte hinter dem letzten Eintrag)
// return Skills als Array von Zahlen
function getSkillsFromHTML(cells, colIdx) {
    const __RESULT = [];
 
    for (let i = colIdx.Einz; i < colIdx.Zus; i++) {
        __RESULT[i - colIdx.Einz] = getIntFromHTML(cells, i);
    }
 
    return __RESULT;
}
 
// Liest aus, ob der Spieler Torwart oder Feldspieler ist
// cells: Die Zellen einer Zeile
// colIdxClass: Spaltenindex einer fuer TOR eingefaerbten Zelle
// return Angabe, der Spieler Torwart oder Feldspieler ist
function isGoalieFromHTML(cells, colIdxClass) {
    return (cells[colIdxClass].className === 'TOR');
}
 
// Liest einen String aus der Spalte einer Zeile der Tabelle aus, nachdem dieser konvertiert wurde
// cells: Die Zellen einer Zeile
// colIdxStr: Spaltenindex der gesuchten Werte
// convertFun: Funktion, die den Wert konvertiert
// return Spalteneintrag als String ("" fuer "nicht gefunden")
function convertStringFromHTML(cells, colIdxStr, convertFun = sameValue) {
    const __CELL = getValue(cells[colIdxStr], { });
    const __TEXT = convertFun(__CELL.textContent, __CELL, colIdxStr, 0);
 
    if (__TEXT !== undefined) {
        __CELL.innerHTML = __TEXT;
    }
 
    return getValue(__TEXT.toString(), "");
}
 
// Liest ein Array von String-Werten aus den Spalten ab einer Zeile der Tabelle aus, nachdem diese konvertiert wurden
// cells: Die Zellen einer Zeile
// colIdxArr: Erster Spaltenindex der gesuchten Werte
// arrOrLength: Entweder ein Datenarray zum Fuellen oder die Anzahl der zu lesenden Werte
// convertFun: Funktion, die die Werte konvertiert
// return Array mit Spalteneintraegen als String ("" fuer "nicht gefunden")
function convertArrayFromHTML(cells, colIdxArr, arrOrLength = 1, convertFun = sameValue) {
    const __ARR = ((typeof arrOrLength === 'number') ? { } : arrOrLength);
    const __LENGTH = getValue(__ARR.length, arrOrLength);
    const __RET = [];
 
    for (let index = 0, colIdx = colIdxArr; index < __LENGTH; index++, colIdx++) {
        const __CELL = getValue(cells[colIdx], { });
        const __TEXT = convertFun(getValue(__ARR[index], __CELL.textContent), __CELL, colIdx, index);
 
        if (__TEXT !== undefined) {
            __CELL.innerHTML = __TEXT;
        }
 
        __RET.push(getValue(__TEXT, "").toString());
    }
 
    return __RET;
}
 
// Konvertiert den Aufwertungstext einer Zelle auf der Jugend-Teamuebersicht
// value: Der Inhalt dieser Zeile ("+1 SKI +1 OPT" bzw. "+2 SKI)
// cell: Zelle, in der der Text stand (optional)
// return Der konvertierte String ("SKI OPT" bzw. "SKI SKI")
function convertAufwertung(value, cell = undefined) {
    if (value !== undefined) {
        value = value.replace(/\+2 (\w+)/, "$1 $1").replace(/\+1 /g, "");
 
        if (cell) {
            if (cell.className === 'TOR') {
                value = convertGoalieSkill(value);
            }
 
            cell.align = 'left';
        }
    }
 
    return value;
}
 
// Konvertiert die allgemeinen Skills in die eines Torwarts
// value: Ein Text, der die Skillnamen enthaelt
// return Der konvertierte String mit Aenderungen (z.B. "FAN" statt "KOB") oder unveraendert
function convertGoalieSkill(value) {
    if (value !== undefined) {
        value = value.replace(/\w+/g, getGoalieSkill);
    }
 
    return value;
}
 
// Konvertiert einen Aufwertungstext fuer einen Skillnamen in den fuer einen Torwart
// name: Allgemeiner Skillname (abgeleitet von den Feldspielern)
// return Der konvertierte String (z.B. "FAN" statt "KOB") oder unveraendert
function getGoalieSkill(name) {
    const __GOALIESKILLS = {
                              'SCH' : 'ABS',
                              'BAK' : 'STS',
                              'KOB' : 'FAN',
                              'ZWK' : 'STB',
                              'DEC' : 'SPL',
                              'GES' : 'REF'
                          };
 
    return getValue(__GOALIESKILLS[name], name);
}
 
// Liest die Aufwertungen eines Spielers aus und konvertiert je nachdem, ob der Spieler Torwart oder Feldspieler ist
// cells: Die Zellen einer Zeile
// colIdxAuf: Spaltenindex der gesuchten Aufwertungen
// shortForm: true = abgekuerzt, false = Originalform
// return Konvertierte Aufwertungen (kurze oder lange Form, aber in jedem Fall fuer Torwart konvertiert)
function getAufwertFromHTML(cells, colIdxAuf, shortForm = true) {
    const __ISGOALIE = isGoalieFromHTML(cells, colIdxAuf);
 
    return convertStringFromHTML(cells, colIdxAuf, (shortForm ? convertAufwertung : __ISGOALIE ? convertGoalieSkill : undefined));
}
 
// Identitaetsfunktion. Konvertiert nichts, sondern liefert einfach den Wert zurueck
// value: Der uebergebene Wert
// return Derselbe Wert
function sameValue(value) {
    return value;
}
 
// Existenzfunktion. Liefert zurueck, ob ein Wert belegt ist
// value: Der uebergebene Wert
// return Angabe ob Wert belegt ist
function existValue(value) {
    return !! value;
}
 
// Liefert den ganzzeiligen Anteil einer Zahl zurueck, indem alles hinter einem Punkt abgeschnitten wird
// value: Eine uebergebene Dezimalzahl
// return Der ganzzeilige Anteil dieser Zahl
function floorValue(value, dot = '.') {
    if ((value === 0) || (value && isFinite(value))) {
        const __VALUE = value.toString();
        const __INDEXDOT = (__VALUE ? __VALUE.indexOf(dot) : -1);
 
        return Number((~ __INDEXDOT) ? __VALUE.substring(0, __INDEXDOT) : __VALUE);
    } else {
        return value;
    }
}
 
// Liefert einen rechtsbuendigen Text zurueck, der links aufgefuellt wird
// value: Ein uebergebener Wert
// size: Zielbreite (clipping fuer < 0: Abschneiden, falls zu lang)
// char: Zeichen zum Auffuellen
// return Ein String, der mindestens |size| lang ist (oder genau, falls size < 0, also clipping)
function padLeft(value, size = 4, char = ' ') {
    const __SIZE = Math.abs(size);
    const __CLIP = (size < 0);
    const __VALUE = (value ? value.toString() : "");
    let i = __VALUE.length;
    let str = "";
 
    while (i < __SIZE) {
        str += char;
        i += char.length;
    }
    str = ((i > __SIZE) ? str.slice(0, __SIZE - __VALUE.length - 1) : str) + __VALUE;
 
    return (__CLIP ? str.slice(size) : str);
}
 
// Liefert eine rechtsbuendigen Zahl zurueck, der links (mit Nullen) aufgefuellt wird
// value: Eine uebergebene Zahl
// size: Zielbreite (Default: 2)
// char: Zeichen zum Auffuellen (Default: '0')
// forceClip: Abschneiden erzwingen, falls zu lang?
// return Eine Zahl als String, der mindestens 'size' lang ist (oder genau, falls size < 0, also clipping)
function padNumber(value, size = 2, char = '0') {
    if ((value === 0) || (value && isFinite(value))) {
        return padLeft(value, size, char);
    } else {
        return value;
    }
}
 
// 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--;
    }
}
 
// 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 = getIdxTrainableSkills();
    const __IDX = parseInt(idx, 10);
    let result = false;
 
    for (let idxTrainable of __TRAINABLESKILLS) {
        if (__IDX === idxTrainable) {
            result = true;
            break;
        }
    }
 
    return result;
}
 
// Gibt alle Skill-Namen zurueck
function getAllSkillNames(isGoalie = false) {
    if (isGoalie) {
        return [ 'ABS', 'STS', 'FAN', 'STB', 'SPL', 'REF', 'FUQ', 'ERF', 'AGG', 'PAS', 'AUS', 'UEB', 'WID', 'SEL', 'DIS', 'ZUV', 'EIN' ];
    } else {
        return [ 'SCH', 'BAK', 'KOB', 'ZWK', 'DEC', 'GES', 'FUQ', 'ERF', 'AGG', 'PAS', 'AUS', 'UEB', 'WID', 'SEL', 'DIS', 'ZUV', 'EIN' ];
    }
}
 
// Gibt den Skill-Namen zu einem Index zurueck
function getSkillName(idx, isGoalie = false) {
    const __ALLNAMES = getAllSkillNames(isGoalie);
 
    return __ALLNAMES[idx];
}
 
// Gibt den Skill-Namen zu einem Index-Array zurueck
function getSkillNameArray(idxArr, isGoalie = false) {
    return (idxArr ? idxArr.map(function(item) {
                                    return getSkillName(item, isGoalie);
                                }) : idxArr);
}
 
// Gibt die Indizes aller Skills zurueck
function getIdxAllSkills() {
    return [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ];
}
 
// Gibt die Indizes der trainierbaren Skills zurueck
function getIdxTrainableSkills() {
    return [ 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 15 ];
}
 
// Gibt die Indizes der Fixskills zurueck
function getIdxFixSkills() {
    return [ 6, 7, 12, 13, 14, 16 ];
}
 
// Gibt die Indizes der Primaerskills zurueck
function getIdxPriSkills(pos) {
    switch (pos) {
        case 'TOR' : return [ 2, 3, 4, 5 ];
        case 'ABW' : return [ 2, 3, 4, 15 ];
        case 'DMI' : return [ 1, 4, 9, 11 ];
        case 'MIT' : return [ 1, 3, 9, 11 ];
        case 'OMI' : return [ 1, 5, 9, 11 ];
        case 'STU' : return [ 0, 2, 3, 5 ];
        default :    return [];
    }
}
 
// Gibt die Indizes der (trainierbaren) Sekundaerskills zurueck
function getIdxSecSkills(pos) {
    switch (pos) {
        case 'TOR' : return [ 0, 1, 8, 9, 10, 11, 15 ];
        case 'ABW' : return [ 0, 1, 5, 8, 9, 10, 11 ];
        case 'DMI' : return [ 0, 2, 3, 5, 8, 10, 15 ];
        case 'MIT' : return [ 0, 2, 4, 5, 8, 10, 15 ];
        case 'OMI' : return [ 0, 2, 3, 4, 8, 10, 15 ];
        case 'STU' : return [ 1, 4, 8, 9, 10, 11, 15 ];
        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";
        case "" :    return "#111166";  // osBlau
        default :    return "";
    }
}
 
// ==================== Ende Abschnitt genereller Code zur Anzeige der Jugend ====================
 
// ==================== Abschnitt fuer interne IDs auf den Seiten ====================
 
const __GAMETYPENRN = {    // "Blind FSS gesucht!"
        'unbekannt'  : -1,
        'reserviert' :  0,
        'Frei'      :  0,
        'spielfrei'  :  0,
        'Friendly'  :  1,
        'Liga'      :  2,
        'LP'        :  3,
        'OSEQ'      :  4,
        'OSE'        :  5,
        'OSCQ'      :  6,
        'OSC'        :  7,
        'Supercup'  : 10
    };
 
const __GAMETYPEALIASES = {
        'unbekannt'  :  "unbekannt",
        'reserviert' :  undefined,
        'Frei'      :  undefined,
        'spielfrei'  :  "",
        'Friendly'  :  "FSS",
        'Liga'      :  undefined,
        'LP'        :  "Pokal",
        'OSEQ'      :  undefined,
        'OSE'        :  undefined,
        'OSCQ'      :  undefined,
        'OSC'        :  undefined,
        'Supercup'  : "Super"
    };
const __GAMETYPES = reverseMapping(__GAMETYPENRN);
 
const __LIGANRN = {
        'unbekannt'  :  0,
        '1. Liga'    :  1,
        '2. Liga A'  :  2,
        '2. Liga B'  :  3,
        '3. Liga A'  :  4,
        '3. Liga B'  :  5,
        '3. Liga C'  :  6,
         '3. Liga D'  :  7
    };
const __LIGATYPES = reverseMapping(__LIGANRN);
 
const __LANDNRN = {
        'unbekannt'              :  0,
        'Albanien'              :  45,
        'Andorra'                :  95,
        'Armenien'              :  83,
        'Aserbaidschan'          : 104,
        'Belgien'                :  12,
        'Bosnien-Herzegowina'    :  66,
        'Bulgarien'              :  42,
        'D\xE4nemark'            :  8,
        'Deutschland'            :  6,
        'England'                :  1,
        'Estland'                :  57,
        'Far\xF6er'              :  68,
        'Finnland'              :  40,
        'Frankreich'            :  32,
        'Georgien'              :  49,
        'Griechenland'          :  30,
        'Irland'                :  5,
        'Island'                :  29,
        'Israel'                :  23,
        'Italien'                :  10,
        'Kasachstan'            : 105,
        'Kroatien'              :  24,
        'Lettland'              :  97,
        'Liechtenstein'          :  92,
        'Litauen'                :  72,
        'Luxemburg'              :  93,
        'Malta'                  :  69,
        'Mazedonien'            :  86,
        'Moldawien'              :  87,
        'Niederlande'            :  11,
        'Nordirland'            :  4,
        'Norwegen'              :  9,
        '\xD6sterreich'          :  14,
        'Polen'                  :  25,
        'Portugal'              :  17,
        'Rum\xE4nien'            :  28,
        'Russland'              :  19,
        'San Marino'            :  98,
        'Schottland'            :  2,
        'Schweden'              :  27,
        'Schweiz'                :  37,
        'Serbien und Montenegro' :  41,
        'Slowakei'              :  70,
        'Slowenien'              :  21,
        'Spanien'                :  13,
        'Tschechien'            :  18,
        'T\xFCrkei'              :  39,
        'Ukraine'                :  20,
        'Ungarn'                :  26,
        'Wales'                  :  3,
        'Weissrussland'          :  71,
        'Zypern'                :  38
    };
const __LAENDER = reverseMapping(__LANDNRN);
 
const __TLALAND = {
        undefined : 'unbekannt',
        'ALB'    : 'Albanien',
        'AND'    : 'Andorra',
        'ARM'    : 'Armenien',
        'AZE'    : 'Aserbaidschan',
        'BEL'    : 'Belgien',
        'BIH'    : 'Bosnien-Herzegowina',
        'BUL'    : 'Bulgarien',
        'DEN'    : 'D\xE4nemark',
        'GER'    : 'Deutschland',
        'ENG'    : 'England',
        'EST'    : 'Estland',
        'FRO'    : 'Far\xF6er',
        'FIN'    : 'Finnland',
        'FRA'    : 'Frankreich',
        'GEO'    : 'Georgien',
        'GRE'    : 'Griechenland',
        'IRL'    : 'Irland',
        'ISL'    : 'Island',
        'ISR'    : 'Israel',
        'ITA'    : 'Italien',
        'KAZ'    : 'Kasachstan',
        'CRO'    : 'Kroatien',
        'LVA'    : 'Lettland',
        'LIE'    : 'Liechtenstein',
        'LTU'    : 'Litauen',
        'LUX'    : 'Luxemburg',
        'MLT'    : 'Malta',
        'MKD'    : 'Mazedonien',
        'MDA'    : 'Moldawien',
        'NED'    : 'Niederlande',
        'NIR'    : 'Nordirland',
        'NOR'    : 'Norwegen',
        'AUT'    : '\xD6sterreich',
        'POL'    : 'Polen',
        'POR'    : 'Portugal',
        'ROM'    : 'Rum\xE4nien',
        'RUS'    : 'Russland',
        'SMR'    : 'San Marino',
        'SCO'    : 'Schottland',
        'SWE'    : 'Schweden',
        'SUI'    : 'Schweiz',
        'SCG'    : 'Serbien und Montenegro',
        'SVK'    : 'Slowakei',
        'SVN'    : 'Slowenien',
        'ESP'    : 'Spanien',
        'CZE'    : 'Tschechien',
        'TUR'    : 'T\xFCrkei',
        'UKR'    : 'Ukraine',
        'HUN'    : 'Ungarn',
        'WAL'    : 'Wales',
        'BLR'    : 'Weissrussland',
        'CYP'    : 'Zypern'
    };
const __LANDTLAS = reverseMapping(__TLALAND);
 
// ==================== Abschnitt fuer Daten des Spielplans ====================
 
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck
// gameType: Name des Wettbewerbs eines Spiels
// defValue: Default-Wert
// return OS2-ID fuer den Spieltyp (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig
function getGameTypeID(gameType, defValue = __GAMETYPENRN.unbekannt) {
    return getValue(__GAMETYPENRN[gameType], defValue);
}
 
// Gibt den Namen eines Wettbewerbs zurueck
// id: OS2-ID des Wettbewerbs eines Spiels (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig
// defValue: Default-Wert
// return Spieltyp fuer die uebergebene OS2-ID
function getGameType(id, defValue) {
    return getValue(__GAMETYPES[id], defValue);
}
 
// Gibt den alternativen (Kurznamen) fuer den Namen eines Wettbewerbs zurueck
// gameType: Name des Wettbewerbs eines Spiels
// return Normalerweise den uebergebenen Parameter, in Einzelfaellen eine Kurzversion
function getGameTypeAlias(gameType) {
    return getValue(__GAMETYPEALIASES[gameType], getValue(gameType, __GAMETYPEALIASES.unbekannt));
}
 
// Gibt den Namen des Landes mit dem uebergebenen Kuerzel (TLA) zurueck.
// tla: Kuerzel (TLA) des Landes
// defValue: Default-Wert
// return Name des Landes, 'unbekannt' fuer undefined
function getLandName(tla, defValue = __TLALAND[undefined]) {
    return getValue(__TLALAND[tla], defValue);
}
 
// Gibt die ID des Landes mit dem uebergebenen Namen zurueck.
// land: Name des Landes
// defValue: Default-Wert
// return OS2-ID des Landes, 0 fuer ungueltig
function getLandNr(land, defValue = __LANDNRN.unbekannt) {
    return getValue(__LANDNRN[land], defValue);
}
 
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck.
// land: Name der Liga
// defValue: Default-Wert
// return OS2-ID der Liga, 0 fuer ungueltig
function getLigaNr(liga, defValue = __LIGANRN.unbekannt) {
    return getValue(__LIGANRN[liga], defValue);
}
 
// Kehrt das Mapping eines Objekts um und liefert ein neues Objekt zurueck.
// obj: Objekt mit key => value
// convFun: Konvertierfunktion fuer die Werte
// return Neues Objekt mit value => key (doppelte value-Werte fallen heraus!)
function reverseMapping(obj, convFun) {
    if (! obj) {
        return obj;
    }
 
    const __RET = { };
 
    for (let key in obj) {
        const __VALUE = obj[key];
 
        __RET[__VALUE] = (convFun ? convFun(key) : key);
    }
 
    return __RET;
}
 
// ==================== Abschnitt fuer sonstige Parameter ====================
 
// Formatiert eine Zelle um (mit einfachen Parametern)
// cell: Zu formatierende Zelle
// bold: Inhalt fett darstellen (true = ja, false = nein)
// color: Falls angegeben, die Schriftfarbe
// bgColor: Falls angegeben, die Hintergrundfarbe
// opacity: Falls angegeben, die Opazitaet
// return Die formatierte Zelle
function formatCell(cell, bold = true, color = undefined, bgColor = undefined, opacity = undefined) {
    if (cell) {
        if (bold) {
            cell.style.fontWeight = 'bold';
        }
        if (color) {
            cell.style.color = color;
        }
        if (bgColor) {
            cell.style.backgroundColor = bgColor;
        }
        if (opacity) {
        cell.style.opacity = opacity;
        }
    }
 
    return cell;
}
 
// Ermittelt die auszugewaehlenden Werte eines Selects (Combo-Box) als Array zurueck
// element: 'select'-Element oder dessen Name auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
// valFun: Funktion zur Ermittlung des Wertes eines 'option'-Eintrags (getSelectedOptionText, getSelectedValue, ...)
// defValue: Default-Wert, falls nichts selektiert ist
// return Array mit den Options-Werten
function getSelectionArray(element, valType = 'String', valFun = getSelectedValue, defValue = undefined) {
    const __SELECT = ((typeof element) === 'string' ? getValue(document.getElementsByName(element), [])[0] : element);
 
    return (__SELECT ? [].map.call(__SELECT.options, function(option) {
                                                        return this[valType](getValue(valFun(option), defValue));
                                                    }) : undefined);
}
 
// Ermittelt den ausgewaehlten Wert eines Selects (Combo-Box) und gibt diesen zurueck
// element: 'select'-Element oder dessen Name auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
// valFun: Funktion zur Ermittlung des Wertes eines 'option'-Eintrags (getSelectedOptionText, getSelectedValue, ...)
// defValue: Default-Wert, falls nichts selektiert ist
// return Ausgewaehlter Wert
function getSelection(element, valType = 'String', valFun = getSelectedOptionText, defValue = undefined) {
    const __SELECT = ((typeof element) === 'string' ? getValue(document.getElementsByName(element), [])[0] : element);
 
    return this[valType](getValue(valFun(__SELECT), defValue));
}
 
// Ermittelt den ausgewaehlten Wert einer Combo-Box und gibt diesen zurueck
// comboBox: Alle 'option'-Eintraege der Combo-Box
// defValue: Default-Wert, falls nichts selektiert ist
// valType: Typ-Klasse der Optionswerte ('String', 'Number', ...)
// return Ausgewaehlter Wert
function getSelectionFromComboBox(comboBox, defValue = undefined, valType = 'String') {
    let selection;
 
    for (let i = 0; i < comboBox.length; i++) {
        const __ENTRY = comboBox[i];
 
        if (__ENTRY.outerHTML.match(/selected/)) {
            selection = __ENTRY.textContent;
        }
    }
 
    return this[valType](getValue(selection, defValue));
}
 
// Liefert den Text (textContent) einer selektierten Option
// element: 'select'-Element auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// return Wert der Selektion (textContent)
function getSelectedOptionText(element) {
    const __SELECTEDOPTIONS = getValue(element, { }).selectedOptions;
    const __OPTION = getValue(__SELECTEDOPTIONS, { })[0];
 
    return (__OPTION ? __OPTION.textContent : undefined);
}
 
// Liefert den 'value' einer selektierten Option
// element: 'select'-Element auf der HTML-Seite mit 'option'-Eintraegen der Combo-Box
// return Wert der Selektion ('value')
function getSelectedValue(element) {
    return getValue(element, { }).value;
}
 
// ==================== Ende Abschnitt fuer sonstige Parameter ====================
 
// ==================== Abschnitt fuer sonstige Parameter des Spielplans ====================
 
const __TEAMSEARCHHAUPT = {  // Parameter zum Team "<b>Willkommen im Managerb&uuml;ro von TEAM</b><br>LIGA LAND<a href=..."
        '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 === undefined) ? "" : 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) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined);
    const __TEAMNAME = ((~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined);
    let liga = (((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined);
 
    if (land !== undefined) {
        if (land.charAt(2) === ' ') {    // Land z.B. hinter "2. Liga A " statt "1. Liga "
            land = land.substr(2);
        }
        if (liga !== undefined) {
            liga = liga.substring(0, liga.length - land.length);
        }
        const __INDEXLAND = land.indexOf(__SEARCHLAND);
        if (~ __INDEXLAND) {
            land = land.substr(__INDEXLAND + __SEARCHLAND.length);
        }
    }
 
    const __TEAM = new Team(__TEAMNAME, land, liga);
 
    return __TEAM;
}
 
// Verarbeitet die URL der Seite und ermittelt die Nummer der gewuenschten Unterseite
// url: Adresse der Seite
// leafs: Liste von Filenamen mit der Default-Seitennummer (falls Query-Parameter nicht gefunden)
// item: Query-Parameter, der die Nummer der Unterseite angibt
// return Parameter aus der URL der Seite als Nummer
function getPageIdFromURL(url, leafs, item = 'page') {
    const __URI = new URI(url);
    const __LEAF = __URI.getLeaf();
 
    for (let leaf in leafs) {
        if (__LEAF === leaf) {
            const __DEFAULT = leafs[leaf];


             return getValue(__URI.getQueryPar(item), __DEFAULT);
        for (let i = 0; i < __PLAYERS.length; i++) {
             __COLMAN.addValues(__PLAYERS[i], __ROWS[i + __ROWOFFSETUPPER], __TITLECOLOR);
         }
         }
    }


    return -1;
        // Format der Trennlinie zwischen den Monaten...
}
        const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
 
// 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 === undefined) ? [] : cell.textContent.split(' '));
    let ZATNr = 0;


    for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) {
        separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Age, __ROWOFFSETUPPER, __ROWOFFSETLOWER, -1, 0, floorValue);
        if (__TEXT[i - 1] === "ZAT") {
            if (__TEXT[i] !== "ist") {
                ZATNr = parseInt(__TEXT[i], 10);
            }
        }
     }
     }
    return ZATNr;
}
}


// ==================== Ende Abschnitt fuer sonstige Parameter des Spielplans ====================
// Verarbeitet Ansicht "Spielereinzelwerte"
 
function procSpielereinzelwerte() {
// ==================== Ende Abschnitt fuer Spielplan und ZATs ====================
 
// ==================== Hauptprogramm ====================
 
// Verarbeitet Ansicht "Haupt" (Managerbuero) zur Ermittlung des aktuellen ZATs
function procHaupt() {
    const __TEAMPARAMS = getTeamParamsFromTable(getTable(1), __TEAMSEARCHHAUPT);  // Link mit Team, Liga, Land...
 
    return buildOptions(__OPTCONFIG, __OPTSET, {
                            'teamParams' : __TEAMPARAMS,
//                            'menuAnchor' : getTable(0, 'div'),
                            'hideMenu'  : true,
                            'showForm'  : {
                                              'zeigeWarnung'        : true,
                                              'zeigeWarnungMonat'    : true,
                                              'zeigeWarnungHome'    : true,
                                              'zeigeWarnungDialog'  : true,
                                              'zeigeWarnungAufstieg' : true,
                                              'zeigeWarnungLegende'  : true,
                                              'ziehAnz'              : true,
                                              'showForm'            : true
                                          }
                        }).then(async optSet => {
            const __ZATCELL = getProp(getProp(getRows(0), 2), 'cells', { })[0];
            const __NEXTZAT = getZATNrFromCell(__ZATCELL);  // "Der naechste ZAT ist ZAT xx und ..."
            const __CURRZAT = __NEXTZAT - 1;
            const __DATAZAT = getOptValue(__OPTSET.datenZat);
 
            // Stand der alten Daten merken...
            setOpt(__OPTSET.oldDatenZat, __DATAZAT, false);
 
            if (__CURRZAT >= 0) {
                __LOG[2]("Aktueller ZAT: " + __CURRZAT);
 
                // Neuen aktuellen ZAT speichern...
                setOpt(__OPTSET.aktuellerZat, __CURRZAT, false);
 
                if (__CURRZAT !== __DATAZAT) {
                    __LOG[2](__LOG.changed(__DATAZAT, __CURRZAT));
 
                    // ... und ZAT-bezogene Daten als veraltet markieren (ausser 'skills', 'positions' und 'ziehAnz')
                    await __TEAMCLASS.deleteOptions({
                                                    'skills'      : true,
                                                    'positions'  : true,
                                                    'datenZat'    : true,
                                                    'oldDatenZat' : true,
                                                    'ziehAnz'    : (__CURRZAT > __DATAZAT)  // nur loeschen, wenn < __DATAZAT
                                                }).catch(defaultCatch);
 
                    // Neuen Daten-ZAT speichern...
                    setOpt(__OPTSET.datenZat, __CURRZAT, false);
                }
            }
 
            const __MSG = new WarnDrawMessage(optSet, __CURRZAT);
            const __MSGAUFSTIEG = new WarnDrawMessageAufstieg(optSet, __CURRZAT);
            const __ANCHOR = getTable(0, 'tbody');
 
            __MSG.showMessage(__ANCHOR, 'tr', true);
            __MSG.showDialog(showAlert);
            __MSGAUFSTIEG.showMessage(__ANCHOR, 'tr', true);
        });
}
 
// Verarbeitet Ansicht "Optionen" zur Ermittlung der Jugendfoerderung
function procOptionen() {
    return buildOptions(__OPTCONFIG, __OPTSET, {
                            'menuAnchor'  : getTable(0, 'div'),
                            'hideMenu'    : true,
                            'getDonation' : true,
                            'showForm'    : {
                                                'foerderung'          : true,
                                                'zeigeWarnung'        : true,
                                                'zeigeWarnungMonat'    : true,
                                                'zeigeWarnungHome'    : true,
                                                'zeigeWarnungDialog'  : true,
                                                'zeigeWarnungAufstieg' : true,
                                                'zeigeWarnungLegende'  : true,
                                                'ziehAnz'              : true,
                                                'showForm'            : true
                                            }
        });
}
 
// Verarbeitet Ansicht "Teamuebersicht"
function procTeamuebersicht() {
     const __ROWOFFSETUPPER = 1;    // Header-Zeile
     const __ROWOFFSETUPPER = 1;    // Header-Zeile
     const __ROWOFFSETLOWER = 1;     // Ziehen-Button
     const __ROWOFFSETLOWER = 0;


     const __COLUMNINDEX = {
     const __COLUMNINDEX = {
             'Age'  : 0,
             'Flg'  : 0,
             'Geb'   : 1,
             'Land' : 1,
             'Flg'  : 2,
             'U'    : 2,
             'Land'  : 3,
            'Age'  : 3,
             'U'     : 4,
             'Einz'  : 4,    // ab hier die Einzelskills
             'Skill' : 5,
            'SCH'  : 4,
             'Tal'  : 6,
             'ABS'   : 4,    // TOR
             'Akt'  : 7,
            'BAK'  : 5,
             'Auf'  : 8,
             'STS'   : 5,    // TOR
             'Zus'  : 9
            'KOB'  : 6,
             'FAN'  : 6,    // TOR
            'ZWK'  : 7,
             'STB'  : 7,    // TOR
            'DEC'  : 8,
             'SPL'  : 8,    // TOR
            'GES'  : 9,
            'REF'  : 9,    // TOR
            'FUQ'  : 10,
            'ERF'  : 11,
            'AGG'  : 12,
            'PAS'  : 13,
            'AUS'  : 14,
            'UEB'  : 15,
            'WID'  : 16,
            'SEL'  : 17,
            'DIS'  : 18,
            'ZUV'  : 19,
            'EIN'  : 20,
             'Zus'  : 21    // Zusaetze hinter den Einzelskills
         };
         };


     if (getElement('transfer') !== undefined) {
     if (getRows(1) === undefined) {
        __LOG[2]("Ziehen-Seite");
    } else if (getRows(1) === undefined) {
         __LOG[2]("Diese Seite ist ohne Team nicht verf\xFCgbar!");
         __LOG[2]("Diese Seite ist ohne Team nicht verf\xFCgbar!");
     } else {
     } else {
         return buildOptions(__OPTCONFIG, __OPTSET, {
         buildOptions(__OPTCONFIG, __OPTSET, {
                                'menuAnchor' : getTable(0, 'div'),
                        'menuAnchor' : getTable(0, "div"),
                                'showForm'  : {
                        'hideForm'  : {
                                                  'kennzeichenEnde'      : true,
                                            'zatAges'      : true,
                                                  'shortAufw'            : true,
                                            'trainiert'     : true,
                                                  'sepStyle'            : true,
                                            'positions'     : true,
                                                  'sepColor'            : true,
                                            'skills'       : true,
                                                  'sepWidth'            : true,
                                            'shortAufw'     : true
                                                  'saison'              : true,
                                        },
                                                  'aktuellerZat'        : true,
                        'formWidth'  : 1
                                                  'foerderung'          : true,
                    });
                                                  'team'                : true,
                                                  'zeigeJahrgang'        : true,
                                                  'zeigeUxx'            : true,
                                                  'zeigeWarnung'        : true,
                                                  'zeigeWarnungMonat'    : true,
                                                  'zeigeWarnungHome'    : true,
                                                  'zeigeWarnungDialog'  : true,
                                                  'zeigeWarnungAufstieg' : true,
                                                  'zeigeWarnungLegende'  : true,
                                                  'zeigeBalken'          : true,
                                                  'absBalken'            : true,
                                                  'zeigeId'              : true,
                                                  'ersetzeAlter'        : true,
                                                  'zeigeAlter'          : true,
                                                  'zeigeQuote'          : true,
                                                  'zeigePosition'        : true,
                                                  'zeigeZatDone'        : true,
                                                  'zeigeZatLeft'        : true,
                                                  'zeigeFixSkills'      : true,
                                                  'zeigeTrainiert'      : true,
                                                  'zeigeAnteilPri'      : true,
                                                  'zeigeAnteilSec'      : true,
                                                  'zeigePrios'          : true,
                                                  'anzahlOpti'          : true,
                                                  'anzahlMW'            : true,
                                                  'zeigeTrainiertEnde'  : true,
                                                  'zeigeAnteilPriEnde'  : true,
                                                  'zeigeAnteilSecEnde'  : true,
                                                  'zeigePriosEnde'      : true,
                                                  'zeigeSkillEnde'      : true,
                                                  'anzahlOptiEnde'      : true,
                                                  'anzahlMWEnde'        : true,
                                                  'ziehAnz'              : true,
                                                  'zatAges'              : true,
                                                  'trainiert'           : true,
                                                  'positions'           : true,
                                                  'skills'               : true,
                                                  'reset'               : true,
                                                  'showForm'            : true
                                              },
                                'formWidth'  : 1
                            }).then(optSet => {
                const __ROWS = getRows(1);
                const __HEADERS = __ROWS[0];
                const __TITLECOLOR = getColor('LEI');  // "#FFFFFF"


                const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, 1);
        const __ROWS = getRows(1);
                const __COLMAN = new ColumnManager(__OPTSET, __COLUMNINDEX, {
        const __HEADERS = __ROWS[0];
                                                    'Default'            : true,
         const __TITLECOLOR = getColor('LEI'); // "#FFFFFF"
                                                    'ersetzeSkills'      : false,
                                                    'zeigeGeb'          : false,
                                                    'zeigeSkill'         : false,
                                                    'zeigeTal'          : false,
                                                    'zeigeAufw'         : false
                                                });


                __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
        const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, false);
        const __COLMAN = new ColumnManager(__OPTSET, __COLUMNINDEX, true);


                for (let i = __ROWOFFSETUPPER, j = 0; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
        __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
                    if (__ROWS[i].cells.length > 1) {
                        __COLMAN.addValues(__PLAYERS[j++], __ROWS[i], __TITLECOLOR);
                    } else {
                        __COLMAN.setGroupTitle(__ROWS[i]);
                    }
                }


                // Format der Trennlinie zwischen den Jahrgaengen...
         for (let i = 0; i < __PLAYERS.length; i++) {
                if (! __COLMAN.gt) {
            __COLMAN.addValues(__PLAYERS[i], __ROWS[i + __ROWOFFSETUPPER], __TITLECOLOR);
                    const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
        }
 
                    separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Land, __ROWOFFSETUPPER, __ROWOFFSETLOWER, 0, 0, existValue);
                }
 
                const __CURRZAT = getOptValue(__OPTSET.datenZat);
                const __MSG = new WarnDrawMessage(__OPTSET, __CURRZAT);
                const __MSGAUFSTIEG = new WarnDrawMessageAufstieg(__OPTSET, __CURRZAT);
                const __ANCHOR = getTable(0, 'div');
                const __SEARCH = '<form method="POST">';
 
                // Kompaktere Darstellung und ohne Links...
                __MSG.out.top = false;
                __MSG.out.label = false;
                __MSG.out.link = false;
                __MSG.out.bottom = false;
                __MSGAUFSTIEG.out.label = false;
                __MSGAUFSTIEG.out.link = false;
                __MSGAUFSTIEG.out.bottom = false;
 
                __MSG.setOptionLegende();
                __MSGAUFSTIEG.setOptionLegende();
 
                __MSG.showMessage(__ANCHOR, 'p', __SEARCH);
                __MSGAUFSTIEG.showMessage(__ANCHOR, 'p', __SEARCH);
            });
    }
 
    // Promise fuer alle Faelle ohne Rueckgabewert...
    return Promise.resolve();
}
 
// Verarbeitet Ansicht "Spielereinzelwerte"
function procSpielereinzelwerte() {
    const __ROWOFFSETUPPER = 1;    // Header-Zeile
    const __ROWOFFSETLOWER = 0;
 
    const __COLUMNINDEX = {
            'Flg'  : 0,
            'Land'  : 1,
            'U'    : 2,
            'X'    : 3,
            'Age'  : 4,
            'Einz'  : 5,    // ab hier die Einzelskills
            'SCH'  : 5,
            'ABS'  : 5,    // TOR
            'BAK'  : 6,
            'STS'  : 6,    // TOR
            'KOB'  : 7,
            'FAN'  : 7,    // TOR
            'ZWK'  : 8,
            'STB'  : 8,    // TOR
            'DEC'  : 9,
            'SPL'  : 9,    // TOR
            'GES'  : 10,
            'REF'  : 10,  // TOR
            'FUQ'  : 11,
            'ERF'  : 12,
            'AGG'  : 13,
            'PAS'  : 14,
            'AUS'  : 15,
            'UEB'  : 16,
            'WID'  : 17,
            'SEL'  : 18,
            'DIS'  : 19,
            'ZUV'  : 20,
            'EIN'  : 21,
            'Zus'  : 22    // Zusaetze hinter den Einzelskills
        };
 
    if (getRows(1) === undefined) {
        __LOG[2]("Diese Seite ist ohne Team nicht verf\xFCgbar!");
    } else {
        return buildOptions(__OPTCONFIG, __OPTSET, {
                                'menuAnchor' : getTable(0, 'div'),
                                'hideForm'  : {
                                                  'zeigeWarnung'         : false,
                                                  'zeigeWarnungMonat'    : false,
                                                  'zeigeWarnungHome'    : false,
                                                  'zeigeWarnungDialog'  : false,
                                                  'zeigeWarnungAufstieg' : false,
                                                  'zeigeWarnungLegende'  : false,
                                                  'ziehAnz'              : true,
                                                  'zatAges'              : true,
                                                  'trainiert'            : true,
                                                  'positions'            : true,
                                                  'skills'              : true,
                                                  'shortAufw'            : true
                                              },
                                'formWidth'  : 1
                            }).then(optSet => {
                const __ROWS = getRows(1);
                const __HEADERS = __ROWS[0];
                const __TITLECOLOR = getColor('LEI');  // "#FFFFFF"
 
                const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, 2);
                const __COLMAN = new ColumnManager(__OPTSET, __COLUMNINDEX, true);
 
                __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
 
                for (let i = __ROWOFFSETUPPER, j = 0; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
                    if (__ROWS[i].cells.length > 1) {
                        __COLMAN.addValues(__PLAYERS[j++], __ROWS[i], __TITLECOLOR);
                    } else {
                        __COLMAN.setGroupTitle(__ROWS[i]);
                    }
                }


                // Format der Trennlinie zwischen den Jahrgaengen...
        // Format der Trennlinie zwischen den Monaten...
                if (! __COLMAN.gt) {
        const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);
                    const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);


                    separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Land, __ROWOFFSETUPPER, __ROWOFFSETLOWER, 0, 0, existValue);
        separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Age, __ROWOFFSETUPPER, __ROWOFFSETLOWER, -1, 0, floorValue);
                }
            });
     }
     }
    // Promise fuer alle Faelle ohne Rueckgabewert...
    return Promise.resolve();
}
}


// Verarbeitet Ansicht "Opt. Skill"
try {
function procOptSkill() {
     // URL-Legende:
    const __ROWOFFSETUPPER = 1;     // Header-Zeile
     // page=0: Managerbuero
    const __ROWOFFSETLOWER = 0;
     // page=1: Teamuebersicht
 
    // page=2: Spielereinzelwerte
    const __COLUMNINDEX = {
            'Flg'  : 0,
            'Land'  : 1,
            'U'    : 2,
            'Age'  : 3,
            'Skill' : 4,
            'TOR'  : 5,
            'ABW'  : 6,
            'DMI'  : 7,
            'MIT'  : 8,
            'OMI'  : 9,
            'STU'  : 10,
            'Zus'  : 11     // Zusaetze hinter den OptSkills
        };
 
    if (getRows(1) === undefined) {
        __LOG[2]("Diese Seite ist ohne Team nicht verf\xFCgbar!");
    } else {
        return buildOptions(__OPTCONFIG, __OPTSET, {
                                'menuAnchor' : getTable(0, 'div'),
                                'showForm'  : {
                                                  'kennzeichenEnde'      : true,
                                                  'sepStyle'            : true,
                                                  'sepColor'            : true,
                                                  'sepWidth'            : true,
                                                  'saison'              : true,
                                                  'aktuellerZat'        : true,
                                                  'foerderung'          : true,
                                                  'team'                : true,
                                                  'zeigeJahrgang'        : true,
                                                  'zeigeUxx'            : true,
                                                  'zeigeWarnung'        : false,
                                                  'zeigeWarnungMonat'    : false,
                                                  'zeigeWarnungHome'     : false,
                                                  'zeigeWarnungDialog'  : false,
                                                  'zeigeWarnungAufstieg' : false,
                                                  'zeigeWarnungLegende'  : false,
                                                  'zeigeBalken'          : true,
                                                  'absBalken'            : true,
                                                  'zeigeId'              : true,
                                                  'ersetzeAlter'        : true,
                                                  'zeigeAlter'          : true,
                                                  'zeigeQuote'          : true,
                                                  'zeigePosition'        : true,
                                                  'zeigeZatDone'        : true,
                                                  'zeigeZatLeft'        : true,
                                                  'zeigeFixSkills'      : true,
                                                  'zeigeTrainiert'      : true,
                                                  'zeigeAnteilPri'      : true,
                                                  'zeigeAnteilSec'      : true,
                                                  'zeigePrios'          : true,
                                                  'zeigeAufw'            : true,
                                                  'zeigeGeb'            : true,
                                                  'zeigeTal'            : true,
                                                  'anzahlOpti'          : true,
                                                  'anzahlMW'            : true,
                                                  'zeigeTrainiertEnde'  : true,
                                                  'zeigeAnteilPriEnde'  : true,
                                                  'zeigeAnteilSecEnde'  : true,
                                                  'zeigePriosEnde'      : true,
                                                  'zeigeSkillEnde'      : true,
                                                  'anzahlOptiEnde'      : true,
                                                  'anzahlMWEnde'        : true,
                                                  'zatAges'              : true,
                                                  'trainiert'            : true,
                                                  'positions'            : true,
                                                  'skills'              : true,
                                                  'reset'                : true,
                                                  'showForm'            : true
                                              },
                                'formWidth'  : 1
                            }).then(optSet => {
                const __ROWS = getRows(1);
                const __HEADERS = __ROWS[0];
                const __TITLECOLOR = getColor('LEI');  // "#FFFFFF"
 
                const __PLAYERS = init(__ROWS, __OPTSET, __COLUMNINDEX, __ROWOFFSETUPPER, __ROWOFFSETLOWER, 3);
                const __COLMAN = new ColumnManager(__OPTSET, __COLUMNINDEX, {
                                                    'Default'            : true,
                                                    'ersetzeSkills'      : false,
                                                    'zeigeSkill'        : false
                                                });
 
                __COLMAN.addTitles(__HEADERS, __TITLECOLOR);
 
                for (let i = __ROWOFFSETUPPER, j = 0; i < __ROWS.length - __ROWOFFSETLOWER; i++) {
                    if (__ROWS[i].cells.length > 1) {
                        __COLMAN.addValues(__PLAYERS[j++], __ROWS[i], __TITLECOLOR);
                    } else {
                        __COLMAN.setGroupTitle(__ROWS[i]);
                    }
                }
 
                // Format der Trennlinie zwischen den Jahrgaengen...
                if (! __COLMAN.gt) {
                    const __BORDERSTRING = getOptValue(__OPTSET.sepStyle) + ' ' + getOptValue(__OPTSET.sepColor) + ' ' + getOptValue(__OPTSET.sepWidth);


                    separateGroups(__ROWS, __BORDERSTRING, __COLUMNINDEX.Land, __ROWOFFSETUPPER, __ROWOFFSETLOWER, 0, 0, existValue);
    // Verzweige in unterschiedliche Verarbeitungen je nach Wert von page:
                }
    switch (getPageIdFromURL(window.location.href, {
            });
                                                      'haupt.php' : 0, // Ansicht "Haupt" (Managerbuero)
                                                      'ju.php'    : 1  // Ansicht "Jugendteam"
                                                  }, 'page')) {
        case 0 : procHaupt(); break;
        case 1  : procTeamuebersicht(); break;
        case 2  : procSpielereinzelwerte(); break;
        default : break;
     }
     }
 
} catch (ex) {
     // Promise fuer alle Faelle ohne Rueckgabewert...
     showAlert('[' + ex.lineNumber + "] " + __DBMOD.Name, ex.message, ex);
     return Promise.resolve();
} finally {
     __LOG[2]("SCRIPT END");
}
}
(() => {
    (async () => {
        try {
            // URL-Legende:
            // page=0: Managerbuero
            // page=1: Teamuebersicht
            // page=2: Spielereinzelwerte
            // page=3: Opt. Skill
            // page=4: Optionen
            // Verzweige in unterschiedliche Verarbeitungen je nach Wert von page:
            switch (getPageIdFromURL(window.location.href, {
                                                              'haupt.php' : 0,  // Ansicht "Haupt" (Managerbuero)
                                                              'ju.php'    : 1  // Ansicht "Jugendteam"
                                                          }, 'page')) {
                case 0  : await procHaupt().catch(defaultCatch); break;
                case 1  : await procTeamuebersicht().catch(defaultCatch); break;
                case 2  : await procSpielereinzelwerte().catch(defaultCatch); break;
                case 3  : await procOptSkill().catch(defaultCatch); break;
                case 4  : await procOptionen().catch(defaultCatch); break;
                default : break;
            }
            return 'OK';
        } catch (ex) {
            return defaultCatch(ex);
        }
    })().then(rc => {
            __LOG[1]('SCRIPT END', __DBMOD.Name, '(' + rc + ')');
        })
})();


// *** EOF ***
// *** EOF ***
</pre>
</pre>

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

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

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