Bearbeiten von „OS2.ergebnisse

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.ergebnisse'''
| colspan="2" style="padding:0.3em; background-color:#9C1818; font-size: 18px; color:#FFFFFF" align=center| '''OS2.ergebnisse'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Dateiname'''
| '''Dateiname'''
| '''OS2.ergebnisse.user.js'''
| '''os2.ergebnisse.user.js'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Version'''
| '''Version'''
| '''0.32 (WebExtensions)'''
| '''0.20'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Autor'''
| '''Autor'''
Zeile 20: Zeile 19:
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Funktionalität'''
| '''Funktionalität'''
| '''Haken wird standardmäßig aktiviert/nicht aktiviert'''<br> '''Benutzermenü für Optionen'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis'''
| '''Haken wird standardmäßig aktiviert/nicht aktiviert'''<br> '''Benutzermenü für Optionen'''<br> '''Erweiterte Optionen auch auf der Seite'''
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
| '''Letzte Änderung'''
| '''Letzte Änderung'''
Zeile 26: Zeile 25:
|- bgcolor="#FFCC00"
|- bgcolor="#FFCC00"
|}
|}
== [https://github.com/Eselce/OS2.scripts/blob/master/OS2.ergebnisse.user.js Quellcode] [https://eselce.github.io/OS2.scripts/OS2.ergebnisse.user.js INSTALLATION] ==
<pre>
<pre>
// ==UserScript==
// ==UserScript==
// @name         OS2.ergebnisse
// @name       OS2.ergebnisse
// @namespace   http://os.ongapo.com/
// @namespace   http://os.ongapo.com/
// @version     0.32+WE+
// @version     0.20
// @copyright   2016+
// @copyright   2016+
// @author       Sven Loges (SLC)
// @author     Sven Loges (SLC)
// @description Aktiviert als Standard die Option "Ergebnisse anzeigen" fuer Online Soccer 2.0
// @description Aktiviert als Standard die Option "Ergebnisse anzeigen" fuer Online Soccer 2.0
// @include     /^https?://(www\.)?(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/(l[sp]|os(eq?|c(q|[hzf]r))|supercup|zer)\.php(\?\S+(&\S+)*)?$/
// @include     http*://os.ongapo.com/ls.php
// @grant        GM.getValue
// @include    http*://os.ongapo.com/ls.php?*
// @grant        GM.setValue
// @include    http*://os.ongapo.com/lp.php
// @grant        GM.deleteValue
// @include    http*://os.ongapo.com/lp.php?*
// @grant        GM.registerMenuCommand
// @include    http*://os.ongapo.com/oseq.php
// @grant        GM.info
// @include    http*://os.ongapo.com/oseq.php?*
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @include    http*://os.ongapo.com/ose.php
// @grant       GM_getValue
// @include    http*://os.ongapo.com/ose.php?*
// @grant       GM_setValue
// @include    http*://os.ongapo.com/oscq.php
// @grant       GM_deleteValue
// @include    http*://os.ongapo.com/oscq.php?*
// @grant       GM_registerMenuCommand
// @include    http*://os.ongapo.com/oschr.php
// @grant        GM_info
// @include    http*://os.ongapo.com/oschr.php?*
// @include    http*://os.ongapo.com/osczr.php
// @include    http*://os.ongapo.com/osczr.php?*
// @include    http*://os.ongapo.com/oscfr.php
// @include    http*://os.ongapo.com/oscfr.php?*
// @include    http*://os.ongapo.com/zer.php
// @include    http*://os.ongapo.com/zer.php?*
// @include    http*://www.os.ongapo.com/ls.php
// @include    http*://www.os.ongapo.com/ls.php?*
// @include    http*://www.os.ongapo.com/lp.php
// @include    http*://www.os.ongapo.com/lp.php?*
// @include    http*://www.os.ongapo.com/oseq.php
// @include    http*://www.os.ongapo.com/oseq.php?*
// @include    http*://www.os.ongapo.com/ose.php
// @include    http*://www.os.ongapo.com/ose.php?*
// @include    http*://www.os.ongapo.com/oscq.php
// @include    http*://www.os.ongapo.com/oscq.php?*
// @include    http*://www.os.ongapo.com/oschr.php
// @include    http*://www.os.ongapo.com/oschr.php?*
// @include    http*://www.os.ongapo.com/osczr.php
// @include    http*://www.os.ongapo.com/osczr.php?*
// @include    http*://www.os.ongapo.com/oscfr.php
// @include    http*://www.os.ongapo.com/oscfr.php?*
// @include    http*://www.os.ongapo.com/zer.php
// @include    http*://www.os.ongapo.com/zer.php?*
// @include    http*://online-soccer.eu/ls.php
// @include    http*://online-soccer.eu/ls.php?*
// @include    http*://online-soccer.eu/lp.php
// @include    http*://online-soccer.eu/lp.php?*
// @include    http*://online-soccer.eu/oseq.php
// @include    http*://online-soccer.eu/oseq.php?*
// @include    http*://online-soccer.eu/ose.php
// @include    http*://online-soccer.eu/ose.php?*
// @include    http*://online-soccer.eu/oscq.php
// @include    http*://online-soccer.eu/oscq.php?*
// @include    http*://online-soccer.eu/oschr.php
// @include    http*://online-soccer.eu/oschr.php?*
// @include    http*://online-soccer.eu/osczr.php
// @include    http*://online-soccer.eu/osczr.php?*
// @include    http*://online-soccer.eu/oscfr.php
// @include    http*://online-soccer.eu/oscfr.php?*
// @include    http*://online-soccer.eu/zer.php
// @include    http*://online-soccer.eu/zer.php?*
// @include    http*://www.online-soccer.eu/ls.php
// @include    http*://www.online-soccer.eu/ls.php?*
// @include    http*://www.online-soccer.eu/lp.php
// @include    http*://www.online-soccer.eu/lp.php?*
// @include    http*://www.online-soccer.eu/oseq.php
// @include    http*://www.online-soccer.eu/oseq.php?*
// @include    http*://www.online-soccer.eu/ose.php
// @include    http*://www.online-soccer.eu/ose.php?*
// @include    http*://www.online-soccer.eu/oscq.php
// @include    http*://www.online-soccer.eu/oscq.php?*
// @include    http*://www.online-soccer.eu/oschr.php
// @include    http*://www.online-soccer.eu/oschr.php?*
// @include    http*://www.online-soccer.eu/osczr.php
// @include    http*://www.online-soccer.eu/osczr.php?*
// @include    http*://www.online-soccer.eu/oscfr.php
// @include    http*://www.online-soccer.eu/oscfr.php?*
// @include    http*://www.online-soccer.eu/zer.php
// @include    http*://www.online-soccer.eu/zer.php?*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_registerMenuCommand
// ==/UserScript==
// ==/UserScript==


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


// ==================== Konfigurations-Abschnitt fuer Optionen ====================
// ==================== Konfigurations-Abschnitt fuer Optionen ====================
const __LOGLEVEL = 3;


// Options-Typen
// Options-Typen
Zeile 72: Zeile 131:
     'NXT' : "set next option value",
     'NXT' : "set next option value",
     'RST' : "reset options"
     'RST' : "reset options"
};
const __OPTMEM = {
    'normal' : {
                  'Name'      : "Browser",
                  'Value'    : localStorage,
                  'Display'  : "localStorage",
                  'Prefix'    : 'run'
              },
    'begrenzt' : {
                  'Name'      : "Session",
                  'Value'    : sessionStorage,
                  'Display'  : "sessionStorage",
                  'Prefix'    : 'run'
              },
    'inaktiv' : {
                  'Name'      : "inaktiv",
                  'Value'    : undefined,
                  'Display'  : "",
                  'Prefix'    : ""
              }
};
};


