OS2.haupt: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
SLC (Diskussion | Beiträge) (Update Version 0.40 (Class, define, setConst, Logging, Speicher umgebaut, FreeValue/SelValue/AutoReset, Menü-Optionen interaktiv abfragen)) |
SLC (Diskussion | Beiträge) (Update Version 0.42 ('hl'-Links für gezielte Spielsuche)) |
||
(3 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.haupt''' | | colspan="2" style="padding:0.3em; background-color:#9C1818; font-size: 18px; color:#FFFFFF" align=center| '''OS2.haupt''' | ||
|- bgcolor="#FFCC00" | |- bgcolor="#FFCC00" | ||
| '''Dateiname''' | | '''Dateiname''' | ||
| ''' | | '''OS2.haupt.user.js''' | ||
|- bgcolor="#FFCC00" | |- bgcolor="#FFCC00" | ||
| '''Version''' | | '''Version''' | ||
| '''0. | | '''0.42 (WebExtensions)''' | ||
|- bgcolor="#FFCC00" | |- bgcolor="#FFCC00" | ||
| '''Autor''' | | '''Autor''' | ||
Zeile 19: | Zeile 20: | ||
|- bgcolor="#FFCC00" | |- bgcolor="#FFCC00" | ||
| '''Funktionalität''' | | '''Funktionalität''' | ||
| '''Vorschau für das letzte Spiel: Bilanz'''<br> '''Beschreibungen zu beiden Spielen'''<br> '''Anzeige des Spieltags bzw. der Runde'''<br> '''Benutzermenü für Optionen'''<br> '''Getrennte Option "Liga:" für Erst- und Zweitteam'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Link auf den jeweiligen Spieltag bzw. die jeweilige Runde'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis''' | | '''Vorschau für das letzte Spiel: Bilanz'''<br> '''Beschreibungen zu beiden Spielen'''<br> '''Anzeige des Spieltags bzw. der Runde'''<br> '''Benutzermenü für Optionen'''<br> '''Getrennte Option "Liga:" für Erst- und Zweitteam'''<br> '''Erweiterte Optionen auch auf der Seite'''<br> '''Link auf den jeweiligen Spieltag bzw. die jeweilige Runde'''<br> '''Interaktive Menü-Optionen'''<br> '''Gemeinsame Code- und Datenbasis'''<br> '''Gezielte "hl"-Links auf die eigenen Spiele''' | ||
|- 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.haupt.user.js Quellcode] [https://eselce.github.io/OS2.scripts/OS2.haupt.user.js INSTALLATION] == | |||
<pre> | <pre> | ||
// ==UserScript== | // ==UserScript== | ||
// @name OS2.haupt | // @name OS2.haupt | ||
// @namespace http://os.ongapo.com/ | // @namespace http://os.ongapo.com/ | ||
// @version 0. | // @version 0.42 | ||
// @copyright 2016+ | // @copyright 2016+ | ||
// @author Sven Loges (SLC) | // @author Sven Loges (SLC) | ||
// @description Managerbuero-Abschnitt aus dem Master-Script fuer Online Soccer 2.0 | // @description Managerbuero-Abschnitt aus dem Master-Script fuer Online Soccer 2.0 | ||
// @include | // @include /^https?://(www\.)?(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/haupt\.php(\?changetosecond=\w+(&\w+=?[+\w]+)*)?(#\w+)?$/ | ||
// @grant GM.getValue | |||
// @ | // @grant GM.setValue | ||
// @ | // @grant GM.deleteValue | ||
// @ | // @grant GM.registerMenuCommand | ||
// @ | // @grant GM.info | ||
// @ | // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js | ||
// @ | |||
// @grant GM_getValue | // @grant GM_getValue | ||
// @grant GM_setValue | // @grant GM_setValue | ||
Zeile 48: | Zeile 50: | ||
// ==/UserScript== | // ==/UserScript== | ||
// ECMAScript 6: | // ECMAScript 6: | ||
/* jshint esnext: true */ | /* jshint esnext: true */ | ||
/* jshint moz: true */ | /* jshint moz: true */ | ||
Zeile 98: | Zeile 100: | ||
'Name' : "saison", | 'Name' : "saison", | ||
'Type' : __OPTTYPES.MC, | 'Type' : __OPTTYPES.MC, | ||
'ValType' : | 'ValType' : 'Number', | ||
'FreeValue' : true, | 'FreeValue' : true, | ||
'SelValue' : false, | 'SelValue' : false, | ||
'Choice' : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], | 'Choice' : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ], | ||
'Default' : | 'Default' : 18, | ||
'Action' : __OPTACTION.NXT, | 'Action' : __OPTACTION.NXT, | ||
'Label' : "Saison: $", | 'Label' : "Saison: $", | ||
Zeile 111: | Zeile 113: | ||
'Name' : "ligaSize", | 'Name' : "ligaSize", | ||
'Type' : __OPTTYPES.MC, | 'Type' : __OPTTYPES.MC, | ||
'ValType' : | 'ValType' : 'Number', | ||
'AutoReset' : true, | 'AutoReset' : true, | ||
'Choice' : [ 10, 18, 20 ], | 'Choice' : [ 10, 18, 20 ], | ||
Zeile 122: | Zeile 124: | ||
'Name' : "dataZAT", | 'Name' : "dataZAT", | ||
'Type' : __OPTTYPES.SD, | 'Type' : __OPTTYPES.SD, | ||
'ValType' : | 'ValType' : 'Number', | ||
'Hidden' : true, | 'Hidden' : true, | ||
'Serial' : true, | 'Serial' : true, | ||
Zeile 142: | Zeile 144: | ||
'Serial' : true, | 'Serial' : true, | ||
'Permanent' : true, | 'Permanent' : true, | ||
'Default' : undefined, // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'LdNr' : 0, 'LgNr' : 0 } | 'Default' : undefined, // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'TmNr' : 0, 'LdNr' : 0, 'LgNr' : 0 } | ||
'Submit' : undefined, | 'Submit' : undefined, | ||
'Cols' : 36, | 'Cols' : 36, | ||
Zeile 151: | Zeile 153: | ||
}, | }, | ||
'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 159: | Zeile 162: | ||
}, | }, | ||
'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 169: | Zeile 173: | ||
}, | }, | ||
'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, | ||
Zeile 176: | Zeile 181: | ||
}, | }, | ||
'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 181: | Zeile 187: | ||
'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 191: | Zeile 199: | ||
// ==================== 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) | // Kompatibilitaetsfunktion zur Ermittlung des Namens einer Funktion (falle <Function>.name nicht vorhanden ist) | ||
Zeile 201: | Zeile 237: | ||
} | } | ||
// | // 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 | // Gibt eine Meldung in der Console aus und oeffnet ein Bestaetigungsfenster mit der Meldung | ||
Zeile 231: | Zeile 263: | ||
// message: Der Meldungs-Text | // message: Der Meldungs-Text | ||
// data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben | // data: Ein Wert. Ist er angegeben, wird er in der Console ausgegeben | ||
// return Liefert die Parameter zurueck | |||
function showAlert(label, message, data = undefined) { | function showAlert(label, message, data = undefined) { | ||
__LOG[ | __LOG[0](label + ": " + message); | ||
if (data !== undefined) { | if (data !== undefined) { | ||
Zeile 239: | Zeile 272: | ||
alert(label + "\n\n" + message); | 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); | |||
} | |||
} | } | ||
Zeile 272: | Zeile 338: | ||
console.assert((__BASE === null) || ((typeof __BASE) === 'function'), "No function:", __BASE); | console.assert((__BASE === null) || ((typeof __BASE) === 'function'), "No function:", __BASE); | ||
console.assert((typeof initFun) === 'function', " | console.assert((typeof initFun) === 'function', "Not a function:", initFun); | ||
this.init = initFun; | this.init = initFun; | ||
Zeile 300: | Zeile 366: | ||
const __CREATEPROTO = ((createProto === undefined) ? true : createProto); | const __CREATEPROTO = ((createProto === undefined) ? true : createProto); | ||
console.assert((typeof this) === 'function'); | console.assert((typeof this) === 'function', "Not a function:", this); | ||
console.assert((typeof __MEMBERS) === 'object'); | console.assert((typeof __MEMBERS) === 'object', "Not an object:", __MEMBERS); | ||
const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init); | const __CLASS = new Class(this.name || __MEMBERS.__name, baseClass, initFun || __MEMBERS.__init); | ||
Zeile 344: | Zeile 410: | ||
return this.className; | return this.className; | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse Class ==================== | // ==================== Ende Abschnitt fuer Klasse Class ==================== | ||
Zeile 391: | Zeile 457: | ||
this.home = home; | this.home = home; | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse Delims ==================== | // ==================== Ende Abschnitt fuer Klasse Delims ==================== | ||
Zeile 470: | Zeile 536: | ||
this.node = node; | this.node = node; | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse UriDelims ==================== | // ==================== Ende Abschnitt fuer Klasse UriDelims ==================== | ||
Zeile 569: | Zeile 635: | ||
return __DIRS.slice(1); | return __DIRS.slice(1); | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse Path ==================== | // ==================== Ende Abschnitt fuer Klasse Path ==================== | ||
Zeile 628: | Zeile 694: | ||
const __NOSCHEME = this.stripSchemePrefix(path); | const __NOSCHEME = this.stripSchemePrefix(path); | ||
const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1); | const __INDEXHOST = (__NOSCHEME ? __NOSCHEME.indexOf(__HOSTDELIM) : -1); | ||
const __PATH = (~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME; | const __PATH = ((~ __INDEXHOST) ? __NOSCHEME.substring(__INDEXHOST + __HOSTDELIM.length) : __NOSCHEME); | ||
const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1); | const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1); | ||
const __HOSTPORT = (~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined; | const __HOSTPORT = ((~ __INDEXHOSTPORT) ? __PATH.substring(0, __INDEXHOSTPORT) : undefined); | ||
const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1); | const __INDEXPORT = (__HOSTPORT ? __HOSTPORT.indexOf(__PORTDELIM) : -1); | ||
const __HOST = (~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT; | const __HOST = ((~ __INDEXPORT) ? __HOSTPORT.substring(0, __INDEXPORT) : __HOSTPORT); | ||
const __PORT = (~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined; | const __PORT = ((~ __INDEXPORT) ? __HOSTPORT.substring(__INDEXPORT + __PORTDELIM.length) : undefined); | ||
return { | return { | ||
Zeile 644: | Zeile 710: | ||
const __ROOTDELIM = this.delims.root + this.delims.delim; | const __ROOTDELIM = this.delims.root + this.delims.delim; | ||
const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1); | const __INDEXHOST = (path ? path.indexOf(__HOSTDELIM) : -1); | ||
const __PATH = (~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path; | const __PATH = ((~ __INDEXHOST) ? path.substring(__INDEXHOST + __HOSTDELIM.length) : path); | ||
const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1); | const __INDEXHOSTPORT = (__PATH ? __PATH.indexOf(__ROOTDELIM) : -1); | ||
return (~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH; | return ((~ __INDEXHOSTPORT) ? __PATH.substring(__INDEXHOSTPORT) : __PATH); | ||
}, | }, | ||
'getSchemePrefix' : function(path = undefined) { | 'getSchemePrefix' : function(path = undefined) { | ||
Zeile 653: | Zeile 719: | ||
const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1); | const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1); | ||
return (~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined; | return ((~ __INDEXSCHEME) ? path.substring(0, __INDEXSCHEME) : undefined); | ||
}, | }, | ||
'stripSchemePrefix' : function(path = undefined) { | 'stripSchemePrefix' : function(path = undefined) { | ||
Zeile 659: | Zeile 725: | ||
const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1); | const __INDEXSCHEME = (path ? path.indexOf(__SCHEMEDELIM) : -1); | ||
return (~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path; | return ((~ __INDEXSCHEME) ? path.substring(__INDEXSCHEME + __INDEXSCHEME.length) : path); | ||
}, | }, | ||
'getNodeSuffix' : function(path = undefined) { | 'getNodeSuffix' : function(path = undefined) { | ||
Zeile 665: | Zeile 731: | ||
const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1); | const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1); | ||
return (~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined; | return ((~ __INDEXNODE) ? path.substring(__INDEXNODE + __NODEDELIM.length) : undefined); | ||
}, | }, | ||
'stripNodeSuffix' : function(path = undefined) { | 'stripNodeSuffix' : function(path = undefined) { | ||
Zeile 671: | Zeile 737: | ||
const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1); | const __INDEXNODE = (path ? path.lastIndexOf(__NODEDELIM) : -1); | ||
return (~ __INDEXNODE) ? path.substring(0, __INDEXNODE) : path; | return ((~ __INDEXNODE) ? path.substring(0, __INDEXNODE) : path); | ||
}, | }, | ||
'getQueryString' : function(path = undefined) { | 'getQueryString' : function(path = undefined) { | ||
Zeile 678: | Zeile 744: | ||
const __INDEXQUERY = (__PATH ? __PATH.indexOf(__QUERYDELIM) : -1); | const __INDEXQUERY = (__PATH ? __PATH.indexOf(__QUERYDELIM) : -1); | ||
return (~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined; | return ((~ __INDEXQUERY) ? __PATH.substring(__INDEXQUERY + __QUERYDELIM.length) : undefined); | ||
}, | }, | ||
'stripQueryString' : function(path = undefined) { | 'stripQueryString' : function(path = undefined) { | ||
Zeile 684: | Zeile 750: | ||
const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1); | const __INDEXQUERY = (path ? path.indexOf(__QUERYDELIM) : -1); | ||
return (~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path; | return ((~ __INDEXQUERY) ? path.substring(0, __INDEXQUERY) : path); | ||
}, | }, | ||
'formatParams' : function(params, formatFun, delim = ' ', assign = '=') { | 'formatParams' : function(params, formatFun, delim = ' ', assign = '=') { | ||
Zeile 706: | Zeile 772: | ||
if (__PARAM) { | if (__PARAM) { | ||
const __INDEX = __PARAM.indexOf(assign); | const __INDEX = __PARAM.indexOf(assign); | ||
const __KEY = (~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM; | const __KEY = ((~ __INDEX) ? __PARAM.substring(0, __INDEX) : __PARAM); | ||
const __VAL = (~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true; | const __VAL = ((~ __INDEX) ? parseFun(__PARAM.substring(__INDEX + assign.length)) : true); | ||
__RET[__KEY] = __VAL; | __RET[__KEY] = __VAL; | ||
Zeile 727: | Zeile 793: | ||
const __LOWER = (value ? value.toLowerCase() : undefined); | const __LOWER = (value ? value.toLowerCase() : undefined); | ||
if ((__LOWER === | if ((__LOWER === 'true') || (__LOWER === 'false')) { | ||
return (value === | return (value === 'true'); | ||
} | } | ||
} | } | ||
Zeile 780: | Zeile 846: | ||
return Path.prototype.getDirs.call(this, __PATH); | return Path.prototype.getDirs.call(this, __PATH); | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse URI ==================== | // ==================== Ende Abschnitt fuer Klasse URI ==================== | ||
Zeile 820: | Zeile 886: | ||
return this.getPath(); | return this.getPath(); | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse Directory ==================== | // ==================== Ende Abschnitt fuer Klasse Directory ==================== | ||
Zeile 848: | Zeile 914: | ||
return ret; | return ret; | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse ObjRef ==================== | // ==================== Ende Abschnitt fuer Klasse ObjRef ==================== | ||
Zeile 893: | Zeile 959: | ||
// Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert | // Gibt ein Produkt zurueck. Ist einer der Multiplikanten nicht definiert, wird ein Alternativwert geliefert | ||
// valueA: Ein | // valueA: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert | ||
// valueB: Ein | // valueB: Ein Multiplikant. Ist dieser undefined, wird als Produkt defValue zurueckgeliefert | ||
// digits: Anzahl der Stellen nach dem Komma fuer das Produkt (Default: 0) | // 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) | // defValue: Default-Wert fuer den Fall, dass ein Multiplikant nicht gesetzt ist (Default: NaN) | ||
Zeile 903: | Zeile 969: | ||
if ((valueA !== undefined) && (valueB !== undefined)) { | if ((valueA !== undefined) && (valueB !== undefined)) { | ||
product = parseFloat(valueA) * parseFloat(valueB); | product = parseFloat(valueA) * parseFloat(valueB); | ||
} | |||
if (isNaN(product)) { | |||
product = defValue; | |||
} | } | ||
Zeile 908: | Zeile 978: | ||
} | } | ||
// Ueberprueft, ob ein Objekt einer bestimmten Klasse angehoert (ggfs. per Vererbung) | // 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 | // obj: Ein (generisches) Objekt | ||
// base: Eine Objektklasse (Konstruktor-Funktion) | // base: Eine Objektklasse (Konstruktor-Funktion) | ||
Zeile 914: | Zeile 1.000: | ||
function instanceOf(obj, base) { | function instanceOf(obj, base) { | ||
while (obj !== null) { | while (obj !== null) { | ||
if (obj === base.prototype) | if (obj === base.prototype) { | ||
return true; | return true; | ||
} | |||
if ((typeof obj) === 'xml') { // Sonderfall mit Selbstbezug | if ((typeof obj) === 'xml') { // Sonderfall mit Selbstbezug | ||
return (base.prototype === XML.prototype); | return (base.prototype === XML.prototype); | ||
Zeile 965: | Zeile 1.052: | ||
let active = true; | let active = true; | ||
if (inList | if (inList) { | ||
active = (inList[item] === true); // gesetzt und true | active = (inList[item] === true); // gesetzt und true | ||
} | } | ||
if (exList | if (exList) { | ||
if (exList[item] === true) { // gesetzt und true | if (exList[item] === true) { // gesetzt und true | ||
active = false; // NICHT anzeigen | active = false; // NICHT anzeigen | ||
Zeile 987: | Zeile 1.074: | ||
if (checkItem(item, addList, ignList)) { | if (checkItem(item, addList, ignList)) { | ||
data[item] = addData[item]; | 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]; | |||
} | } | ||
} | } | ||
Zeile 1.068: | Zeile 1.170: | ||
} | } | ||
// | // Replacer fuer JSON.stringify() oder safeStringify(), der Arrays kompakter darstellt | ||
// | // key: Der uebergebene Schluessel | ||
// value: | // value: Der uebergebene Wert | ||
// return | // return Fuer Arrays eine kompakte Darstellung, sonst derselbe Wert | ||
function | 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 | 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 ( | 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); | |||
} | } | ||
if ( | 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 | return result; | ||
} | } | ||
// | // Speichert einen String/Integer/Boolean-Wert unter einem Namen ab | ||
// | // name: GM.setValue()-Name, unter dem die Daten gespeichert werden | ||
// name: Name | // value: Zu speichernder String/Integer/Boolean-Wert | ||
// value: | // return Promise auf ein Objekt, das 'name' und 'value' der Operation enthaelt | ||
// | function storeValue(name, value) { | ||
// return | __LOG[4](name + " >> " + value); | ||
function | |||
return | 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 | ||
// return | // defValue: Default-Wert fuer den Fall, dass nichts gespeichert ist | ||
function | // 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(); | |||
} | } | ||
} | |||
return ( | |||
// 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 | // Fuehrt die in einem Storage gespeicherte Operation aus | ||
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv | // memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv | ||
// return Array von | // return Array von Objekten mit 'cmd' / 'key' / 'val' (derzeit maximal ein Kommando) oder undefined | ||
function | function getStoredCmds(memory = undefined) { | ||
const | const __STORAGE = getMemory(memory); | ||
const | const __MEMORY = __STORAGE.Value; | ||
const | const __RUNPREFIX = __STORAGE.Prefix; | ||
const __STOREDCMDS = []; | |||
if (__MEMORY !== undefined) { | |||
const | const __GETITEM = function(item) { | ||
const __CMD = | return __MEMORY.getItem(__RUNPREFIX + item); | ||
const __KEY = | }; | ||
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"... | |||
} | } | ||
switch (__OPTACTION[__CMD]) { | |||
case __OPTACTION.SET : __LOG[4]("SET '" + __KEY + "' " + __VAL); | __STOREDCMDS.push({ | ||
setStored(__KEY, __VAL, false, false); | 'cmd' : __CMD, | ||
break; | 'key' : __KEY, | ||
case __OPTACTION.NXT : __LOG[4]("SETNEXT '" + __KEY + "' " + __VAL); | 'val' : value | ||
//setNextStored(__CONFIG.Choice, __KEY, __VAL, false, false); | }); | ||
setStored(__KEY, __VAL, false, false); | } | ||
break; | |||
case __OPTACTION.RST : __LOG[4]("RESET (delayed)"); | __DELITEM('cmd'); | ||
__LOADEDCMDS.push(__STORED); | __DELITEM('key'); | ||
break; | __DELITEM('val'); | ||
default : break; | } | ||
} | |||
} else { | 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]) { | switch (__OPTACTION[__CMD]) { | ||
case __OPTACTION.SET : | case __OPTACTION.SET : | ||
Zeile 1.217: | Zeile 1.449: | ||
break; | break; | ||
case __OPTACTION.RST : __LOG[4]("RESET"); | case __OPTACTION.RST : __LOG[4]("RESET"); | ||
resetOptions(optSet, false); | await resetOptions(optSet, false); | ||
loadOptions(optSet); // Reset auf umbenannte Optionen anwenden! | await loadOptions(optSet); // Reset auf umbenannte Optionen anwenden! | ||
break; | break; | ||
default : break; | default : break; | ||
Zeile 1.320: | Zeile 1.552: | ||
// force: Laedt auch Optionen mit 'AutoReset'-Attribut | // force: Laedt auch Optionen mit 'AutoReset'-Attribut | ||
// return Gesetzter Wert | // return Gesetzter Wert | ||
function getOptValue(opt, defValue = undefined, load = true, force = false) { | function getOptValue(opt, defValue = undefined, load = true, asyncLoad = false, force = false) { | ||
let value; | let value; | ||
if (opt !== undefined) { | if (opt !== undefined) { | ||
if (load && ! opt.Loaded) { | 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 { | } else { | ||
value = opt.Value; | value = opt.Value; | ||
Zeile 1.415: | Zeile 1.652: | ||
// Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus | // Gibt rekursiv und detailliert die Groesse des benutzten Speichers fuer ein Objekt aus | ||
// value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird | // value: (Enumerierbares) Objekt oder Wert, dessen Groesse gemessen wird | ||
// out: Logfunktion, etwa | // out: Logfunktion, etwa __LOG[4] | ||
// depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen) | // depth: Gewuenschte Rekursionstiefe (0 = nur dieses Objekt, -1 = alle Ebenen) | ||
// name: Name des Objekts | // name: Name des Objekts | ||
Zeile 1.424: | Zeile 1.661: | ||
const __SIZE = value.length; | const __SIZE = value.length; | ||
__OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value. | __OUT("USAGE: " + name + '\t' + __SIZE + '\t' + value.slice(0, 255)); | ||
} else if ((typeof value) === 'object') { | } else if ((typeof value) === 'object') { | ||
if (depth === 0) { | if (depth === 0) { | ||
Zeile 1.446: | Zeile 1.683: | ||
// Restauriert den vorherigen Speicher (der in einer Option definiert ist) | // Restauriert den vorherigen Speicher (der in einer Option definiert ist) | ||
// opt: Option zur Wahl des Speichers | // opt: Option zur Wahl des Speichers | ||
// return | // return Promise auf gesuchten Speicher oder Null-Speicher ('inaktiv') | ||
function restoreMemoryByOpt(opt) { | async function restoreMemoryByOpt(opt) { | ||
// Memory Storage fuer vorherige Speicherung... | // Memory Storage fuer vorherige Speicherung... | ||
const __STORAGE = getOptValue(opt, __MEMNORMAL, true, true); | const __STORAGE = await getOptValue(opt, __MEMNORMAL, true, true, true); | ||
return __OPTMEM[__STORAGE]; | return __OPTMEM[__STORAGE]; | ||
Zeile 1.457: | Zeile 1.694: | ||
// opt: Option zur Wahl des Speichers | // opt: Option zur Wahl des Speichers | ||
// saveOpt: Option zur Speicherung der Wahl des Speichers (fuer restoreMemoryByOpt) | // 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 | // return Gesuchter Speicher oder Null-Speicher ('inaktiv'), falls speichern nicht moeglich ist | ||
function startMemoryByOpt(opt, saveOpt = undefined) { | function startMemoryByOpt(opt, saveOpt = undefined, onFulfilled = undefined, onRejected = undefined) { | ||
// Memory Storage fuer naechste Speicherung... | // Memory Storage fuer naechste Speicherung... | ||
let storage = getOptValue(opt, __MEMNORMAL); | let storage = getOptValue(opt, __MEMNORMAL); | ||
Zeile 1.471: | Zeile 1.710: | ||
if (saveOpt !== undefined) { | if (saveOpt !== undefined) { | ||
setOpt(saveOpt, storage, false); | setOpt(saveOpt, storage, false, onFulfilled, onRejected); | ||
} | } | ||
Zeile 1.482: | Zeile 1.721: | ||
// Initialisiert das Script-Modul und ermittelt die beschreibenden Daten | // Initialisiert das Script-Modul und ermittelt die beschreibenden Daten | ||
// meta: Metadaten des Scripts (Default: | // meta: Metadaten des Scripts (Default: GM.info.script) | ||
// return Beschreibende Daten fuer __DBMOD | // return Beschreibende Daten fuer __DBMOD | ||
function ScriptModule(meta) { | function ScriptModule(meta) { | ||
'use strict'; | 'use strict'; | ||
const __META = getValue(meta, | const __META = getValue(meta, GM.info.script); | ||
const __PROPS = { | const __PROPS = { | ||
'name' : true, | 'name' : true, | ||
Zeile 1.593: | Zeile 1.832: | ||
// funOff: Funktion zum Ausschalten | // funOff: Funktion zum Ausschalten | ||
// keyOff: Hotkey zum Ausschalten im Menu | // keyOff: Hotkey zum Ausschalten im Menu | ||
// return Promise von GM.registerMenuCommand() | |||
function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) { | function registerMenuOption(val, menuOn, funOn, keyOn, menuOff, funOff, keyOff) { | ||
const __ON = (val ? '*' : ""); | const __ON = (val ? '*' : ""); | ||
Zeile 1.600: | Zeile 1.840: | ||
if (val) { | if (val) { | ||
return GM.registerMenuCommand(menuOff, funOff, keyOff).then(result => menuOn); | |||
} else { | } else { | ||
return GM.registerMenuCommand(menuOn, funOn, keyOn).then(result => menuOff); | |||
} | } | ||
} | } | ||
Zeile 1.612: | Zeile 1.852: | ||
// fun: Funktion zum Setzen des naechsten Wertes | // fun: Funktion zum Setzen des naechsten Wertes | ||
// key: Hotkey zum Setzen des naechsten Wertes im Menu | // key: Hotkey zum Setzen des naechsten Wertes im Menu | ||
// return Promise von GM.registerMenuCommand() | |||
function registerNextMenuOption(val, arr, menu, fun, key) { | function registerNextMenuOption(val, arr, menu, fun, key) { | ||
const __MENU = menu | const __MENU = substParam(menu, val); | ||
let options = "OPTION " + __MENU; | let options = "OPTION " + __MENU; | ||
Zeile 1.625: | Zeile 1.866: | ||
__LOG[3](options); | __LOG[3](options); | ||
return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU); | |||
} | } | ||
Zeile 1.635: | Zeile 1.876: | ||
// hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar) | // hidden: Angabe, ob Menupunkt nicht sichtbar sein soll (Default: sichtbar) | ||
// serial: Serialization fuer komplexe Daten | // serial: Serialization fuer komplexe Daten | ||
// return Promise von GM.registerMenuCommand() (oder String-Version des Wertes) | |||
function registerDataOption(val, menu, fun, key, hidden = false, serial = true) { | function registerDataOption(val, menu, fun, key, hidden = false, serial = true) { | ||
const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val); | const __VALUE = ((serial && (val !== undefined)) ? safeStringify(val) : val); | ||
const __MENU = | const __MENU = substParam(menu, __VALUE); | ||
const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU + | const __OPTIONS = (hidden ? "HIDDEN " : "") + "OPTION " + __MENU + | ||
getValue(__VALUE, "", " = " + __VALUE); | getValue(__VALUE, "", " = " + __VALUE); | ||
Zeile 1.643: | Zeile 1.885: | ||
__LOG[hidden ? 4 : 3](__OPTIONS); | __LOG[hidden ? 4 : 3](__OPTIONS); | ||
if ( | if (hidden) { | ||
return Promise.resolve(__VALUE); | |||
} else { | |||
return GM.registerMenuCommand(__MENU, fun, key).then(result => __MENU); | |||
} | } | ||
} | } | ||
Zeile 1.650: | Zeile 1.894: | ||
// 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); | ||
Zeile 1.661: | Zeile 1.906: | ||
if (! __CONFIG.HiddenMenu) { | if (! __CONFIG.HiddenMenu) { | ||
switch (__CONFIG.Type) { | switch (__CONFIG.Type) { | ||
case __OPTTYPES.MC : registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY) | case __OPTTYPES.MC : return registerNextMenuOption(__VALUE, __CONFIG.Choice, __LABEL, __ACTION, __HOTKEY); | ||
case __OPTTYPES.SW : return registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY, | |||
case __OPTTYPES.SW : 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.TF : registerMenuOption(__VALUE, __LABEL, __ACTION, __HOTKEY, | 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); | |||
case __OPTTYPES.SD : registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL) | |||
case __OPTTYPES.SI : registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL) | |||
default : | |||
} | } | ||
} else { | } else { | ||
// Nur Anzeige im Log... | // Nur Anzeige im Log... | ||
registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL); | return registerDataOption(__VALUE, __LABEL, __ACTION, __HOTKEY, __HIDDEN, __SERIAL); | ||
} | } | ||
} | } | ||
Zeile 1.729: | Zeile 1.969: | ||
switch (optAction) { | switch (optAction) { | ||
case __OPTACTION.SET : fun = function() { | case __OPTACTION.SET : fun = function() { | ||
return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD); | return setOptByName(optSet, item, __CONFIG.SetValue, __RELOAD).catch(defaultCatch); | ||
}; | }; | ||
break; | break; | ||
case __OPTACTION.NXT : fun = function() { | case __OPTACTION.NXT : fun = function() { | ||
return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD, | return promptNextOptByName(optSet, item, __CONFIG.SetValue, __RELOAD, | ||
__CONFIG.FreeValue, __CONFIG.SelValue, __CONFIG.MinChoice); | __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 1.787: | Zeile 2.029: | ||
const __OBJREF = getSharedRef(__SHARED, item); // Gemeinsame Daten | const __OBJREF = getSharedRef(__SHARED, item); // Gemeinsame Daten | ||
if (getValue(__SHARED.item, '$') !== '$') { // | if (getValue(__SHARED.item, '$') !== '$') { // __OBJREF ist ein Item | ||
const __REF = valueOf(__OBJREF); | const __REF = valueOf(__OBJREF); | ||
Zeile 1.793: | Zeile 2.035: | ||
addProps(config, getOptConfig(__REF)); | addProps(config, getOptConfig(__REF)); | ||
addProps(config, optConfig); | addProps(config, optConfig); | ||
config.setConst('SharedData', getOptValue(__REF)); | config.setConst('SharedData', getOptValue(__REF), false); // Wert muss schon da sein, NICHT nachladen, sonst ggfs. Promise | ||
} else { // | } else { // __OBJREF enthaelt die Daten selbst | ||
if (! config.Name) { | if (! config.Name) { | ||
config.Name = __OBJREF.getPath(); | config.Name = __OBJREF.getPath(); | ||
Zeile 1.825: | Zeile 2.067: | ||
const __CONFIG = getSharedConfig(__OPTCONFIG, opt); | const __CONFIG = getSharedConfig(__OPTCONFIG, opt); | ||
const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action); | const __ALTACTION = getValue(__CONFIG.AltAction, __CONFIG.Action); | ||
// Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat? | // Gab es vorher einen Aufruf, der einen Stub-Eintrag erzeugt hat, und wurden Daten geladen? | ||
const | const __LOADED = ((preInit === false) && optSet[opt].Loaded); | ||
const | const __PROMISE = ((__LOADED || ! optSet[opt]) ? undefined : optSet[opt].Promise); | ||
const __VALUE = ( | const __VALUE = (__LOADED ? optSet[opt].Value : undefined); | ||
optSet[opt] = { | optSet[opt] = { | ||
Zeile 1.834: | Zeile 2.076: | ||
'Config' : __CONFIG, | 'Config' : __CONFIG, | ||
'Loaded' : (__ISSHARED || __LOADED), | 'Loaded' : (__ISSHARED || __LOADED), | ||
'Promise' : __PROMISE, | |||
'Value' : initOptValue(__CONFIG, __VALUE), | 'Value' : initOptValue(__CONFIG, __VALUE), | ||
'SetValue' : __CONFIG.SetValue, | 'SetValue' : __CONFIG.SetValue, | ||
Zeile 1.845: | Zeile 2.088: | ||
'Config' : __OPTCONFIG, | 'Config' : __OPTCONFIG, | ||
'Loaded' : false, | 'Loaded' : false, | ||
'Promise' : undefined, | |||
'Value' : initOptValue(__OPTCONFIG), | 'Value' : initOptValue(__OPTCONFIG), | ||
'ReadOnly' : (__ISSHARED || __OPTCONFIG.ReadOnly) | 'ReadOnly' : (__ISSHARED || __OPTCONFIG.ReadOnly) | ||
Zeile 1.877: | Zeile 2.121: | ||
// 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, classification = undefined) { | async function startOptions(optConfig, optSet = undefined, classification = undefined) { | ||
optSet = initOptions(optConfig, optSet, true); // PreInit | optSet = initOptions(optConfig, optSet, true); // PreInit | ||
// Memory Storage fuer vorherige Speicherung... | // Memory Storage fuer vorherige Speicherung... | ||
myOptMemSize = getMemSize(myOptMem = restoreMemoryByOpt(optSet.oldStorage)); | myOptMemSize = getMemSize(myOptMem = await restoreMemoryByOpt(optSet.oldStorage)); | ||
// Zwischengespeicherte Befehle auslesen... | // Zwischengespeicherte Befehle auslesen... | ||
const __STOREDCMDS = getStoredCmds(myOptMem); | const __STOREDCMDS = getStoredCmds(myOptMem); | ||
// ... ermittelte Befehle | // ... ermittelte Befehle ausfuehren... | ||
const __LOADEDCMDS = runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad | const __LOADEDCMDS = await runStoredCmds(__STOREDCMDS, optSet, true); // BeforeLoad | ||
// Bisher noch nicht geladenene Optionen laden... | // Bisher noch nicht geladenene Optionen laden... | ||
loadOptions(optSet); | await loadOptions(optSet); | ||
// Memory Storage fuer naechste Speicherung... | // Memory Storage fuer naechste Speicherung... | ||
Zeile 1.903: | Zeile 2.147: | ||
if (classification !== undefined) { | if (classification !== undefined) { | ||
// Umbenennungen durchfuehren... | // Umbenennungen durchfuehren... | ||
classification.renameOptions(); | await classification.renameOptions(); | ||
} | } | ||
// ... ermittelte Befehle | // ... ermittelte Befehle ausfuehren... | ||
runStoredCmds(__LOADEDCMDS, optSet, false); // Rest | await runStoredCmds(__LOADEDCMDS, optSet, false); // Rest | ||
// Als globale Daten speichern... | // Als globale Daten speichern... | ||
Zeile 1.924: | Zeile 2.168: | ||
// '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")); | ||
} | } | ||
Zeile 1.932: | Zeile 2.177: | ||
buildForm(optParams.menuAnchor, optSet, optParams); | buildForm(optParams.menuAnchor, optSet, optParams); | ||
} | } | ||
return optSet; | |||
} | } | ||
Zeile 1.939: | Zeile 2.186: | ||
// 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 1.968: | Zeile 2.217: | ||
// 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 | // return Gesetzter Wert | ||
function setNextOpt(opt, value = undefined, reload = false) { | function setNextOpt(opt, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) { | ||
return setOpt(opt, getNextOpt(opt, value), reload); | return setOpt(opt, getNextOpt(opt, value), reload, onFulfilled, onRejected); | ||
} | } | ||
Zeile 1.979: | Zeile 2.230: | ||
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false) | // freeValue: Angabe, ob Freitext zugelassen ist (Default: false) | ||
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3) | // minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3) | ||
// onFulfilled: Reaktion auf Speicherung im resolve-Fall (1. Promise.then()-Parameter) | |||
// onRejected: Reaktion auf Speicherung im reject-Fall (2. Promise.then()-Parameter) | |||
// return Gesetzter Wert | // return Gesetzter Wert | ||
function promptNextOpt(opt, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3) { | 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; | const __CHOICE = __CONFIG.Choice; | ||
if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) { | if (value || (! __CHOICE) || (__CHOICE.length < minChoice)) { | ||
return setNextOpt(opt, value, reload); | return setNextOpt(opt, value, reload, onFulfilled, onRejected); | ||
} | } | ||
Zeile 1.998: | Zeile 2.251: | ||
message += (index + 1) + ") " + __CHOICE[index] + '\n'; | message += (index + 1) + ") " + __CHOICE[index] + '\n'; | ||
} | } | ||
message += "\nNummer eingeben:"; | message += "\nNummer oder Wert eingeben:"; | ||
} else { | } else { | ||
message = __CHOICE.join(" / ") + "\n\nWert eingeben:"; | message = __CHOICE.join(" / ") + "\n\nWert eingeben:"; | ||
Zeile 2.020: | Zeile 2.273: | ||
if (nextVal !== __VALUE) { | if (nextVal !== __VALUE) { | ||
if (nextVal) { | if (nextVal) { | ||
return setOpt(opt, nextVal, reload); | return setOpt(opt, nextVal, reload, onFulfilled, onRejected); | ||
} | } | ||
const __LABEL = __CONFIG.Label | const __LABEL = substParam(__CONFIG.Label, __VALUE); | ||
showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER); | showAlert(__LABEL, "Ung\xFCltige Eingabe: " + __ANSWER); | ||
Zeile 2.029: | Zeile 2.282: | ||
} | } | ||
} catch (ex) { | } catch (ex) { | ||
__LOG[ | __LOG[0]("promptNextOpt: " + ex.message); | ||
} | } | ||
Zeile 2.041: | Zeile 2.294: | ||
// 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); | ||
} | } | ||
Zeile 2.064: | Zeile 2.319: | ||
// 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 | // return Gesetzter Wert | ||
function setNextOptByName(optSet, item, value = undefined, reload = false) { | function setNextOptByName(optSet, item, value = undefined, reload = false, onFulfilled = undefined, onRejected = undefined) { | ||
const __OPT = getOptByName(optSet, item); | const __OPT = getOptByName(optSet, item); | ||
return setNextOpt(__OPT, value, reload); | return setNextOpt(__OPT, value, reload, onFulfilled, onRejected); | ||
} | } | ||
Zeile 2.078: | Zeile 2.335: | ||
// freeValue: Angabe, ob Freitext zugelassen ist (Default: false) | // freeValue: Angabe, ob Freitext zugelassen ist (Default: false) | ||
// minChoice: Ab wievielen Auswahlmoeglichkeiten soll abgefragt werden? (Default: 3) | // 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 promptNextOptByName(optSet, item, value = undefined, reload = false, freeValue = false, selValue = true, minChoice = 3) { | 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 promptNextOpt(__OPT, value, reload, freeValue, selValue, minChoice); | 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()"); | __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); | |||
} | } | ||
} | } | ||
Zeile 2.098: | Zeile 2.360: | ||
// opt: Zu invalidierende Option | // opt: Zu invalidierende Option | ||
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut | // force: Invalidiert auch Optionen mit 'AutoReset'-Attribut | ||
// return Promise auf resultierenden Wert | |||
function invalidateOpt(opt, force = false) { | function invalidateOpt(opt, force = false) { | ||
if (! opt.ReadOnly) { | 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 | // Invalidiert die (ueber Menu) gesetzten Optionen | ||
// optSet: | // optSet: Object mit den Optionen | ||
// force: Invalidiert auch Optionen mit 'AutoReset'-Attribut | // force: Invalidiert auch Optionen mit 'AutoReset'-Attribut | ||
// return | // return Promise auf Object mit den geladenen Optionen | ||
function invalidateOpts(optSet, force = false) { | async function invalidateOpts(optSet, force = false) { | ||
for (let opt in optSet) { | for (let opt in optSet) { | ||
const __OPT = optSet[opt]; | const __OPT = optSet[opt]; | ||
await invalidateOpt(__OPT, force); | |||
} | } | ||
Zeile 2.131: | Zeile 2.396: | ||
// 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; | |||
return | |||
} | } | ||
// 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]; | const __OPT = optSet[opt]; | ||
if (! __OPT.Loaded) { | if (! __OPT.Loaded) { | ||
loadOption(__OPT, force); | 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 2.181: | Zeile 2.474: | ||
// 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); | ||
Zeile 2.190: | Zeile 2.484: | ||
__LOG[4]("DELETE " + __NAME); | __LOG[4]("DELETE " + __NAME); | ||
return GM.deleteValue(__NAME).then(voidValue => { | |||
if (reset || __CONFIG.AutoReset) { | |||
setOptValue(opt, initOptValue(__CONFIG)); | |||
} | |||
}, defaultCatch); | |||
} | |||
return Promise.resolve(); | |||
} | } | ||
Zeile 2.203: | Zeile 2.499: | ||
// 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, optSelect = undefined, force = false, reset = true) { | // return Promise auf diesen Vorgang | ||
const __DELETEALL = (optSelect === undefined) || (optSelect === true); | async function deleteOptions(optSet, optSelect = undefined, force = false, reset = true) { | ||
const __DELETEALL = ((optSelect === undefined) || (optSelect === true)); | |||
const __OPTSELECT = getValue(optSelect, { }); | const __OPTSELECT = getValue(optSelect, { }); | ||
for (let opt in optSet) { | for (let opt in optSet) { | ||
if (getValue(__OPTSELECT[opt], __DELETEALL)) { | if (getValue(__OPTSELECT[opt], __DELETEALL)) { | ||
deleteOption(optSet[opt], force, reset); | await deleteOption(optSet[opt], force, reset); | ||
} | } | ||
} | } | ||
return Promise.resolve(); | |||
} | } | ||
Zeile 2.219: | Zeile 2.518: | ||
// 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); | ||
} | } | ||
Zeile 2.261: | Zeile 2.564: | ||
// - name: Neu zu setzender Name (Speicheradresse) | // - name: Neu zu setzender Name (Speicheradresse) | ||
// - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix | // - param: Parameter "renameParam" von oben, z.B. Prefix oder Postfix | ||
function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) { | // return Promise auf diesen Vorgang | ||
async function renameOptions(optSet, optSelect, renameParam = undefined, renameFun = prefixName) { | |||
if (renameFun === undefined) { | if (renameFun === undefined) { | ||
__LOG[ | __LOG[0]("RENAME: Illegale Funktion!"); | ||
} | } | ||
for (let opt in optSelect) { | for (let opt in optSelect) { | ||
Zeile 2.270: | Zeile 2.574: | ||
if (__OPT === undefined) { | if (__OPT === undefined) { | ||
__LOG[ | __LOG[0]("RENAME: Option '" + opt + "' nicht gefunden!"); | ||
} else { | } else { | ||
const __NAME = getOptName(__OPT); | const __NAME = getOptName(__OPT); | ||
Zeile 2.280: | Zeile 2.584: | ||
const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force); | const __FORCE = (__ISSCALAR ? true : __OPTPARAMS.force); | ||
renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE); | await renameOption(__OPT, __NEWNAME, __RELOAD, __FORCE); | ||
} | } | ||
} | } | ||
Zeile 2.288: | Zeile 2.592: | ||
// optSet: Gesetzte Optionen | // optSet: Gesetzte Optionen | ||
// reload: Seite mit "Werkseinstellungen" neu laden | // reload: Seite mit "Werkseinstellungen" neu laden | ||
function resetOptions(optSet, reload = true) { | // return Promise auf diesen Vorgang | ||
async function resetOptions(optSet, reload = true) { | |||
// Alle (nicht 'Permanent') gesetzten Optionen entfernen... | // Alle (nicht 'Permanent') gesetzten Optionen entfernen... | ||
deleteOptions(optSet, true, false, ! reload); | await deleteOptions(optSet, true, false, ! reload); | ||
// ... und ggfs. Seite neu laden (mit "Werkseinstellungen")... | |||
refreshPage(reload); | |||
} | } | ||
Zeile 2.305: | Zeile 2.608: | ||
// 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 2.325: | Zeile 2.628: | ||
// 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'); | ||
} | } | ||
Zeile 2.338: | Zeile 2.641: | ||
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[ | __LOG[0]("Could not add " + type + " event:"); | ||
__LOG[2](callback); | __LOG[2](callback); | ||
Zeile 2.357: | Zeile 2.660: | ||
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[ | __LOG[0]("Could not remove " + type + " event:"); | ||
__LOG[2](callback); | __LOG[2](callback); | ||
Zeile 2.396: | Zeile 2.699: | ||
// return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden) | // return Gesuchtes Element mit der lfd. Nummer index oder undefined (falls nicht gefunden) | ||
function getElement(name, index = 0, doc = document) { | function getElement(name, index = 0, doc = document) { | ||
const __TAGS = | const __TAGS = doc.getElementsByName(name); | ||
const __TABLE = (__TAGS | const __TABLE = (__TAGS ? __TAGS[index] : undefined); | ||
return __TABLE; | return __TABLE; | ||
Zeile 2.407: | Zeile 2.710: | ||
// doc: Dokument (document) | // doc: Dokument (document) | ||
// return Gesuchtes Element oder undefined (falls nicht gefunden) | // return Gesuchtes Element oder undefined (falls nicht gefunden) | ||
function getTable(index, tag = | function getTable(index, tag = 'table', doc = document) { | ||
const __TAGS = | const __TAGS = doc.getElementsByTagName(tag); | ||
const __TABLE = (__TAGS | 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; | |||
} | } | ||
Zeile 2.419: | Zeile 2.734: | ||
// return Gesuchte Zeilen oder undefined (falls nicht gefunden) | // 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 2.451: | Zeile 2.777: | ||
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) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE); | const __VALSTR = ((value !== undefined) ? safeStringify(value) : __SERIAL ? "JSON.stringify(" + __TVALUE + ')' : __TVALUE); | ||
Zeile 2.480: | Zeile 2.806: | ||
// memory: __OPTMEM.normal = unbegrenzt gespeichert (localStorage), __OPTMEM.begrenzt = bis Browserende gespeichert (sessionStorage), __OPTMEM.inaktiv | // 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, memory); | const __ACTION = getFormAction(opt, isAlt, value, serial, memory); | ||
Zeile 2.486: | Zeile 2.812: | ||
} | } | ||
// Zeigt eine Option auf der Seite als Auswahlbox an | // Hilfsfunktion: Wendet eine Konvertierung auf jede "Zeile" innerhalb eines Textes an | ||
// opt: Anzuzeigende Option | // text: Urspruenglicher Text | ||
// return String mit dem HTML-Code | // convFun: function(line, index, arr): Konvertiert line in "Zeile" line des Arrays arr | ||
function getOptionSelect(opt) { | // separator: Zeilentrenner im Text (Default: '\n') | ||
const __CONFIG = getOptConfig(opt); | // thisArg: optionaler this-Parameter fuer die Konvertierung | ||
const __NAME = getOptName(opt); | // limit: optionale Begrenzung der Zeilen | ||
const __VALUE = getOptValue(opt); | // return String mit dem neuen Text | ||
const __ACTION = getFormActionEvent(opt, false, undefined, | function eachLine(text, convFun, separator = '\n', thisArg = undefined, limit = undefined) { | ||
const __FORMLABEL = getValue(__CONFIG. | const __ARR = text.split(separator, limit); | ||
const __LABEL = '<label for="' + __NAME + '">' + __FORMLABEL + '</label>'; | const __RES = __ARR.map(convFun, thisArg); | ||
let element = '<select name="' + __NAME + '" id="' + __NAME + '"' + __ACTION + '>'; | |||
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))) { | if (__CONFIG.FreeValue && ! (~ __CONFIG.Choice.indexOf(__VALUE))) { | ||
Zeile 2.508: | Zeile 2.879: | ||
element += '\n</select>'; | element += '\n</select>'; | ||
return __LABEL | return withTitle(substParam(__LABEL, element), __TITLE); | ||
} | } | ||
Zeile 2.518: | Zeile 2.889: | ||
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 2.530: | Zeile 2.905: | ||
' /><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 2.541: | Zeile 2.921: | ||
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 2.557: | Zeile 2.939: | ||
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 = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': ""); | //const __ONSUBMIT = (__SUBMIT.length ? ' onKeyDown="' + __SUBMIT + '"': ""); | ||
const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': ""); | const __ONSUBMIT = (__SUBMIT ? ' onKeyDown="' + __SUBMIT + '"': ""); | ||
const __FORMLABEL = | 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 + | ||
Zeile 2.567: | Zeile 2.950: | ||
safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>'; | safeStringify(__VALUE, __CONFIG.Replace, __CONFIG.Space) + '</textarea>'; | ||
return [ __ELEMENTLABEL, __ELEMENTTEXT ]; | return [ withTitle(__ELEMENTLABEL, __TITLE), __ELEMENTTEXT ]; | ||
} | } | ||
Zeile 2.577: | Zeile 2.960: | ||
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 2.614: | Zeile 2.998: | ||
} | } | ||
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 2.636: | Zeile 3.041: | ||
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 (checkItem( | 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; | column = 1; | ||
} | |||
} | |||
if (column === 1) { | |||
form += '</tr><tr>'; | |||
} | } | ||
form += '\n<td' + __TDOPT + '>' + __ELEMENT.replace('|', '</td><td>') + '</td>'; | |||
} | } | ||
} | } | ||
} | } | ||
Zeile 2.735: | Zeile 3.143: | ||
if (__PARAM !== undefined) { | if (__PARAM !== undefined) { | ||
// Klassifizierte Optionen umbenennen... | // Klassifizierte Optionen umbenennen... | ||
renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun); | return renameOptions(this.optSet, this.optSelect, __PARAM, this.renameFun); | ||
} else { | |||
return Promise.resolve(); | |||
} | } | ||
}, | }, | ||
'deleteOptions' : function() { | 'deleteOptions' : function(ignList) { | ||
return deleteOptions(this.optSet, | const __OPTSELECT = addProps([], this.optSelect, null, ignList); | ||
return deleteOptions(this.optSet, __OPTSELECT, true, true); | |||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse Classification ==================== | // ==================== Ende Abschnitt fuer Klasse Classification ==================== | ||
Zeile 2.768: | Zeile 3.180: | ||
} | } | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse TeamClassification ==================== | // ==================== Ende Abschnitt fuer Klasse TeamClassification ==================== | ||
Zeile 2.775: | Zeile 3.187: | ||
// Klasse fuer Teamdaten | // Klasse fuer Teamdaten | ||
this.Team = team; | /*class*/ function Team /*{ | ||
constructor*/(team, land, liga, teamId) { | |||
'use strict'; | |||
this.Team = team; | |||
} | this.Land = land; | ||
this.Liga = liga; | |||
this.TmNr = (teamId || 0); | |||
this.LdNr = getLandNr(land); | |||
this.LgNr = getLigaNr(liga); | |||
} | |||
//} | |||
Class.define(Team, Object, { | Class.define(Team, Object, { | ||
Zeile 2.790: | Zeile 3.206: | ||
'Liga' : true, | 'Liga' : true, | ||
'Land' : true, | 'Land' : true, | ||
'TmNr' : true, | |||
'LdNr' : true, | 'LdNr' : true, | ||
'LgNr' : true | 'LgNr' : true | ||
} | } | ||
} ); | }); | ||
// ==================== Ende Abschnitt fuer Klasse Team ==================== | // ==================== Ende Abschnitt fuer Klasse Team ==================== | ||
// ==================== Spezialisierter Abschnitt fuer Optionen ==================== | // ==================== Abschnitt fuer Klasse Verein ==================== | ||
// Klasse fuer Vereinsdaten | |||
/*class*/ function Verein /*extends Team { | |||
constructor*/(team, land, liga, teamId, manager, flags) { | |||
'use strict'; | |||
Team.call(this, team, land, liga, teamId); | |||
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, | |||
'TmNr' : true, | |||
'LdNr' : true, | |||
'LgNr' : true, | |||
'Manager' : true, | |||
'Flags' : true | |||
} | |||
}); | |||
// ==================== Ende Abschnitt fuer Klasse Verein ==================== | |||
// ==================== Spezialisierter Abschnitt fuer Optionen ==================== | |||
// Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt): | // Gesetzte Optionen (wird von initOptions() angelegt und von loadOptions() gefuellt): | ||
Zeile 2.813: | Zeile 3.260: | ||
// Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option | // Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option | ||
// optSet: Platz fuer die gesetzten Optionen | // optSet: Platz fuer die gesetzten Optionen | ||
// teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'LdNr' und 'LgNr') | // teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'TmNr', 'LdNr' und 'LgNr') | ||
// myTeam: Objekt fuer die Teamdaten | // myTeam: Objekt fuer die Teamdaten | ||
// return Die Teamdaten oder undefined bei Fehler | // return Die Teamdaten oder undefined bei Fehler | ||
Zeile 2.820: | Zeile 3.267: | ||
addProps(myTeam, teamParams, myTeam.__TEAMITEMS); | addProps(myTeam, teamParams, myTeam.__TEAMITEMS); | ||
__LOG[2]("Ermittelt: " + safeStringify(myTeam)); | __LOG[2]("Ermittelt: " + safeStringify(myTeam)); | ||
// ... und abspeichern... | // ... und abspeichern, falls erweunscht... | ||
setOpt(optSet.team, myTeam, false); | if (optSet && optSet.team) { | ||
setOpt(optSet.team, myTeam, false); | |||
} | |||
} else { | } else { | ||
const __TEAM = getOptValue(optSet.team); // Gespeicherte Parameter | const __TEAM = ((optSet && optSet.team) ? getOptValue(optSet.team) : undefined); // Gespeicherte Parameter | ||
if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) { | if ((__TEAM !== undefined) && (__TEAM.Land !== undefined)) { | ||
Zeile 2.829: | Zeile 3.278: | ||
__LOG[2]("Gespeichert: " + safeStringify(myTeam)); | __LOG[2]("Gespeichert: " + safeStringify(myTeam)); | ||
} else { | } else { | ||
__LOG[ | __LOG[6]("Team nicht ermittelt: " + safeStringify(__TEAM)); | ||
} | } | ||
} | } | ||
return myTeam; | return myTeam; | ||
} | } | ||
Zeile 2.848: | Zeile 3.296: | ||
// 'formWidth': Anzahl der Elemente pro Zeile | // 'formWidth': Anzahl der Elemente pro Zeile | ||
// 'formBreak': Elementnummer des ersten Zeilenumbruchs | // 'formBreak': Elementnummer des ersten Zeilenumbruchs | ||
// return | // return Promise auf gefuelltes Objekt mit den gesetzten Optionen | ||
function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) { | function buildOptions(optConfig, optSet = undefined, optParams = { 'hideMenu' : false }) { | ||
// Klassifikation ueber Land und Liga des Teams... | // Klassifikation ueber Land und Liga des Teams... | ||
Zeile 2.854: | Zeile 3.302: | ||
__TEAMCLASS.teamParams = optParams.teamParams; // Ermittelte Parameter | __TEAMCLASS.teamParams = optParams.teamParams; // Ermittelte Parameter | ||
return startOptions(optConfig, optSet, __TEAMCLASS).then( | |||
optSet => showOptions(optSet, optParams), | |||
defaultCatch); | |||
} | } | ||
Zeile 2.897: | Zeile 3.343: | ||
this.uri.setQueryPar('landauswahl', team.LdNr); | this.uri.setQueryPar('landauswahl', team.LdNr); | ||
this.uri.setQueryPar('ligaauswahl', team.LgNr); | this.uri.setQueryPar('ligaauswahl', team.LgNr); | ||
this.uri.setQueryPar('hl', team.TmNr); }, | |||
'setPage' : function(page, label) { | 'setPage' : function(page, label) { | ||
this.uri.home(); | this.uri.home(); | ||
Zeile 2.959: | Zeile 3.405: | ||
'saison' : saison, | 'saison' : saison, | ||
'ZAT' : 0, | 'ZAT' : 0, | ||
'gameType' : | 'gameType' : 'spielfrei', | ||
'heim' : true, | 'heim' : true, | ||
'gegner' : "", | 'gegner' : "", | ||
Zeile 3.056: | Zeile 3.502: | ||
const __LINK = new RundenLink(currZAT.saison, __TEAMCLASS.team); | const __LINK = new RundenLink(currZAT.saison, __TEAMCLASS.team); | ||
if (currZAT.gameType === | if (currZAT.gameType === 'Liga') { | ||
if (currZAT.ZAT < 70) { | if (currZAT.ZAT < 70) { | ||
__LINK.setRunde( | __LINK.setRunde('stauswahl', currZAT.ligaSpieltag); | ||
__LINK.setPage( | __LINK.setPage('ls', __LINK.runde + ". Spieltag"); | ||
} else { | } else { | ||
__LINK.setLabel("Relegation"); | __LINK.setLabel("Relegation"); | ||
} | } | ||
} else if (currZAT.gameType === | } else if (currZAT.gameType === 'LP') { | ||
__LINK.setRunde( | __LINK.setRunde('stauswahl', currZAT.pokalRunde); | ||
__LINK.setPage( | __LINK.setPage('lp', __POKALRUNDEN[__LINK.runde]); | ||
} else if ((currZAT.gameType === | } else if ((currZAT.gameType === 'OSCQ') || (currZAT.gameType === 'OSEQ')) { | ||
__LINK.setRunde( | __LINK.setRunde('runde', currZAT.euroRunde); | ||
__LINK.setPage(((currZAT.gameType === | __LINK.setPage(((currZAT.gameType === 'OSCQ') ? 'oscq' : 'oseq'), __QUALIRUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]); | ||
} else if (currZAT.gameType === | } else if (currZAT.gameType === 'OSC') { | ||
if (currZAT.euroRunde < 9) { | if (currZAT.euroRunde < 9) { | ||
const __GRUPPENPHASE = ((currZAT.euroRunde < 6) ? "HR-Grp. " : "ZR-Grp. "); | const __GRUPPENPHASE = ((currZAT.euroRunde < 6) ? "HR-Grp. " : "ZR-Grp. "); | ||
__LINK.setRunde("", (currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck); | __LINK.setRunde("", (currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck); | ||
__LINK.setPage(((currZAT.euroRunde < 6) ? | __LINK.setPage(((currZAT.euroRunde < 6) ? 'oschr' : 'osczr'), __GRUPPENPHASE + "Spiel " + __LINK.runde); | ||
} else { | } else { | ||
__LINK.setPage( | __LINK.setPage('oscfr', __OSCRUNDEN[currZAT.euroRunde - 8] + __HINRUECK[currZAT.hinRueck]); | ||
} | } | ||
} else if (currZAT.gameType === | } else if (currZAT.gameType === 'OSE') { | ||
__LINK.setRunde( | __LINK.setRunde('runde', currZAT.euroRunde - 3); | ||
__LINK.setPage( | __LINK.setPage('ose', __OSERUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]); | ||
} else if (currZAT.gameType === 'Supercup') { | |||
__LINK.setRunde("", 1); | |||
__LINK.setPage('supercup', currZAT.gameType); | |||
} else { | } else { | ||
__LINK.setLabel(); // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert") | __LINK.setLabel(); // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert") | ||
Zeile 3.092: | Zeile 3.541: | ||
// ==================== Abschnitt fuer interne IDs auf den Seiten ==================== | // ==================== Abschnitt fuer interne IDs auf den Seiten ==================== | ||
const | const __GAMETYPENRN = { // "Blind FSS gesucht!" | ||
'unbekannt' : -1, | '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 = { | const __LIGANRN = { | ||
Zeile 3.116: | Zeile 3.582: | ||
'3. Liga D' : 7 | '3. Liga D' : 7 | ||
}; | }; | ||
const __LIGATYPES = reverseMapping(__LIGANRN); | |||
const __LANDNRN = { | const __LANDNRN = { | ||
Zeile 3.172: | Zeile 3.639: | ||
'Zypern' : 38 | '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 ==================== | // ==================== Abschnitt fuer Daten des Spielplans ==================== | ||
Zeile 3.177: | Zeile 3.702: | ||
// Gibt die ID fuer den Namen eines Wettbewerbs zurueck | // Gibt die ID fuer den Namen eines Wettbewerbs zurueck | ||
// gameType: Name des Wettbewerbs eines Spiels | // gameType: Name des Wettbewerbs eines Spiels | ||
// return OS2-ID fuer den Spieltyp (1 bis 7), 0 fuer spielfrei/Frei/reserviert, -1 fuer ungueltig | // defValue: Default-Wert | ||
function getGameTypeID(gameType) { | // return OS2-ID fuer den Spieltyp (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig | ||
return getValue(__GAMETYPES[gameType], | 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. | // Gibt die ID des Landes mit dem uebergebenen Namen zurueck. | ||
// land: Name des Landes | // land: Name des Landes | ||
// defValue: Default-Wert | |||
// return OS2-ID des Landes, 0 fuer ungueltig | // return OS2-ID des Landes, 0 fuer ungueltig | ||
function getLandNr(land) { | function getLandNr(land, defValue = __LANDNRN.unbekannt) { | ||
return getValue(__LANDNRN[land], | return getValue(__LANDNRN[land], defValue); | ||
} | } | ||
// Gibt die ID der Liga mit dem uebergebenen Namen zurueck. | // Gibt die ID der Liga mit dem uebergebenen Namen zurueck. | ||
// land: Name der Liga | // land: Name der Liga | ||
// defValue: Default-Wert | |||
// return OS2-ID der Liga, 0 fuer ungueltig | // return OS2-ID der Liga, 0 fuer ungueltig | ||
function getLigaNr(liga) { | function getLigaNr(liga, defValue = __LIGANRN.unbekannt) { | ||
return getValue(__LIGANRN[liga], | return getValue(__LIGANRN[liga], defValue); | ||
} | } | ||
// Kehrt das Mapping eines Objekts um und liefert ein neues Objekt zurueck. | |||
// obj: Objekt mit key => value | |||
// convFun: Konvertierfunktion fuer die Werte | |||
// return Neues Objekt mit value => key (doppelte value-Werte fallen heraus!) | |||
function reverseMapping(obj, convFun) { | |||
if (! obj) { | |||
return obj; | |||
} | |||
const __RET = { }; | |||
for (let key in obj) { | |||
const __VALUE = obj[key]; | |||
__RET[__VALUE] = (convFun ? convFun(key) : key); | |||
} | |||
return __RET; | |||
} | |||
// ==================== Abschnitt fuer sonstige Parameter ==================== | |||
// Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck | // Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck | ||
Zeile 3.241: | Zeile 3.814: | ||
// currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT | // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT | ||
// cell: Tabellenzelle mit Eintrag "2 : 1" | // cell: Tabellenzelle mit Eintrag "2 : 1" | ||
// return Modifizierter Spielplanzeiger | |||
function setErgebnisFromCell(currZAT, cell) { | function setErgebnisFromCell(currZAT, cell) { | ||
const __ERGEBNIS = getErgebnisFromCell(cell); | const __ERGEBNIS = getErgebnisFromCell(cell); | ||
Zeile 3.251: | Zeile 3.825: | ||
currZAT.gAga = -1; | currZAT.gAga = -1; | ||
} | } | ||
return currZAT; | |||
} | } | ||
Zeile 3.260: | Zeile 3.836: | ||
currZAT.gameType = __SPIELART[0]; | currZAT.gameType = __SPIELART[0]; | ||
currZAT.heim = (__SPIELART.length < 2) || (__SPIELART[1] === | currZAT.heim = (__SPIELART.length < 2) || (__SPIELART[1] === 'Heim'); | ||
} | } | ||
Zeile 3.272: | Zeile 3.848: | ||
let ret = ""; | let ret = ""; | ||
if (cell.textContent !== | if (cell.textContent !== 'Vorschau') { // Nur falls Link nicht bereits vorhanden | ||
if (__GAMETYPEID > 1) { // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert" | if (__GAMETYPEID > 1) { // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert" | ||
const __SEARCHFUN = ":os_bericht("; | const __SEARCHFUN = ":os_bericht("; | ||
Zeile 3.302: | Zeile 3.878: | ||
const __TEAMSEARCHHAUPT = { // Parameter zum Team "<b>Willkommen im Managerbüro von TEAM</b><br>LIGA LAND<a href=..." | const __TEAMSEARCHHAUPT = { // Parameter zum Team "<b>Willkommen im Managerbüro von TEAM</b><br>LIGA LAND<a href=..." | ||
'Zeile' | 'Tabelle' : 1, | ||
'Spalte' : 1, | 'Zeile' : 0, | ||
'start' | 'Spalte' : 1, | ||
'middle' : "</b><br>", | 'start' : " von ", | ||
'liga' | 'middle' : "</b><br>", | ||
'land' | 'liga' : ". Liga", | ||
'end' | 'land' : ' ', | ||
'end' : "<a href=" | |||
}; | }; | ||
const __TEAMSEARCHTEAM = { // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>" | const __TEAMSEARCHTEAM = { // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>" | ||
'Zeile' | 'Tabelle' : 1, | ||
'Spalte' : 0, | 'Zeile' : 0, | ||
'start' | 'Spalte' : 0, | ||
'middle' : " - ", | 'start' : "<b>", | ||
'liga' | 'middle' : " - ", | ||
'land' | 'liga' : ". Liga", | ||
'end' : "</a></b>" | 'land' : 'target="_blank">', | ||
'end' : "</a></b>" | |||
}; | |||
const __TEAMIDSEARCHHAUPT = { // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>" | |||
'Tabelle' : 0, | |||
'Zeile' : 6, | |||
'Spalte' : 0, | |||
'start' : '<a href="livegame/index.php?spiele=', | |||
'end' : ',0">LIVEGAME</a>' | |||
}; | |||
const __TEAMIDSEARCHTEAM = { // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>" | |||
'Tabelle' : 0, | |||
'Zeile' : 1, | |||
'Spalte' : 1, | |||
'start' : '<a hspace="20" href="javascript:tabellenplatz(', | |||
'end' : ')">Tabellenpl\xE4tze</a>' | |||
}; | }; | ||
// Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam) | // Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam) | ||
// | // teamSearch: Muster fuer die Suche nach Team, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt, ausserdem die | ||
// | // Adresse der Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]" | ||
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER }, | // teamIdSearch: Muster fuer die Suche nach Team-ID, die Eintraege fuer 'start' und 'end' enthaelt, ausserdem die | ||
// z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'LdNr' : 20, 'LgNr' : 1 } | // Adresse der Tabellenzelle mit den Parametern zur Team-ID "startTEAMIDend" | ||
function getTeamParamsFromTable( | // doc: Optionale Angabe des Dokuments, in dem die Tabelle gesucht wird (Default: document) | ||
// return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'TmNr' : TEAMID, 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER }, | |||
// z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'TmNr' : 930, 'LdNr' : 20, 'LgNr' : 1 } | |||
function getTeamParamsFromTable(teamSearch, teamIdSearch, doc = document) { | |||
// Ermittlung von Team, Liga und Land... | |||
const __TEAMSEARCH = getValue(teamSearch, __TEAMSEARCHHAUPT); | const __TEAMSEARCH = getValue(teamSearch, __TEAMSEARCHHAUPT); | ||
const __TEAMTABLE = getTable(getValue(__TEAMSEARCH.Tabelle, 1), 'table', doc); | |||
const __TEAMCELLROW = getValue(__TEAMSEARCH.Zeile, 0); | const __TEAMCELLROW = getValue(__TEAMSEARCH.Zeile, 0); | ||
const __TEAMCELLCOL = getValue(__TEAMSEARCH.Spalte, 0); | const __TEAMCELLCOL = getValue(__TEAMSEARCH.Spalte, 0); | ||
const __TEAMCELLSTR = ( | const __TEAMCELLSTR = (__TEAMTABLE === undefined) ? "" : __TEAMTABLE.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML; | ||
const __SEARCHSTART = __TEAMSEARCH.start; | const __SEARCHSTART = __TEAMSEARCH.start; | ||
const __SEARCHMIDDLE = __TEAMSEARCH.middle; | const __SEARCHMIDDLE = __TEAMSEARCH.middle; | ||
Zeile 3.343: | Zeile 3.942: | ||
const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE); | const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE); | ||
let land = (~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined; | let land = ((~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined); | ||
const __TEAMNAME = (~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined; | const __TEAMNAME = ((~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined); | ||
let liga = ((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined; | let liga = (((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined); | ||
if (land !== undefined) { | if (land !== undefined) { | ||
Zeile 3.360: | Zeile 3.959: | ||
} | } | ||
const __TEAM = new Team(__TEAMNAME, land, liga); | // Ermittlung der Team-ID (indirekt ueber den Livegame- bzw. Tabellenplatz-Link)... | ||
const __TEAMIDSEARCH = getValue(teamIdSearch, __TEAMIDSEARCHHAUPT); | |||
const __TEAMIDTABLE = getTable(getValue(__TEAMIDSEARCH.Tabelle, 0), 'table', doc); | |||
const __TEAMIDCELLROW = getValue(__TEAMIDSEARCH.Zeile, 6); | |||
const __TEAMIDCELLCOL = getValue(__TEAMIDSEARCH.Spalte, 0); | |||
const __TEAMIDCELLSTR = (__TEAMIDTABLE === undefined) ? "" : __TEAMIDTABLE.rows[__TEAMIDCELLROW].cells[__TEAMIDCELLCOL].innerHTML; | |||
const __SEARCHIDSTART = __TEAMIDSEARCH.start; | |||
const __SEARCHIDEND = __TEAMIDSEARCH.end; | |||
const __INDEXIDSTART = __TEAMIDCELLSTR.indexOf(__SEARCHIDSTART); | |||
const __INDEXIDEND = __TEAMIDCELLSTR.indexOf(__SEARCHIDEND); | |||
const __TEAMIDSTR = __TEAMIDCELLSTR.substring(__INDEXIDSTART + __SEARCHIDSTART.length, __INDEXIDEND); | |||
const __TEAMID = Number.parseInt(__TEAMIDSTR, 10); | |||
const __TEAM = new Team(__TEAMNAME, land, liga, __TEAMID); | |||
return __TEAM; | return __TEAM; | ||
Zeile 3.439: | Zeile 4.051: | ||
} | } | ||
incZAT(currZAT, anzZAT); | incZAT(currZAT, anzZAT); | ||
appendHTML(__ROW, "& | appendHTML(__ROW, " " + getZusatz(currZAT, true)); | ||
__CELLS[__COLUMNINDEX.Zus].className = __CELLS[__COLUMNINDEX.Art].className; | __CELLS[__COLUMNINDEX.Zus].className = __CELLS[__COLUMNINDEX.Art].className; | ||
} | } | ||
Zeile 3.448: | Zeile 4.060: | ||
// Verarbeitet Ansicht "Haupt" (Managerbuero) | // Verarbeitet Ansicht "Haupt" (Managerbuero) | ||
function procHaupt() { | function procHaupt() { | ||
const __TEAMPARAMS = getTeamParamsFromTable( | const __TEAMPARAMS = getTeamParamsFromTable(__TEAMSEARCHHAUPT, __TEAMIDSEARCHHAUPT); | ||
buildOptions(__OPTCONFIG, __OPTSET, { | return buildOptions(__OPTCONFIG, __OPTSET, { | ||
'teamParams' : __TEAMPARAMS, | |||
'menuAnchor' : getTable(1, 'div'), | |||
'hideForm' : { | |||
'team' : true | |||
} | |||
}).then(optSet => { | |||
const __ZAT = firstZAT(getOptValue(__OPTSET.saison), getOptValue(__OPTSET.ligaSize)); | |||
const __ZATCELL = getProp(getProp(getRows(0), 2), 'cells', { })[0]; | |||
const __NEXTZAT = getZATNrFromCell(__ZATCELL); // "Der naechste ZAT ist ZAT xx und ..." | |||
const __CURRZAT = __NEXTZAT - 1; | |||
addZusatz(getProp(getRows(2), 0), __ZAT, __CURRZAT, true); // "Dein letztes Spiel:" (+ __CURRZAT) | |||
addZusatz(getProp(getRows(3), 0), __ZAT); // "Dein naechstes Spiel:" (+ 1 ZAT) | |||
}); | |||
} | |||
(() => { | |||
(async () => { | |||
try { | |||
await procHaupt().catch(defaultCatch); | |||
return 'OK'; | |||
} catch (ex) { | |||
} catch (ex) { | return defaultCatch(ex); | ||
} | |||
} | })().then(rc => { | ||
__LOG[1]('SCRIPT END', __DBMOD.Name, '(' + rc + ')'); | |||
} | }) | ||
})(); | |||
// *** EOF *** | // *** EOF *** | ||
</pre> | </pre> |
Aktuelle Version vom 15. März 2022, 05:54 Uhr
OS2.haupt | |
Dateiname | OS2.haupt.user.js |
Version | 0.42 (WebExtensions) |
Autor | Sven Loges (SLC), Choromonets Odessa |
Beschreibung | Strukturiert und erweitert das Managerbüro eines OS2-Teams |
Webseiten | haupt.php Managerbüro |
Funktionalität | Vorschau für das letzte Spiel: Bilanz Beschreibungen zu beiden Spielen Anzeige des Spieltags bzw. der Runde Benutzermenü für Optionen Getrennte Option "Liga:" für Erst- und Zweitteam Erweiterte Optionen auch auf der Seite Link auf den jeweiligen Spieltag bzw. die jeweilige Runde Interaktive Menü-Optionen Gemeinsame Code- und Datenbasis Gezielte "hl"-Links auf die eigenen Spiele |
Letzte Änderung | 15.03.2022 |
Quellcode INSTALLATION[Bearbeiten]
// ==UserScript== // @name OS2.haupt // @namespace http://os.ongapo.com/ // @version 0.42 // @copyright 2016+ // @author Sven Loges (SLC) // @description Managerbuero-Abschnitt aus dem Master-Script fuer Online Soccer 2.0 // @include /^https?://(www\.)?(os\.ongapo\.com|online-soccer\.eu|os-zeitungen\.com)/haupt\.php(\?changetosecond=\w+(&\w+=?[+\w]+)*)?(#\w+)?$/ // @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 = { 'saison' : { // Laufende Saison 'Name' : "saison", 'Type' : __OPTTYPES.MC, 'ValType' : 'Number', 'FreeValue' : true, 'SelValue' : false, 'Choice' : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ], 'Default' : 18, 'Action' : __OPTACTION.NXT, 'Label' : "Saison: $", 'Hotkey' : 'a', 'FormLabel' : "Saison:|$" }, 'ligaSize' : { // Ligengroesse 'Name' : "ligaSize", 'Type' : __OPTTYPES.MC, 'ValType' : 'Number', 'AutoReset' : true, 'Choice' : [ 10, 18, 20 ], 'Action' : __OPTACTION.NXT, 'Label' : "Liga: $er", 'Hotkey' : 'i', 'FormLabel' : "Liga:|$er" }, 'datenZat' : { // Stand der Daten zum Team und ZAT 'Name' : "dataZAT", 'Type' : __OPTTYPES.SD, 'ValType' : 'Number', 'Hidden' : true, 'Serial' : true, 'AutoReset' : true, 'Permanent' : true, 'Default' : undefined, 'Action' : __OPTACTION.SET, 'Submit' : undefined, 'Cols' : 3, 'Rows' : 1, 'Replace' : null, 'Space' : 0, 'Label' : "Daten-ZAT: $" }, 'team' : { // Datenspeicher fuer Daten des Erst- bzw. Zweitteams 'Name' : "team", 'Type' : __OPTTYPES.SD, 'Hidden' : true, 'Serial' : true, 'Permanent' : true, 'Default' : undefined, // new Team() // { 'Team' : undefined, 'Liga' : undefined, 'Land' : undefined, 'TmNr' : 0, 'LdNr' : 0, 'LgNr' : 0 } 'Submit' : undefined, 'Cols' : 36, 'Rows' : 6, 'Replace' : null, 'Space' : 1, 'Label' : "Verein:" }, '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 /*class*/ function Team /*{ constructor*/(team, land, liga, teamId) { 'use strict'; this.Team = team; this.Land = land; this.Liga = liga; this.TmNr = (teamId || 0); 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, 'TmNr' : true, 'LdNr' : true, 'LgNr' : true } }); // ==================== Ende Abschnitt fuer Klasse Team ==================== // ==================== Abschnitt fuer Klasse Verein ==================== // Klasse fuer Vereinsdaten /*class*/ function Verein /*extends Team { constructor*/(team, land, liga, teamId, manager, flags) { 'use strict'; Team.call(this, team, land, liga, teamId); 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, 'TmNr' : true, 'LdNr' : true, 'LgNr' : 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 = { 'datenZat' : true, 'ligaSize' : true }; // Gibt die Teamdaten zurueck und aktualisiert sie ggfs. in der Option // optSet: Platz fuer die gesetzten Optionen // teamParams: Dynamisch ermittelte Teamdaten ('Team', 'Liga', 'Land', 'TmNr', '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 Spielplan und ZATs ==================== // Beschreibungstexte aller Runden const __POKALRUNDEN = [ "", "1. Runde", "2. Runde", "3. Runde", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ]; const __QUALIRUNDEN = [ "", "Quali 1", "Quali 2", "Quali 3" ]; const __OSCRUNDEN = [ "", "Viertelfinale", "Halbfinale", "Finale" ]; const __OSERUNDEN = [ "", "Runde 1", "Runde 2", "Runde 3", "Runde 4", "Achtelfinale", "Viertelfinale", "Halbfinale", "Finale" ]; const __HINRUECK = [ " Hin", " R\xFCck", "" ]; // ==================== Abschnitt fuer Klasse RundenLink ==================== function RundenLink(saison, team) { 'use strict'; this.uri = new URI("http://os.ongapo.com/?erganzeigen=1&stataktion=Statistik+ausgeben"); this.runde = 0; this.prop = ""; this.label = ""; if (saison) { this.setSaison(saison); } if (team) { this.setTeam(team); } } Class.define(RundenLink, Object, { 'setSaison' : function(saison) { this.uri.setQueryPar('saauswahl', saison); }, 'setTeam' : function(team) { this.uri.setQueryPar('landauswahl', team.LdNr); this.uri.setQueryPar('ligaauswahl', team.LgNr); this.uri.setQueryPar('hl', team.TmNr); }, 'setPage' : function(page, label) { this.uri.home(); this.uri.down(page + ".php"); this.setLabel(label); }, 'setRunde' : function(prop, runde) { this.prop = prop; this.runde = runde; }, 'setLabel' : function(label) { this.label = (label || ""); }, 'setAnzeigen' : function(show = true) { this.uri.setQueryPar('erganzeigen', (show ? 1 : 0)); }, 'getLabel' : function() { return (this.label || "Link"); }, 'getHTML' : function(target = undefined) { if ((this.runde <= 0) || (! this.uri.getLeaf())) { return this.label; } else { if (this.prop) { this.uri.setQueryPar(this.prop, this.runde); } return "<a " + URI.prototype.formatParams({ 'href' : this.uri.getPath(), 'target' : (target ? target : '_blank') }, function(value) { return '"' + value + '"'; }, ' ', '=') + '>' + this.getLabel() + "</a>"; } } }); // ==================== Ende Abschnitt fuer Klasse RundenLink ==================== // Liefert einen vor den ersten ZAT zurueckgesetzten Spielplanzeiger // saison: Enthaelt die Nummer der laufenden Saison // ligaSize: Anzahl der Teams in dieser Liga (Gegner + 1) // - ZATs pro Abrechnungsmonat // - Saison // - ZAT // - GameType // - Heim/Auswaerts // - Gegner // - Tore // - Gegentore // - Ligengroesse // - Ligaspieltag // - Pokalrunde // - Eurorunde // - Hin/Rueck // - ZAT Rueck // - ZAT Korr function firstZAT(saison, ligaSize) { return { 'anzZATpMonth' : ((saison < 2) ? 7 : 6), // Erste Saison 7 ZAT, danach 6 ZAT... 'saison' : saison, 'ZAT' : 0, 'gameType' : 'spielfrei', 'heim' : true, 'gegner' : "", 'gFor' : 0, 'gAga' : 0, 'ligaSize' : ligaSize, 'ligaSpieltag' : 0, 'pokalRunde' : 1, 'euroRunde' : 0, 'hinRueck' : 2, // 0: Hin, 1: Rueck, 2: unbekannt 'ZATrueck' : 0, 'ZATkorr' : 0 }; } // Liefert den ZAT als String // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // longStats: Formatiert die Langversion des Textes function getZAT(currZAT, longStats) { return (longStats ? currZAT.gameType + ' ' + (currZAT.heim ? "Heim" : "Ausw\xE4rts") + ' ' : "") + (longStats ? '[' + currZAT.ligaSpieltag + ' ' + currZAT.pokalRunde + ' ' + currZAT.euroRunde + "] " : "") + (longStats ? '[' + currZAT.ZATrueck + __HINRUECK[currZAT.hinRueck] + ' ' + ((currZAT.ZATkorr < 0) ? "" : '+') + currZAT.ZATkorr + "] " : "") + (longStats ? currZAT.gegner + ((currZAT.gFor > -1) ? " (" + currZAT.gFor + " : " + currZAT.gAga + ')' : "") + ' ' : "") + 'S' + currZAT.saison + "-Z" + ((currZAT.ZAT < 10) ? '0' : "") + currZAT.ZAT; } // Liefert die ZATs der Sonderspieltage fuer 10er- (2) und 20er-Ligen (4) // saison: Enthaelt die Nummer der laufenden Saison // return [ 10erHin, 10erRueck, 20erHin, 20erRueck ], ZAT-Nummern der Zusatzspieltage function getLigaExtra(saison) { if (saison < 3) { return [ 8, 64, 32, 46 ]; } else { return [ 9, 65, 33, 57 ]; } } // Spult die Daten um anzZAT ZATs vor und schreibt Parameter // anhand des Spielplans fort. Also Spieltag, Runde, etc. // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // anzZAT: Anzahl der ZAT, um die vorgespult wird function incZAT(currZAT, anzZAT = 1) { const __LIGAEXTRA = getLigaExtra(currZAT.saison); const __LIGAFIRST = 3 - (__LIGAEXTRA[0] % 2); for (let i = anzZAT; i > 0; i--) { currZAT.ZAT++; if ((currZAT.ZAT - __LIGAFIRST + 1) % 2 === 1) { currZAT.ligaSpieltag++; } else { const __POS = __LIGAEXTRA.indexOf(currZAT.ZAT); if (~ __POS) { if (__POS < 2 * (currZAT.ligaSize % 9)) { currZAT.ligaSpieltag++; } } } if ((currZAT.ZAT > 12) && ((currZAT.ZAT % 10) === 5)) { // passt fuer alle Saisons: 12, 20, 30, 40, 48, 58, 68 / 3, 15, 27, 39, 51, 63, 69 currZAT.pokalRunde++; } if (((currZAT.ZAT + currZAT.ZATkorr) % 6) === 4) { if (currZAT.ZAT < 63) { currZAT.ZATrueck = currZAT.ZAT + 2; currZAT.euroRunde++; currZAT.hinRueck = 0; } else { currZAT.euroRunde = 11; // Finale currZAT.hinRueck = 2; } } if (currZAT.ZAT === currZAT.ZATrueck) { currZAT.hinRueck = 1; // 5, 7; 11, 13; (17, 19) / 23, 25; 29, 31; 35, 37; 41, 43; 47, 49; 53, 55; 59, 61; 69 if (currZAT.saison < 3) { // 4, 6; 10, 14*; (16, 22*) / 24**, 26; 34, 36; 38*, 42; 44*, 50; 52, 54; 56*, 60; 62*, 66; 70 if (currZAT.ZAT === 22) { currZAT.ZATkorr = 4; } else if ((currZAT.ZAT - 6) % 20 > 6) { currZAT.ZATkorr = 2; } else { currZAT.ZATkorr = 0; } if ((currZAT.ZAT === 22) || (currZAT.ZAT === 30)) { currZAT.euroRunde--; // Frueher: 3. Quali-Rueckspiel erst knapp vor 1. Hauptrunde } } } } } // Liefert die Beschreibung des Spiels am aktuellen ZAT // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // showLink: Angabe, ob ein Link eingefuegt werden soll // return Beschreibung des Spiels function getZusatz(currZAT, showLink = true) { const __LINK = new RundenLink(currZAT.saison, __TEAMCLASS.team); if (currZAT.gameType === 'Liga') { if (currZAT.ZAT < 70) { __LINK.setRunde('stauswahl', currZAT.ligaSpieltag); __LINK.setPage('ls', __LINK.runde + ". Spieltag"); } else { __LINK.setLabel("Relegation"); } } else if (currZAT.gameType === 'LP') { __LINK.setRunde('stauswahl', currZAT.pokalRunde); __LINK.setPage('lp', __POKALRUNDEN[__LINK.runde]); } else if ((currZAT.gameType === 'OSCQ') || (currZAT.gameType === 'OSEQ')) { __LINK.setRunde('runde', currZAT.euroRunde); __LINK.setPage(((currZAT.gameType === 'OSCQ') ? 'oscq' : 'oseq'), __QUALIRUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]); } else if (currZAT.gameType === 'OSC') { if (currZAT.euroRunde < 9) { const __GRUPPENPHASE = ((currZAT.euroRunde < 6) ? "HR-Grp. " : "ZR-Grp. "); __LINK.setRunde("", (currZAT.euroRunde % 3) * 2 + 1 + currZAT.hinRueck); __LINK.setPage(((currZAT.euroRunde < 6) ? 'oschr' : 'osczr'), __GRUPPENPHASE + "Spiel " + __LINK.runde); } else { __LINK.setPage('oscfr', __OSCRUNDEN[currZAT.euroRunde - 8] + __HINRUECK[currZAT.hinRueck]); } } else if (currZAT.gameType === 'OSE') { __LINK.setRunde('runde', currZAT.euroRunde - 3); __LINK.setPage('ose', __OSERUNDEN[__LINK.runde] + __HINRUECK[currZAT.hinRueck]); } else if (currZAT.gameType === 'Supercup') { __LINK.setRunde("", 1); __LINK.setPage('supercup', currZAT.gameType); } else { __LINK.setLabel(); // irgendwie besser lesbar! ("Friendly" bzw. "spielfrei"/"Frei"/"reserviert") } return (showLink ? __LINK.getHTML() : ""); } // ==================== Abschnitt fuer Statistiken des Spielplans ==================== // ==================== Abschnitt fuer interne IDs auf den Seiten ==================== const __GAMETYPENRN = { // "Blind FSS gesucht!" 'unbekannt' : -1, 'reserviert' : 0, 'Frei' : 0, 'spielfrei' : 0, 'Friendly' : 1, 'Liga' : 2, 'LP' : 3, 'OSEQ' : 4, 'OSE' : 5, 'OSCQ' : 6, 'OSC' : 7, 'Supercup' : 10 }; const __GAMETYPEALIASES = { 'unbekannt' : "unbekannt", 'reserviert' : undefined, 'Frei' : undefined, 'spielfrei' : "", 'Friendly' : "FSS", 'Liga' : undefined, 'LP' : "Pokal", 'OSEQ' : undefined, 'OSE' : undefined, 'OSCQ' : undefined, 'OSC' : undefined, 'Supercup' : "Super" }; const __GAMETYPES = reverseMapping(__GAMETYPENRN); const __LIGANRN = { 'unbekannt' : 0, '1. Liga' : 1, '2. Liga A' : 2, '2. Liga B' : 3, '3. Liga A' : 4, '3. Liga B' : 5, '3. Liga C' : 6, '3. Liga D' : 7 }; const __LIGATYPES = reverseMapping(__LIGANRN); const __LANDNRN = { 'unbekannt' : 0, 'Albanien' : 45, 'Andorra' : 95, 'Armenien' : 83, 'Aserbaidschan' : 104, 'Belgien' : 12, 'Bosnien-Herzegowina' : 66, 'Bulgarien' : 42, 'D\xE4nemark' : 8, 'Deutschland' : 6, 'England' : 1, 'Estland' : 57, 'Far\xF6er' : 68, 'Finnland' : 40, 'Frankreich' : 32, 'Georgien' : 49, 'Griechenland' : 30, 'Irland' : 5, 'Island' : 29, 'Israel' : 23, 'Italien' : 10, 'Kasachstan' : 105, 'Kroatien' : 24, 'Lettland' : 97, 'Liechtenstein' : 92, 'Litauen' : 72, 'Luxemburg' : 93, 'Malta' : 69, 'Mazedonien' : 86, 'Moldawien' : 87, 'Niederlande' : 11, 'Nordirland' : 4, 'Norwegen' : 9, '\xD6sterreich' : 14, 'Polen' : 25, 'Portugal' : 17, 'Rum\xE4nien' : 28, 'Russland' : 19, 'San Marino' : 98, 'Schottland' : 2, 'Schweden' : 27, 'Schweiz' : 37, 'Serbien und Montenegro' : 41, 'Slowakei' : 70, 'Slowenien' : 21, 'Spanien' : 13, 'Tschechien' : 18, 'T\xFCrkei' : 39, 'Ukraine' : 20, 'Ungarn' : 26, 'Wales' : 3, 'Weissrussland' : 71, 'Zypern' : 38 }; const __LAENDER = reverseMapping(__LANDNRN); const __TLALAND = { undefined : 'unbekannt', 'ALB' : 'Albanien', 'AND' : 'Andorra', 'ARM' : 'Armenien', 'AZE' : 'Aserbaidschan', 'BEL' : 'Belgien', 'BIH' : 'Bosnien-Herzegowina', 'BUL' : 'Bulgarien', 'DEN' : 'D\xE4nemark', 'GER' : 'Deutschland', 'ENG' : 'England', 'EST' : 'Estland', 'FRO' : 'Far\xF6er', 'FIN' : 'Finnland', 'FRA' : 'Frankreich', 'GEO' : 'Georgien', 'GRE' : 'Griechenland', 'IRL' : 'Irland', 'ISL' : 'Island', 'ISR' : 'Israel', 'ITA' : 'Italien', 'KAZ' : 'Kasachstan', 'CRO' : 'Kroatien', 'LVA' : 'Lettland', 'LIE' : 'Liechtenstein', 'LTU' : 'Litauen', 'LUX' : 'Luxemburg', 'MLT' : 'Malta', 'MKD' : 'Mazedonien', 'MDA' : 'Moldawien', 'NED' : 'Niederlande', 'NIR' : 'Nordirland', 'NOR' : 'Norwegen', 'AUT' : '\xD6sterreich', 'POL' : 'Polen', 'POR' : 'Portugal', 'ROM' : 'Rum\xE4nien', 'RUS' : 'Russland', 'SMR' : 'San Marino', 'SCO' : 'Schottland', 'SWE' : 'Schweden', 'SUI' : 'Schweiz', 'SCG' : 'Serbien und Montenegro', 'SVK' : 'Slowakei', 'SVN' : 'Slowenien', 'ESP' : 'Spanien', 'CZE' : 'Tschechien', 'TUR' : 'T\xFCrkei', 'UKR' : 'Ukraine', 'HUN' : 'Ungarn', 'WAL' : 'Wales', 'BLR' : 'Weissrussland', 'CYP' : 'Zypern' }; const __LANDTLAS = reverseMapping(__TLALAND); // ==================== Abschnitt fuer Daten des Spielplans ==================== // Gibt die ID fuer den Namen eines Wettbewerbs zurueck // gameType: Name des Wettbewerbs eines Spiels // defValue: Default-Wert // return OS2-ID fuer den Spieltyp (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig function getGameTypeID(gameType, defValue = __GAMETYPENRN.unbekannt) { return getValue(__GAMETYPENRN[gameType], defValue); } // Gibt den Namen eines Wettbewerbs zurueck // id: OS2-ID des Wettbewerbs eines Spiels (1 bis 7 oder 10), 0 fuer "spielfrei"/"Frei"/"reserviert", -1 fuer ungueltig // defValue: Default-Wert // return Spieltyp fuer die uebergebene OS2-ID function getGameType(id, defValue) { return getValue(__GAMETYPES[id], defValue); } // Gibt den alternativen (Kurznamen) fuer den Namen eines Wettbewerbs zurueck // gameType: Name des Wettbewerbs eines Spiels // return Normalerweise den uebergebenen Parameter, in Einzelfaellen eine Kurzversion function getGameTypeAlias(gameType) { return getValue(__GAMETYPEALIASES[gameType], getValue(gameType, __GAMETYPEALIASES.unbekannt)); } // Gibt den Namen des Landes mit dem uebergebenen Kuerzel (TLA) zurueck. // tla: Kuerzel (TLA) des Landes // defValue: Default-Wert // return Name des Landes, 'unbekannt' fuer undefined function getLandName(tla, defValue = __TLALAND[undefined]) { return getValue(__TLALAND[tla], defValue); } // Gibt die ID des Landes mit dem uebergebenen Namen zurueck. // land: Name des Landes // defValue: Default-Wert // return OS2-ID des Landes, 0 fuer ungueltig function getLandNr(land, defValue = __LANDNRN.unbekannt) { return getValue(__LANDNRN[land], defValue); } // Gibt die ID der Liga mit dem uebergebenen Namen zurueck. // land: Name der Liga // defValue: Default-Wert // return OS2-ID der Liga, 0 fuer ungueltig function getLigaNr(liga, defValue = __LIGANRN.unbekannt) { return getValue(__LIGANRN[liga], defValue); } // Kehrt das Mapping eines Objekts um und liefert ein neues Objekt zurueck. // obj: Objekt mit key => value // convFun: Konvertierfunktion fuer die Werte // return Neues Objekt mit value => key (doppelte value-Werte fallen heraus!) function reverseMapping(obj, convFun) { if (! obj) { return obj; } const __RET = { }; for (let key in obj) { const __VALUE = obj[key]; __RET[__VALUE] = (convFun ? convFun(key) : key); } return __RET; } // ==================== Abschnitt fuer sonstige Parameter ==================== // Ermittelt den Spielgegner aus einer Tabellenzelle und liefert den Namen zurueck // cell: Tabellenzelle mit dem Namen des Gegners // return Der Name des Gegners function getGegnerFromCell(cell) { const __GEGNER = cell.textContent; const __POS = __GEGNER.indexOf(" ("); if (~ __POS) { return __GEGNER.substr(0, __POS); } else { return __GEGNER; } } // Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle, etwa "2 : 1", und liefert zwei Werte zurueck // cell: Tabellenzelle mit Eintrag "2 : 1" // return { '2', '1' } im Beispiel function getErgebnisFromCell(cell) { const __ERGEBNIS = cell.textContent.split(" : ", 2); return __ERGEBNIS; } // Ermittelt die Spielart aus einer Tabellenzelle, etwa "Liga : Heim", und liefert zwei Werte zurueck // cell: Tabellenzelle mit Eintrag "Liga : Heim" (Spielplan) oder "Liga Heim: " (Managerbuero) // return { "Liga", "Heim" } im Beispiel function getSpielArtFromCell(cell) { const __TEXT = cell.textContent.replace('\xA0', "").replace(':', "").replace(" ", ' '); const __SPIELART = __TEXT.split(' ', 2); return __SPIELART; } // Ermittelt den Namen des Spielgegners aus einer Tabellenzelle und setzt gegner im Spielplanzeiger // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // cell: Tabellenzelle mit dem Namen des Gegners function setGegnerFromCell(currZAT, cell) { const __GEGNER = getGegnerFromCell(cell); currZAT.gegner = __GEGNER; } // Ermittelt das Spiel-Ergebnis aus einer Tabellenzelle und setzt tore/gtore im Spielplanzeiger // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // cell: Tabellenzelle mit Eintrag "2 : 1" // return Modifizierter Spielplanzeiger function setErgebnisFromCell(currZAT, cell) { const __ERGEBNIS = getErgebnisFromCell(cell); if (__ERGEBNIS.length === 2) { currZAT.gFor = parseInt(__ERGEBNIS[0], 10); currZAT.gAga = parseInt(__ERGEBNIS[1], 10); } else { currZAT.gFor = -1; currZAT.gAga = -1; } return currZAT; } // Ermittelt die Spielart aus einer Tabellenzelle und setzt gameType/heim im Spielplanzeiger // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // cell: Tabellenzelle mit Eintrag "Liga : Heim" oder "Liga Heim" function setSpielArtFromCell(currZAT, cell) { const __SPIELART = getSpielArtFromCell(cell); currZAT.gameType = __SPIELART[0]; currZAT.heim = (__SPIELART.length < 2) || (__SPIELART[1] === 'Heim'); } // Gibt die ID fuer den Namen eines Wettbewerbs zurueck // cell: Tabellenzelle mit Link auf den Spielberichts-Link // gameType: Name des Wettbewerbs eines Spiels // label: Anzuklickender Text des neuen Links // return HTML-Link auf die Preview-Seite fuer diesen Spielbericht function getBilanzLinkFromCell(cell, gameType, label) { const __GAMETYPEID = getGameTypeID(gameType); let ret = ""; if (cell.textContent !== 'Vorschau') { // Nur falls Link nicht bereits vorhanden if (__GAMETYPEID > 1) { // nicht moeglich fuer "Friendly" bzw. "spielfrei"/"Frei"/"reserviert" const __SEARCHFUN = ":os_bericht("; let paarung = cell.innerHTML.substr(cell.innerHTML.indexOf(__SEARCHFUN) + __SEARCHFUN.length); paarung = paarung.substr(0, paarung.indexOf(')')); paarung = paarung.substr(0, paarung.lastIndexOf(',')); paarung = paarung.substr(0, paarung.lastIndexOf(',')); ret = ' <a href="javascript:spielpreview(' + paarung + ',' + __GAMETYPEID + ')">' + label + "</a>"; } } return ret; } // Addiert einen Link auf die Bilanz hinter den Spielberichts-Link // cell: Tabellenzelle mit Link auf den Spielberichts-Link // gameType: Name des Wettbewerbs eines Spiels // label: Anzuklickender Text des neuen Links function addBilanzLinkToCell(cell, gameType, label) { const __BILANZLINK = getBilanzLinkFromCell(cell, gameType, label); if (__BILANZLINK !== "") { cell.innerHTML += __BILANZLINK; } } // ==================== Abschnitt fuer sonstige Parameter des Spielplans ==================== const __TEAMSEARCHHAUPT = { // Parameter zum Team "<b>Willkommen im Managerbüro von TEAM</b><br>LIGA LAND<a href=..." 'Tabelle' : 1, 'Zeile' : 0, 'Spalte' : 1, 'start' : " von ", 'middle' : "</b><br>", 'liga' : ". Liga", 'land' : ' ', 'end' : "<a href=" }; const __TEAMSEARCHTEAM = { // Parameter zum Team "<b>TEAM - LIGA <a href=...>LAND</a></b>" 'Tabelle' : 1, 'Zeile' : 0, 'Spalte' : 0, 'start' : "<b>", 'middle' : " - ", 'liga' : ". Liga", 'land' : 'target="_blank">', 'end' : "</a></b>" }; const __TEAMIDSEARCHHAUPT = { // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>" 'Tabelle' : 0, 'Zeile' : 6, 'Spalte' : 0, 'start' : '<a href="livegame/index.php?spiele=', 'end' : ',0">LIVEGAME</a>' }; const __TEAMIDSEARCHTEAM = { // Parameter zur Team-ID "<b>Deine Spiele in</b>...<a href="livegame/index.php?spiele=TEAMID,0">LIVEGAME</a>" 'Tabelle' : 0, 'Zeile' : 1, 'Spalte' : 1, 'start' : '<a hspace="20" href="javascript:tabellenplatz(', 'end' : ')">Tabellenpl\xE4tze</a>' }; // Ermittelt, wie das eigene Team heisst und aus welchem Land bzw. Liga es kommt (zur Unterscheidung von Erst- und Zweitteam) // teamSearch: Muster fuer die Suche nach Team, die Eintraege fuer 'start', 'middle', 'liga', 'land' und 'end' enthaelt, ausserdem die // Adresse der Tabellenzelle mit den Parametern zum Team "startTEAMmiddleLIGA...landLANDend", LIGA = "#liga[ (A|B|C|D)]" // teamIdSearch: Muster fuer die Suche nach Team-ID, die Eintraege fuer 'start' und 'end' enthaelt, ausserdem die // Adresse der Tabellenzelle mit den Parametern zur Team-ID "startTEAMIDend" // doc: Optionale Angabe des Dokuments, in dem die Tabelle gesucht wird (Default: document) // return Im Beispiel { 'Team' : "TEAM", 'Liga' : "LIGA", 'Land' : "LAND", 'TmNr' : TEAMID, 'LdNr' : LAND-NUMMER, 'LgNr' : LIGA-NUMMER }, // z.B. { 'Team' : "Choromonets Odessa", 'Liga' : "1. Liga", 'Land' : "Ukraine", 'TmNr' : 930, 'LdNr' : 20, 'LgNr' : 1 } function getTeamParamsFromTable(teamSearch, teamIdSearch, doc = document) { // Ermittlung von Team, Liga und Land... const __TEAMSEARCH = getValue(teamSearch, __TEAMSEARCHHAUPT); const __TEAMTABLE = getTable(getValue(__TEAMSEARCH.Tabelle, 1), 'table', doc); const __TEAMCELLROW = getValue(__TEAMSEARCH.Zeile, 0); const __TEAMCELLCOL = getValue(__TEAMSEARCH.Spalte, 0); const __TEAMCELLSTR = (__TEAMTABLE === undefined) ? "" : __TEAMTABLE.rows[__TEAMCELLROW].cells[__TEAMCELLCOL].innerHTML; const __SEARCHSTART = __TEAMSEARCH.start; const __SEARCHMIDDLE = __TEAMSEARCH.middle; const __SEARCHLIGA = __TEAMSEARCH.liga; const __SEARCHLAND = __TEAMSEARCH.land; const __SEARCHEND = __TEAMSEARCH.end; const __INDEXSTART = __TEAMCELLSTR.indexOf(__SEARCHSTART); const __INDEXEND = __TEAMCELLSTR.indexOf(__SEARCHEND); let teamParams = __TEAMCELLSTR.substring(__INDEXSTART + __SEARCHSTART.length, __INDEXEND); const __INDEXLIGA = teamParams.indexOf(__SEARCHLIGA); const __INDEXMIDDLE = teamParams.indexOf(__SEARCHMIDDLE); let land = ((~ __INDEXLIGA) ? teamParams.substring(__INDEXLIGA + __SEARCHLIGA.length) : undefined); const __TEAMNAME = ((~ __INDEXMIDDLE) ? teamParams.substring(0, __INDEXMIDDLE) : undefined); let liga = (((~ __INDEXLIGA) && (~ __INDEXMIDDLE)) ? teamParams.substring(__INDEXMIDDLE + __SEARCHMIDDLE.length) : undefined); if (land !== undefined) { if (land.charAt(2) === ' ') { // Land z.B. hinter "2. Liga A " statt "1. Liga " land = land.substr(2); } if (liga !== undefined) { liga = liga.substring(0, liga.length - land.length); } const __INDEXLAND = land.indexOf(__SEARCHLAND); if (~ __INDEXLAND) { land = land.substr(__INDEXLAND + __SEARCHLAND.length); } } // Ermittlung der Team-ID (indirekt ueber den Livegame- bzw. Tabellenplatz-Link)... const __TEAMIDSEARCH = getValue(teamIdSearch, __TEAMIDSEARCHHAUPT); const __TEAMIDTABLE = getTable(getValue(__TEAMIDSEARCH.Tabelle, 0), 'table', doc); const __TEAMIDCELLROW = getValue(__TEAMIDSEARCH.Zeile, 6); const __TEAMIDCELLCOL = getValue(__TEAMIDSEARCH.Spalte, 0); const __TEAMIDCELLSTR = (__TEAMIDTABLE === undefined) ? "" : __TEAMIDTABLE.rows[__TEAMIDCELLROW].cells[__TEAMIDCELLCOL].innerHTML; const __SEARCHIDSTART = __TEAMIDSEARCH.start; const __SEARCHIDEND = __TEAMIDSEARCH.end; const __INDEXIDSTART = __TEAMIDCELLSTR.indexOf(__SEARCHIDSTART); const __INDEXIDEND = __TEAMIDCELLSTR.indexOf(__SEARCHIDEND); const __TEAMIDSTR = __TEAMIDCELLSTR.substring(__INDEXIDSTART + __SEARCHIDSTART.length, __INDEXIDEND); const __TEAMID = Number.parseInt(__TEAMIDSTR, 10); const __TEAM = new Team(__TEAMNAME, land, liga, __TEAMID); return __TEAM; } // Gibt die laufende Nummer des ZATs im Text einer Zelle zurueck // cell: Tabellenzelle mit der ZAT-Nummer im Text // return ZAT-Nummer im Text function getZATNrFromCell(cell) { const __TEXT = ((cell === undefined) ? [] : cell.textContent.split(' ')); let ZATNr = 0; for (let i = 1; (ZATNr === 0) && (i < __TEXT.length); i++) { if (__TEXT[i - 1] === "ZAT") { if (__TEXT[i] !== "ist") { ZATNr = parseInt(__TEXT[i], 10); } } } return ZATNr; } // ==================== Ende Abschnitt fuer Spielplan und ZATs ==================== // Fuegt eine Zelle ans Ende der uebergebenen Zeile hinzu und fuellt sie // row: Zeile, die verlaengert wird // content: Textinhalt der neuen Zelle // color: Schriftfarbe der neuen Zelle (z.B. "#FFFFFF" fuer weiss) // Bei Aufruf ohne Farbe wird die Standardfarbe benutzt function appendCell(row, content, color) { row.insertCell(-1); const __COLIDX = row.cells.length - 1; row.cells[__COLIDX].textContent = content; row.cells[__COLIDX].align = "center"; row.cells[__COLIDX].style.color = color; } // Fuegt eine Zelle ans Ende der uebergebenen Zeile hinzu und fuellt sie // row: Zeile, die verlaengert wird // content: HTML-Inhalt der neuen Zelle // color: Schriftfarbe der neuen Zelle (z.B. "#FFFFFF" fuer weiss) // Bei Aufruf ohne Farbe wird die Standardfarbe benutzt function appendHTML(row, content, color) { row.insertCell(-1); const __COLIDX = row.cells.length - 1; row.cells[__COLIDX].innerHTML = content; row.cells[__COLIDX].align = "center"; row.cells[__COLIDX].style.color = color; } // Spult die Daten um anzZAT ZATs vor und schreibt Parameter // anhand des Spielplans fort. Also Spieltag, Runde, etc. // row: Zeile mit den Daten zum Spiel (Spielart, Gegner) // currZAT: Enthaelt den Spielplanzeiger auf den aktuellen ZAT // anzZAT: Anzahl der ZAT, um die vorgespult wird // bilanz: Angabe, ob Bilanz-Link eingefuegt werden oll function addZusatz(row, currZAT, anzZAT = 1, bilanz = false) { const __ROW = getValue(row, { }); const __CELLS = __ROW.cells; const __COLUMNINDEX = { 'Lbl' : 0, 'Art' : 1, 'Geg' : 2, 'Ber' : 2, 'Zus' : 3 }; if (__CELLS) { setGegnerFromCell(currZAT, __CELLS[__COLUMNINDEX.Geg]); setSpielArtFromCell(currZAT, __CELLS[__COLUMNINDEX.Art]); if (bilanz) { addBilanzLinkToCell(__CELLS[__COLUMNINDEX.Ber], currZAT.gameType, "(Bilanz)"); } incZAT(currZAT, anzZAT); appendHTML(__ROW, " " + getZusatz(currZAT, true)); __CELLS[__COLUMNINDEX.Zus].className = __CELLS[__COLUMNINDEX.Art].className; } } // ==================== Hauptprogramm ==================== // Verarbeitet Ansicht "Haupt" (Managerbuero) function procHaupt() { const __TEAMPARAMS = getTeamParamsFromTable(__TEAMSEARCHHAUPT, __TEAMIDSEARCHHAUPT); return buildOptions(__OPTCONFIG, __OPTSET, { 'teamParams' : __TEAMPARAMS, 'menuAnchor' : getTable(1, 'div'), 'hideForm' : { 'team' : true } }).then(optSet => { const __ZAT = firstZAT(getOptValue(__OPTSET.saison), getOptValue(__OPTSET.ligaSize)); const __ZATCELL = getProp(getProp(getRows(0), 2), 'cells', { })[0]; const __NEXTZAT = getZATNrFromCell(__ZATCELL); // "Der naechste ZAT ist ZAT xx und ..." const __CURRZAT = __NEXTZAT - 1; addZusatz(getProp(getRows(2), 0), __ZAT, __CURRZAT, true); // "Dein letztes Spiel:" (+ __CURRZAT) addZusatz(getProp(getRows(3), 0), __ZAT); // "Dein naechstes Spiel:" (+ 1 ZAT) }); } (() => { (async () => { try { await procHaupt().catch(defaultCatch); return 'OK'; } catch (ex) { return defaultCatch(ex); } })().then(rc => { __LOG[1]('SCRIPT END', __DBMOD.Name, '(' + rc + ')'); }) })(); // *** EOF ***