OS2.ergebnisse: Unterschied zwischen den Versionen
		
		
		
		
		
		Zur Navigation springen
		Zur Suche springen
		
				
		
		
	
SLC (Diskussion | Beiträge)  (Update Version 0.21 (Speicherfunktionen, Land- und Liga-Nummern))  | 
				SLC (Diskussion | Beiträge)   (Vorab-Version 0.32 (ECMAScript6-Fehler behoben). Geeignet für Greasemonkey 4.0/Firefox 57.0 Quantum)  | 
				||
| (4 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| 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'''  | ||
|- bgcolor="#FFCC00"  | |- bgcolor="#FFCC00"  | ||
| '''Version'''  | | '''Version'''  | ||
| '''0.  | | '''0.32 (WebExtensions)'''  | ||
|- bgcolor="#FFCC00"  | |- bgcolor="#FFCC00"  | ||
| '''Autor'''  | | '''Autor'''  | ||
| Zeile 19: | Zeile 20: | ||
|- 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'''  | | '''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'''  | ||
|- bgcolor="#FFCC00"  | |- bgcolor="#FFCC00"  | ||
| '''Letzte Änderung'''  | | '''Letzte Änderung'''  | ||
| Zeile 25: | Zeile 26: | ||
|- 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   | // @name         OS2.ergebnisse  | ||
// @namespace   | // @namespace    http://os.ongapo.com/  | ||
// @version   | // @version      0.32+WE+  | ||
// @copyright   | // @copyright    2016+  | ||
// @author   | // @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   | // @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+)*)?$/  | ||
// @grant        GM.getValue  | |||
// @grant        GM.setValue  | |||
// @grant        GM.deleteValue  | |||
// @grant        GM.registerMenuCommand  | |||
// @grant        GM.info  | |||
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js  | |||
// @grant        GM_getValue  | |||
// @grant        GM_setValue  | |||
// @grant        GM_deleteValue  | |||
// @grant        GM_registerMenuCommand  | |||
// @grant        GM_info  | |||
// @  | |||
// @  | |||
// @  | |||
// @  | |||
// @  | |||
// @  | |||
/  | |||
// @  | |||
// @grant   | |||
// @grant   | |||
// @grant   | |||
// @grant   | |||
// ==/UserScript==  | // ==/UserScript==  | ||
// ECMAScript 6:   | // ECMAScript 6:  | ||
/* 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 135: | Zeile 76: | ||
const __OPTMEM = {  | const __OPTMEM = {  | ||
     'normal' : {  |      'normal' : {  | ||
                   'Name'      : "Browser",  | |||
                   'Value'     : localStorage,  | |||
                   'Display'   : "localStorage",  | |||
                   'Prefix'    : 'run'  | |||
               },  | |||
    'begrenzt' : {  | |||
                    'Name'      : "Session",  |                     'Name'      : "Session",  | ||
                    'Value'     : sessionStorage,  |                     'Value'     : sessionStorage,  | ||
                    'Display'   : "sessionStorage",  |                     'Display'   : "sessionStorage",  | ||
                    'Prefix'    : 'run'  |                     'Prefix'    : 'run'  | ||
                },  |                 },  | ||
| Zeile 153: | Zeile 94: | ||
                }  |                 }  | ||
};  | };  | ||
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):  | // Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):  | ||
| Zeile 170: | Zeile 109: | ||
                },  |                 },  | ||
     '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 178: | Zeile 118: | ||
                },  |                 },  | ||
     'storage' : {         // Browserspeicher fuer die Klicks auf Optionen  |      'storage' : {         // Browserspeicher fuer die Klicks auf Optionen  | ||
                   'FormPrio'  : undefined,  | |||
                    'Name'      : "storage",  |                     'Name'      : "storage",  | ||
                    'Type'      : __OPTTYPES.MC,  |                     'Type'      : __OPTTYPES.MC,  | ||
                    'ValType'   :   |                     'ValType'   : 'String',  | ||
                    'Choice'    : Object.keys(__OPTMEM),  |                     'Choice'    : Object.keys(__OPTMEM),  | ||
                    'Action'    : __OPTACTION.NXT,  |                     'Action'    : __OPTACTION.NXT,  | ||
| Zeile 188: | Zeile 129: | ||
                },  |                 },  | ||
     'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen  |      'oldStorage' : {      // Vorheriger Browserspeicher fuer die Klicks auf Optionen  | ||
                   'FormPrio'  : undefined,  | |||
                    'Name'      : "oldStorage",  |                     'Name'      : "oldStorage",  | ||
                    'Type'      : __OPTTYPES.SD,  |                     'Type'      : __OPTTYPES.SD,  | ||
                   'PreInit'   : true,  | |||
                    'AutoReset' : true,  |                     'AutoReset' : true,  | ||
                    'Hidden'    : 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 199: | Zeile 143: | ||
                    '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 209: | Zeile 155: | ||
// ==================== 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, wird ein Alternativwert geliefert  | // Gibt einen Wert zurueck. Ist dieser nicht definiert oder null, wird ein Alternativwert geliefert  | ||
// value: Ein Wert. Ist dieser nicht undefined, wird er zurueckgeliefert  | // value: Ein Wert. Ist dieser nicht undefined oder null, wird er zurueckgeliefert (oder retValue)  | ||
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist  | // defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist  | ||
// retValue: Falls definiert, Rueckgabe-Wert fuer den Fall, dass value nicht undefined ist  | // retValue: Falls definiert, Rueckgabe-Wert fuer den Fall, dass value nicht undefined oder null ist  | ||
// return Der Wert. Sind weder value noch defValue definiert, dann undefined  | // return Der Wert. Sind weder value noch defValue definiert, dann undefined  | ||
function getValue(value, defValue = undefined, retValue = undefined) {  | function getValue(value, defValue = undefined, retValue = undefined) {  | ||
     return (value === undefined) ? defValue : (retValue === undefined) ? value : retValue;  |      return ((value === undefined) || (value === null)) ? defValue : (retValue === undefined) ? value : retValue;  | ||
}  | }  | ||
| Zeile 248: | Zeile 912: | ||
     return arr[getValueIn(__POS, 0, arr.length - 1, 0)];  |      return arr[getValueIn(__POS, 0, arr.length - 1, 0)];  | ||
}  | |||
// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert  | |||
// valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert  | |||
// valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert  | |||
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)  | |||
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)  | |||
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue  | |||
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {  | |||
    let product = defValue;  | |||
    if ((valueA !== undefined) && (valueB !== undefined)) {  | |||
        product = parseFloat(valueA) * parseFloat(valueB);  | |||
    }  | |||
    if (isNaN(product)) {  | |||
        product = defValue;  | |||
    }  | |||
    return parseFloat(product.toFixed(digits));  | |||
}  | |||
// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck  | |||
// value: Eine ganze Zahl  | |||
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')  | |||
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben  | |||
function getOrdinal(value, defValue = '*') {  | |||
    return getValue(value, defValue, value + '.');  | |||
}  | |||
// 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)  | |||
// 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;  | |||
}  | |||
// Liefert alle Basisklassen des Objekts (inkl. Vererbung)  | |||
// obj: Ein (generisches) Objekt  | |||
// 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;  | |||
}  | |||
// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)  | |||
// obj: Ein (generisches) Objekt  | |||
// return Array von Items (Property-Namen)  | |||
function getAllProperties(obj) {  | |||
    let ret = [];  | |||
    for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {  | |||
      ret = ret.concat(Object.getOwnPropertyNames(o));  | |||
    }  | |||
    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 {  | |||
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:  | |||
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:  | |||
        const __TEMP = reverseString(numberString);  | |||
        let result = "";  | |||
        for (let i = 0; i < __TEMP.length; i++) {  | |||
            if ((i > 0) && (i % 3 === 0)) {  | |||
                result += '.';  | |||
            }  | |||
            result += __TEMP.substr(i, 1);  | |||
        }  | |||
        return reverseString(result);  | |||
    }  | |||
}  | |||
// Dreht den uebergebenen String um  | |||
// 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  | // Speichert einen beliebiegen (strukturierten) Wert unter einem Namen ab  | ||
// name:   | // name: GM.setValue()-Name, unter dem die Daten gespeichert werden  | ||
// value: Beliebiger (strukturierter) Wert  | // value: Beliebiger (strukturierter) Wert  | ||
// return String-Darstellung des Wertes  | // return Promise auf ein Objekt, das 'name' und 'value' in der String-Darstellung des Wertes enthaelt  | ||
function serialize(name, value) {  | function serialize(name, value) {  | ||
     const __STREAM = (value !== undefined) ?   |      const __STREAM = ((value !== undefined) ? safeStringify(value) : value);  | ||
     return storeValue(name, __STREAM);  | |||
}  | }  | ||
// Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck  | // Holt einen beliebiegen (strukturierter) Wert unter einem Namen zurueck  | ||
// name:   | // name: GM.getValue()-Name, unter dem die Daten gespeichert wurden  | ||
// defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist  | // defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist  | ||
// return Objekt, das unter dem Namen gespeichert war  | // return Promise fuer das Objekt, das unter dem Namen gespeichert war  | ||
function deserialize(name, defValue = undefined) {  | 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  | |||
     if (  | function refreshPage(reload = true) {  | ||
     if (reload) {  | |||
         __LOG[2]("Seite wird neu geladen...");  | |||
         window.location.reload();  | |||
     }  |      }  | ||
}  | }  | ||
| Zeile 288: | Zeile 1.292: | ||
// value: Zu setzender Wert  | // value: Zu setzender 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 setStored(name, value, reload =   | 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 308: | Zeile 1.310: | ||
// 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 =   | function setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {  | ||
     return setStored(name, getNextValue(arr, value), reload, serial  |      return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);  | ||
}  | }  | ||
// 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  | ||
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined  | |||
function   | function getStoredCmds(memory = undefined) {  | ||
     const __STORAGE =   |      const __STORAGE = getMemory(memory);  | ||
     const __MEMORY = __STORAGE.Value;  |      const __MEMORY = __STORAGE.Value;  | ||
     const __RUNPREFIX = __STORAGE.Prefix;  |      const __RUNPREFIX = __STORAGE.Prefix;  | ||
    const __STOREDCMDS = [];  | |||
     if (__MEMORY !== undefined) {  |      if (__MEMORY !== undefined) {  | ||
| Zeile 358: | Zeile 1.343: | ||
                 value = JSON.parse(value);  |                  value = JSON.parse(value);  | ||
             } catch (ex) {  |              } catch (ex) {  | ||
                 __LOG[0]("getStoredCmds(): " + __CMD + " '" + __KEY + "' hat illegalen Wert '" + value + "'");  | |||
                 // ... meist kann man den String selber aber speichern, daher kein "return"...  |                  // ... meist kann man den String selber aber speichern, daher kein "return"...  | ||
             }  |              }  | ||
             __STOREDCMDS.push({  | |||
                                'cmd' : __CMD,  | |||
                                'key' : __KEY,  | |||
                                'val' : value  | |||
                            });  | |||
        }  | |||
        __DELITEM('cmd');  | |||
        __DELITEM('key');  | |||
        __DELITEM('val');  | |||
    }  | |||
    return (__STOREDCMDS.length ? __STOREDCMDS : undefined);  | |||
}  | |||
// Fuehrt die in einem Storage gespeicherte Operation aus  | |||
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())  | |||
// optSet: Object mit den Optionen  | |||
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird  | |||
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)  | |||
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)  | |||
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben  | |||
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {  | |||
    const __BEFORELOAD = getValue(beforeLoad, true);  | |||
    const __STOREDCMDS = getValue(storedCmds, []);  | |||
    const __LOADEDCMDS = [];  | |||
    let invalidated = false;  | |||
    while (__STOREDCMDS.length) {  | |||
        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]) {  |              switch (__OPTACTION[__CMD]) {  | ||
             case __OPTACTION.SET :   |              case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL);  | ||
                                    setStored(__KEY, __VAL, false, false);  |                                     setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);  | ||
                                    break;  |                                     break;  | ||
             case __OPTACTION.NXT :   |              case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL);  | ||
                                    //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false);  |                                     //setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false, onFulfilled, onRejected);  | ||
                                    setStored(__KEY, __VAL, false, false);  |                                     setStored(__KEY, __VAL, false, false, onFulfilled, onRejected);  | ||
                                    break;  |                                     break;  | ||
             case __OPTACTION.RST :   |              case __OPTACTION.RST : __LOG[4]("RESET (delayed)");  | ||
                                    resetOptions(optSet, false);  |                                    __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;  |                                     break;  | ||
             default :              break;  |              default :              break;  | ||
             }  |              }  | ||
         }  |          }  | ||
    }  | |||
    return (__LOADEDCMDS.length ? __LOADEDCMDS : undefined);  | |||
}  | }  | ||
| Zeile 420: | Zeile 1.451: | ||
// return Gesetzter Name der Option  | // return Gesetzter Name der Option  | ||
function setOptName(opt, name) {  | function setOptName(opt, name) {  | ||
     const __CONFIG = getOptConfig(opt);  | |||
    const __NAME = getOptName(opt);  | |||
    if (__NAME !== name) {  | |||
        __LOG[4]("RENAME " + __NAME + " => " + name);  | |||
        __CONFIG.Name = name;  | |||
    }  | |||
    return name;  | |||
}  | }  | ||
| Zeile 427: | Zeile 1.467: | ||
// return Name der Option  | // return Name der Option  | ||
function getOptName(opt) {  | function getOptName(opt) {  | ||
     const __CONFIG = getOptConfig(opt);  | |||
    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 435: | Zeile 1.490: | ||
// return Gesetzter Wert  | // return Gesetzter Wert  | ||
function setOptValue(opt, value) {  | function setOptValue(opt, value) {  | ||
     if (opt !== undefined) {  | |||
        if (! opt.ReadOnly) {  | |||
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));  | |||
            opt.Value = value;  | |||
        }  | |||
        return opt.Value;  | |||
    } else {  | |||
        return undefined;  | |||
    }  | |||
}  | }  | ||
| Zeile 441: | Zeile 1.505: | ||
// 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) {  | function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {  | ||
     let value;  | |||
    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  | ||
// menuOn: Text zum Setzen im Menu  | // menuOn: Text zum Setzen im Menu  | ||
// funOn: Funktion zum Setzen  | // funOn: Funktion zum Setzen  | ||
| Zeile 458: | Zeile 1.788: | ||
// funOff: Funktion zum Ausschalten  | // funOff: Funktion zum Ausschalten  | ||
// keyOff: Hotkey zum Ausschalten im Menu  | // keyOff: Hotkey zum Ausschalten im Menu  | ||
function registerMenuOption(  | // return Promise von GM.registerMenuCommand()  | ||
     const __ON  = (  | function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {  | ||
     const __OFF = (  |      const __ON  = (val ? '*' : "");  | ||
     const __OFF = (val ? "" : '*');  | |||
     __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);  | |||
     if (  |      if (val) {  | ||
         return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);  | |||
     } else {  |      } else {  | ||
         return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);  | |||
     }  |      }  | ||
}  | }  | ||
// 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  | ||
// 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  | ||
function registerNextMenuOption(  | // return Promise von GM.registerMenuCommand()  | ||
     const __MENU = menu  | function registerNextMenuOption(val, arr, menu, fun, key) {  | ||
     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 ===   |          if (value === val) {  | ||
             options += " / *" + value + '*';  |              options += " / *" + value + '*';  | ||
         } else {  |          } else {  | ||
| Zeile 488: | Zeile 1.820: | ||
         }  |          }  | ||
     }  |      }  | ||
     __LOG[3](options);  | |||
     return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);  | |||
}  | }  | ||
// 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  | ||
// 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 (  | // hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)  | ||
// serial: Serialization fuer komplexe Daten  | // serial: Serialization fuer komplexe Daten  | ||
function registerDataOption(  | // return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)  | ||
     const __VALUE = ((serial && (  | function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {  | ||
     const __MENU =   |      const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);  | ||
     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);  | |||
     if (  |      if (hidden) {  | ||
         return Promise.resolve(__VALUE);  | |||
    } else {  | |||
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);  | |||
     }  |      }  | ||
}  | }  | ||
| Zeile 515: | Zeile 1.850: | ||
// 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 : registerNextMenuOption(  |          case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);  | ||
         case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,  | |||
                                                       __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);  | |||
         case __OPTTYPES.SW : registerMenuOption(  |          case __OPTTYPES.TF : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,  | ||
                                                       __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);  | |||
         case __OPTTYPES.SD : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);  | |||
         case __OPTTYPES.TF : registerMenuOption(  |          case __OPTTYPES.SI : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);  | ||
         default :            return Promise.resolve(__VALUE);  | |||
         case __OPTTYPES.SD : registerDataOption(  | |||
         case __OPTTYPES.SI : registerDataOption(  | |||
         default :              | |||
         }  |          }  | ||
     } else {  |      } else {  | ||
         // Nur Anzeige im Log...  |          // Nur Anzeige im Log...  | ||
         registerDataOption(  |          return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);  | ||
     }  |      }  | ||
}  | }  | ||
| Zeile 547: | Zeile 1.881: | ||
// 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) {  | function initOptValue(config, setValue = undefined) {  | ||
     let value = config.Default;  // Standard  |      let value = getValue(setValue, config.Default);  // Standard  | ||
    if (config.SharedData !== undefined) {  | |||
        value = config.SharedData;  | |||
    }  | |||
     switch (config.Type) {  |      switch (config.Type) {  | ||
| Zeile 575: | Zeile 1.914: | ||
// 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) {  | function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {  | ||
     let fun;  | |||
     if (optAction !== undefined) {  |      if (optAction !== undefined) {  | ||
         const __CONFIG = getOptConfig(getOptByName(optSet, item));  |          const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));  | ||
         const __RELOAD = ((__CONFIG   |          const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);  | ||
         switch (optAction) {  |          switch (optAction) {  | ||
         case __OPTACTION.SET : fun = function() {  |          case __OPTACTION.SET : fun = function() {  | ||
                                        return setOptByName(optSet, item,   |                                         return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);  | ||
                                    };  |                                     };  | ||
                                break;  |                                 break;  | ||
         case __OPTACTION.NXT : fun = function() {  |          case __OPTACTION.NXT : fun = function() {  | ||
                                        return   |                                         return promptNextOptByName(optSet, item, __CONFIG.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);  |                                         return resetOptions(optSet, __RELOAD).then(  | ||
                                               result => __LOG[3]("RESETTING (" + result + ")..."),  | |||
                                               defaultCatch);  | |||
                                    };  |                                     };  | ||
                                break;  |                                 break;  | ||
| Zeile 601: | Zeile 1.944: | ||
     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 606: | Zeile 2.006: | ||
// 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) {  | function initOptions(optConfig, optSet = undefined, preInit = undefined) {  | ||
     let value;  | |||
     if (optSet === undefined) {  |      if (optSet === undefined) {  | ||
| Zeile 615: | Zeile 2.016: | ||
     for (let opt in optConfig) {  |      for (let opt in optConfig) {  | ||
         const   |          const __OPTCONFIG = optConfig[opt];  | ||
         const   |          const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);  | ||
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);  | |||
         optSet[opt] = {  |          if ((preInit === undefined) || (__PREINIT === preInit)) {  | ||
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);  | |||
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);  | |||
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?  | |||
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);  | |||
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);  | |||
         };  |             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;  |      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  | // Initialisiert die gesetzten Optionen und den Speicher und laedt die Optionen zum Start  | ||
// optConfig: Konfiguration der Optionen  | // optConfig: Konfiguration der Optionen  | ||
// optSet: Platz fuer die gesetzten Optionen  | // optSet: Platz fuer die gesetzten Optionen  | ||
// return   | // return Promise auf gefuelltes Objekt mit den gesetzten Optionen  | ||
function startOptions(optConfig, optSet = undefined) {  | 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  | |||
     const   | |||
     // Bisher noch nicht geladenene Optionen laden...  | |||
     loadOptions(optSet);  |      await loadOptions(optSet);  | ||
     // Memory Storage fuer naechste Speicherung...  |      // 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;  |      return optSet;  | ||
| Zeile 671: | Zeile 2.124: | ||
// 'formWidth': Anzahl der Elemente pro Zeile  | // 'formWidth': Anzahl der Elemente pro Zeile  | ||
// 'formBreak': Elementnummer des ersten Zeilenumbruchs  | // 'formBreak': Elementnummer des ersten Zeilenumbruchs  | ||
// return Liefert die gesetzten Optionen zurueck  | |||
function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {  | function showOptions(optSet = undefined, optParams = { 'hideMenu' : false }) {  | ||
     if (! optParams.hideMenu) {  |      if (! optParams.hideMenu) {  | ||
         buildMenu(optSet);  |          buildMenu(optSet).then(() => __LOG[3]("Menu OK"));  | ||
     }  |      }  | ||
     if ((optParams.menuAnchor !== undefined) && (myOptMem !==   |      if ((optParams.menuAnchor !== undefined) && (myOptMem !== __OPTMEMINAKTIVE)) {  | ||
         buildForm(optParams.menuAnchor, optSet, optParams);  |          buildForm(optParams.menuAnchor, optSet, optParams);  | ||
     }  |      }  | ||
    return optSet;  | |||
}  | }  | ||
| Zeile 686: | Zeile 2.142: | ||
// 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) {  | function setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {  | ||
     return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial));  |      return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));  | ||
}  | }  | ||
| Zeile 715: | Zeile 2.173: | ||
// 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   | function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {  | ||
     const __CONFIG = getOptConfig(opt);  |      const __CONFIG = getOptConfig(opt);  | ||
    const __CHOICE = __CONFIG.Choice;  | |||
     return   |      if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) {  | ||
        return setNextOpt(opt, value, reload, onFulfilled, onRejected);  | |||
    }  | |||
    const __VALUE = getOptValue(opt, value);  | |||
    try {  | |||
        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 728: | Zeile 2.250: | ||
// 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) {  | function setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {  | ||
     const __OPT = getOptByName(optSet, item);  |      const __OPT = getOptByName(optSet, item);  | ||
     return setOpt(__OPT, value, reload);  |      return setOpt(__OPT, value, reload, onFulfilled, onRejected);  | ||
}  | }  | ||
// Ermittelt die naechste moegliche Option (Version mit Key)  | // Ermittelt die naechste moegliche Option (Version mit Key)  | ||
// 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:   | // value: Default fuer ggfs. zu setzenden Wert  | ||
// return Zu setzender Wert  | // return Zu setzender Wert  | ||
function getNextOptByName(optSet, item, value = undefined) {  | function getNextOptByName(optSet, item, value = undefined) {  | ||
| Zeile 748: | Zeile 2.271: | ||
// Setzt die naechste moegliche Option (Version mit Key)  | // Setzt die naechste moegliche Option (Version mit Key)  | ||
// 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:   | // value: Default fuer ggfs. zu setzenden Wert  | ||
// reload: Seite mit neuem Wert neu laden  | |||
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)  | |||
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)  | |||
// return Gesetzter Wert  | |||
function setNextOptByName(optSet, item, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {  | |||
    const __OPT = getOptByName(optSet, item);  | |||
    return setNextOpt(__OPT, value, reload, onFulfilled, onRejected);  | |||
}  | |||
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)  | |||
// 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  | // 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   | function promptNextOptByName(optSet, item, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {  | ||
     const __OPT = getOptByName(optSet, item);  |      const __OPT = getOptByName(optSet, item);  | ||
     return   |      return promptNextOpt(__OPT, value, reload, freeValue, selValue, minChoice, onFulfilled, onRejected);  | ||
}  | }  | ||
// Baut das Benutzermenu auf  | // Baut das Benutzermenu auf (asynchron im Hintergrund)  | ||
// optSet: Gesetzte Optionen  | // optSet: Gesetzte Optionen  | ||
function buildMenu(optSet) {  | // return Promise auf void  | ||
async function buildMenu(optSet) {  | |||
     __LOG[3]("buildMenu()");  | |||
     for (let opt in optSet) {  |      for (let opt in optSet) {  | ||
         registerOption(optSet[opt]);  |          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) {  | |||
        const __OPT = optSet[opt];  | |||
        await invalidateOpt(__OPT, force);  | |||
    }  | |||
    return optSet;  | |||
}  | }  | ||
| Zeile 773: | Zeile 2.352: | ||
// opt: Zu ladende Option  | // opt: Zu ladende Option  | ||
// force: Laedt auch Optionen mit 'AutoReset'-Attribut  | // force: Laedt auch Optionen mit 'AutoReset'-Attribut  | ||
// return   | // return Promise auf gesetzten Wert der gelandenen Option  | ||
function loadOption(opt, force = false) {  | function loadOption(opt, force = false) {  | ||
     const __CONFIG = getOptConfig(opt);  |      if (! opt.Promise) {  | ||
        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) {  | |||
            const __ERROR = "Error: Oprion '" + __NAME + "' bereits geladen!";  | |||
            __LOG[0](__MESSAGE);  | |||
            return Promise.reject(__MESSAGE);  | |||
        }  | |||
        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:   | // optSet: Object mit den Optionen  | ||
// force: Laedt auch Optionen mit 'AutoReset'-Attribut  | // force: Laedt auch Optionen mit 'AutoReset'-Attribut  | ||
// return   | // return Array mit Promises neuer Ladevorgaenge (fuer Objekte mit 'name' und 'value')  | ||
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];  | |||
        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   |      return Promise.all(__PROMISES);  | ||
}  | }  | ||
| Zeile 801: | Zeile 2.430: | ||
// 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  | // reset: Setzt bei Erfolg auf Initialwert der Option (auch fuer nicht 'AutoReset')  | ||
// 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);  | |||
        __LOG[4]("DELETE " + __NAME);  | |||
         if (reset) {  |          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  | ||
function deleteOptions(optSet, force = false, reset = true) {  | // return Promise auf diesen Vorgang  | ||
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) {  | ||
         deleteOption(optSet[opt], force, reset);  |          if (getValue(__OPTSELECT[opt], __DELETEALL)) {  | ||
            await deleteOption(optSet[opt], force, reset);  | |||
        }  | |||
     }  |      }  | ||
    return Promise.resolve();  | |||
}  | }  | ||
//   | // Benennt eine Option um und laedt sie ggfs. nach  | ||
// 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  | // force: Laedt auch Optionen mit 'AutoReset'-Attribut  | ||
// return   | // return Promise auf umbenannte Option  | ||
function renameOption(opt, name, reload = false, force = false) {  | async function renameOption(opt, name, reload = false, force = false) {  | ||
     const __NAME = getOptName(opt);  |      const __NAME = getOptName(opt);  | ||
     if (__NAME !== name) {  |      if (__NAME !== name) {  | ||
         deleteOption(opt, true, ! reload);  |          await deleteOption(opt, true, ! reload);  | ||
         setOptName(opt, name);  |          setOptName(opt, name);  | ||
        await invalidateOpt(opt, opt.Loaded);  | |||
         if (reload) {  |          if (reload) {  | ||
             loadOption(opt, force);  |              opt.Loaded = false;  | ||
            await loadOption(opt, force);  | |||
         }  |          }  | ||
     }  |      }  | ||
     return opt;  |      return Promise.resolve(opt);  | ||
}  | }  | ||
//   | // Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()  | ||
//   | // name: Gesetzter Name (Speicheradresse)  | ||
//   | // prefix: Prefix, das vorangestellt werden soll  | ||
function   | // return Neu zu setzender Name (Speicheradresse)  | ||
function prefixName(name, prefix) {  | |||
     return (prefix + name);  | |||
}  | |||
// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()  | |||
// name: Gesetzter Name (Speicheradresse)  | |||
// postfix: Postfix, das angehaengt werden soll  | |||
// return Neu zu setzender Name (Speicheradresse)  | |||
function postfixName(name, postfix) {  | |||
     return (name + postfix);  | |||
}  | }  | ||
//   | // Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach  | ||
// optSet: Gesetzte Optionen  | |||
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen  | |||
// 'reload': Option nachladen?  | |||
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?  | |||
// renameParam: Wird an renameFun uebergeen  | |||
// renameFun: function(name, param) zur Ermittlung des neuen Namens  | |||
// - name: Neu zu setzender Name (Speicheradresse)  | |||
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix  | |||
// return Promise auf diesen Vorgang  | |||
async function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {  | |||
    if (renameFun === undefined) {  | |||
        __LOG[0]("RENAME: Illegale Funktion!");  | |||
    }  | |||
    for (let opt in optSelect) {  | |||
        const __OPTPARAMS = optSelect[opt];  | |||
        const __OPT = optSet[opt];  | |||
        if (__OPT === undefined) {  | |||
const   |             __LOG[0]("RENAME: Option '" + opt + "' nicht gefunden!");  | ||
        } else {  | |||
            const __NAME = getOptName(__OPT);  | |||
            const __NEWNAME = renameFun(__NAME, renameParam);  | |||
            const __ISSCALAR = ((typeof __OPTPARAMS) === 'boolean');  | |||
            // Laedt die unter dem neuen Namen gespeicherten Daten nach?  | |||
            const __RELOAD = (__ISSCALAR ? __OPTPARAMS : __OPTPARAMS.reload);  | |||
            // Laedt auch Optionen mit 'AutoReset'-Attribut?  | |||
            const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force);  | |||
            await renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE);  | |||
        }  | |||
    }  | |||
}  | |||
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts  | |||
// optSet: Gesetzte Optionen  | |||
// 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")...  | |||
    refreshPage(reload);  | |||
}  | }  | ||
| Zeile 890: | Zeile 2.564: | ||
// 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 =   | 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(  |              field = document.createElement('input');  | ||
             field.type = type;  |              field.type = type;  | ||
             field.name = fieldName;  |              field.name = fieldName;  | ||
| Zeile 910: | Zeile 2.584: | ||
// return Ergaenztes Form-Konstrukt  | // return Ergaenztes Form-Konstrukt  | ||
function addHiddenField(form, props) {  | function addHiddenField(form, props) {  | ||
     return addInputField(form, props,   |      return addInputField(form, props, 'hidden');  | ||
}  | }  | ||
//   | // Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein  | ||
// obj: Betroffenes Objekt, z.B. ein Eingabeelement  | // obj: Betroffenes Objekt, z.B. ein Eingabeelement  | ||
// type: Name des Events, z.B. "click"  | // type: Name des Events, z.B. "click"  | ||
| Zeile 923: | Zeile 2.597: | ||
         return obj.addEventListener(type, callback, capture);  |          return obj.addEventListener(type, callback, capture);  | ||
     } else if (obj.attachEvent) {  |      } else if (obj.attachEvent) {  | ||
         return obj.attachEvent(  |          return obj.attachEvent('on' + type, callback);  | ||
     } else {  |      } else {  | ||
         __LOG[0]("Could not add " + type + " event:");  | |||
         __LOG[2](callback);  | |||
         return false;  |          return false;  | ||
| Zeile 932: | Zeile 2.606: | ||
}  | }  | ||
//   | // Hilfsfunktion 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 942: | Zeile 2.616: | ||
         return obj.removeEventListener(type, callback, capture);  |          return obj.removeEventListener(type, callback, capture);  | ||
     } else if (obj.detachEvent) {  |      } else if (obj.detachEvent) {  | ||
         return obj.detachEvent(  |          return obj.detachEvent('on' + type, callback);  | ||
     } else {  |      } else {  | ||
         __LOG[0]("Could not remove " + type + " event:");  | |||
         __LOG[2](callback);  | |||
         return false;  |          return false;  | ||
| Zeile 951: | Zeile 2.625: | ||
}  | }  | ||
//   | // Hilfsfunktion 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 963: | Zeile 2.637: | ||
}  | }  | ||
//   | // Hilfsfunktion 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 975: | Zeile 2.649: | ||
}  | }  | ||
//   | // Hilfsfunktion fuer die Ermittlung eines Elements der Seite  | ||
//   | // name: Name des Elements (siehe "name=")  | ||
//   | // index: Laufende Nummer des Elements (0-based), Default: 0  | ||
//   | // doc: Dokument (document)  | ||
// return   | // return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)  | ||
function   | function getElement(name, index = 0, doc = document) {  | ||
     const __TAGS = doc.getElementsByName(name);  | |||
    const __TABLE = (__TAGS ? __TAGS[index] : undefined);  | |||
     return __TABLE;  | |||
     return   | |||
}  | }  | ||
//   | // Hilfsfunktion 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)  | ||
function getTable(index, tag =   | // return Gesuchtes Element oder undefined (falls nicht gefunden)  | ||
     const __TAGS =   | function getTable(index, tag = 'table', doc = document) {  | ||
     const __TABLE = __TAGS[index];  |      const __TAGS = doc.getElementsByTagName(tag);  | ||
     const __TABLE = (__TAGS ? __TAGS[index] : undefined);  | |||
     return __TABLE;  |      return __TABLE;  | ||
}  | }  | ||
//   | // Hilfsfunktion 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,   |      const __TABLE = getTable(index, 'table', doc);  | ||
     const __ROWS = (__TABLE ===   |      const __ROWS = (__TABLE ? __TABLE.rows : undefined);  | ||
    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 1.023: | Zeile 2.714: | ||
// 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,   | function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {  | ||
     const __STORAGE =   |      const __STORAGE = getMemory(memory);  | ||
     const __MEMORY = __STORAGE.Value;  |      const __MEMORY = __STORAGE.Value;  | ||
     const __MEMSTR = __STORAGE.Display;  |      const __MEMSTR = __STORAGE.Display;  | ||
| Zeile 1.042: | Zeile 2.733: | ||
         const __CONFIG = getOptConfig(opt);  |          const __CONFIG = getOptConfig(opt);  | ||
         const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));  |          const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));  | ||
         const __THISVAL = ((__CONFIG.ValType ===   |          const __THISVAL = ((__CONFIG.ValType === 'String') ? "'\\x22' + this.value + '\\x22'" : "this.value");  | ||
         const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');  |          const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');  | ||
         const __VALSTR = ((value !== undefined) ?   |          const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);  | ||
         const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);  |          const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);  | ||
| Zeile 1.069: | Zeile 2.760: | ||
// 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 =   | function getFormActionEvent(opt, isAlt = false, value = undefined, type = 'click', serial = undefined, memory = undefined) {  | ||
     const __ACTION = getFormAction(opt, isAlt, value, serial,   |      const __ACTION = getFormAction(opt, isAlt, value, serial, memory);  | ||
     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 1.084: | Zeile 2.819: | ||
     const __NAME = getOptName(opt);  |      const __NAME = getOptName(opt);  | ||
     const __VALUE = getOptValue(opt);  |      const __VALUE = getOptValue(opt);  | ||
     const __ACTION = getFormActionEvent(opt, false, undefined,   |      const __ACTION = getFormActionEvent(opt, false, undefined, 'change', undefined);  | ||
     const __FORMLABEL = getValue(__CONFIG.  |      const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label, true);  | ||
    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 1.096: | Zeile 2.835: | ||
     element += '\n</select>';  |      element += '\n</select>';  | ||
     return __LABEL  |      return withTitle(substParam(__LABEL, element), __TITLE);  | ||
}  | }  | ||
| Zeile 1.106: | Zeile 2.845: | ||
     const __NAME = getOptName(opt);  |      const __NAME = getOptName(opt);  | ||
     const __VALUE = getOptValue(opt, false);  |      const __VALUE = getOptValue(opt, false);  | ||
     const __ACTION = getFormActionEvent(opt, false, true,   |      const __ACTION = getFormActionEvent(opt, false, true, 'click', false);  | ||
     const __ALTACTION = getFormActionEvent(opt, true, 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 1.118: | Zeile 2.861: | ||
                          ' /><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   |      return ((__FORMLABEL && __FORMLABEL.length) ? __ELEMENT : __ELEMENT.slice(1, 3));  | ||
}  | }  | ||
| Zeile 1.129: | Zeile 2.877: | ||
     const __NAME = getOptName(opt);  |      const __NAME = getOptName(opt);  | ||
     const __VALUE = getOptValue(opt, false);  |      const __VALUE = getOptValue(opt, false);  | ||
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE,   |      const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);  | ||
     const __FORMLABEL =   |     const __VALUELABEL = (__VALUE ? __CONFIG.Label : getValue(__CONFIG.AltLabel, __CONFIG.Label));  | ||
     const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);  | |||
    const __TITLE = substParam(getValue(__VALUE ? __CONFIG.Title : getValue(__CONFIG.AltTitle, __CONFIG.Title), '$'), __VALUELABEL);  | |||
     return '<input type="checkbox" name="' + __NAME +  |      return withTitle('<input type="checkbox" name="' + __NAME +  | ||
                     '" id="' + __NAME + '" value="' + __VALUE + '"' +  | |||
                     (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +  | |||
                     __NAME + '">' + __FORMLABEL + '</label>', __TITLE);  | |||
}  | }  | ||
| Zeile 1.145: | Zeile 2.895: | ||
     const __NAME = getOptName(opt);  |      const __NAME = getOptName(opt);  | ||
     const __VALUE = getOptValue(opt);  |      const __VALUE = getOptValue(opt);  | ||
     const __ACTION = getFormActionEvent(opt, false, undefined,   |      const __ACTION = getFormActionEvent(opt, false, undefined, 'submit', undefined);  | ||
     const __SUBMIT = getValue(__CONFIG.Submit, "");  |      const __SUBMIT = getValue(__CONFIG.Submit, "");  | ||
     const __ONSUBMIT =   |      //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");  | ||
     const __FORMLABEL =   |     const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");  | ||
     const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);  | |||
    const __TITLE = substParam(getValue(__CONFIG.Title, '$'), __FORMLABEL);  | |||
     const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';  |      const __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>';  | |||
     return [ __ELEMENTLABEL, __ELEMENTTEXT ];  |      return [ withTitle(__ELEMENTLABEL, __TITLE), __ELEMENTTEXT ];  | ||
}  | }  | ||
| Zeile 1.164: | Zeile 2.916: | ||
     const __NAME = getOptName(opt);  |      const __NAME = getOptName(opt);  | ||
     const __VALUE = getOptValue(opt, false);  |      const __VALUE = getOptValue(opt, false);  | ||
     const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE,   |      const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);  | ||
     const __BUTTONLABEL = (__VALUE ? __CONFIG.AltLabel : __CONFIG.Label);  |      const __BUTTONLABEL = (__VALUE ? getValue(__CONFIG.AltLabel, __CONFIG.Label) : __CONFIG.Label);  | ||
     const __FORMLABEL = getValue(__CONFIG.  |      const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __BUTTONLABEL);  | ||
    const __BUTTONTITLE = substParam(getValue(__VALUE ? getValue(__CONFIG.AltTitle, __CONFIG.Title) : __CONFIG.Title, '$'), __BUTTONLABEL);  | |||
     return '<label for="' + __NAME + '">' + __FORMLABEL +  |      return '<label for="' + __NAME + '">' + __FORMLABEL + '</label>' +  | ||
           withTitle('<input type="button" name="" + ' + __NAME +  | |||
                     '" id="' + __NAME + '" value="' + __BUTTONLABEL +  | |||
                     '"' + __ACTION + '/>', __BUTTONTITLE);  | |||
}  | }  | ||
| Zeile 1.201: | Zeile 2.954: | ||
         }  |          }  | ||
         if (element  |          if ((typeof element) !== 'string') {  | ||
             element = '<div>' + element  |              element = '<div>' + Array.from(element).join('<br />') + '</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 1.223: | Zeile 2.997: | ||
     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   |      for (let optKeys of Object.values(__PRIOOPTS)) {  | ||
         if (  |          for (let optKey of optKeys) {  | ||
            if (checkItem(optKey, __SHOWFORM, optParams.hideForm)) {  | |||
                const __ELEMENT = getOptionElement(optSet[optKey]);  | |||
                const __TDOPT = ((~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"');  | |||
                if (__ELEMENT) {  | |||
                    if (++count > __FORMBREAK) {  | |||
                        if (++column > __FORMWIDTH) {  | |||
                            column = 1;  | |||
                        }  | |||
                     }  |                      }  | ||
                    if (column === 1) {  | |||
                        form += '</tr><tr>';  | |||
                    }  | |||
                    form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';  | |||
                 }  |                  }  | ||
             }  |              }  | ||
         }  |          }  | ||
| Zeile 1.276: | Zeile 3.053: | ||
// 'formBreak': Elementnummer des ersten Zeilenumbruchs  | // 'formBreak': Elementnummer des ersten Zeilenumbruchs  | ||
function buildForm(anchor, optSet, optParams = { }) {  | function buildForm(anchor, optSet, optParams = { }) {  | ||
     __LOG[3]("buildForm()");  | |||
     const __FORM = getForm(optSet, optParams);  |      const __FORM = getForm(optSet, optParams);  | ||
| Zeile 1.296: | Zeile 3.073: | ||
                    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] = { 'Script' : script, 'Form' : form };  |      __FORMS[anchor] = {  | ||
                          '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 1.307: | Zeile 3.487: | ||
// Verarbeitet eine Ergebnis-Ansicht  | // Verarbeitet eine Ergebnis-Ansicht  | ||
function procErgebnisse() {  | function procErgebnisse() {  | ||
     buildOptions(__OPTCONFIG, __OPTSET, {  |      return buildOptions(__OPTCONFIG, __OPTSET, {  | ||
                            'menuAnchor' : getTable(0, 'div'),  | |||
                            'formWidth'  : 2  | |||
                        }).then(optSet => {  | |||
            // Aktiviere Checkbox "Ergebnisse anzeigen" je nach Einstellung der Option  | |||
            getElement("erganzeigen").checked = getOptValue(optSet.showErgs);  | |||
        });  | |||
}  | }  | ||
procErgebnisse();  | (() => {  | ||
    (async () => {  | |||
        try {  | |||
            await procErgebnisse().catch(defaultCatch);  | |||
            return 'OK';  | |||
        } catch (ex) {  | |||
            return defaultCatch(ex);  | |||
        }  | |||
    })().then(rc => {  | |||
            __LOG[1]('SCRIPT END', __DBMOD.Name, '(' + rc + ')');  | |||
        })  | |||
})();  | |||
// *** EOF ***  | // *** EOF ***  | ||
</pre>  | </pre>  | ||
Aktuelle Version vom 23. November 2017, 00:21 Uhr
| OS2.ergebnisse | |
| Dateiname | OS2.ergebnisse.user.js | 
| Version | 0.32 (WebExtensions) | 
| Autor | Sven Loges (SLC), Choromonets Odessa | 
| Beschreibung | Aktiviert als Standard die Option "Ergebnisse anzeigen" | 
| Webseiten | ls.php lp.php oseq.php ose.php oscq.php oschr.php osczr.php oscfr.php zer.php Alle Seiten mit Ergebnisanzeige  | 
| Funktionalität | Haken wird standardmäßig aktiviert/nicht aktiviert Benutzermenü für Optionen Erweiterte Optionen auch auf der Seite Interaktive Menü-Optionen Gemeinsame Code- und Datenbasis  | 
| Letzte Änderung | 23.11.2017 | 
Quellcode INSTALLATION[Bearbeiten]
// ==UserScript==
// @name         OS2.ergebnisse
// @namespace    http://os.ongapo.com/
// @version      0.32+WE+
// @copyright    2016+
// @author       Sven Loges (SLC)
// @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+)*)?$/
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.deleteValue
// @grant        GM.registerMenuCommand
// @grant        GM.info
// @require      https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @grant        GM_info
// ==/UserScript==
// ECMAScript 6:
/* jshint esnext: true */
/* jshint moz: true */
// ==================== Konfigurations-Abschnitt fuer Optionen ====================
const __LOGLEVEL = 3;
// Options-Typen
const __OPTTYPES = {
    'MC' : "multiple choice",
    'SW' : "switch",
    'TF' : "true/false",
    'SD' : "simple data",
    'SI' : "simple option"
};
// Options-Typen
const __OPTACTION = {
    'SET' : "set option value",
    'NXT' : "set next option value",
    'RST' : "reset options"
};
const __OPTMEM = {
    'normal' : {
                   'Name'      : "Browser",
                   'Value'     : localStorage,
                   'Display'   : "localStorage",
                   'Prefix'    : 'run'
               },
    'begrenzt' : {
                   'Name'      : "Session",
                   'Value'     : sessionStorage,
                   'Display'   : "sessionStorage",
                   'Prefix'    : 'run'
               },
    'inaktiv' : {
                   'Name'      : "inaktiv",
                   'Value'     : undefined,
                   'Display'   : "",
                   'Prefix'    : ""
               }
};
// Moegliche Optionen (hier die Standardwerte editieren oder ueber das Benutzermenu setzen):
const __OPTCONFIG = {
    'showErgs' : {        // Standardeinstellung der Ergebnisanzeige
                   'Name'      : "showErgs",
                   'Type'      : __OPTTYPES.SW,
                   'Default'   : true,
                   'Action'    : __OPTACTION.NXT,
                   'Label'     : "Ergebnisse anzeigen",
                   'Hotkey'    : 'E',
                   'AltLabel'  : "Keine Ergebnisse",
                   'AltHotkey' : 'K',
                   'FormLabel' : "Ergebnisse anzeigen"
               },
    'reset' : {           // Optionen auf die "Werkseinstellungen" zuruecksetzen
                   'FormPrio'  : undefined,
                   'Name'      : "reset",
                   'Type'      : __OPTTYPES.SI,
                   'Action'    : __OPTACTION.RST,
                   'Label'     : "Standard-Optionen",
                   'Hotkey'    : 'O',
                   '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)
                   'FormPrio'  : 1,
                   'Name'      : "showForm",
                   'Type'      : __OPTTYPES.SW,
                   'FormType'  : __OPTTYPES.SI,
                   'Permanent' : true,
                   'Default'   : false,
                   'Title'     : "$V Optionen",
                   'Action'    : __OPTACTION.NXT,
                   'Label'     : "Optionen anzeigen",
                   'Hotkey'    : 'a',
                   'AltTitle'  : "$V schlie\xDFen",
                   'AltLabel'  : "Optionen verbergen",
                   'AltHotkey' : 'v',
                   'FormLabel' : ""
               }
};
// ==================== 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 ====================
// Gibt einen Wert zurueck. Ist dieser nicht definiert oder null, wird ein Alternativwert geliefert
// value: Ein Wert. Ist dieser nicht undefined oder null, wird er zurueckgeliefert (oder retValue)
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist
// retValue: Falls definiert, Rueckgabe-Wert fuer den Fall, dass value nicht undefined oder null ist
// return Der Wert. Sind weder value noch defValue definiert, dann undefined
function getValue(value, defValue = undefined, retValue = undefined) {
    return ((value === undefined) || (value === null)) ? defValue : (retValue === undefined) ? value : retValue;
}
// Gibt einen Wert zurueck. Ist dieser nicht definiert, wird ein Alternativwert geliefert
// value: Ein Wert. Ist dieser definiet und in den Grenzen, wird er zurueckgeliefert
// minValue: Untere Grenze fuer den Wert, falls angegeben
// minValue: Obere Grenze fuer den Wert, falls angegeben
// defValue: Default-Wert fuer den Fall, dass nichts gesetzt ist oder der Wert ausserhalb liegt
// return Der Wert. Sind weder value (in den Grenzen) noch defValue definiert, dann undefined
function getValueIn(value, minValue = undefined, maxValue = undefined, defValue = undefined) {
    const __VALUE = getValue(value, defValue);
    if ((minValue !== undefined) && (__VALUE < minValue)) {
        return defValue;
    }
    if ((maxValue !== undefined) && (__VALUE > maxValue)) {
        return defValue;
    }
    return __VALUE;
}
// Ermittelt den naechsten Wert aus einer Array-Liste
// arr: Array-Liste mit den moeglichen Werte
// value: Vorher gesetzter Wert
// return Naechster Wert in der Array-Liste
function getNextValue(arr, value) {
    const __POS = arr.indexOf(value) + 1;
    return arr[getValueIn(__POS, 0, arr.length - 1, 0)];
}
// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert
// valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0)
// defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN)
// return Das Produkt auf digits Stellen genau. Ist dieses nicht definiert, dann defValue
function getMulValue(valueA, valueB, digits = 0, defValue = NaN) {
    let product = defValue;
    if ((valueA !== undefined) && (valueB !== undefined)) {
        product = parseFloat(valueA) * parseFloat(valueB);
    }
    if (isNaN(product)) {
        product = defValue;
    }
    return parseFloat(product.toFixed(digits));
}
// Gibt eine Ordinalzahl zur uebergebenen Zahl zurueck
// value: Eine ganze Zahl
// defValue: Default-Wert fuer den Fall, dass der Wert nicht gesetzt ist (Default: '*')
// return Die Ordinalzahl als String der Form "n." oder defValue, falls nicht angegeben
function getOrdinal(value, defValue = '*') {
    return getValue(value, defValue, value + '.');
}
// 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)
// 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;
}
// Liefert alle Basisklassen des Objekts (inkl. Vererbung)
// obj: Ein (generisches) Objekt
// 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;
}
// Liefert alle Attribute/Properties des Objekts (inkl. Vererbung)
// obj: Ein (generisches) Objekt
// return Array von Items (Property-Namen)
function getAllProperties(obj) {
    let ret = [];
    for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
      ret = ret.concat(Object.getOwnPropertyNames(o));
    }
    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 {
        // Kein Dezimalpunkt, fuege Tausender-Trennpunkte ein:
        // String umdrehen, nach jedem dritten Zeichen Punkt einfuegen, dann wieder umdrehen:
        const __TEMP = reverseString(numberString);
        let result = "";
        for (let i = 0; i < __TEMP.length; i++) {
            if ((i > 0) && (i % 3 === 0)) {
                result += '.';
            }
            result += __TEMP.substr(i, 1);
        }
        return reverseString(result);
    }
}
// Dreht den uebergebenen String um
// 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) {
        __LOG[2]("Seite wird neu geladen...");
        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;
}
// Setzt den naechsten Wert aus einer Array-Liste als Option
// arr: Array-Liste mit den moeglichen Optionen
// name: Name der Option als Speicherort
// value: Vorher gesetzter Wert
// reload: Seite mit neuem Wert neu laden
// 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 setNextStored(arr, name, value, reload = false, serial = false, onFulfilled = undefined, onRejected = undefined) {
    return setStored(name, getNextValue(arr, value), reload, serial, onFulfilled, onRejected);
}
// Fuehrt die in einem Storage gespeicherte Operation aus
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined
function getStoredCmds(memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __RUNPREFIX = __STORAGE.Prefix;
    const __STOREDCMDS = [];
    if (__MEMORY !== undefined) {
        const __GETITEM = function(item) {
                              return __MEMORY.getItem(__RUNPREFIX + item);
                          };
        const __DELITEM = function(item) {
                              return __MEMORY.removeItem(__RUNPREFIX + item);
                          };
        const __CMD = ((__MEMORY !== undefined) ? __GETITEM('cmd') : undefined);
        if (__CMD !== undefined) {
            const __KEY = __GETITEM('key');
            let value = __GETITEM('val');
            try {
                value = JSON.parse(value);
            } catch (ex) {
                __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');
        __DELITEM('key');
        __DELITEM('val');
    }
    return (__STOREDCMDS.length ? __STOREDCMDS : undefined);
}
// Fuehrt die in einem Storage gespeicherte Operation aus
// storedCmds: Array von Objekten mit 'cmd' / 'key' / 'val' (siehe getStoredCmds())
// optSet: Object mit den Optionen
// beforeLoad: Angabe, ob nach der Speicherung noch loadOptions() aufgerufen wird
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Promise auf ein Array von Operationen (wie storedCmds), die fuer die naechste Phase uebrig bleiben
async function runStoredCmds(storedCmds, optSet = undefined, beforeLoad = undefined, onFulfilled = undefined, onRejected = undefined) {
    const __BEFORELOAD = getValue(beforeLoad, true);
    const __STOREDCMDS = getValue(storedCmds, []);
    const __LOADEDCMDS = [];
    let invalidated = false;
    while (__STOREDCMDS.length) {
        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);
}
// Gibt eine Option sicher zurueck
// opt: Config und Value der Option, ggfs. undefined
// defOpt: Rueckgabewert, falls undefined
// return Daten zur Option (oder defOpt)
function getOpt(opt, defOpt = { }) {
    return getValue(opt, defOpt);
}
// Gibt eine Option sicher zurueck (Version mit Key)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// defOpt: Rueckgabewert, falls nicht zu finden
// return Daten zur Option (oder defOpt)
function getOptByName(optSet, item, defOpt = { }) {
    if ((optSet !== undefined) && (item !== undefined)) {
        return getOpt(optSet[item], defOpt);
    } else {
        return defOpt;
    }
}
// Gibt die Konfigurationsdaten einer Option zurueck
// opt: Config und Value der Option
// defConfig: Rueckgabewert, falls Config nicht zu finden
// return Konfigurationsdaten der Option
function getOptConfig(opt, defConfig = { }) {
    return getValue(getOpt(opt).Config, defConfig);
}
// Setzt den Namen einer Option
// opt: Config und Value der Option
// name: Zu setzender Name der Option
// reload: Seite mit neuem Wert neu laden
// return Gesetzter Name der Option
function setOptName(opt, name) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    if (__NAME !== name) {
        __LOG[4]("RENAME " + __NAME + " => " + name);
        __CONFIG.Name = name;
    }
    return name;
}
// Gibt den Namen einer Option zurueck
// opt: Config und Value der Option
// return Name der Option
function getOptName(opt) {
    const __CONFIG = getOptConfig(opt);
    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;
}
// Setzt den Wert einer Option
// opt: Config und Value der Option
// name: Zu setzender Wert der Option
// return Gesetzter Wert
function setOptValue(opt, value) {
    if (opt !== undefined) {
        if (! opt.ReadOnly) {
            __LOG[6](getOptName(opt) + ": " + __LOG.changed(opt.Value, value));
            opt.Value = value;
        }
        return opt.Value;
    } else {
        return undefined;
    }
}
// Gibt den Wert einer Option zurueck
// opt: Config und Value der Option
// 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
function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) {
    let value;
    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 ====================
// ==================== 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 ====================
// Zeigt den Eintrag im Menu einer Option
// val: Derzeitiger Wert der Option
// menuOn: Text zum Setzen im Menu
// funOn: Funktion zum Setzen
// keyOn: Hotkey zum Setzen im Menu
// menuOff: Text zum Ausschalten im Menu
// funOff: Funktion zum Ausschalten
// keyOff: Hotkey zum Ausschalten im Menu
// return Promise von GM.registerMenuCommand()
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) {
    const __ON  = (val ? '*' : "");
    const __OFF = (val ? "" : '*');
    __LOG[3]("OPTION " + __ON + menuOn + __ON + " / " + __OFF + menuOff + __OFF);
    if (val) {
        return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn);
    } else {
        return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff);
    }
}
// Zeigt den Eintrag im Menu einer Option mit Wahl des naechsten Wertes
// val: Derzeitiger Wert der Option
// arr: Array-Liste mit den moeglichen Optionen
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// fun: Funktion zum Setzen des naechsten Wertes
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// return Promise von GM.registerMenuCommand()
function registerNextMenuOption(val, arr, menu, fun, key) {
    const __MENU = substParam(menu, val);
    let options = "OPTION " + __MENU;
    for (let value of arr) {
        if (value === val) {
            options += " / *" + value + '*';
        } else {
            options += " / " + value;
        }
    }
    __LOG[3](options);
    return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
}
// Zeigt den Eintrag im Menu einer Option, falls nicht hidden
// val: Derzeitiger Wert der Option
// menu: Text zum Setzen im Menu ($ wird durch gesetzten Wert ersetzt)
// fun: Funktion zum Setzen des naechsten Wertes
// key: Hotkey zum Setzen des naechsten Wertes im Menu
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar)
// serial: Serialization fuer komplexe Daten
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) {
    const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val);
    const __MENU = substParam(menu, __VALUE);
    const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU +
                      getValue(__VALUE, "", " = " + __VALUE);
    __LOG[hidden ? 4 : 3](__OPTIONS);
    if (hidden) {
        return Promise.resolve(__VALUE);
    } else {
        return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU);
    }
}
// Zeigt den Eintrag im Menu einer Option
// opt: Config und Value der Option
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes)
function registerOption(opt) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt);
    const __LABEL = __CONFIG.Label;
    const __ACTION = opt.Action;
    const __HOTKEY = __CONFIG.Hotkey;
    const __HIDDEN = __CONFIG.HiddenMenu;
    const __SERIAL = __CONFIG.Serial;
    if (! __CONFIG.HiddenMenu) {
        switch (__CONFIG.Type) {
        case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY);
        case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                       __CONFIG.AltLabel, __ACTION, __CONFIG.AltHotkey);
        case __OPTTYPES.TF : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY,
                                                       __CONFIG.AltLabel, opt.AltAction, __CONFIG.AltHotkey);
        case __OPTTYPES.SD : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
        case __OPTTYPES.SI : return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
        default :            return Promise.resolve(__VALUE);
        }
    } else {
        // Nur Anzeige im Log...
        return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL);
    }
}
// ==================== Ende Abschnitt fuer das Benutzermenu ====================
// Initialisiert die gesetzten Option
// config: Konfiguration der Option
// setValue: Zu uebernehmender Default-Wert (z.B. der jetzt gesetzte)
// return Initialwert der gesetzten Option
function initOptValue(config, setValue = undefined) {
    let value = getValue(setValue, config.Default);  // Standard
    if (config.SharedData !== undefined) {
        value = config.SharedData;
    }
    switch (config.Type) {
    case __OPTTYPES.MC : if ((value === undefined) && (config.Choice !== undefined)) {
                             value = config.Choice[0];
                         }
                         break;
    case __OPTTYPES.SW : break;
    case __OPTTYPES.TF : break;
    case __OPTTYPES.SD : config.Serial = true;
                         break;
    case __OPTTYPES.SI : break;
    default :            break;
    }
    if (config.Serial || config.Hidden) {
        config.HiddenMenu = true;
    }
    return value;
}
// Initialisiert die Menue-Funktion einer Option
// optAction: Typ der Funktion
// item: Key der Option
// optSet: Platz fuer die gesetzten Optionen (und Config)
// optConfig: Konfiguration der Option
// return Funktion fuer die Option
function initOptAction(optAction, item = undefined, optSet = undefined, optConfig = undefined) {
    let fun;
    if (optAction !== undefined) {
        const __CONFIG = ((optConfig !== undefined) ? optConfig : getOptConfig(getOptByName(optSet, item)));
        const __RELOAD = getValue(getValue(__CONFIG, { }).ActionReload, true);
        switch (optAction) {
        case __OPTACTION.SET : fun = function() {
                                       return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch);
                                   };
                               break;
        case __OPTACTION.NXT : fun = function() {
                                       return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD,
                                                  __CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice).catch(defaultCatch);
                                   };
                               break;
        case __OPTACTION.RST : fun = function() {
                                       return resetOptions(optSet, __RELOAD).then(
                                               result => __LOG[3]("RESETTING (" + result + ")..."),
                                               defaultCatch);
                                   };
                               break;
        default :              break;
        }
    }
    return fun;
}
// Gibt fuer einen 'Shared'-Eintrag eine ObjRef zurueck
// 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;
}
// Initialisiert die gesetzten Optionen
// optConfig: Konfiguration der Optionen
// optSet: Platz fuer die gesetzten Optionen
// preInit: Vorinitialisierung einzelner Optionen mit 'PreInit'-Attribut
// return Gefuelltes Objekt mit den gesetzten Optionen
function initOptions(optConfig, optSet = undefined, preInit = undefined) {
    let value;
    if (optSet === undefined) {
        optSet = { };
    }
    for (let opt in optConfig) {
        const __OPTCONFIG = optConfig[opt];
        const __PREINIT = getValue(__OPTCONFIG.PreInit, false, true);
        const __ISSHARED = getValue(__OPTCONFIG.Shared, false, true);
        if ((preInit === undefined) || (__PREINIT === preInit)) {
            const __CONFIG = getSharedConfig(__OPTCONFIG, opt);
            const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action);
            // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen?
            const __LOADED = ((preInit === false) && optSet[opt].Loaded);
            const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise);
            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);
    }
    return optSet;
}
// Setzt eine Option auf einen vorgegebenen Wert
// Fuer kontrollierte Auswahl des Values siehe setNextOpt()
// opt: Config und vorheriger Value der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter)
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter)
// return Gesetzter Wert
function setOpt(opt, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
    return setOptValue(opt, setStored(getOptName(opt), value, reload, getOptConfig(opt).Serial, onFulfilled, onRejected));
}
// Ermittelt die naechste moegliche Option
// opt: Config und Value der Option
// value: Ggfs. zu setzender Wert
// return Zu setzender Wert
function getNextOpt(opt, value = undefined) {
    const __CONFIG = getOptConfig(opt);
    const __VALUE = getOptValue(opt, value);
    switch (__CONFIG.Type) {
    case __OPTTYPES.MC : return getValue(value, getNextValue(__CONFIG.Choice, __VALUE));
    case __OPTTYPES.SW : return getValue(value, ! __VALUE);
    case __OPTTYPES.TF : return getValue(value, ! __VALUE);
    case __OPTTYPES.SD : return getValue(value, __VALUE);
    case __OPTTYPES.SI : break;
    default :            break;
    }
    return __VALUE;
}
// Setzt die naechste moegliche Option
// opt: Config und Value der Option
// value: Default fuer ggfs. zu setzenden Wert
// reload: Seite mit neuem Wert neu laden
// 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
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3, onFulfilled = undefined, onRejected = undefined) {
    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);
    try {
        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;
}
// Setzt eine Option auf einen vorgegebenen Wert (Version mit Key)
// Fuer kontrollierte Auswahl des Values siehe setNextOptByName()
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: (Bei allen Typen) Zu setzender Wert
// reload: Seite mit neuem Wert neu laden
// 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 setOptByName(optSet, item, value, reload = false, onFulfilled = undefined, onRejected = undefined) {
    const __OPT = getOptByName(optSet, item);
    return setOpt(__OPT, value, reload, onFulfilled, onRejected);
}
// Ermittelt die naechste moegliche Option (Version mit Key)
// optSet: Platz fuer die gesetzten Optionen (und Config)
// item: Key der Option
// value: Default fuer ggfs. zu setzenden Wert
// return Zu setzender Wert
function getNextOptByName(optSet, item, value = undefined) {
    const __OPT = getOptByName(optSet, item);
    return getNextOpt(__OPT, value);
}
// Setzt die naechste moegliche Option (Version mit Key)
// 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
// 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 setNextOptByName(optSet, item, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) {
    const __OPT = getOptByName(optSet, item);
    return setNextOpt(__OPT, value, reload, onFulfilled, onRejected);
}
// Setzt die naechste moegliche Option oder fragt ab einer gewissen Anzahl interaktiv ab (Version mit Key)
// 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
// return Promise auf void
async function buildMenu(optSet) {
    __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) {
        const __OPT = optSet[opt];
        await invalidateOpt(__OPT, force);
    }
    return optSet;
}
// Laedt eine (ueber Menu) gesetzte Option
// opt: Zu ladende Option
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Promise auf gesetzten Wert der gelandenen Option
function loadOption(opt, force = false) {
    if (! opt.Promise) {
        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) {
            const __ERROR = "Error: Oprion '" + __NAME + "' bereits geladen!";
            __LOG[0](__MESSAGE);
            return Promise.reject(__MESSAGE);
        }
        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
// optSet: Object mit den Optionen
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Array mit Promises neuer Ladevorgaenge (fuer Objekte mit 'name' und 'value')
function loadOptions(optSet, force = false) {
    const __PROMISES = [];
    for (let opt in optSet) {
        const __OPT = optSet[opt];
        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);
}
// Entfernt eine (ueber Menu) gesetzte Option (falls nicht 'Permanent')
// opt: Gesetzte Option
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option (auch fuer nicht 'AutoReset')
// return Promise von GM.deleteValue() (oder void)
function deleteOption(opt, force = false, reset = true) {
    const __CONFIG = getOptConfig(opt);
    if (force || ! __CONFIG.Permanent) {
        const __NAME = getOptName(opt);
        __LOG[4]("DELETE " + __NAME);
        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')
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = entfernen, false = nicht entfernen
// force: Entfernt auch Optionen mit 'Permanent'-Attribut
// reset: Setzt bei Erfolg auf Initialwert der Option
// return Promise auf diesen Vorgang
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) {
        if (getValue(__OPTSELECT[opt], __DELETEALL)) {
            await deleteOption(optSet[opt], force, reset);
        }
    }
    return Promise.resolve();
}
// Benennt eine Option um und laedt sie ggfs. nach
// opt: Gesetzte Option
// name: Neu zu setzender Name (Speicheradresse)
// reload: Wert nachladen statt beizubehalten
// force: Laedt auch Optionen mit 'AutoReset'-Attribut
// return Promise auf umbenannte Option
async function renameOption(opt, name, reload = false, force = false) {
    const __NAME = getOptName(opt);
    if (__NAME !== name) {
        await deleteOption(opt, true, ! reload);
        setOptName(opt, name);
        await invalidateOpt(opt, opt.Loaded);
        if (reload) {
            opt.Loaded = false;
            await loadOption(opt, force);
        }
    }
    return Promise.resolve(opt);
}
// Ermittelt einen neuen Namen mit einem Prefix. Parameter fuer renameOptions()
// name: Gesetzter Name (Speicheradresse)
// prefix: Prefix, das vorangestellt werden soll
// return Neu zu setzender Name (Speicheradresse)
function prefixName(name, prefix) {
    return (prefix + name);
}
// Ermittelt einen neuen Namen mit einem Postfix. Parameter fuer renameOptions()
// name: Gesetzter Name (Speicheradresse)
// postfix: Postfix, das angehaengt werden soll
// return Neu zu setzender Name (Speicheradresse)
function postfixName(name, postfix) {
    return (name + postfix);
}
// Benennt selektierte Optionen nach einem Schema um und laedt sie ggfs. nach
// optSet: Gesetzte Optionen
// optSelect: Liste von ausgewaehlten Optionen, true = nachladen, false = nicht nachladen
// 'reload': Option nachladen?
// 'force': Option auch mit 'AutoReset'-Attribut nachladen?
// renameParam: Wird an renameFun uebergeen
// renameFun: function(name, param) zur Ermittlung des neuen Namens
// - name: Neu zu setzender Name (Speicheradresse)
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix
// return Promise auf diesen Vorgang
async function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) {
    if (renameFun === undefined) {
        __LOG[0]("RENAME: Illegale Funktion!");
    }
    for (let opt in optSelect) {
        const __OPTPARAMS = optSelect[opt];
        const __OPT = optSet[opt];
        if (__OPT === undefined) {
            __LOG[0]("RENAME: Option '" + opt + "' nicht gefunden!");
        } else {
            const __NAME = getOptName(__OPT);
            const __NEWNAME = renameFun(__NAME, renameParam);
            const __ISSCALAR = ((typeof __OPTPARAMS) === 'boolean');
            // Laedt die unter dem neuen Namen gespeicherten Daten nach?
            const __RELOAD = (__ISSCALAR ? __OPTPARAMS : __OPTPARAMS.reload);
            // Laedt auch Optionen mit 'AutoReset'-Attribut?
            const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force);
            await renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE);
        }
    }
}
// Setzt die Optionen in optSet auf die "Werkseinstellungen" des Skripts
// optSet: Gesetzte Optionen
// 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")...
    refreshPage(reload);
}
// ==================== Abschnitt fuer diverse Utilities ====================
// Legt Input-Felder in einem Form-Konstrukt an, falls noetig
// form: <form>...</form>
// props: Map von name:value-Paaren
// type: Typ der Input-Felder (Default: unsichtbare Daten)
// return Ergaenztes Form-Konstrukt
function addInputField(form, props, type = 'hidden') {
    for (let fieldName in props) {
        let field = form[fieldName];
        if (! field) {
            field = document.createElement('input');
            field.type = type;
            field.name = fieldName;
            form.appendChild(field);
        }
        field.value = props[fieldName];
    }
    return form;
}
// Legt unsichtbare Input-Daten in einem Form-Konstrukt an, falls noetig
// form: <form>...</form>
// props: Map von name:value-Paaren
// return Ergaenztes Form-Konstrukt
function addHiddenField(form, props) {
    return addInputField(form, props, 'hidden');
}
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function addEvent(obj, type, callback, capture = false) {
    if (obj.addEventListener) {
        return obj.addEventListener(type, callback, capture);
    } else if (obj.attachEvent) {
        return obj.attachEvent('on' + type, callback);
    } else {
        __LOG[0]("Could not add " + type + " event:");
        __LOG[2](callback);
        return false;
    }
}
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// obj: Betroffenes Objekt, z.B. ein Eingabeelement
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function removeEvent(obj, type, callback, capture = false) {
    if (obj.removeEventListener) {
        return obj.removeEventListener(type, callback, capture);
    } else if (obj.detachEvent) {
        return obj.detachEvent('on' + type, callback);
    } else {
        __LOG[0]("Could not remove " + type + " event:");
        __LOG[2](callback);
        return false;
    }
}
// Hilfsfunktion fuer alle Browser: Fuegt fuer ein Event eine Reaktion ein
// id: ID des betroffenen Eingabeelements
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function addDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);
    return addEvent(__OBJ, type, callback, capture);
}
// Hilfsfunktion fuer alle Browser: Entfernt eine Reaktion fuer ein Event
// id: ID des betroffenen Eingabeelements
// type: Name des Events, z.B. "click"
// callback: Funktion als Reaktion
// capture: Event fuer Parent zuerst (true) oder Child (false als Default)
// return false bei Misserfolg
function removeDocEvent(id, type, callback, capture = false) {
    const __OBJ = document.getElementById(id);
    return removeEvent(__OBJ, type, callback, capture);
}
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite
// name: Name des Elements (siehe "name=")
// index: Laufende Nummer des Elements (0-based), Default: 0
// doc: Dokument (document)
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden)
function getElement(name, index = 0, doc = document) {
    const __TAGS = doc.getElementsByName(name);
    const __TABLE = (__TAGS ? __TAGS[index] : undefined);
    return __TABLE;
}
// Hilfsfunktion fuer die Ermittlung eines Elements der Seite (Default: Tabelle)
// index: Laufende Nummer des Elements (0-based)
// tag: Tag des Elements ("table")
// doc: Dokument (document)
// return Gesuchtes Element oder undefined (falls nicht gefunden)
function getTable(index, tag = 'table', doc = document) {
    const __TAGS = doc.getElementsByTagName(tag);
    const __TABLE = (__TAGS ? __TAGS[index] : undefined);
    return __TABLE;
}
// Hilfsfunktion 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)
// doc: Dokument (document)
// return Gesuchte Zeilen oder undefined (falls nicht gefunden)
function getRows(index, doc = document) {
    const __TABLE = getTable(index, 'table', doc);
    const __ROWS = (__TABLE ? __TABLE.rows : undefined);
    return __ROWS;
}
// 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;
}
// ==================== Abschnitt fuer Optionen auf der Seite ====================
// Liefert den Funktionsaufruf zur Option als String
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormAction(opt, isAlt = false, value = undefined, serial = undefined, memory = undefined) {
    const __STORAGE = getMemory(memory);
    const __MEMORY = __STORAGE.Value;
    const __MEMSTR = __STORAGE.Display;
    const __RUNPREFIX = __STORAGE.Prefix;
    if (__MEMORY !== undefined) {
        const __RELOAD = "window.location.reload()";
        const __SETITEM = function(item, val, quotes = true) {
                              return (__MEMSTR + ".setItem('" + __RUNPREFIX + item + "', " + (quotes ? "'" + val + "'" : val) + "),");
                          };
        const __SETITEMS = function(cmd, key = undefined, val = undefined) {
                              return ('(' + __SETITEM('cmd', cmd) + ((key === undefined) ? "" :
                                      __SETITEM('key', key) + __SETITEM('val', val, false)) + __RELOAD + ')');
                          };
        const __CONFIG = getOptConfig(opt);
        const __SERIAL = getValue(serial, getValue(__CONFIG.Serial, false));
        const __THISVAL = ((__CONFIG.ValType === 'String') ? "'\\x22' + this.value + '\\x22'" : "this.value");
        const __TVALUE = getValue(__CONFIG.ValType, __THISVAL, "new " + __CONFIG.ValType + '(' + __THISVAL + ')');
        const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE);
        const __ACTION = (isAlt ? getValue(__CONFIG.AltAction, __CONFIG.Action) : __CONFIG.Action);
        if (__ACTION !== undefined) {
            switch (__ACTION) {
            case __OPTACTION.SET : //return "doActionSet('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                   return __SETITEMS('SET', getOptName(opt), __VALSTR);
            case __OPTACTION.NXT : //return "doActionNxt('" + getOptName(opt) + "', " + getNextOpt(opt, __VALSTR) + ')';
                                   return __SETITEMS('NXT', getOptName(opt), __VALSTR);
            case __OPTACTION.RST : //return "doActionRst()";
                                   return __SETITEMS('RST');
            default :              break;
            }
        }
    }
    return undefined;
}
// Liefert die Funktionsaufruf zur Option als String
// opt: Auszufuehrende Option
// isAlt: Angabe, ob AltAction statt Action gemeint ist
// value: Ggfs. zu setzender Wert
// type: Event-Typ fuer <input>, z.B. "click" fuer "onclick="
// serial: Serialization fuer String-Werte (Select, Textarea)
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv
// return String mit dem (reinen) Funktionsaufruf
function getFormActionEvent(opt, isAlt = false, value = undefined, type = 'click', serial = undefined, memory = undefined) {
    const __ACTION = getFormAction(opt, isAlt, value, serial, memory);
    return getValue(__ACTION, "", ' on' + type + '="' + __ACTION + '"');
}
// 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;
    }
}
// Zeigt eine Option auf der Seite als Auswahlbox an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionSelect(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, 'change', undefined);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label, true);
    const __TITLE = substParam(getValue(__CONFIG.Title, __CONFIG.Label), __VALUE);
    const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>';
    if (__CONFIG.FreeValue && ! (~ __CONFIG.Choice.indexOf(__VALUE))) {
        element += '\n<option value="' + __VALUE + '" SELECTED>' + __VALUE + '</option>';
    }
    for (let value of __CONFIG.Choice) {
        element += '\n<option value="' + value + '"' +
                   ((value === __VALUE) ? ' SELECTED' : "") +
                   '>' + value + '</option>';
    }
    element += '\n</select>';
    return withTitle(substParam(__LABEL, element), __TITLE);
}
// Zeigt eine Option auf der Seite als Radiobutton an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionRadio(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, false, true, 'click', false);
    const __ALTACTION = getFormActionEvent(opt, true, false, 'click', false);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel); // nur nutzen, falls angegeben
    const __TITLE = getValue(__CONFIG.Title, '$');
    const __TITLEON = substParam(__TITLE, __CONFIG.Label);
    const __TITLEOFF = substParam(getValue(__CONFIG.AltTitle, __TITLE), __CONFIG.AltLabel);
    const __ELEMENTON  = '<input type="radio" name="' + __NAME +
                         '" id="' + __NAME + 'ON" value="1"' +
                         (__VALUE ? ' CHECKED' : __ACTION) +
                         ' /><label for="' + __NAME + 'ON">' +
                         __CONFIG.Label + '</label>';
    const __ELEMENTOFF = '<input type="radio" name="' + __NAME +
                         '" id="' + __NAME + 'OFF" value="0"' +
                         (__VALUE ? __ALTACTION : ' CHECKED') +
                         ' /><label for="' + __NAME + 'OFF">' +
                         __CONFIG.AltLabel + '</label>';
    const __ELEMENT = [
                          withTitle(__FORMLABEL, __VALUE ? __TITLEON : __TITLEOFF),
                          withTitle(__ELEMENTON, __TITLEON),
                          withTitle(__ELEMENTOFF, __TITLEOFF)
                      ];
    return ((__FORMLABEL && __FORMLABEL.length) ? __ELEMENT : __ELEMENT.slice(1, 3));
}
// Zeigt eine Option auf der Seite als Checkbox an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionCheckbox(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
    const __VALUELABEL = (__VALUE ? __CONFIG.Label : getValue(__CONFIG.AltLabel, __CONFIG.Label));
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
    const __TITLE = substParam(getValue(__VALUE ? __CONFIG.Title : getValue(__CONFIG.AltTitle, __CONFIG.Title), '$'), __VALUELABEL);
    return withTitle('<input type="checkbox" name="' + __NAME +
                     '" id="' + __NAME + '" value="' + __VALUE + '"' +
                     (__VALUE ? ' CHECKED' : "") + __ACTION + ' /><label for="' +
                     __NAME + '">' + __FORMLABEL + '</label>', __TITLE);
}
// Zeigt eine Option auf der Seite als Daten-Textfeld an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionTextarea(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt);
    const __ACTION = getFormActionEvent(opt, false, undefined, 'submit', undefined);
    const __SUBMIT = getValue(__CONFIG.Submit, "");
    //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': "");
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __CONFIG.Label);
    const __TITLE = substParam(getValue(__CONFIG.Title, '$'), __FORMLABEL);
    const __ELEMENTLABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>';
    const __ELEMENTTEXT = '<textarea name="' + __NAME + '" id="' + __NAME + '" cols="' + __CONFIG.Cols +
                           '" rows="' + __CONFIG.Rows + '"' + __ONSUBMIT + __ACTION + '>' +
                           safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>';
    return [ withTitle(__ELEMENTLABEL, __TITLE), __ELEMENTTEXT ];
}
// Zeigt eine Option auf der Seite als Button an
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionButton(opt) {
    const __CONFIG = getOptConfig(opt);
    const __NAME = getOptName(opt);
    const __VALUE = getOptValue(opt, false);
    const __ACTION = getFormActionEvent(opt, __VALUE, ! __VALUE, 'click', false);
    const __BUTTONLABEL = (__VALUE ? getValue(__CONFIG.AltLabel, __CONFIG.Label) : __CONFIG.Label);
    const __FORMLABEL = formatLabel(__CONFIG.FormLabel, __BUTTONLABEL);
    const __BUTTONTITLE = substParam(getValue(__VALUE ? getValue(__CONFIG.AltTitle, __CONFIG.Title) : __CONFIG.Title, '$'), __BUTTONLABEL);
    return '<label for="' + __NAME + '">' + __FORMLABEL + '</label>' +
           withTitle('<input type="button" name="" + ' + __NAME +
                     '" id="' + __NAME + '" value="' + __BUTTONLABEL +
                     '"' + __ACTION + '/>', __BUTTONTITLE);
}
// Zeigt eine Option auf der Seite an (je nach Typ)
// opt: Anzuzeigende Option
// return String mit dem HTML-Code
function getOptionElement(opt) {
    const __CONFIG = getOptConfig(opt);
    const __TYPE = getValue(__CONFIG.FormType, __CONFIG.Type);
    let element = "";
    if (! __CONFIG.Hidden) {
        switch (__TYPE) {
        case __OPTTYPES.MC : element = getOptionSelect(opt);
                             break;
        case __OPTTYPES.SW : if (__CONFIG.FormLabel !== undefined) {
                                 element = getOptionCheckbox(opt);
                             } else {
                                 element = getOptionRadio(opt);
                             }
                             break;
        case __OPTTYPES.TF : element = getOptionCheckbox(opt);
                             break;
        case __OPTTYPES.SD : element = getOptionTextarea(opt);
                             break;
        case __OPTTYPES.SI : element = getOptionButton(opt);
                             break;
        default :            break;
        }
        if ((typeof element) !== 'string') {
            element = '<div>' + Array.from(element).join('<br />') + '</div>';
        }
    }
    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);
}
// Baut das Benutzermenu auf der Seite auf
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
// return String mit dem HTML-Code
function getForm(optSet, optParams = { }) {
    const __FORM = '<form id="options" method="POST"><table><tbody><tr>';
    const __FORMEND = '</tr></tbody></table></form>';
    const __FORMWIDTH = getValue(optParams.formWidth, 3);
    const __FORMBREAK = getValue(optParams.formBreak, __FORMWIDTH);
    const __SHOWFORM = getOptValue(optSet.showForm, true) ? optParams.showForm : { 'showForm' : true };
    const __PRIOOPTS = groupData(optSet, opt => getOptConfig(opt).FormPrio);
    let form = __FORM;
    let count = 0;   // Bisher angezeigte Optionen
    let column = 0;  // Spalte der letzten Option (1-basierend)
    for (let optKeys of Object.values(__PRIOOPTS)) {
        for (let optKey of optKeys) {
            if (checkItem(optKey, __SHOWFORM, optParams.hideForm)) {
                const __ELEMENT = getOptionElement(optSet[optKey]);
                const __TDOPT = ((~ __ELEMENT.indexOf('|')) ? "" : ' colspan="2"');
                if (__ELEMENT) {
                    if (++count > __FORMBREAK) {
                        if (++column > __FORMWIDTH) {
                            column = 1;
                        }
                    }
                    if (column === 1) {
                        form += '</tr><tr>';
                    }
                    form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>';
                }
            }
        }
    }
    form += '\n' + __FORMEND;
    return form;
}
// Fuegt das Script in die Seite ein
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// return String mit dem HTML-Code fuer das Script
function getScript(optSet, optParams = { }) {
    //const __SCRIPT = '<script type="text/javascript">function activateMenu() { console.log("TADAAA!"); }</script>';
    //const __SCRIPT = '<script type="text/javascript">\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionNxt(key, value) { alert("SET " + key + " = " + value); }\n\tfunction doActionRst(key, value) { alert("RESET"); }\n</script>';
    //const __FORM = '<form method="POST"><input type="button" id="showOpts" name="showOpts" value="Optionen anzeigen" onclick="activateMenu()" /></form>';
    const __SCRIPT = "";
    //window.eval('function activateMenu() { console.log("TADAAA!"); }');
    return __SCRIPT;
}
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// anchor: Element, das als Anker fuer die Anzeige dient
// optSet: Gesetzte Optionen
// optParams: Eventuell notwendige Parameter
// 'showForm': Checkliste der auf der Seite sichtbaren Optionen (true fuer sichtbar)
// 'hideForm': Checkliste der auf der Seite unsichtbaren Optionen (true fuer unsichtbar)
// 'formWidth': Anzahl der Elemente pro Zeile
// 'formBreak': Elementnummer des ersten Zeilenumbruchs
function buildForm(anchor, optSet, optParams = { }) {
    __LOG[3]("buildForm()");
    const __FORM = getForm(optSet, optParams);
    const __SCRIPT = getScript(optSet, optParams);
    addForm(anchor, __FORM, __SCRIPT);
}
// Informationen zu hinzugefuegten Forms
const __FORMS = { };
// Zeigt das Optionsmenu auf der Seite an (im Gegensatz zum Benutzermenu)
// anchor: Element, das als Anker fuer die Anzeige dient
// form: HTML-Form des Optionsmenu (hinten angefuegt)
// script: Script mit Reaktionen
function addForm(anchor, form = "", script = "") {
    const __OLDFORM = __FORMS[anchor];
    const __REST = (__OLDFORM === undefined) ? anchor.innerHTML :
                   anchor.innerHTML.substring(0, anchor.innerHTML.length - __OLDFORM.Script.length - __OLDFORM.Form.length);
    __FORMS[anchor] = {
                          'Script' : script,
                          'Form'   : form
                      };
    anchor.innerHTML = __REST + script + form;
}
// ==================== 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 ====================
// ==================== 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 ====================
// Verarbeitet eine Ergebnis-Ansicht
function procErgebnisse() {
    return buildOptions(__OPTCONFIG, __OPTSET, {
                            'menuAnchor' : getTable(0, 'div'),
                            'formWidth'  : 2
                        }).then(optSet => {
            // Aktiviere Checkbox "Ergebnisse anzeigen" je nach Einstellung der Option
            getElement("erganzeigen").checked = getOptValue(optSet.showErgs);
        });
}
(() => {
    (async () => {
        try {
            await procErgebnisse().catch(defaultCatch);
            return 'OK';
        } catch (ex) {
            return defaultCatch(ex);
        }
    })().then(rc => {
            __LOG[1]('SCRIPT END', __DBMOD.Name, '(' + rc + ')');
        })
})();
// *** EOF ***