Zeile 109: Zeile 147:
               },
               },
     'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
     'reset' : {          // Optionen auf die "Werkseinstellungen" zuruecksetzen
                  'FormPrio'  : undefined,
                   'Name'      : "reset",
                   'Name'      : "reset",
                   'Type'      : __OPTTYPES.SI,
                   'Type'      : __OPTTYPES.SI,
Zeile 116: Zeile 153:
                   'Hotkey'    : 'O',
                   'Hotkey'    : 'O',
                   'FormLabel' : ""
                   'FormLabel' : ""
              },
    'storage' : {        // Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                  'Name'      : "storage",
                  'Type'      : __OPTTYPES.MC,
                  'ValType'  : 'String',
                  'Choice'    : Object.keys(__OPTMEM),
                  'Action'    : __OPTACTION.NXT,
                  'Label'    : "Speicher: $",
                  'Hotkey'    : 'c',
                  'FormLabel' : "Speicher:|$"
              },
    'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen
                  'FormPrio'  : undefined,
                  'Name'      : "oldStorage",
                  'Type'      : __OPTTYPES.SD,
                  'PreInit'  : true,
                  'AutoReset' : true,
                  'Hidden'    : true
               },
               },
     'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
     'showForm' : {        // Optionen auf der Webseite (true = anzeigen, false = nicht anzeigen)
                  'FormPrio'  : 1,
                   'Name'      : "showForm",
                   'Name'      : "showForm",
                   'Type'      : __OPTTYPES.SW,
                   'Type'      : __OPTTYPES.SW,
Zeile 143: Zeile 160:
                   'Permanent' : true,
                   'Permanent' : true,
                   'Default'  : false,
                   'Default'  : false,
                  'Title'    : "$V Optionen",
                   'Action'    : __OPTACTION.NXT,
                   'Action'    : __OPTACTION.NXT,
                   'Label'    : "Optionen anzeigen",
                   'Label'    : "Optionen anzeigen",
                   'Hotkey'    : 'a',
                   'Hotkey'    : 'a',
                  'AltTitle'  : "$V schlie\xDFen",
                   'AltLabel'  : "Optionen verbergen",
                   'AltLabel'  : "Optionen verbergen",
                   'AltHotkey' : 'v',
                   'AltHotkey' : 'v',
Zeile 155: Zeile 170:


// ==================== Invarianter Abschnitt fuer Optionen ====================
// ==================== Invarianter Abschnitt fuer Optionen ====================
// Ein Satz von Logfunktionen, die je nach Loglevel zur Verfuegung stehen. Aufruf: __LOG[level](text)
const __LOG = {
                  'logFun'    : [
                                    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.log    // [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;
                                }
              };
__LOG.init(window, __LOGLEVEL);
// Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist)
if (Function.prototype.name === undefined) {
    Object.defineProperty(Function.prototype, 'name', {
            get : function() {
                      return /function ([^(\s]*)/.exec(this.toString())[1];
                  }
        });
}
// Ergaenzung fuer Strings: Links oder rechts auffuellen nach Vorlage
// padStr: Vorlage, z.B. "00" fuer zweistellige Zahlen
// padLeft: true = rechtsbuendig, false = linksbuendig
// 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));
};
// Ersetzt in einem String {0}, {1}, ... durch die entsprechenden Parameter
// arguments: Parameter, die fuer {0}, {1}, ... eingesetzt werden sollen
// 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
// label: Eine Ueberschrift
// message: Der Meldungs-Text
// 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) {
        __LOG[2](data);
    }
    alert(label + "\n\n" + message);
    return arguments;
}
// Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster
// mit der Meldung zu einer Exception oder einer Fehlermeldung
// label: Eine Ueberschrift
// ex: Exception oder sonstiges Fehlerobjekt
// return Liefert die showAlert()-Parameter zurueck
function showException(label, ex) {
    if (ex && ex.message) {  // Exception
        showAlert(label, ex.message, ex);
    } else {  // sonstiger Fehler
        showAlert(label, ex);
    }
}
// Standard-Callback-Funktion fuer onRejected, also abgefangener Fehler
// 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
            return showException(__LABEL, error.message, error);
        } else {
            return showException(__LABEL, error);
        }
    } catch (ex) {
        return showException(`[${ex.lineNumber}] ${__DBMOD.Name}`, ex.message, ex);
    }
}
// ==================== Abschnitt fuer Klasse Class ====================
function Class(className, baseClass, initFun) {
    'use strict';
    try {
        const __BASE = ((baseClass !== undefined) ? baseClass : Object);
        const __BASEPROTO = (__BASE ? __BASE.prototype : undefined);
        const __BASECLASS = (__BASEPROTO ? __BASEPROTO.__class : undefined);
        this.className = (className || '?');
        this.baseClass = __BASECLASS;
        Object.setConst(this, 'baseProto', __BASEPROTO, false);
        if (! initFun) {
            const __BASEINIT = (__BASECLASS || { }).init;
            if (__BASEINIT) {
                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);
        console.assert((typeof initFun) === 'function', "Not a function:", initFun);
        this.init = initFun;
    } catch (ex) {
        showAlert('[' + ex.lineNumber + "] Error in Class " + className, ex.message, ex);
    }
}
Class.define = function(subClass, baseClass, members = undefined, initFun = undefined, createProto = true) {
        return (subClass.prototype = subClass.subclass(baseClass, members, initFun, createProto));
    };
Object.setConst = function(obj, item, value, config) {
        return Object.defineProperty(obj, item, {
                        enumerable  : false,
                        configurable : (config || true),
                        writable    : false,
                        value        : value
                    });
    };
Object.setConst(Object.prototype, 'subclass', function(baseClass, members, initFun, createProto) {
        'use strict';
        try {
            const __MEMBERS = (members || { });
            const __CREATEPROTO = ((createProto === undefined) ? true : createProto);
            console.assert((typeof this) === 'function', "Not a function:", this);
            console.assert((typeof __MEMBERS) === 'object', "Not an object:", __MEMBERS);
            const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init);
            const __PROTO = (__CREATEPROTO ? Object.create(__CLASS.baseProto) : this.prototype);
            for (let item in __MEMBERS) {
                if ((item !== '__name') && (item !== '__init')) {
                    Object.setConst(__PROTO, item, __MEMBERS[item]);
                }
            }
            Object.setConst(__PROTO, '__class', __CLASS, ! __CREATEPROTO);
            return __PROTO;
        } catch (ex) {
            showAlert('[' + ex.lineNumber + "] Error in subclassing", ex.message, ex);
        }
    }, false);
Class.define(Object, null, {
                    '__init'      : function() {
                                        // Oberstes Basisklassen-Init...
                                        return true;
                                    },
                    'getClass'    : function() {
                                        return this.__class;
                                    },
                    'getClassName' : function() {
                                        const __CLASS = this.getClass();
                                        return (__CLASS ? __CLASS.getName() : undefined);
                                    },
                    'setConst'    : function(item, value, config = undefined) {
                                        return Object.setConst(this, item, value, config);
                                    }
                }, undefined, false);
Class.define(Function, Object);
Class.define(Class, Object, {
                    'getName'      : function() {
                                        return this.className;
                                    }
                });
// ==================== Ende Abschnitt fuer Klasse Class ====================
// ==================== Abschnitt fuer Klasse Delims ====================
// 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';
    if ((typeof delim) === 'object') {
        // Erster Parameter ist Objekt mit den Properties...
        if (back === undefined) {
            back = delim.back;
        }
        if (root === undefined) {
            root = delim.root;
        }
        if (home === undefined) {
            home = delim.home;
        }
        delim = delim.delim;
    }
    this.setDelim(delim);
    this.setBack(back);
    this.setRoot(root);
    this.setHome(home);
}
Class.define(Delims, Object, {
              '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 ====================
// ==================== Abschnitt fuer Klasse UriDelims ====================
// Basisklasse fuer die Verwaltung der Trennzeichen und Symbole von URIs
// 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
// scheme: (Optional) Trennzeichen fuer den Schema-/Protokollnamen vorne
// host: (Optional) Prefix fuer Hostnamen hinter dem Scheme-Trenner
// 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';
    if ((typeof delim) === 'object') {
        // Erster Parameter ist Objekt mit den Properties...
        if (scheme === undefined) {
            scheme = delim.scheme;
        }
        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;
        }
    }
    Delims.call(this, delim, back, root, home);
    this.setScheme(scheme);
    this.setHost(host);
    this.setPort(port);
    this.setQuery(query);
    this.setQrySep(qrySep);
    this.setQryAss(qryAss);
    this.setNode(node);
}
Class.define(UriDelims, Delims, {
              '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;
                                }
          });
// ==================== Ende Abschnitt fuer Klasse UriDelims ====================
// ==================== Abschnitt fuer Klasse Path ====================
// Basisklasse fuer die Verwaltung eines Pfades
// 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 Path(homePath, delims) {
    'use strict';
    this.dirs = [];
    this.setDelims(delims);
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });
    this.home();
}
Class.define(Path, Object, {
                  'root'          : function() {
                                        this.dirs.splice(0, this.dirs.length);
                                    },
                  'home'          : function() {
                                        this.dirs = this.homeDirs.slice();
                                    },
                  'up'            : function() {
                                        this.dirs.pop();
                                    },
                  'down'          : function(subDir) {
                                        this.dirs.push(subDir);
                                    },
                  'setDelims'      : function(delims = undefined) {
                                        this.setConst('delims', new Delims(delims));
                                    },
                  'setDelim'      : function(delim = undefined) {
                                        this.delims.setDelim(delim || '/');
                                    },
                  '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);
                                        return ((__DIRS && __DIRS.length) ? __DIRS.slice(-1)[0] : "");
                                    },
                  '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 Path ====================
// ==================== Abschnitt fuer Klasse URI ====================
// Basisklasse fuer die Verwaltung einer URI/URL
// 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 URI(homePath, delims) {
    'use strict';
    Path.call(this);
    const __HOSTPORT = this.getHostPort(homePath);
    this.scheme = this.getSchemePrefix(homePath);
    this.host = __HOSTPORT.host;
    this.port = this.parseValue(__HOSTPORT.port);
    this.query = this.parseQuery(this.getQueryString(homePath));
    this.node = this.getNodeSuffix(homePath);
    this.homeDirs = this.getDirs(homePath, { 'home' : "" });
    this.home();
}
Class.define(URI, Path, {
              'setDelims'        : function() {
                                        this.setConst('delims', new UriDelims('/', "..", "", '~', ':', "//", ':', '?', '&', '=', '#'));
                                    },
              'setSchemeDelim'    : function(schemeDelim = undefined) {
                                        this.delims.setScheme(schemeDelim || ':');
                                    },
              '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 || '#');
                                    },
              '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 {
                                                    '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);
                                    },
              'getSchemePrefix'  : function(path = undefined) {
                                        const __SCHEMEDELIM = this.delims.scheme;
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);
                                        return ((~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined);
                                    },
              'stripSchemePrefix' : function(path = undefined) {
                                        const __SCHEMEDELIM = this.delims.scheme;
                                        const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1);
                                        return ((~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path);
                                    },
              'getNodeSuffix'    : function(path = undefined) {
                                        const __NODEDELIM = this.delims.node;
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                                        return ((~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined);
                                    },
              'stripNodeSuffix'  : function(path = undefined) {
                                        const __NODEDELIM = this.delims.node;
                                        const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1);
                                        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);
                                        return ((~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined);
                                    },
              'stripQueryString'  : function(path = undefined) {
                                        const __QUERYDELIM = this.delims.query;
                                        const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1);
                                        return ((~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path);
                                    },
              'formatParams'      : function(params, formatFun, delim = ' ', assign = '=') {
                                        const __PARAMS = [];
                                        for (let param in params) {
                                            __PARAMS.push(param + assign + formatFun(params[param]));
                                        }
                                        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];
                                                if (__PARAM) {
                                                    const __INDEX = __PARAM.indexOf(assign);
                                                    const __KEY = ((~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM);
                                                    const __VAL = ((~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true);
                                                    __RET[__KEY] = __VAL;
                                                }
                                            }
                                        }
                                        return __RET;
                                    },
              'rawValue'          : function(value) {
                                        return value;
                                    },
              'parseValue'        : function(value) {
                                        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;
                                    },
              'getQuery'          : function(delims = { }) {
                                        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);
                                    },
              '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);
                                    },
              'setQuery'          : function(query) {
                                        this.query = query;
                                    },
              '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;
                                    },
              'getDirs'          : function(path = undefined, delims = undefined) {
                                        const __PATH = this.getServerPath(path);
                                        return Path.prototype.getDirs.call(this, __PATH);
                                    }
          });
// ==================== Ende Abschnitt fuer Klasse URI ====================
// ==================== Abschnitt fuer Klasse Directory ====================
// Basisklasse fuer eine Verzeichnisstruktur
// 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, {
                    'chDir' : function(subDir = undefined) {
                                  if (subDir === undefined) {
                                      this.root();
                                  } else if ((typeof subDir) === 'object') {
                                      for (let sub of subDir) {
                                          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';
    Directory.call(this, undefined, new Delims('/', "..", '/', '~'));
    this.setConst('rootObj', rootObj);  // Wichtig: Verweis nicht verfolgen! Gefahr durch Zyklen!
}
Class.define(ObjRef, Directory, {
                    'valueOf' : function() {
                                    let ret = this.rootObj;
                                    for (let name of this.dirs) {
                                        if (ret === undefined) {
                                            break;
                                        }
                                        ret = ret[name];
                                    }
                                    return ret;
                                }
                });
// ==================== Ende Abschnitt fuer Klasse ObjRef ====================


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


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


Zeile 914: Zeile 211:
}
}


// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// valueA: Ein 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
// value: Beliebiger (strukturierter) Wert
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// return String-Darstellung des Wertes
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
function serialize(name, value) {
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue
     const __STREAM = (value !== undefined) ? JSON.stringify(value) : value;
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {
     let product = defValue;


     if ((valueA !== undefined) && (valueB !== undefined)) {
     console.log(name + " >> " + __STREAM);
        product = parseFloat(valueA) * parseFloat(valueB);
    }


     if (isNaN(product)) {
     GM_setValue(name, __STREAM);
        product = defValue;
    }


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


// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck
// value: Eine ganze Zahl
// name: GM_setValue-Name, unter dem die Daten gespeichert werden
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben
// return Objekt, das unter dem Namen gespeichert war
function getOrdinal(value, defValue = '*') {
function deserialize(name, defValue = undefined) {
     return getValue(value, defValue, value + '.');
     const __STREAM = GM_getValue(name, defValue);
}
 
// Hilfsfunktion fuer Array.sort(): Vergleich zweier Zahlen
// valueA: Erster Zahlenwert
// 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)
     console.log(name + " << " + __STREAM);
// 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;
     if ((__STREAM !== undefined) && (__STREAM.length !== 0)) {
}
         try {
 
            return JSON.parse(__STREAM);
// Liefert alle Basisklassen des Objekts (inkl. Vererbung)
         } catch (ex) {
// obj: Ein (generisches) Objekt
             console.error(name + ": " + ex.message);
// return true, wenn der Prototyp rekursiv gefunden werden konnte
function getPrototypes(obj) {
    let ret = [];
 
    while (obj !== null) {
         const __PROTO = Object.getPrototypeOf(obj);
 
        ret.push(__PROTO);
         if ((typeof obj) === 'xml') { // Sonderfall mit Selbstbezug
             break;
         }
         }
        obj = __PROTO;
     }
     }


     return ret;
     return undefined;
}
}


// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)
// Setzt eine Option dauerhaft und laedt die Seite neu
// obj: Ein (generisches) Objekt
// name: Name der Option als Speicherort
// return Array von Items (Property-Namen)
// value: Zu setzender Wert
function getAllProperties(obj) {
// reload: Seite mit neuem Wert neu laden
    let ret = [];
// return Gespeicherter Wert fuer setOptValue()
 
function setStored(name, value, reload = true, serial = false) {
    for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
     if (serial) {
      ret = ret.concat(Object.getOwnPropertyNames(o));
         serialize(name, value);
    }
 
    return ret;
}
 
// 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;
 
    if (inList) {
        active = (inList[item] === true);  // gesetzt und true
    }
    if (exList) {
        if (exList[item] === true) {  // gesetzt und true
            active = false;  // NICHT anzeigen
        }
    }
 
    return active;
}
 
// Fuegt Properties zu einem Objekt hinzu, die in einem zweiten stehen. Doppelte Werte werden ueberschrieben
// data: Objekt, dem Daten hinzugefuegt werden
// addData: Objekt, das zusaetzliche Properties enthaelt
// addList: Checkliste der zu setzenden Items (true fuer kopieren), falls angegeben
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
// return Das gemergete Objekt mit allen Properties
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
// data: Objekt, deren Properties bearbeitet werden
// delList: Checkliste der zu loeschenden Items (true fuer loeschen), falls angegeben
// ignList: Checkliste der ignorierten Items (true fuer auslassen), falls angegeben
// return Das veraenderte Objekt ohne die geloeschten Properties
function delProps(data, delList = undefined, ignList = undefined) {
    for (let item in getValue(data, { })) {
        if (checkItem(item, delList, ignList)) {
            delete data[item];
        }
    }
 
    return data;
}
 
// 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;
    }
 
    const __PROP = obj[item];
 
    if ((__PROP !== undefined) && (__PROP !== null)) {
        return __PROP;
    }
 
    return (obj[item] = defValue);
}
 
// Sicheres obj.valueOf() fuer alle Daten
// data: Objekt oder Wert
// return Bei Objekten valueOf() oder das Objekt selber, bei Werten der Wert
function valueOf(data) {
    return (((typeof data) === 'object') ? data.valueOf() : data);
}
 
// Sicheres JSON.stringify(), das auch mit Zyklen umgehen kann
// value: Auszugebene Daten. Siehe JSON.stringify()
// 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
// 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 = [];
 
     if (! cycleReplacer) {
         cycleReplacer = function(key, value) {
                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);
            }
 
            return ((! replacer) ? value : replacer.call(this, key, value));
        };
}
 
// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// key: Der uebergebene Schluessel
// value: Der uebergebene Wert
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function replaceArraySimple(key, value) {
    if (Array.isArray(value)) {
        return "[ " + value.join(", ") + " ]";
    }
 
    return value;
}
 
// Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt
// key: Der uebergebene Schluessel
// value: Der uebergebene Wert
// return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert
function replaceArray(key, value) {
    if (Array.isArray(value)) {
        __RET = value.map(function(element) {
                              return safeStringify(element, replaceArray, 0);
                          });
 
        return __RET;
    }
 
    return value;
}
 
// Moegliche einfache Ersetzungen mit '$'...
let textSubst;
 
// Substituiert '$'-Parameter in einem Text
// 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) {
        textSubst  = {
                'n' : __DBMOD.name,
                'v' : __DBMOD.version,
                'V' : __DBMOD.Name
            };
    }
 
    for (let ch in textSubst) {
        const __SUBST = textSubst[ch];
 
        ret = ret.replace('$' + ch, __SUBST);
    }
 
    return ret.replace('$', par1);
}
 
// Fuegt in die uebergebene Zahl Tausender-Trennpunkte ein
// Wandelt einen etwaig vorhandenen Dezimalpunkt in ein Komma um
// 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;
     } else {
     } else {
         // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
         GM_setValue(name, value);
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
        const __TEMP = reverseString(numberString);
        let result = "";
 
        for (let i = 0; i < __TEMP.length; i++) {
            if ((i > 0) && (i % 3 === 0)) {
                result += '.';
            }
            result += __TEMP.substr(i, 1);
        }
 
        return reverseString(result);
    }
}
 
// Dreht den uebergebenen String um
// string: Eine Zeichenkette
// return Dieselbe Zeichenkette rueckwaerts
function reverseString(string) {
    let result = "";
 
    for (let i = string.length - 1; i >= 0; i--) {
        result += string.substr(i, 1);
     }
     }


    return result;
}
// Speichert einen String/Integer/Boolean-Wert unter einem Namen ab
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
// value: Zu speichernder String/Integer/Boolean-Wert
// return Promise auf ein Objekt, das 'name' und 'value' der Operation enthaelt
function storeValue(name, value) {
    __LOG[4](name + " >> " + value);
    return GM.setValue(name, value).then(voidValue => {
            __LOG[5]("OK " + name + " >> " + value);
            return Promise.resolve({
                    'name'  : name,
                    'value' : value
                });
        }, defaultCatch);
}
// Holt einen String/Integer/Boolean-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 den String/Integer/Boolean-Wert, der unter dem Namen gespeichert war
function summonValue(name, defValue = undefined) {
    return GM.getValue(name, defValue).then(value => {
            __LOG[4](name + " << " + value);
            return Promise.resolve(value);
        }, ex => {
            __LOG[0](name + ": " + ex.message);
            return Promise.reject(ex);
        }, defaultCatch);
}
// Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab
// name: GM.setValue()-Name, unter dem die Daten gespeichert werden
// value: Beliebiger (strukturierter) Wert
// return Promise auf ein Objekt, das 'name' und 'value' in der String-Darstellung des Wertes enthaelt
function serialize(name, value) {
    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;
            }
        });
}
// Setzt die Seite gemaess der Aenderungen zurueck...
// reload: Seite wird ganz neu geladen
function refreshPage(reload = true) {
     if (reload) {
     if (reload) {
        __LOG[2]("Seite wird neu geladen...");
         window.location.reload();
         window.location.reload();
     }
     }
}
// Setzt eine Option dauerhaft und laedt die Seite neu
// name: Name der Option als Speicherort
// value: Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// serial: Serialization fuer komplexe Daten
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// 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 value;
Zeile 1.310: Zeile 269:
// value: Vorher gesetzter Wert
// value: Vorher gesetzter Wert
// reload: Seite mit neuem Wert neu laden
// reload: Seite mit neuem Wert neu laden
// serial: Serialization fuer komplexe Daten
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gespeicherter Wert fuer setOptValue()
// return Gespeicherter Wert fuer setOptValue()
function setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
function setNextStored(arr, name, value, reload = true, serial = false) {
     return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);
     return setStored(name, getNextValue(arr, value), reload, serial);
}
}


// Fuehrt die in einem Storage gespeicherte Operation aus
// Fuehrt die in einem Storage gespeicherte Operation aus
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// optSet: Set mit den Optionen
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
// session: true = bis Browserende gespeichert (sessionStorage), false = unbegrenzt gespeichert (localStorage)
function getStoredCmds(memory = undefined) {
function runStored(optSet, session = true) {
     const __STORAGE = getMemory(memory);
     const __STORAGE = (session ? sessionStorage : localStorage);
     const __MEMORY = __STORAGE.Value;
     const __CMD = ((__STORAGE !== undefined) ? __STORAGE.getItem('runcmd') : undefined);
    const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];


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


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


         __DELITEM('cmd');
         const __VAL = value;
        __DELITEM('key');
        __DELITEM('val');
    }


    return (__STOREDCMDS.length ? __STOREDCMDS : undefined);
         switch (__OPTACTION[__CMD]) {
}
        case __OPTACTION.SET : console.log("SET '" + __KEY + "' " + __VAL);
 
                              setStored(__KEY, __VAL, false, false);
// Fuehrt die in einem Storage gespeicherte Operation aus
                              break;
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
        case __OPTACTION.NXT : console.log("SETNEXT '" + __KEY + "' " + __VAL);
// optSet: Object mit den Optionen
                              //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
                              setStored(__KEY, __VAL, false, false);
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
                              break;
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
        case __OPTACTION.RST : console.log("RESET");
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
                              resetOptions(optSet, false);
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {
                              break;
    const __BEFORELOAD = getValue(beforeLoad, true);
        default :              break;
    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 (__BEFORELOAD) {
            if (__STOREDCMDS.length) {
                await invalidateOpts(optSet);  // alle Optionen invalidieren
                invalidated = true;
            }
            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);
     __STORAGE.removeItem('runcmd');
    __STORAGE.removeItem('runkey');
    __STORAGE.removeItem('runval');
}
}


Zeile 1.451: Zeile 349:
// return Gesetzter Name der Option
// return Gesetzter Name der Option
function setOptName(opt, name) {
function setOptName(opt, name) {
     const __CONFIG = getOptConfig(opt);
     return (getOptConfig(opt).Name = name);
    const __NAME = getOptName(opt);
 
    if (__NAME !== name) {
        __LOG[4]("RENAME " + __NAME + " => " + name);
 
        __CONFIG.Name = name;
    }
 
    return name;
}
}


Zeile 1.467: Zeile 356:
// return Name der Option
// return Name der Option
function getOptName(opt) {
function getOptName(opt) {
     const __CONFIG = getOptConfig(opt);
     return getOptConfig(opt).Name;
    const __NAME = __CONFIG.Name;
 
    if (! __NAME) {
        const __SHARED = __CONFIG.Shared;
 
        if (__SHARED && ! opt.Loaded) {
            const __OBJREF = getSharedRef(__SHARED, opt.Item);
 
            return __OBJREF.getPath();
        }
 
        showAlert("Error", "Option ohne Namen", safeStringify(__CONFIG));
    }
 
    return __NAME;
}
}


Zeile 1.490: Zeile 364:
// return Gesetzter Wert
// return Gesetzter Wert
function setOptValue(opt, value) {
function setOptValue(opt, value) {
     if (opt !== undefined) {
     return (opt !== undefined) ? (opt.Value = value) : undefined;
        if (! opt.ReadOnly) {
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));
 
            opt.Value = value;
        }
        return opt.Value;
    } else {
        return undefined;
    }
}
}


Zeile 1.505: Zeile 370:
// opt: Config und Value der Option
// opt: Config und Value der Option
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// load: Laedt die Option per loadOption(), falls noetig
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Gesetzter Wert
// return Gesetzter Wert
function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {
function getOptValue(opt, defValue = undefined) {
     let value;
     return getValue((opt !== undefined) ? opt.Value : undefined, defValue);
 
    if (opt !== undefined) {
        if (load && ! opt.Loaded) {
            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 ====================
// ==================== Ende Abschnitt fuer diverse Utilities ====================
// ==================== Abschnitt fuer Speicher und die Scriptdatenbank ====================
// Namen des Default-, Temporaer- und Null-Memories...
const __MEMNORMAL  = 'normal';
const __MEMSESSION  = 'begrenzt';
const __MEMINAKTIVE = 'inaktiv';
// Definition des Default-, Dauer- und Null-Memories...
const __OPTMEMNORMAL  = __OPTMEM[__MEMNORMAL];
const __OPTMEMSESSION  = __OPTMEM[__MEMSESSION];
const __OPTMEMINAKTIVE = __OPTMEM[__MEMINAKTIVE];
// Medium fuer die Datenbank (Speicher)
let myOptMem = __OPTMEMNORMAL;
let myOptMemSize;
// Infos ueber dieses Script-Modul
const __DBMOD = new ScriptModule();
// Inhaltsverzeichnis der DB-Daten (indiziert durch die Script-Namen)
const __DBTOC = { };
// Daten zu den Modulen (indiziert durch die Script-Namen)
const __DBDATA = { };
// ==================== Abschnitt fuer Speicher ====================
// Ermittelt fuer die uebergebene Speicher-Konfiguration einen Speicher
// memory: __OPTMEM.normal = 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);
}
// 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 ret;
}
// Ermittelt die Groesse des benutzten Speichers
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return Groesse des genutzten Speichers in Bytes
function getMemSize(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    //getMemUsage(__MEMORY);
    if (__MEMORY !== undefined) {
        const __SIZE = safeStringify(__MEMORY).length;
        __LOG[2]("MEM: " + __SIZE + " bytes");
        return __SIZE;
    } else {
        return 0;
    }
}
// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird
// out: Logfunktion, etwa __LOG[4]
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen)
// name: Name des Objekts
function getMemUsage(value = undefined, out = undefined, depth = -1, name = '$') {
    const __OUT = (out || __LOG[4]);
    if ((typeof value) === 'string') {
        const __SIZE = value.length;
        __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.slice(0, 255));
    } else if ((typeof value) === 'object') {
        if (depth === 0) {
            const __SIZE = safeStringify(value).length;
            __OUT("USAGE: " + name + '\t' + __SIZE);
        } else {
            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);
    }
}
// Restauriert den vorherigen Speicher (der in einer Option definiert ist)
// opt: Option zur Wahl des Speichers
// return Promise auf gesuchten Speicher oder Null-Speicher ('inaktiv')
async function restoreMemoryByOpt(opt) {
    // Memory Storage fuer vorherige Speicherung...
    const __STORAGE = await getOptValue(opt, __MEMNORMAL, true, true, true);
    return __OPTMEM[__STORAGE];
}
// Initialisiert den Speicher (der in einer Option definiert ist) und merkt sich diesen ggfs.
// opt: Option zur Wahl des Speichers
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt)
// 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)) {
        if (storage !== __MEMINAKTIVE) {
            storage = __MEMINAKTIVE;
            optMem = __OPTMEM[storage];
        }
    }
    if (saveOpt !== undefined) {
        setOpt(saveOpt, storage, false, onFulfilled, onRejected);
    }
    return optMem;
}
// ==================== Ende Abschnitt fuer Speicher ====================
// ==================== Abschnitt fuer die Scriptdatenbank ====================
// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten
// meta: Metadaten des Scripts (Default: GM.info.script)
// return Beschreibende Daten fuer __DBMOD
function ScriptModule(meta) {
    'use strict';
    const __META = getValue(meta, GM.info.script);
    const __PROPS = {
                'name'        : true,
                'version'    : true,
                'namespace'  : true,
                'description' : true
            };
    const __DBMOD = { };
    __LOG[5](__META);
    // Infos zu diesem Script...
    addProps(__DBMOD, __META, __PROPS);
    // Voller Name fuer die Ausgabe...
    Object.defineProperty(__DBMOD, 'Name', {
                    get : function() {
                              return this.name + " (" + this.version + ')';
                          },
                    set : undefined
                });
    __LOG[2](__DBMOD);
    return __DBMOD;
}
Class.define(ScriptModule, Object);
// Initialisiert die Scriptdatenbank, die einen Datenaustausch zwischen den Scripten ermoeglicht
// optSet: Gesetzte Optionen (und Config)
function initScriptDB(optSet) {
    // Speicher fuer die DB-Daten...
    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 (__DBMEM !== undefined) {
        // ... und die Daten der Fremdscripte laden...
        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: 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...
    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));
        // Aktualisierung der Speichergroesse...
        myOptMemSize = getMemSize(myOptMem);
    }
    // Jetzt die inzwischen gefuellten Daten *dieses* Scripts ergaenzen...
    scriptDB(__DBMOD.name, getValue(optSet, { }));
    __LOG[2](__DBDATA);
}
// Holt die globalen Daten zu einem Modul aus der Scriptdatenbank
// module: Gesetzte Optionen (und Config)
// initValue: Falls angegeben, zugewiesener Startwert
// return Daten zu diesem Modul
function scriptDB(module, initValue = undefined) {
    const __NAMESPACE = __DBTOC.namespaces[module];
    const __DBMODS = getProp(__DBDATA, __NAMESPACE, { });
    if (initValue !== undefined) {
        return (__DBMODS[module] = initValue);
    } else {
        return getProp(__DBMODS, module, { });
    }
}
// ==================== Ende Abschnitt fuer die Scriptdatenbank ====================
// ==================== Ende Abschnitt fuer Speicher und die Scriptdatenbank ====================


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


// Zeigt den Eintrag im Menu einer Option
// Zeigt den Eintrag im Menu einer Option
// val: Derzeitiger Wert der Option
// opt: Derzeitiger Wert der Option
// menuOn: Text zum Setzen im Menu
// menuOn: Text zum Setzen im Menu
// funOn: Funktion zum Setzen
// funOn: Funktion zum Setzen
Zeile 1.788: Zeile 387:
// funOff: Funktion zum Ausschalten
// funOff: Funktion zum Ausschalten
// keyOff: Hotkey zum Ausschalten im Menu
// keyOff: Hotkey zum Ausschalten im Menu
// return Promise von GM.registerMenuCommand()
function registerMenuOption(opt, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
     const __ON  = (opt ? '*' : "");
     const __ON  = (val ? '*' : "");
     const __OFF = (opt ? "" : '*');
     const __OFF = (val ? "" : '*');


     __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
     console.log("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);


     if (val) {
     if (opt) {
         return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);
         GM_registerMenuCommand(menuOff, funOff, keyOff);
     } else {
     } else {
         return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);
         GM_registerMenuCommand(menuOn, funOn, keyOn);
     }
     }
}
}


// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// val: Derzeitiger Wert der Option
// opt: Derzeitiger Wert der Option
// arr: Array-Liste mit den moeglichen Optionen
// arr: Array-Liste mit den moeglichen Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// fun: Funktion zum Setzen des naechsten Wertes
// fun: Funktion zum Setzen des naechsten Wertes
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// return Promise von GM.registerMenuCommand()
function registerNextMenuOption(opt, arr, menu, fun, key) {
function registerNextMenuOption(val, arr, menu, fun, key) {
     const __MENU = menu.replace('$', opt);
     const __MENU = substParam(menu, val);
     let options = "OPTION " + __MENU;
     let options = "OPTION " + __MENU;


     for (let value of arr) {
     for (let value of arr) {
         if (value === val) {
         if (value === opt) {
             options += " / *" + value + '*';
             options += " / *" + value + '*';
         } else {
         } else {
Zeile 1.820: Zeile 417:
         }
         }
     }
     }
     __LOG[3](options);
     console.log(options);


     return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
     GM_registerMenuCommand(__MENU, fun, key);
}
}


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


     __LOG[hidden ? 4 : 3](__OPTIONS);
     console.log(__OPTIONS);


     if (hidden) {
     if (! hidden) {
         return Promise.resolve(__VALUE);
         GM_registerMenuCommand(__MENU, fun, key);
    } else {
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
     }
     }
}
}
Zeile 1.850: Zeile 444:
// Zeigt den Eintrag im Menu einer Option
// Zeigt den Eintrag im Menu einer Option
// opt: Config und Value der Option
// opt: Config und Value der Option
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
function registerOption(opt) {
function registerOption(opt) {
     const __CONFIG = getOptConfig(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;


     if (! __CONFIG.HiddenMenu) {
     if (! __CONFIG.HiddenMenu) {
         switch (__CONFIG.Type) {
         switch (__CONFIG.Type) {
         case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
         case __OPTTYPES.MC : registerNextMenuOption(getOptValue(opt), __CONFIG.Choice,
         case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                                  __CONFIG.Label, opt.Action, __CONFIG.Hotkey);
                                                      __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
                            break;
         case __OPTTYPES.TF : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
         case __OPTTYPES.SW : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                      __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                                                                  __CONFIG.AltLabel, opt.Action, __CONFIG.AltHotkey);
         case __OPTTYPES.SD : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
                            break;
         case __OPTTYPES.SI : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
         case __OPTTYPES.TF : registerMenuOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
         default :            return Promise.resolve(__VALUE);
                                                                  __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
                            break;
         case __OPTTYPES.SD : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
                            break;
         case __OPTTYPES.SI : registerDataOption(getOptValue(opt), __CONFIG.Label, opt.Action, __CONFIG.Hotkey,
                                                                  __CONFIG.HiddenMenu, __CONFIG.Serial);
                            break;
         default :            break;
         }
         }
     } else {
     } else {
         // Nur Anzeige im Log...
         // Nur Anzeige im Log...
         return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
         registerDataOption(getOptValue(opt), getOptName(opt), opt.Action, __CONFIG.Hotkey, __CONFIG.HiddenMenu, __CONFIG.Serial);
     }
     }
}
}
Zeile 1.881: Zeile 476:
// Initialisiert die gesetzten Option
// Initialisiert die gesetzten Option
// config: Konfiguration der Option
// config: Konfiguration der Option
// setValue: Zu uebernehmender Default-Wert (z.B. der jetzt gesetzte)
// return Initialwert der gesetzten Option
// return Initialwert der gesetzten Option
function initOptValue(config, setValue = undefined) {
function initOptValue(config) {
     let value = getValue(setValue, config.Default);  // Standard
     let value = config.Default;  // Standard
 
    if (config.SharedData !== undefined) {
        value = config.SharedData;
    }


     switch (config.Type) {
     switch (config.Type) {
Zeile 1.914: Zeile 504:
// item: Key der Option
// item: Key der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// optConfig: Konfiguration der Option
// return Funktion fuer die Option
// return Funktion fuer die Option
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
function initOptAction(optAction, item = undefined, optSet = undefined) {
     let fun;
     var fun;


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


         switch (optAction) {
         switch (optAction) {
         case __OPTACTION.SET : fun = function() {
         case __OPTACTION.SET : fun = function() {
                                       return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);
                                       return setOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                   };
                                   };
                               break;
                               break;
         case __OPTACTION.NXT : fun = function() {
         case __OPTACTION.NXT : fun = function() {
                                       return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
                                       return setNextOptByName(optSet, item, optSet.SetValue, __RELOAD);
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice).catch(defaultCatch);
                                   };
                                   };
                               break;
                               break;
         case __OPTACTION.RST : fun = function() {
         case __OPTACTION.RST : fun = function() {
                                       return resetOptions(optSet, __RELOAD).then(
                                       return resetOptions(optSet, __RELOAD);
                                              result => __LOG[3]("RESETTING (" + result + ")..."),
                                              defaultCatch);
                                   };
                                   };
                               break;
                               break;
Zeile 1.944: Zeile 530:


     return fun;
     return fun;
}
// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
// shared: Object mit den Angaben 'namespace', 'module' und ggfs. 'item'
// item: Key der Option
// return ObjRef, die das Ziel definiert
function getSharedRef(shared, item = undefined) {
    if (shared === undefined) {
        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) {
        const __DEFAULT = __DEFAULTS[stage];
        const __PROP = __PROPS[stage];
        const __NAME = shared[__PROP];
        if (__NAME === '$') {
            break;
        }
        __OBJREF.chDir(getValue(__NAME, __DEFAULT));
    }
    return __OBJREF;
}
// Gibt diese Config oder, falls 'Shared', ein Referenz-Objekt mit gemeinsamen Daten zurueck
// optConfig: Konfiguration der Option
// item: Key der Option
// return Entweder optConfig oder gemergete Daten auf Basis des in 'Shared' angegebenen Objekts
function getSharedConfig(optConfig, item = undefined) {
    let config = getValue(optConfig, { });
    const __SHARED = config.Shared;
    if (__SHARED !== undefined) {
        const __OBJREF = getSharedRef(__SHARED, item);  // Gemeinsame Daten
        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;
}
}


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


     if (optSet === undefined) {
     if (optSet === undefined) {
Zeile 2.016: Zeile 544:


     for (let opt in optConfig) {
     for (let opt in optConfig) {
         const __OPTCONFIG = optConfig[opt];
         const __CONFIG = optConfig[opt];
         const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
         const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);


         if ((preInit === undefined) || (__PREINIT === preInit)) {
         optSet[opt] = {
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
             'Config'    : __CONFIG,
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
            'Value'    : initOptValue(__CONFIG),
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?
            'SetValue'  : undefined,
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);
            'Action'    : initOptAction(__CONFIG.Action, opt, optSet),
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);
            'AltAction' : initOptAction(__ALTACTION, opt, optSet)
            const __VALUE = (__LOADED ? optSet[opt].Value : undefined);
         };
 
             optSet[opt] = {
                'Item'      : opt,
                '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)
            };
         }
    }
 
    return optSet;
}
 
    // Abhaengigkeiten:
    // ================
    // initOptions (PreInit):
    // restoreMemoryByOpt: PreInit oldStorage
    // getStoredCmds: restoreMemoryByOpt
    // runStoredCmds (beforeLoad): getStoredCmds
    // 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
// optConfig: Konfiguration der Optionen
// 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...
    myOptMemSize = getMemSize(myOptMem = await restoreMemoryByOpt(optSet.oldStorage));
 
    // Zwischengespeicherte Befehle auslesen...
    const __STOREDCMDS = getStoredCmds(myOptMem);
 
    // ... ermittelte Befehle ausfuehren...
    const __LOADEDCMDS = await runStoredCmds(__STOREDCMDS, optSet, true);  // BeforeLoad
 
    // Bisher noch nicht geladenene Optionen laden...
    await loadOptions(optSet);
 
    // Memory Storage fuer naechste Speicherung...
    myOptMemSize = getMemSize(myOptMem = startMemoryByOpt(optSet.storage, optSet.oldStorage));
 
    // Globale Daten ermitteln...
    initScriptDB(optSet);
 
    optSet = initOptions(optConfig, optSet, false);  // Rest
 
    if (classification !== undefined) {
        // Umbenennungen durchfuehren...
        await classification.renameOptions();
    }
 
    // ... ermittelte Befehle ausfuehren...
    await runStoredCmds(__LOADEDCMDS, optSet, false);  // Rest
 
    // Als globale Daten speichern...
    updateScriptDB(optSet);
 
    return optSet;
}
 
// Installiert die Visualisierung und Steuerung der Optionen
// optSet: Platz fuer die gesetzten Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return Liefert die gesetzten Optionen zurueck
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {
    if (! optParams.hideMenu) {
        buildMenu(optSet).then(() => __LOG[3]("Menu OK"));
    }
 
    if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {
        buildForm(optParams.menuAnchor, optSet, optParams);
     }
     }


Zeile 2.142: Zeile 564:
// value: (Bei allen Typen) Zu setzender Wert
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// 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
// return Gesetzter Wert
function setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
function setOpt(opt, value, reload = false) {
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));
}
}


Zeile 2.173: Zeile 593:
// value: Default fuer ggfs. zu setzenden Wert
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// 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 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
// 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)
// 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
// return Gesetzter Wert
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
function setNextOpt(opt, value = undefined, reload = true) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);
    const __CHOICE = __CONFIG.Choice;
    if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {
        return setNextOpt(opt, value, reload, onFulfilled, onRejected);
    }
     const __VALUE = getOptValue(opt, value);
     const __VALUE = getOptValue(opt, value);


     try {
     return setOpt(opt, getNextOpt(opt, __VALUE), reload);
        const __NEXTVAL = getNextValue(__CHOICE, __VALUE);
        let message = "";
 
        if (selValue) {
            for (let index = 0; index < __CHOICE.length; index++) {
                message += (index + 1) + ") " + __CHOICE[index] + '\n';
            }
            message += "\nNummer oder Wert eingeben:";
        } else {
            message = __CHOICE.join(" / ") + "\n\nWert eingeben:";
        }
 
        const __ANSWER = prompt(message, __NEXTVAL);
 
        if (__ANSWER) {
            const __INDEX = parseInt(__ANSWER, 10) - 1;
            let nextVal = (selValue ? __CHOICE[__INDEX] : undefined);
 
            if (nextVal === undefined) {
                const __VALTYPE = getValue(__CONFIG.ValType, 'String');
                const __CASTVAL = this[__VALTYPE](__ANSWER);
 
                if (freeValue || (~ __CHOICE.indexOf(__CASTVAL))) {
                    nextVal = __CASTVAL;
                }
            }
 
            if (nextVal !== __VALUE) {
                if (nextVal) {
                    return setOpt(opt, nextVal, reload, onFulfilled, onRejected);
                }
 
                const __LABEL = substParam(__CONFIG.Label, __VALUE);
 
                showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER);
            }
        }
    } catch (ex) {
        __LOG[0]("promptNextOpt: " + ex.message);
    }
 
    return __VALUE;
}
}


Zeile 2.250: Zeile 607:
// value: (Bei allen Typen) Zu setzender Wert
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// 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
// return Gesetzter Wert
function setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
function setOptByName(optSet, item, value, reload = false) {
     const __OPT = getOptByName(optSet, item);
     const __OPT = getOptByName(optSet, item);


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


// Ermittelt die naechste moegliche Option (Version mit Key)
// Ermittelt die naechste moegliche Option (Version mit Key)
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
function getNextOptByName(optSet, item, value = undefined) {
Zeile 2.271: Zeile 627:


// Setzt die naechste moegliche Option (Version mit Key)
// Setzt die naechste moegliche Option (Version mit Key)
// opt: Config und Value der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// value: Ggfs. zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// 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
// return Gesetzter Wert
function setNextOptByName(optSet, item, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
function setNextOptByName(optSet, item, value = undefined, reload = true) {
     const __OPT = getOptByName(optSet, item);
     const __OPT = getOptByName(optSet, item);


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


// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
// Baut das Benutzermenu auf
// 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
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false)
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3)
// 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 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);
}
 
// Baut das Benutzermenu auf (asynchron im Hintergrund)
// optSet: Gesetzte Optionen
// optSet: Gesetzte Optionen
// return Promise auf void
function buildMenu(optSet) {
async function buildMenu(optSet) {
     console.log("buildMenu()");
     __LOG[3]("buildMenu()");
 
    for (let opt in optSet) {
        await registerOption(optSet[opt]).then(
                result => __LOG[6](`REGISTEROPTION[${opt}] = ${result}`),
                defaultCatch);
    }
}
 
// Invalidiert eine (ueber Menu) gesetzte Option
// opt: Zu invalidierende Option
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return Promise auf resultierenden Wert
function invalidateOpt(opt, force = false) {
    return Promise.resolve(opt.Promise).then(value => {
            if (opt.Loaded && ! opt.ReadOnly) {
                const __CONFIG = getOptConfig(opt);
 
                // Wert "ungeladen"...
                opt.Loaded = (force || ! __CONFIG.AutoReset);
 
                if (opt.Loaded && __CONFIG.AutoReset) {
                    // Nur zuruecksetzen, gilt als geladen...
                    setOptValue(opt, initOptValue(__CONFIG));
                }
            }
 
            return getOptValue(opt);
        }, defaultCatch);
}


// Invalidiert die (ueber Menu) gesetzten Optionen
// optSet: Object mit den Optionen
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut
// return Promise auf Object mit den geladenen Optionen
async function invalidateOpts(optSet, force = false) {
     for (let opt in optSet) {
     for (let opt in optSet) {
         const __OPT = optSet[opt];
         registerOption(optSet[opt]);
 
        await invalidateOpt(__OPT, force);
     }
     }
    return optSet;
}
}


Zeile 2.352: Zeile 652:
// opt: Zu ladende Option
// opt: Zu ladende Option
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Promise auf gesetzten Wert der gelandenen Option
// return Gesetzter Wert der gelandenen Option
function loadOption(opt, force = false) {
function loadOption(opt, force = false) {
     if (! opt.Promise) {
     const __CONFIG = getOptConfig(opt);
        const __CONFIG = getOptConfig(opt);
        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 (! force && __CONFIG.AutoReset) {
            const __ERROR = "Error: Oprion '" + __NAME + "' bereits geladen!";
        return setOptValue(opt, initOptValue(__CONFIG));
 
    } else if (__CONFIG.Serial) {
            __LOG[0](__MESSAGE);
        return setOptValue(opt, deserialize(getOptName(opt), getOptValue(opt)));
 
    } else {
            return Promise.reject(__MESSAGE);
        return setOptValue(opt, GM_getValue(getOptName(opt), getOptValue(opt)));
        }
 
        if (opt.ReadOnly || __ISSHARED) {
            value = __DEFAULT;
        } 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 => {
                // 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
// Laedt die (ueber Menu) gesetzten Optionen
// optSet: Object mit den Optionen
// optSet: Set mit den Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Array mit Promises neuer Ladevorgaenge (fuer Objekte mit 'name' und 'value')
// return Set mit den geladenen Optionen
function loadOptions(optSet, force = false) {
function loadOptions(optSet, force = false) {
    const __PROMISES = [];
     for (let opt in optSet) {
     for (let opt in optSet) {
         const __OPT = optSet[opt];
         loadOption(optSet[opt], force);
 
        if (! __OPT.Loaded) {
            const __PROMISE = loadOption(__OPT, force).then(value => {
                    __LOG[5]("LOADED " + opt + " << " + value);
 
                    return Promise.resolve({
                            'name'  : opt,
                            'value' : value
                        });
                }, defaultCatch);
 
            __PROMISES.push(__PROMISE);
        }
     }
     }


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


Zeile 2.430: Zeile 680:
// opt: Gesetzte Option
// opt: Gesetzte Option
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option (auch fuer nicht 'AutoReset')
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Promise von GM.deleteValue() (oder void)
function deleteOption(opt, force = false, reset = true) {
function deleteOption(opt, force = false, reset = true) {
     const __CONFIG = getOptConfig(opt);
     const __CONFIG = getOptConfig(opt);


     if (force || ! __CONFIG.Permanent) {
     if (force || ! __CONFIG.Permanent) {
         const __NAME = getOptName(opt);
         GM_deleteValue(getOptName(opt));


         __LOG[4]("DELETE " + __NAME);
         if (reset) {
 
            setOptValue(opt, initOptValue(__CONFIG));
        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')
// Entfernt die (ueber Menu) gesetzten Optionen (falls nicht 'Permanent')
// optSet: Gesetzte Optionen
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Promise auf diesen Vorgang
function deleteOptions(optSet, force = false, reset = true) {
async function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) {
    const __DELETEALL = ((optSelect === undefined) || (optSelect === true));
    const __OPTSELECT = getValue(optSelect, { });
 
     for (let opt in optSet) {
     for (let opt in optSet) {
         if (getValue(__OPTSELECT[opt], __DELETEALL)) {
         deleteOption(optSet[opt], force, reset);
            await deleteOption(optSet[opt], force, reset);
        }
     }
     }
    return Promise.resolve();
}
}


// Benennt eine Option um und laedt sie ggfs. nach
// Entfernt eine (ueber Menu) gesetzte Option
// opt: Gesetzte Option
// opt: Gesetzte Option
// name: Neu zu setzender Name (Speicheradresse)
// name: Neu zu setzender Name (Speicheradresse)
// reload: Wert nachladen statt beizubehalten
// reload: Wert nachladen statt beizubehalten
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Umbenannte Option
// return Promise auf umbenannte Option
function renameOption(opt, name, reload = false) {
async function renameOption(opt, name, reload = false, force = false) {
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);


     if (__NAME !== name) {
     if (__NAME !== name) {
         await deleteOption(opt, true, ! reload);
         deleteOption(opt, true, ! reload);


         setOptName(opt, name);
         setOptName(opt, name);
        await invalidateOpt(opt, opt.Loaded);


         if (reload) {
         if (reload) {
             opt.Loaded = false;
             loadOption(opt);
 
            await loadOption(opt, force);
         }
         }
     }
     }


     return Promise.resolve(opt);
     return opt;
}
}


// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// name: Gesetzter Name (Speicheradresse)
// reload: Seite mit "Werkseinstellungen" neu laden
// prefix: Prefix, das vorangestellt werden soll
// optSet: Gesetzte Optionen
// return Neu zu setzender Name (Speicheradresse)
function resetOptions(optSet, reload = true) {
function prefixName(name, prefix) {
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
    return (prefix + name);
    deleteOptions(optSet, false, true);
 
    if (reload) {
        // ... und Seite neu laden (mit "Werkseinstellungen")...
        window.location.reload();
    }
}
}


// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// name: Gesetzter Name (Speicheradresse)
 
// postfix: Postfix, das angehaengt werden soll
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
// return Neu zu setzender Name (Speicheradresse)
const __OPTSET = { };
function postfixName(name, postfix) {
    return (name + postfix);
}


// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// Behandelt die Optionen und laedt das Benutzermenu
// optSet: Gesetzte Optionen
// optConfig: Konfiguration der Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// optSet: Platz fuer die gesetzten Optionen
// 'reload': Option nachladen?
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// renameParam: Wird an renameFun uebergeen
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// renameFun: function(name, param) zur Ermittlung des neuen Namens
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// - name: Neu zu setzender Name (Speicheradresse)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix
// 'formWidth': Anzahl der Elemente pro Zeile
// return Promise auf diesen Vorgang
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
async function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
// return Gefuelltes Objekt mit den gesetzten Optionen
    if (renameFun === undefined) {
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) {
        __LOG[0]("RENAME: Illegale Funktion!");
    optSet = initOptions(optConfig, optSet);
    }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];


        if (__OPT === undefined) {
    runStored(optSet, true);
            __LOG[0]("RENAME: Option '" + opt + "' nicht gefunden!");
    loadOptions(optSet);
        } 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);
    if (! optParams.hideMenu) {
         }
         buildMenu(optSet);
     }
     }
}


// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
    if (optParams.menuAnchor !== undefined) {
// optSet: Gesetzte Optionen
        buildForm(optParams.menuAnchor, optSet, optParams);
// reload: Seite mit "Werkseinstellungen" neu laden
    }
// return Promise auf diesen Vorgang
async function resetOptions(optSet, reload = true) {
    // Alle (nicht 'Permanent') gesetzten Optionen entfernen...
    await deleteOptions(optSet, true, false, ! reload);


     // ... und ggfs. Seite neu laden (mit "Werkseinstellungen")...
     return optSet;
    refreshPage(reload);
}
}


Zeile 2.564: Zeile 777:
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = 'hidden') {
function addInputField(form, props, type = "hidden") {
     for (let fieldName in props) {
     for (let fieldName in props) {
         let field = form[fieldName];
         let field = form[fieldName];
         if (! field) {
         if (! field) {
             field = document.createElement('input');
             field = document.createElement("input");
             field.type = type;
             field.type = type;
             field.name = fieldName;
             field.name = fieldName;
Zeile 2.584: Zeile 797:
// return Ergaenztes Form-Konstrukt
// return Ergaenztes Form-Konstrukt
function addHiddenField(form, props) {
function addHiddenField(form, props) {
     return addInputField(form, props, 'hidden');
     return addInputField(form, props, "hidden");
}
}


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// type: Name des Events, z.B. "click"
Zeile 2.597: Zeile 810:
         return obj.addEventListener(type, callback, capture);
         return obj.addEventListener(type, callback, capture);
     } else if (obj.attachEvent) {
     } else if (obj.attachEvent) {
         return obj.attachEvent('on' + type, callback);
         return obj.attachEvent("on" + type, callback);
     } else {
     } else {
         __LOG[0]("Could not add " + type + " event:");
         console.log("Could not add " + type + " event:");
         __LOG[2](callback);
         console.log(callback);


         return false;
         return false;
Zeile 2.606: Zeile 819:
}
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// type: Name des Events, z.B. "click"
Zeile 2.616: Zeile 829:
         return obj.removeEventListener(type, callback, capture);
         return obj.removeEventListener(type, callback, capture);
     } else if (obj.detachEvent) {
     } else if (obj.detachEvent) {
         return obj.detachEvent('on' + type, callback);
         return obj.detachEvent("on" + type, callback);
     } else {
     } else {
         __LOG[0]("Could not remove " + type + " event:");
         console.log("Could not remove " + type + " event:");
         __LOG[2](callback);
         console.log(callback);


         return false;
         return false;
Zeile 2.625: Zeile 838:
}
}


// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// Helferfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// id: ID des betroffenen Eingabeelements
// id: ID des betroffenen Eingabeelements
// type: Name des Events, z.B. "click"
// type: Name des Events, z.B. "click"
Zeile 2.637: Zeile 850:
}
}


// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// Helferfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// id: ID des betroffenen Eingabeelements
// id: ID des betroffenen Eingabeelements
// type: Name des Events, z.B. "click"
// type: Name des Events, z.B. "click"
Zeile 2.649: Zeile 862:
}
}


// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// Helferfunktion fuer die Ueberpruefung, ob ein Item sichtbar sein soll
// name: Name des Elements (siehe "name=")
// item: Name des betroffenen Items
// index: Laufende Nummer des Elements (0-based), Default: 0
// showList: Checkliste der sichtbaren Items (true fuer sichtbar)
// doc: Dokument (document)
// hideList: Checkliste der unsichtbaren Items (true fuer unsichtbar)
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
// return Angabe, ob das Item sichtbar sein soll
function getElement(name, index = 0, doc = document) {
function checkVisible(item, showList, hideList = undefined) {
     const __TAGS = doc.getElementsByName(name);
     let show = true;
     const __TABLE = (__TAGS ? __TAGS[index] : undefined);
 
    if (showList !== undefined) {
        show = (showList[item] === true); // gesetzt und true
     }
    if (hideList !== undefined) {
        if (hideList[item] === true) {  // gesetzt und true
            show = false; // NICHT anzeigen
        }
    }


     return __TABLE;
     return show;
}
}


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


     return __TABLE;
     return __TABLE;
}
}


// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// Helferfunktion fuer die Ermittlung der Zeilen einer Tabelle
// 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;
}
 
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// index: Laufende Nummer des Elements (0-based)
// index: Laufende Nummer des Elements (0-based)
// doc: Dokument (document)
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getRows(index, doc = document) {
function getRows(index, doc = document) {
     const __TABLE = getTable(index, 'table', doc);
     const __TABLE = getTable(index, "table", doc);
     const __ROWS = (__TABLE ? __TABLE.rows : undefined);
     const __ROWS = (__TABLE === undefined) ? undefined : __TABLE.rows;
 
    return __ROWS;
}
 
// Hilfsfunktion fuer die Ermittlung der Zeilen einer Tabelle
// id: ID des Tabellen-Elements
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getRowsById(id, doc = document) {
    const __TABLE = doc.getElementById(id);
    const __ROWS = (__TABLE ? __TABLE.rows : undefined);


     return __ROWS;
     return __ROWS;
Zeile 2.714: Zeile 910:
// value: Ggfs. zu setzender Wert
// value: Ggfs. zu setzender Wert
// serial: Serialization fuer String-Werte (Select, Textarea)
// 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
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined) {
     const __STORAGE = getMemory(memory);
    const __CONFIG = getOptConfig(opt);
     const __MEMORY = __STORAGE.Value;
    const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
     const __MEMSTR = __STORAGE.Display;
    const __NAMSTR = "'" + getOptName(opt) + "'";
     const __RUNPREFIX = __STORAGE.Prefix;
     const __THISVAL = ((__CONFIG.ValType === "String") ? "'\\x22' + this.value + '\\x22'" : "this.value");
     const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
     const __VALSTR = ((value !== undefined) ? JSON.stringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
     const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);


     if (__MEMORY !== undefined) {
     if (__ACTION !== undefined) {
         const __RELOAD = "window.location.reload()";
         switch (__ACTION) {
         const __SETITEM = function(item, val, quotes = true) {
         case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                              return "(sessionStorage.setItem('runcmd', 'SET'), sessionStorage.setItem('runkey', " + __NAMSTR + "), sessionStorage.setItem('runval', " + __VALSTR + "), window.location.reload())";
                          };
         case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                              return "(sessionStorage.setItem('runcmd', 'NXT'), sessionStorage.setItem('runkey', " + __NAMSTR + "), sessionStorage.setItem('runval', " + __VALSTR + "), window.location.reload())";
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
        case __OPTACTION.RST : //return "doActionRst()";
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                              return "(sessionStorage.setItem('runcmd', 'RST'), window.location.reload())";
                          };
        default :              break;
        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;
            }
         }
         }
     }
     }
Zeile 2.760: Zeile 941:
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// serial: Serialization fuer String-Werte (Select, Textarea)
// 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
// return String mit dem (reinen) Funktionsaufruf
function getFormActionEvent(opt, isAlt = false, value = undefined, type = 'click', serial = undefined, memory = undefined) {
function getFormActionEvent(opt, isAlt = false, value = undefined, type = "click", serial = undefined) {
     const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
     const __ACTION = getFormAction(opt, isAlt, value, serial);


     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
     return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
}
// Hilfsfunktion: Wendet eine Konvertierung auf jede "Zeile" innerhalb eines Textes an
// 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);
}
// Hilfsfunktion: Ergaenzt einen HTML-Code um einen Titel (ToolTip)
// html: Urspruenglicher HTML-Code (z.B. ein HTML-Element oder Text)
// 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)
// label: Config-Eintrag fuer Label oder FormLabel
// defLabel: Ersatzwert, falls label nicht angegeben
// 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)) {
        return __LABEL + (isForm ? "|$" : " $");
    } else {
        return __LABEL;
    }
}
}


Zeile 2.819: Zeile 955:
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt);
     const __VALUE = getOptValue(opt);
     const __ACTION = getFormActionEvent(opt, false, undefined, 'change', undefined);
     const __ACTION = getFormActionEvent(opt, false, undefined, "change", undefined);
     const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label, true);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
    const __TITLE = substParam(getValue(__CONFIG.Title, __CONFIG.Label), __VALUE);
     const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';
     let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';


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


     return withTitle(substParam(__LABEL, element), __TITLE);
     return __LABEL.replace('$', element);
}
}


Zeile 2.845: Zeile 977:
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt, false);
     const __VALUE = getOptValue(opt, false);
     const __ACTION = getFormActionEvent(opt, false, true, 'click', false);
     const __ACTION = getFormActionEvent(opt, false, true, "click", false);
     const __ALTACTION = getFormActionEvent(opt, true, false, 'click', false);
     const __ALTACTION = getFormActionEvent(opt, true, false, "click", false);
    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 +
     const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                         '" id="' + __NAME + 'ON" value="1"' +
                         '" id="' + __NAME + 'ON" value="1"' +
Zeile 2.861: Zeile 989:
                         ' /><label for="' + __NAME + 'OFF">' +
                         ' /><label for="' + __NAME + 'OFF">' +
                         __CONFIG.AltLabel + '</label>';
                         __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 [ __ELEMENTON, __ELEMENTOFF ];
}
}


Zeile 2.877: Zeile 1.000:
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt, false);
     const __VALUE = getOptValue(opt, false);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
     const __VALUELABEL = (__VALUE ? __CONFIG.Label : getValue(__CONFIG.AltLabel, __CONFIG.Label));
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __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 +
     return '<input type="checkbox" name="' + __NAME +
                    '" id="' + __NAME + '" value="' + __VALUE + '"' +
          '" id="' + __NAME + '" value="' + __VALUE + '"' +
                    (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
          (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
                    __NAME + '">' + __FORMLABEL + '</label>', __TITLE);
          __NAME + '">' + __FORMLABEL + '</label>';
}
}


Zeile 2.895: Zeile 1.016:
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt);
     const __VALUE = getOptValue(opt);
     const __ACTION = getFormActionEvent(opt, false, undefined, 'submit', undefined);
     const __ACTION = getFormActionEvent(opt, false, undefined, "submit", undefined);
     const __SUBMIT = getValue(__CONFIG.Submit, "");
     const __SUBMIT = getValue(__CONFIG.Submit, "");
     //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");
     const __ONSUBMIT = ((__SUBMIT.length > 0) ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
     const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
    const __TITLE = substParam(getValue(__CONFIG.Title, '$'), __FORMLABEL);
     const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
     const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
     const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                           '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                           '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                           safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';
                           JSON.stringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';


     return [ withTitle(__ELEMENTLABEL, __TITLE), __ELEMENTTEXT ];
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];
}
}


Zeile 2.916: Zeile 1.035:
     const __NAME = getOptName(opt);
     const __NAME = getOptName(opt);
     const __VALUE = getOptValue(opt, false);
     const __VALUE = getOptValue(opt, false);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, "click", false);
     const __BUTTONLABEL = (__VALUE ? getValue(__CONFIG.AltLabel, __CONFIG.Label) : __CONFIG.Label);
     const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);
     const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __BUTTONLABEL);
     const __FORMLABEL = getValue(__CONFIG.FormLabel, __CONFIG.Label);
    const __BUTTONTITLE = substParam(getValue(__VALUE ? getValue(__CONFIG.AltTitle, __CONFIG.Title) : __CONFIG.Title, '$'), __BUTTONLABEL);


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


Zeile 2.954: Zeile 1.072:
         }
         }


         if ((typeof element) !== 'string') {
         if (element.length === 2) {
             element = '<div>' + Array.from(element).join('<br />') + '</div>';
             element = '<div>' + element[0] + '<br />' + element[1] + '</div>';
         }
         }
     }
     }


     return element;
     return element;
}
// Gruppiert die Daten eines Objects nach einem Kriterium
// data: Object mit Daten
// byFun: function(val), die das Kriterium ermittelt. Default: value
// 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()
// return Neues Object mit Eintraegen der Form <Kriterium> : [ <alle Keys zu diesem Kriterium> ]
function groupData(data, byFun, filterFun, sortFun) {
    const __BYFUN = (byFun || (val => val));
    const __FILTERFUN = (filterFun || ((key, index, arr) => (arr[index] === key)));
    const __KEYS = Object.keys(data);
    const __VALS = Object.values(data);
    const __BYKEYS = __VALS.map(__BYFUN);
    const __BYKEYSET = new Set(__BYKEYS);
    const __BYKEYARRAY = [...__BYKEYSET];
    const __SORTEDKEYS = __BYKEYARRAY.sort(sortFun);
    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] }))));
    return __ASSIGN(__SORTEDKEYS, __GROUPEDKEYS);
}
}


Zeile 2.997: Zeile 1.094:
     const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
     const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
     const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
     const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    const __PRIOOPTS = groupData(optSet, opt => getOptConfig(opt).FormPrio);
     let form = __FORM;
     let form = __FORM;
     let count = 0;  // Bisher angezeigte Optionen
     let count = 0;  // Bisher angezeigte Optionen
     let column = 0;  // Spalte der letzten Option (1-basierend)
     let column = 0;  // Spalte der letzten Option (1-basierend)


     for (let optKeys of Object.values(__PRIOOPTS)) {
     for (let opt in optSet) {
         for (let optKey of optKeys) {
         if (checkVisible(opt, __SHOWFORM, optParams.hideForm)) {
            if (checkItem(optKey, __SHOWFORM, optParams.hideForm)) {
            const __ELEMENT = getOptionElement(optSet[opt]);
                const __ELEMENT = getOptionElement(optSet[optKey]);
            const __TDOPT = (__ELEMENT.indexOf('|') < 0) ? ' colspan="2"' : "";
                const __TDOPT = ((~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"');


                if (__ELEMENT) {
            if (__ELEMENT.length > 0) {
                    if (++count > __FORMBREAK) {
                if (++count > __FORMBREAK) {
                        if (++column > __FORMWIDTH) {
                    if (++column > __FORMWIDTH) {
                            column = 1;
                        column = 1;
                        }
                     }
                     }
                    if (column === 1) {
                        form += '</tr><tr>';
                    }
                    form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
                 }
                 }
                if (column === 1) {
                    form += '</tr><tr>';
                }
                form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
             }
             }
         }
         }
Zeile 3.053: Zeile 1.147:
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function buildForm(anchor, optSet, optParams = { }) {
function buildForm(anchor, optSet, optParams = { }) {
     __LOG[3]("buildForm()");
     console.log("buildForm()");


     const __FORM = getForm(optSet, optParams);
     const __FORM = getForm(optSet, optParams);
Zeile 3.073: Zeile 1.167:
                   anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
                   anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);


     __FORMS[anchor] = {
     __FORMS[anchor] = { 'Script' : script, 'Form' : form };
                          'Script' : script,
                          'Form'   : form
                      };


     anchor.innerHTML = __REST + script + form;
     anchor.innerHTML = __REST + script + form;
}
// ==================== Abschnitt fuer Klasse Classification ====================
// Basisklasse fuer eine Klassifikation der Optionen nach Kriterium (z.B. Erst- und Zweitteam oder Fremdteam)
function Classification() {
    'use strict';
    this.renameFun = prefixName;
    //this.renameParamFun = undefined;
    this.optSet = undefined;
    this.optSelect = { };
}
Class.define(Classification, Object, {
                    'renameOptions' : function() {
                                          const __PARAM = this.renameParamFun();
                                          if (__PARAM !== undefined) {
                                              // Klassifizierte Optionen umbenennen...
                                              return renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun);
                                          } else {
                                              return Promise.resolve();
                                          }
                                      },
                    'deleteOptions' : function(ignList) {
                                          const __OPTSELECT = addProps([], this.optSelect, null, ignList);
                                          return deleteOptions(this.optSet, __OPTSELECT, true, true);
                                      }
                });
// ==================== Ende Abschnitt fuer Klasse Classification ====================
// ==================== Abschnitt fuer Klasse TeamClassification ====================
// Klasse fuer die Klassifikation der Optionen nach Team (Erst- und Zweitteam oder Fremdteam)
function TeamClassification() {
    'use strict';
    Classification.call(this);
    this.team = undefined;
    this.teamParams = undefined;
}
Class.define(TeamClassification, Classification, {
                    'renameParamFun' : function() {
                                          const __MYTEAM = (this.team = getMyTeam(this.optSet, this.teamParams, this.team));
                                          if (__MYTEAM.LdNr) {
                                              // Prefix fuer die Optionen mit gesonderten Behandlung...
                                              return __MYTEAM.LdNr.toString() + '.' + __MYTEAM.LgNr.toString() + ':';
                                          } else {
                                              return undefined;
                                          }
                                      }
                });
// ==================== Ende Abschnitt fuer Klasse TeamClassification ====================
// ==================== Abschnitt fuer Klasse Team ====================
// Klasse fuer Teamdaten
function Team(team, land, liga) {
    'use strict';
    this.Team = team;
    this.Land = land;
    this.Liga = liga;
    this.LdNr = getLandNr(land);
    this.LgNr = getLigaNr(liga);
}
Class.define(Team, Object, {
                    '__TEAMITEMS' : {  // Items, die in Team als Teamdaten gesetzt werden...
                                        'Team' : true,
                                        'Liga' : true,
                                        'Land' : true,
                                        'LdNr' : true,
                                        'LgNr' : true
                                    }
                });
// ==================== Ende Abschnitt fuer Klasse Team ====================
// ==================== Abschnitt fuer Klasse Verein ====================
// Klasse fuer Vereinsdaten
function Verein(team, land, liga, id, manager, flags) {
    'use strict';
    Team.call(this, team, land, liga);
    this.ID = id;
    this.Manager = manager;
    this.Flags = (flags || []);
}
Class.define(Verein, Team, {
                    '__TEAMITEMS' : {  // Items, die in Verein als Teamdaten gesetzt werden...
                                        'Team'    : true,
                                        'Liga'    : true,
                                        'Land'    : true,
                                        'LdNr'    : true,
                                        'LgNr'    : true,
                                        'ID'      : true,
                                        'Manager' : true,
                                        'Flags'  : true
                                    }
                });
// ==================== Ende Abschnitt fuer Klasse Verein ====================
// ==================== Spezialisierter Abschnitt fuer Optionen ====================
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt):
const __OPTSET = { };
// Teamparameter fuer getrennte Speicherung der Optionen fuer Erst- und Zweitteam...
const __TEAMCLASS = new TeamClassification();
// Optionen mit Daten, die ZAT- und Team-bezogen gemerkt werden...
__TEAMCLASS.optSelect = { };
// 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, falls erweunscht...
        if (optSet && optSet.team) {
            setOpt(optSet.team, myTeam, false);
        }
    } else {
        const __TEAM = ((optSet && optSet.team) ? getOptValue(optSet.team) : undefined);  // Gespeicherte Parameter
        if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) {
            addProps(myTeam, __TEAM, myTeam.__TEAMITEMS);
            __LOG[2]("Gespeichert: " + safeStringify(myTeam));
        } else {
            __LOG[6]("Team nicht ermittelt: " + safeStringify(__TEAM));
        }
    }
    return myTeam;
}
// Behandelt die Optionen und laedt das Benutzermenu
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// optParams: Eventuell notwendige Parameter zur Initialisierung
// 'hideMenu': Optionen werden zwar geladen und genutzt, tauchen aber nicht im Benutzermenu auf
// 'teamParams': Getrennte Daten-Option wird genutzt, hier: Team() mit 'LdNr'/'LgNr' des Erst- bzw. Zweitteams
// 'menuAnchor': Startpunkt fuer das Optionsmenu auf der Seite
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return 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 => showOptions(optSet, optParams),
                defaultCatch);
}
}


// ==================== Ende Abschnitt fuer Optionen ====================
// ==================== Ende Abschnitt fuer Optionen ====================
// ==================== 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;
}


// ==================== Hauptprogramm ====================
// ==================== Hauptprogramm ====================
Zeile 3.487: Zeile 1.178:
// Verarbeitet eine Ergebnis-Ansicht
// Verarbeitet eine Ergebnis-Ansicht
function procErgebnisse() {
function procErgebnisse() {
     return buildOptions(__OPTCONFIG, __OPTSET, {
     buildOptions(__OPTCONFIG, __OPTSET, {
                            'menuAnchor' : getTable(0, 'div'),
                    'menuAnchor' : getTable(0, "div")
                            'formWidth'  : 2
                });
                        }).then(optSet => {
 
            // Aktiviere Checkbox "Ergebnisse anzeigen" je nach Einstellung der Option
    // Aktiviere Checkbox "Ergebnisse anzeigen" je nach Einstellung der Option
            getElement("erganzeigen").checked = getOptValue(optSet.showErgs);
    getTable(0, "input").checked = getOptValue(__OPTSET.showErgs);
        });
}
}


(() => {
procErgebnisse();
    (async () => {
        try {
            await procErgebnisse().catch(defaultCatch);


            return 'OK';
console.log("SCRIPT END");
        } 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)