iobroker.alias-workbench 0.9.0 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/admin/geraetewissen/tasmota-befehle.json +3 -1
- package/admin/i18n/de.json +17 -6
- package/admin/i18n/en.json +17 -6
- package/admin/js/aufzaehlungen.js +11 -1
- package/admin/js/baum.js +90 -32
- package/admin/js/detail.js +18 -3
- package/admin/js/entwurf.js +176 -42
- package/admin/js/enums.js +50 -7
- package/admin/js/ergebnis.js +47 -10
- package/admin/js/katalog.js +12 -3
- package/admin/js/mqtt.js +76 -13
- package/admin/js/musterwahl.js +10 -0
- package/admin/js/objekte.js +29 -3
- package/admin/js/pruefungen.js +10 -2
- package/admin/js/quellentausch.js +31 -2
- package/admin/js/schreiben.js +209 -24
- package/admin/js/sprache.js +51 -7
- package/admin/js/start.js +32 -2
- package/admin/js/vorlagen.js +92 -4
- package/admin/js/vorlagenblatt.js +193 -23
- package/admin/js/werte.js +113 -6
- package/admin/js/zuordnung.js +30 -9
- package/admin/js/zustandsliste.js +14 -10
- package/admin/tab.html +17 -3
- package/admin/vorlagen/hm-dimmer.json +1 -1
- package/admin/vorlagen/hm-schaltaktor-lampe.json +2 -2
- package/admin/vorlagen/hm-schaltaktor-mehrfach.json +2 -2
- package/admin/vorlagen/hm-schaltaktor-steckdose.json +2 -2
- package/io-package.json +92 -1
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import { katalogFehlt } from './katalog.js';
|
|
|
11
11
|
import { funktionsAuswahl, kennungZuFunktion } from './aufzaehlungen.js';
|
|
12
12
|
import { musterVon, rolleTrifft } from './erkennung.js';
|
|
13
13
|
import { musterZeile, musterName } from './musternamen.js';
|
|
14
|
-
import { ladeVorlagen, aendereVorlagen, setzeMeta, pruefeVorlage, INSTANZ_ID } from './vorlagen.js';
|
|
14
|
+
import { ladeVorlagen, aendereVorlagen, setzeMeta, pruefeVorlage, hinweisTaugt, INSTANZ_ID } from './vorlagen.js';
|
|
15
15
|
import { kindZustaende, hatPunkt, feldAusFormel } from './werte.js';
|
|
16
16
|
import { opt } from './entwurf.js';
|
|
17
17
|
import { zeichneErgebnis } from './ergebnis.js';
|
|
@@ -326,6 +326,15 @@ export function zeichneVorlagenBlatt(host) {
|
|
|
326
326
|
nb.style.padding = '0 12px 11px';
|
|
327
327
|
nb.appendChild(feld(tr('tpls.nameHint'), (v.erkennung || {}).namenshinweis || '',
|
|
328
328
|
function (x, w) { x.erkennung = x.erkennung || {}; if (w) { x.erkennung.namenshinweis = w; } else { delete x.erkennung.namenshinweis; } }, '260px'));
|
|
329
|
+
/* Der Text ist ein Ausdruck, und ein unbrauchbarer Ausdruck faellt
|
|
330
|
+
sonst nirgends auf: Er wird still zu „trifft nicht", und die Vorlage
|
|
331
|
+
verliert leise ihren Namensbonus. Also hier sagen, wo er steht. */
|
|
332
|
+
if (!hinweisTaugt((v.erkennung || {}).namenshinweis || '')) {
|
|
333
|
+
var nbw = el('div', 'hint');
|
|
334
|
+
nbw.style.color = 'var(--bad)';
|
|
335
|
+
nbw.textContent = tr('tpls.nameHintBad');
|
|
336
|
+
nb.appendChild(nbw);
|
|
337
|
+
}
|
|
329
338
|
nb.appendChild(el('div', 'hint', tr('tv.nameHintShort')));
|
|
330
339
|
kk.appendChild(nb);
|
|
331
340
|
host.appendChild(kk);
|
|
@@ -909,12 +918,61 @@ export function vEinlesenDatei(datei) {
|
|
|
909
918
|
leser.readAsText(datei);
|
|
910
919
|
}
|
|
911
920
|
|
|
921
|
+
/* Was eingelesen wird, wird gespeichert — und danach an jedem Geraet
|
|
922
|
+
ausgewertet. Geprueft wurden bisher nur `id` und `zustaende`.
|
|
923
|
+
|
|
924
|
+
Zwei Wege in den Totalschaden: `erkennung.erforderlich` als Text
|
|
925
|
+
statt als Liste laesst `pruefeVorlage` an JEDEM Geraet werfen
|
|
926
|
+
(„map is not a function", gemessen 09.09.2026), ein fehlendes `name`
|
|
927
|
+
wirft schon beim Oeffnen im Blatt. Die Knopfleiste „Einlesen und
|
|
928
|
+
speichern" steht zu diesem Zeitpunkt laengst im DOM — man konnte
|
|
929
|
+
also eine Vorlage speichern, die den Reiter danach unbrauchbar
|
|
930
|
+
machte.
|
|
931
|
+
|
|
932
|
+
Listenfelder werden nicht abgewiesen, wenn ein einzelner Text
|
|
933
|
+
gemeint sein kann: `erforderlich: "tele.STATE"` ist eindeutig und
|
|
934
|
+
wird zur Liste gemacht. Abgewiesen wird nur, was keinen Sinn ergibt. */
|
|
912
935
|
function vPruefeEingelesen(o) {
|
|
913
936
|
if (!o || typeof o !== 'object' || Array.isArray(o)) { return tr('tv.badNotObject'); }
|
|
914
937
|
if (!o.id || typeof o.id !== 'string') { return tr('tv.badNoId'); }
|
|
915
938
|
if (!Array.isArray(o.zustaende) || !o.zustaende.length) { return tr('tv.badNoStates'); }
|
|
916
939
|
var schlecht = o.zustaende.filter(function (z) { return !z || !z.name || !z.lesen; });
|
|
917
940
|
if (schlecht.length) { return tr('tv.badState', schlecht.length); }
|
|
941
|
+
|
|
942
|
+
if (o.name !== undefined &&
|
|
943
|
+
!(typeof o.name === 'string' ||
|
|
944
|
+
(o.name && typeof o.name === 'object' && !Array.isArray(o.name)))) {
|
|
945
|
+
return tr('tv.badName');
|
|
946
|
+
}
|
|
947
|
+
if (o.erkennung !== undefined &&
|
|
948
|
+
(!o.erkennung || typeof o.erkennung !== 'object' || Array.isArray(o.erkennung))) {
|
|
949
|
+
return tr('tv.badRecog');
|
|
950
|
+
}
|
|
951
|
+
var erk = o.erkennung || {};
|
|
952
|
+
var listen = ['erforderlich', 'verboten'];
|
|
953
|
+
for (var i = 0; i < listen.length; i++) {
|
|
954
|
+
var w = erk[listen[i]];
|
|
955
|
+
if (w === undefined) { continue; }
|
|
956
|
+
if (typeof w === 'string') { erk[listen[i]] = [w]; continue; }
|
|
957
|
+
if (!Array.isArray(w) || w.some(function (x) { return typeof x !== 'string'; })) {
|
|
958
|
+
return tr('tv.badList', listen[i]);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (erk.inhalt !== undefined &&
|
|
962
|
+
(!erk.inhalt || typeof erk.inhalt !== 'object' || Array.isArray(erk.inhalt))) {
|
|
963
|
+
return tr('tv.badContent');
|
|
964
|
+
}
|
|
965
|
+
if (erk.namenshinweis !== undefined &&
|
|
966
|
+
(typeof erk.namenshinweis !== 'string' || !hinweisTaugt(erk.namenshinweis))) {
|
|
967
|
+
return tr('tv.badNameHint');
|
|
968
|
+
}
|
|
969
|
+
/* Ein Musternamen, den dieser Detector nicht kennt, wirft spaeter in
|
|
970
|
+
der Musterwahl — und der Entwurf wird dann gar nicht erst
|
|
971
|
+
gezeichnet. Lieber hier abweisen, wo noch jemand zusieht. */
|
|
972
|
+
if (o.geraetetyp !== undefined &&
|
|
973
|
+
(typeof o.geraetetyp !== 'string' || (o.geraetetyp && !musterVon(o.geraetetyp)))) {
|
|
974
|
+
return tr('tv.badType', String(o.geraetetyp));
|
|
975
|
+
}
|
|
918
976
|
return null;
|
|
919
977
|
}
|
|
920
978
|
|
|
@@ -998,35 +1056,96 @@ function relZu(id, kanal) {
|
|
|
998
1056
|
/* stat.POWER1 → stat.POWER%N%. Ersetzt wird nur am Ende eines
|
|
999
1057
|
Abschnitts und nur, wenn davor noch etwas steht — sonst wuerde aus
|
|
1000
1058
|
ENERGY.Power beim Speichern Unsinn. */
|
|
1001
|
-
function zuPlatzhalter(pfad, inst) {
|
|
1059
|
+
function zuPlatzhalter(pfad, inst, vorspaenne) {
|
|
1002
1060
|
var i = (inst === null || inst === undefined) ? '' : String(inst);
|
|
1003
1061
|
if (!i) { return pfad; }
|
|
1004
1062
|
return String(pfad).split('.').map(function (t) {
|
|
1005
|
-
|
|
1063
|
+
if (!(t.length > i.length && t.slice(-i.length) === i)) { return t; }
|
|
1064
|
+
/* Nicht jede Endziffer ist eine Ausgangsnummer.
|
|
1065
|
+
|
|
1066
|
+
Ersetzt wurde rein nach Endziffer: an einem Mehrfachgeraet mit
|
|
1067
|
+
Ausgang 2 wurde damit auch die IP-Zeile aus `tele.INFO2` zu
|
|
1068
|
+
`tele.INFO%N%` (gemessen 09.09.2026). Der Schaden zeigt sich erst
|
|
1069
|
+
am naechsten Geraet — und an Ausgang 1 meldete `andereAusgaenge`
|
|
1070
|
+
dieselbe Zeile als fremden Ausgang, „Andere weglassen" warf sie
|
|
1071
|
+
hinaus. `INFO2` heisst bei Tasmota die zweite Info-Seite, an
|
|
1072
|
+
jedem Ausgang gleich. */
|
|
1073
|
+
return (vorspaenne.indexOf(t.slice(0, -i.length)) === -1)
|
|
1074
|
+
? t : t.slice(0, -i.length) + '%N%';
|
|
1006
1075
|
}).join('.');
|
|
1007
1076
|
}
|
|
1008
1077
|
|
|
1078
|
+
/* Welche Vorspaenne tragen wirklich die Ausgangsnummer?
|
|
1079
|
+
|
|
1080
|
+
Drei Quellen, in dieser Reihenfolge belastbar: was die Vorlage im
|
|
1081
|
+
Original schon mit `%N%` fuehrte, der Vorspann aus `kanalname`
|
|
1082
|
+
(`POWER%N%` → `POWER`), und was mit derselben Nummer mehrfach
|
|
1083
|
+
vorkommt — `cmnd.POWER2` und `stat.POWER2` sagen gemeinsam mehr als
|
|
1084
|
+
ein einzelnes `tele.INFO2`. */
|
|
1085
|
+
function ausgangsVorspaenne(zeilen, k) {
|
|
1086
|
+
var i = (k.instanz === null || k.instanz === undefined) ? '' : String(k.instanz);
|
|
1087
|
+
var raus = [];
|
|
1088
|
+
var dazu = function (v) { if (v && raus.indexOf(v) === -1) { raus.push(v); } };
|
|
1089
|
+
|
|
1090
|
+
var kn = String(k.kanalname || 'POWER%N%');
|
|
1091
|
+
if (kn.indexOf('%N%') !== -1) { dazu(kn.split('%N%')[0]); }
|
|
1092
|
+
|
|
1093
|
+
((k.quelle && k.quelle.zustaende) || []).forEach(function (z) {
|
|
1094
|
+
[z.lesen, z.schreiben].forEach(function (pf) {
|
|
1095
|
+
String(pf || '').split('.').forEach(function (t) {
|
|
1096
|
+
var n = t.indexOf('%N%');
|
|
1097
|
+
if (n > 0) { dazu(t.slice(0, n)); }
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
if (i) {
|
|
1103
|
+
var wie = {};
|
|
1104
|
+
(zeilen || []).forEach(function (r) {
|
|
1105
|
+
if (r.weg) { return; }
|
|
1106
|
+
[r.lesenAbs ? '' : r.lesenRoh, r.schreibenAbs ? '' : r.schreibenRoh].forEach(function (pf) {
|
|
1107
|
+
String(pf || '').split('.').forEach(function (t) {
|
|
1108
|
+
if (t.length > i.length && t.slice(-i.length) === i) {
|
|
1109
|
+
var v = t.slice(0, -i.length);
|
|
1110
|
+
wie[v] = (wie[v] || 0) + 1;
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
});
|
|
1114
|
+
});
|
|
1115
|
+
Object.keys(wie).forEach(function (v) { if (wie[v] > 1) { dazu(v); } });
|
|
1116
|
+
}
|
|
1117
|
+
return raus;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1009
1120
|
/* Welcher Pfad steht am Ende in der Vorlage? Bei einem Mehrfachgeraet
|
|
1010
1121
|
tritt der Platzhalter an die Stelle der Ausgangsnummer. */
|
|
1011
|
-
function zPfad(r, k, schreibend) {
|
|
1122
|
+
function zPfad(r, k, schreibend, zeilen) {
|
|
1012
1123
|
var roh = schreibend ? r.schreibenRoh : r.lesenRoh;
|
|
1013
1124
|
var abs = schreibend ? r.schreibenAbs : r.lesenAbs;
|
|
1014
1125
|
if (!roh) { return ''; }
|
|
1015
1126
|
if (!k.mehrfach || abs || !k.instanz) { return roh; }
|
|
1016
|
-
return zuPlatzhalter(roh, k.instanz
|
|
1127
|
+
return zuPlatzhalter(roh, k.instanz,
|
|
1128
|
+
ausgangsVorspaenne(zeilen || (S.tplZ && S.tplZ.zeilen) || [], k));
|
|
1017
1129
|
}
|
|
1018
1130
|
|
|
1019
1131
|
/* Endet die Nummer eines Ausgangs in den Pfaden? Haeufigste Zahl am
|
|
1020
1132
|
Ende eines Abschnitts gewinnt — cmnd.POWER1, stat.POWER1 sagen 1. */
|
|
1021
|
-
function rateInstanz(zeilen, geraetName) {
|
|
1022
|
-
var zaehler = {};
|
|
1133
|
+
function rateInstanz(zeilen, geraetName, vorspann) {
|
|
1134
|
+
var zaehler = {}, nurVorspann = {};
|
|
1023
1135
|
zeilen.forEach(function (r) {
|
|
1024
1136
|
if (r.lesenAbs) { return; }
|
|
1025
1137
|
String(r.lesenRoh).split('.').forEach(function (t) {
|
|
1026
1138
|
var m = /^(.*[^0-9])([0-9]{1,2})$/.exec(t);
|
|
1027
|
-
if (m) {
|
|
1139
|
+
if (!m) { return; }
|
|
1140
|
+
zaehler[m[2]] = (zaehler[m[2]] || 0) + 1;
|
|
1141
|
+
if (vorspann && m[1] === vorspann) { nurVorspann[m[2]] = (nurVorspann[m[2]] || 0) + 1; }
|
|
1028
1142
|
});
|
|
1029
1143
|
});
|
|
1144
|
+
/* Zaehlt der Vorspann des Ausgangs mit (`POWER%N%` → `POWER`), gilt
|
|
1145
|
+
allein er: sonst hob ein `tele.INFO2` die Nummer eines Geraets an,
|
|
1146
|
+
das gar keinen zweiten Ausgang hat. Findet sich damit nichts,
|
|
1147
|
+
bleibt es beim alten Weg — besser eine geratene Nummer als keine. */
|
|
1148
|
+
if (Object.keys(nurVorspann).length) { zaehler = nurVorspann; }
|
|
1030
1149
|
var mn = /^(.*[^0-9])([0-9]{1,2})$/.exec(geraetName || '');
|
|
1031
1150
|
if (mn) { zaehler[mn[2]] = (zaehler[mn[2]] || 0) + 1; }
|
|
1032
1151
|
var beste = '', wie = 0;
|
|
@@ -1042,15 +1161,19 @@ function rateInstanz(zeilen, geraetName) {
|
|
|
1042
1161
|
entstehen daraus. Zeigt der Entwurf noch auf POWER2, POWER3 …, waere
|
|
1043
1162
|
die Vorlage an dieses eine Geraet gefesselt. Also einsammeln und
|
|
1044
1163
|
sagen, statt still etwas Falsches zu speichern. */
|
|
1045
|
-
function andereAusgaenge(zeilen, instanz) {
|
|
1164
|
+
function andereAusgaenge(zeilen, instanz, vorspaenne) {
|
|
1046
1165
|
var raus = { punkte: [], nummern: [] };
|
|
1047
1166
|
if (!instanz) { return raus; }
|
|
1167
|
+
var vs = vorspaenne || [];
|
|
1048
1168
|
zeilen.forEach(function (r) {
|
|
1049
1169
|
if (r.weg || r.lesenAbs) { return; }
|
|
1050
1170
|
var fremd = null;
|
|
1051
1171
|
String(r.lesenRoh).split('.').forEach(function (t) {
|
|
1052
1172
|
var m = /^(.*[^0-9])([0-9]{1,2})$/.exec(t);
|
|
1053
|
-
|
|
1173
|
+
/* Dieselbe Regel wie beim Platzhalter: nur was den Ausgang traegt.
|
|
1174
|
+
Sonst stand `tele.INFO2` an Ausgang 1 als fremder Ausgang da,
|
|
1175
|
+
und „Andere weglassen" warf die IP-Zeile hinaus. */
|
|
1176
|
+
if (m && m[2] !== String(instanz) && vs.indexOf(m[1]) !== -1) { fremd = m[2]; }
|
|
1054
1177
|
});
|
|
1055
1178
|
if (fremd) {
|
|
1056
1179
|
raus.punkte.push(r.name);
|
|
@@ -1167,7 +1290,8 @@ export function vorlagenVorbereiten(e) {
|
|
|
1167
1290
|
maxRang: maxRang,
|
|
1168
1291
|
mehrfach: mehrfach,
|
|
1169
1292
|
instanz: mehrfach ? String(inst === null || inst === undefined ? '' : inst)
|
|
1170
|
-
: rateInstanz(zeilen, geraetName
|
|
1293
|
+
: rateInstanz(zeilen, geraetName,
|
|
1294
|
+
String((quelle && quelle.kanalname) || 'POWER%N%').split('%N%')[0]),
|
|
1171
1295
|
kanalname: (quelle && quelle.kanalname) ? quelle.kanalname : 'POWER%N%',
|
|
1172
1296
|
verboten: quelle ? (((quelle.erkennung || {}).verboten) || []).join(', ') : '',
|
|
1173
1297
|
geraetName: geraetName
|
|
@@ -1191,8 +1315,8 @@ export function baueVorlage(z) {
|
|
|
1191
1315
|
var erf = [];
|
|
1192
1316
|
z.zeilen.forEach(function (r) {
|
|
1193
1317
|
if (r.weg || !r.pflicht || r.lesenAbs) { return; }
|
|
1194
|
-
var lp = zPfad(r, k, false);
|
|
1195
|
-
var sp = zPfad(r, k, true);
|
|
1318
|
+
var lp = zPfad(r, k, false, z.zeilen);
|
|
1319
|
+
var sp = zPfad(r, k, true, z.zeilen);
|
|
1196
1320
|
if (erf.indexOf(lp) === -1) { erf.push(lp); }
|
|
1197
1321
|
if (sp && !r.schreibenAbs && erf.indexOf(sp) === -1) { erf.push(sp); }
|
|
1198
1322
|
});
|
|
@@ -1225,10 +1349,10 @@ export function baueVorlage(z) {
|
|
|
1225
1349
|
rolle: s.role || undefined,
|
|
1226
1350
|
typ: s.typ || undefined,
|
|
1227
1351
|
einheit: s.unit || undefined,
|
|
1228
|
-
lesen: zPfad(r, k, false)
|
|
1352
|
+
lesen: zPfad(r, k, false, z.zeilen)
|
|
1229
1353
|
};
|
|
1230
1354
|
if (r.lesenAbs) { zu.absolut = true; }
|
|
1231
|
-
if (r.schreibenRoh) { zu.schreiben = zPfad(r, k, true); }
|
|
1355
|
+
if (r.schreibenRoh) { zu.schreiben = zPfad(r, k, true, z.zeilen); }
|
|
1232
1356
|
if (f) { zu.feld = f; } else if (s.f) { zu.leseformel = s.f; }
|
|
1233
1357
|
if (s.fw && r.schreibenRoh) { zu.schreibformel = s.fw; }
|
|
1234
1358
|
if (!r.pflicht) { zu.optional = true; }
|
|
@@ -1244,7 +1368,15 @@ export function baueVorlage(z) {
|
|
|
1244
1368
|
.map(function (w) {
|
|
1245
1369
|
var o = { punkt: w.punkt };
|
|
1246
1370
|
if (w.feld) { o.feld = w.feld; }
|
|
1247
|
-
|
|
1371
|
+
/* `leseformel`, nicht `formel`. `wendeAn` legt die Wege mit
|
|
1372
|
+
dem Schluessel `formel` ab, jeder Leser sucht in
|
|
1373
|
+
`lesenSonst` aber `leseformel` (vorlagen.js:462,
|
|
1374
|
+
vorlagenblatt.js:720). Die Formel jeder Ersatzquelle ging
|
|
1375
|
+
beim Aktualisieren still verloren: bei tasmota-steckdose
|
|
1376
|
+
landete der boolesche Alias an Geraeten ohne `stat.POWER`
|
|
1377
|
+
danach auf dem rohen JSON-Text von `tele.STATE` statt auf
|
|
1378
|
+
`POWER` (gemessen 09.09.2026). */
|
|
1379
|
+
if (w.formel) { o.leseformel = w.formel; }
|
|
1248
1380
|
return o;
|
|
1249
1381
|
});
|
|
1250
1382
|
if (rest.length) { zu.lesenSonst = (rest.length === 1) ? rest[0] : rest; }
|
|
@@ -1450,6 +1582,24 @@ export function zeigeVorlagenDialog() {
|
|
|
1450
1582
|
$('#dlg-tpl').showModal();
|
|
1451
1583
|
}
|
|
1452
1584
|
|
|
1585
|
+
/* Zeigt die Kennung auf eine fremde Vorlage?
|
|
1586
|
+
|
|
1587
|
+
`freierSchluessel` laeuft beim Radio-Wechsel und beim Vorbereiten,
|
|
1588
|
+
nicht nach der Handeingabe im Kennungsfeld. `aendereVorlagen` ersetzt
|
|
1589
|
+
bei gleicher Kennung ohne Rueckfrage (`liste[i] = vorlage`), im Modus
|
|
1590
|
+
„neu" sogar mit Version 1: Wer die Kennung einer anderen eigenen
|
|
1591
|
+
Vorlage eintippte, ueberschrieb sie still — gemessen 09.09.2026, aus
|
|
1592
|
+
„I30 Opfer" v7 mit einem Zustand wurde „Tasmota-Lampe" v1 mit
|
|
1593
|
+
vierzehn. Die eigene Quelle ist ausgenommen: sie zu ersetzen ist ja
|
|
1594
|
+
gerade der Sinn von „aktualisieren". */
|
|
1595
|
+
function fremdeMitId(k) {
|
|
1596
|
+
if (!k || !k.id) { return null; }
|
|
1597
|
+
var t = vorlageMitId(k.id);
|
|
1598
|
+
if (!t) { return null; }
|
|
1599
|
+
if (k.modus === 'update' && k.quelle && k.quelle.id === k.id) { return null; }
|
|
1600
|
+
return t;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1453
1603
|
function tplFeld(label, wert, aendern, breit) {
|
|
1454
1604
|
var l = el('label', 'feld');
|
|
1455
1605
|
l.appendChild(el('span', 'feldlabel', label));
|
|
@@ -1531,7 +1681,16 @@ export function zeichneVorlagenDialog() {
|
|
|
1531
1681
|
|
|
1532
1682
|
kb.appendChild(tplFeld(tr('tpls.nameDe'), k.nameDe, function (x) { k.nameDe = x; }, '150px'));
|
|
1533
1683
|
kb.appendChild(tplFeld(tr('tpls.nameEn'), k.nameEn, function (x) { k.nameEn = x; }, '150px'));
|
|
1534
|
-
|
|
1684
|
+
var kf = tplFeld(tr('tpls.id'), k.id, function (x) { k.id = schluesselAus(x); }, '150px');
|
|
1685
|
+
var stoert = fremdeMitId(k);
|
|
1686
|
+
if (stoert) {
|
|
1687
|
+
var kw = el('span', 'hint');
|
|
1688
|
+
kw.style.color = 'var(--bad)';
|
|
1689
|
+
kw.style.flexBasis = '100%';
|
|
1690
|
+
kw.textContent = tr('tpls.idTaken', textIn(stoert.name, sprache), stoert.version || 1);
|
|
1691
|
+
kf.appendChild(kw);
|
|
1692
|
+
}
|
|
1693
|
+
kb.appendChild(kf);
|
|
1535
1694
|
|
|
1536
1695
|
var lt = el('label', 'feld');
|
|
1537
1696
|
lt.appendChild(el('span', 'feldlabel', tr('tpls.deviceType')));
|
|
@@ -1577,6 +1736,13 @@ export function zeichneVorlagenDialog() {
|
|
|
1577
1736
|
var nh = el('div');
|
|
1578
1737
|
nh.style.padding = '0 12px 11px';
|
|
1579
1738
|
var nf = tplFeld(tr('tpls.nameHint'), k.namenshinweis, function (x) { k.namenshinweis = x; }, '260px');
|
|
1739
|
+
if (!hinweisTaugt(k.namenshinweis)) {
|
|
1740
|
+
var nw = el('span', 'hint');
|
|
1741
|
+
nw.style.color = 'var(--bad)';
|
|
1742
|
+
nw.style.flexBasis = '100%';
|
|
1743
|
+
nw.textContent = tr('tpls.nameHintBad');
|
|
1744
|
+
nf.appendChild(nw);
|
|
1745
|
+
}
|
|
1580
1746
|
nh.appendChild(nf);
|
|
1581
1747
|
var nt = el('div', 'hint', tr('tpls.nameHintHelp', k.geraetName));
|
|
1582
1748
|
nt.style.marginTop = '4px';
|
|
@@ -1594,7 +1760,10 @@ export function zeichneVorlagenDialog() {
|
|
|
1594
1760
|
mcb.checked = !!k.mehrfach;
|
|
1595
1761
|
mcb.addEventListener('change', function () {
|
|
1596
1762
|
k.mehrfach = mcb.checked;
|
|
1597
|
-
if (k.mehrfach && !k.instanz) {
|
|
1763
|
+
if (k.mehrfach && !k.instanz) {
|
|
1764
|
+
k.instanz = rateInstanz(z.zeilen, k.geraetName,
|
|
1765
|
+
String(k.kanalname || 'POWER%N%').split('%N%')[0]);
|
|
1766
|
+
}
|
|
1598
1767
|
zeichneVorlagenDialog();
|
|
1599
1768
|
});
|
|
1600
1769
|
ml.appendChild(mcb);
|
|
@@ -1614,7 +1783,7 @@ export function zeichneVorlagenDialog() {
|
|
|
1614
1783
|
mf.appendChild(mz);
|
|
1615
1784
|
mf.appendChild(el('div', 'hint',
|
|
1616
1785
|
k.instanz ? tr('tpls.multipleHelp', k.instanz) : tr('tpls.multipleNoNumber')));
|
|
1617
|
-
var fremd = andereAusgaenge(z.zeilen, k.instanz);
|
|
1786
|
+
var fremd = andereAusgaenge(z.zeilen, k.instanz, ausgangsVorspaenne(z.zeilen, k));
|
|
1618
1787
|
if (fremd.punkte.length) {
|
|
1619
1788
|
var fw2 = el('div', 'aside w');
|
|
1620
1789
|
fw2.style.margin = '9px 0 0';
|
|
@@ -1740,9 +1909,9 @@ export function zeichneVorlagenDialog() {
|
|
|
1740
1909
|
|
|
1741
1910
|
var pw = el('span', 'rx pf');
|
|
1742
1911
|
pw.style.fontFamily = 'var(--mono)';
|
|
1743
|
-
pw.title = zPfad(r, k, false) + (r.schreibenRoh ? ' → ' + zPfad(r, k, true) : '');
|
|
1744
|
-
pw.appendChild(document.createTextNode(zPfad(r, k, false)));
|
|
1745
|
-
if (r.schreibenRoh) { pw.appendChild(el('span', 'chip mut', '→ ' + zPfad(r, k, true))); }
|
|
1912
|
+
pw.title = zPfad(r, k, false, z.zeilen) + (r.schreibenRoh ? ' → ' + zPfad(r, k, true, z.zeilen) : '');
|
|
1913
|
+
pw.appendChild(document.createTextNode(zPfad(r, k, false, z.zeilen)));
|
|
1914
|
+
if (r.schreibenRoh) { pw.appendChild(el('span', 'chip mut', '→ ' + zPfad(r, k, true, z.zeilen))); }
|
|
1746
1915
|
if (r.lesenAbs) { pw.appendChild(el('span', 'chip warn', tr('tpls.absolute'))); }
|
|
1747
1916
|
row.appendChild(pw);
|
|
1748
1917
|
|
|
@@ -1848,7 +2017,8 @@ export function zeichneVorlagenDialog() {
|
|
|
1848
2017
|
|
|
1849
2018
|
var b = $('#btn-tpl-save');
|
|
1850
2019
|
if (b) {
|
|
1851
|
-
b.disabled = fehlerErk || !k.id ||
|
|
2020
|
+
b.disabled = fehlerErk || !k.id || !!fremdeMitId(k) ||
|
|
2021
|
+
!hinweisTaugt(k.namenshinweis) ||
|
|
1852
2022
|
!z.zeilen.filter(function (r) { return !r.weg; }).length;
|
|
1853
2023
|
b.textContent = k.modus === 'update' ? tr('tpls.saveUpdate') : tr('tpls.saveNew');
|
|
1854
2024
|
}
|
package/admin/js/werte.js
CHANGED
|
@@ -52,10 +52,34 @@ export function feldWert(obj, pfad) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
/* Alle Kennungen mit diesem Vorspann — ohne die Gesamtliste zu lesen.
|
|
56
|
+
|
|
57
|
+
`S.keysSorted` ist sortiert, die gesuchten Eintraege liegen also am
|
|
58
|
+
Stueck. Vorher ging jeder dieser Aufrufe die ganze Liste durch:
|
|
59
|
+
24.786 Vergleiche fuer zehn Treffer, rund zwanzigmal je Neuzeichnen,
|
|
60
|
+
und bei laufenden Werten alle 700 ms (gemessen am Produktivsystem
|
|
61
|
+
10.09.2026: Alias 51,9 ms, Quelle 15,8 ms je Zeichnen).
|
|
62
|
+
|
|
63
|
+
Die Binaersuche findet den Anfang in ~15 Schritten, danach wird nur
|
|
64
|
+
noch der Bereich selbst gelesen. */
|
|
65
|
+
export function mitVorspann(pre) {
|
|
66
|
+
var liste = S.keysSorted;
|
|
67
|
+
var lo = 0, hi = liste.length;
|
|
68
|
+
while (lo < hi) {
|
|
69
|
+
var m = (lo + hi) >> 1;
|
|
70
|
+
if (liste[m] < pre) { lo = m + 1; } else { hi = m; }
|
|
71
|
+
}
|
|
72
|
+
var raus = [];
|
|
73
|
+
for (var i = lo; i < liste.length && liste[i].indexOf(pre) === 0; i++) {
|
|
74
|
+
raus.push(liste[i]);
|
|
75
|
+
}
|
|
76
|
+
return raus;
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
export function kindZustaende(kanal) {
|
|
56
80
|
var pre = kanal + '.';
|
|
57
|
-
return
|
|
58
|
-
return
|
|
81
|
+
return mitVorspann(pre).filter(function (k) {
|
|
82
|
+
return S.objects[k] && S.objects[k].type === 'state';
|
|
59
83
|
});
|
|
60
84
|
}
|
|
61
85
|
|
|
@@ -64,6 +88,30 @@ export function direkteZustaende(kanal) {
|
|
|
64
88
|
return kindZustaende(kanal).filter(function (k) { return k.slice(pre.length).indexOf('.') === -1; });
|
|
65
89
|
}
|
|
66
90
|
|
|
91
|
+
/* Wohin schreibt dieser Aliaspunkt?
|
|
92
|
+
|
|
93
|
+
Fuenfmal woertlich gleich ausprogrammiert gewesen — dreimal in
|
|
94
|
+
`entwurf.js`, zweimal in `zuordnung.js` —, und die Kommentare an zwei
|
|
95
|
+
dieser Stellen dokumentieren je einen Fehler, der genau daraus
|
|
96
|
+
entstanden ist: einmal wurde aus einem schaltenden Punkt ein lesender
|
|
97
|
+
ohne Umrechnung, einmal verloren OPEN, SET und pct ihre Schreibquelle,
|
|
98
|
+
weil dort `q.einfach ? '' : …` stand und `common.write` gar nicht
|
|
99
|
+
angesehen wurde.
|
|
100
|
+
|
|
101
|
+
Die Regel selbst: Bei getrennten Quellen (`alias.id.read`/`.write`)
|
|
102
|
+
gilt, was unter `write` steht. Bei einer schlichten `alias.id` zeigen
|
|
103
|
+
Lesen und Schreiben auf denselben Punkt — geschrieben wird dorthin
|
|
104
|
+
aber nur, wenn der Alias das auch sagt: durch `common.write` oder
|
|
105
|
+
durch eine hinterlegte Schreibformel. Wer eine Schreibformel
|
|
106
|
+
hinterlegt hat, wollte schreiben. */
|
|
107
|
+
export function schreibQuelle(o) {
|
|
108
|
+
var c = (o && o.common) || {};
|
|
109
|
+
var a = c.alias || {};
|
|
110
|
+
var q = aliasQuellen(o);
|
|
111
|
+
if (!q.einfach) { return q.write || ''; }
|
|
112
|
+
return (c.write === true || typeof a.write === 'string') ? (q.write || '') : '';
|
|
113
|
+
}
|
|
114
|
+
|
|
67
115
|
export function aliasQuellen(obj) {
|
|
68
116
|
var a = obj && obj.common && obj.common.alias;
|
|
69
117
|
if (!a) { return {}; }
|
|
@@ -114,17 +162,76 @@ export function holeEinzelne(ids, fertig) {
|
|
|
114
162
|
}
|
|
115
163
|
|
|
116
164
|
/* ================== Formel und Wert ================== */
|
|
165
|
+
|
|
166
|
+
/* Leseformeln sind fremder Code, der hier laeuft.
|
|
167
|
+
|
|
168
|
+
Sie stehen in `common.alias.read` jedes Alias und in `leseformel`
|
|
169
|
+
jeder Vorlage, und sie werden je Zeile bei JEDEM Zeichnen ausgefuehrt
|
|
170
|
+
— ungefragt, nicht auf Knopfdruck. Das ist keine neue
|
|
171
|
+
Rechteausweitung: Wer `common.alias.read` schreiben darf, hat
|
|
172
|
+
dieselbe Formel schon im js-controller laufen. Es ist aber ein
|
|
173
|
+
zusaetzlicher Ausfuehrungsort, und einer ohne Grenzen: `try/catch`
|
|
174
|
+
faengt nur Ausnahmen, kein `while(true){}`.
|
|
175
|
+
|
|
176
|
+
Zwei Schranken, beide billig:
|
|
177
|
+
|
|
178
|
+
1. Gemerkt wird je (Formel, Rohwert). Dieselbe Formel auf demselben
|
|
179
|
+
Wert laeuft genau einmal — und genau das ist der Regelfall, wenn
|
|
180
|
+
alle 700 ms neu gezeichnet wird, ohne dass sich etwas geaendert
|
|
181
|
+
hat.
|
|
182
|
+
2. Wer einmal zu lange gebraucht hat, laeuft nicht wieder. Die Zeile
|
|
183
|
+
sagt dann, warum. Den Reiter kann man damit noch einmal
|
|
184
|
+
einfrieren, aber nicht dauerhaft — und nach dem Neuladen steht
|
|
185
|
+
die Sperre wieder. */
|
|
186
|
+
var GEDULD_MS = 250;
|
|
187
|
+
var formelCache = new Map();
|
|
188
|
+
var formelGesperrt = {};
|
|
189
|
+
|
|
190
|
+
function cacheSchluessel(formel, roh) {
|
|
191
|
+
var t = typeof roh;
|
|
192
|
+
if (t === 'string') { return roh.length > 300 ? null : ('s:' + formel + '\u0000' + roh); }
|
|
193
|
+
if (t === 'number' || t === 'boolean') { return t.charAt(0) + ':' + formel + '\u0000' + roh; }
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
117
197
|
export function auswerten(formel, roh) {
|
|
118
198
|
if (roh === undefined || roh === null) { return { ok: false, txt: tr('detail.noValue') }; }
|
|
119
199
|
if (!formel) { return { ok: true, val: roh }; }
|
|
200
|
+
if (formelGesperrt[formel]) {
|
|
201
|
+
return { ok: false, txt: tr('detail.formulaTooSlow', formelGesperrt[formel]), fehler: true };
|
|
202
|
+
}
|
|
203
|
+
var sch = cacheSchluessel(formel, roh);
|
|
204
|
+
if (sch !== null) {
|
|
205
|
+
var da = formelCache.get(sch);
|
|
206
|
+
if (da !== undefined) { return da; }
|
|
207
|
+
}
|
|
208
|
+
var raus;
|
|
209
|
+
var t0 = performance.now();
|
|
120
210
|
try {
|
|
121
211
|
var v = (new Function('val', 'return (' + formel + ');'))(roh);
|
|
122
|
-
if (v === undefined) {
|
|
123
|
-
if (typeof v === 'number' && isNaN(v)) {
|
|
124
|
-
|
|
212
|
+
if (v === undefined) { raus = { ok: false, txt: 'undefined', leer: true }; }
|
|
213
|
+
else if (typeof v === 'number' && isNaN(v)) { raus = { ok: false, txt: 'NaN', leer: true }; }
|
|
214
|
+
else { raus = { ok: true, val: v }; }
|
|
125
215
|
} catch (e) {
|
|
126
|
-
|
|
216
|
+
raus = { ok: false, txt: tr('detail.formulaThrows', e.message), fehler: true };
|
|
127
217
|
}
|
|
218
|
+
var gedauert = performance.now() - t0;
|
|
219
|
+
if (gedauert > GEDULD_MS) {
|
|
220
|
+
formelGesperrt[formel] = Math.round(gedauert);
|
|
221
|
+
console.warn('[alias-workbench] Formel gesperrt nach ' + Math.round(gedauert) + ' ms: ' + formel);
|
|
222
|
+
return { ok: false, txt: tr('detail.formulaTooSlow', Math.round(gedauert)), fehler: true };
|
|
223
|
+
}
|
|
224
|
+
if (sch !== null) {
|
|
225
|
+
/* Nicht unbegrenzt wachsen lassen: bei laufenden Werten entsteht je
|
|
226
|
+
neuem Rohwert ein Eintrag. Zweitausend reichen fuer ein Zeichnen
|
|
227
|
+
um ein Vielfaches; darueber wird der aelteste verworfen. */
|
|
228
|
+
if (formelCache.size >= 2000) {
|
|
229
|
+
var ersteR = formelCache.keys().next();
|
|
230
|
+
if (!ersteR.done) { formelCache.delete(ersteR.value); }
|
|
231
|
+
}
|
|
232
|
+
formelCache.set(sch, raus);
|
|
233
|
+
}
|
|
234
|
+
return raus;
|
|
128
235
|
}
|
|
129
236
|
|
|
130
237
|
var JA = ['ON', 'TRUE', 'YES', 'JA', '1'];
|
package/admin/js/zuordnung.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { S } from './zustand.js';
|
|
5
5
|
import { el } from './basis.js';
|
|
6
6
|
import { tr, txt } from './sprache.js';
|
|
7
|
-
import { enums } from './enums.js';
|
|
7
|
+
import { enums, enumsUnbekannt } from './enums.js';
|
|
8
8
|
import {
|
|
9
9
|
enumListe,
|
|
10
10
|
enumsVon,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from './aufzaehlungen.js';
|
|
15
15
|
import { enumVorlagen, katalogFehlt, ikonCache, ikonErlaubt, ikonNachtrag,
|
|
16
16
|
ikonTaugt, vorlageZu, holeIkon } from './katalog.js';
|
|
17
|
-
import { kindZustaende, aliasQuellen, holeEinzelne } from './werte.js';
|
|
17
|
+
import { kindZustaende, aliasQuellen, holeEinzelne, schreibQuelle } from './werte.js';
|
|
18
18
|
import { aliasFuer, zielId, knotenDa } from './entwurf.js';
|
|
19
19
|
import { zeichneErgebnis, entwurfAngefasst, angebotFrisch } from './ergebnis.js';
|
|
20
20
|
|
|
@@ -35,6 +35,14 @@ import { musterVon, rolleTrifft } from './erkennung.js';
|
|
|
35
35
|
export function enumAenderungen(e) {
|
|
36
36
|
var raus = [];
|
|
37
37
|
if (!e || !e.kanal) { return raus; }
|
|
38
|
+
/* Ohne verlaesslichen Vorrat wird keine Aufzaehlung angefasst.
|
|
39
|
+
|
|
40
|
+
Was hier entstuende, waere aus der Vorlage gebaut und truege genau
|
|
41
|
+
ein Mitglied - `setObject` ersetzt das vorhandene Objekt damit
|
|
42
|
+
vollstaendig. Lieber gar kein Raum als ein Raum, der die anderen
|
|
43
|
+
Mitglieder verschluckt (09.09.2026). Die Oberflaeche sagt es am
|
|
44
|
+
Feld und im Trockenlauf. */
|
|
45
|
+
if (enumsUnbekannt()) { return raus; }
|
|
38
46
|
/* Im Aliasmodus gibt es kein gewaehltes Ziel — der Alias *ist* das Ziel.
|
|
39
47
|
Eine frueher hier stehende Abfrage brach in genau diesem Fall ab, und
|
|
40
48
|
eine im Aliasmodus geaenderte Zuordnung wurde stillschweigend nicht
|
|
@@ -343,6 +351,11 @@ export function enumZeile(host, e) {
|
|
|
343
351
|
});
|
|
344
352
|
el2.appendChild(z);
|
|
345
353
|
});
|
|
354
|
+
if (enumsUnbekannt()) {
|
|
355
|
+
var ef = el('div', 'vz zurueck');
|
|
356
|
+
ef.appendChild(el('span', null, tr('enums.unknown')));
|
|
357
|
+
el2.appendChild(ef);
|
|
358
|
+
}
|
|
346
359
|
if (katalogFehlt) {
|
|
347
360
|
var kf = el('div', 'vz zurueck');
|
|
348
361
|
kf.appendChild(el('span', null, tr('catalog.missing')));
|
|
@@ -609,14 +622,24 @@ export function uebernehmeBestand(e) {
|
|
|
609
622
|
Belegt ist das Schreiben durch `common.write` oder durch eine
|
|
610
623
|
hinterlegte Schreibformel; bei getrennten Quellen zaehlt, was in
|
|
611
624
|
`alias.id.write` steht. */
|
|
612
|
-
z.srcW =
|
|
613
|
-
? ((c.write === true || typeof a.write === 'string') ? (q2.write || '') : '')
|
|
614
|
-
: (q2.write || '');
|
|
625
|
+
z.srcW = schreibQuelle(o);
|
|
615
626
|
if (q2.write && !q2.einfach && q2.write !== q2.read) {
|
|
616
627
|
/* Die Gegenzeile eines Paars ist keine eigene mehr - ihre Quelle
|
|
617
628
|
steckt jetzt als Schreibquelle in dieser Zeile. */
|
|
618
629
|
e.states.forEach(function (x) {
|
|
619
|
-
if (x
|
|
630
|
+
if (x === z || x.srcR !== q2.write) { return; }
|
|
631
|
+
/* Aber nur, was nicht selbst im Alias steht.
|
|
632
|
+
|
|
633
|
+
Sonst haengt es an der Reihenfolge von `Object.keys`, ob ein
|
|
634
|
+
bestehender Punkt ueberlebt: wird er vor der Paarzeile
|
|
635
|
+
verarbeitet, bleibt er aus, die Aenderungs-Karte meldet „wird
|
|
636
|
+
entfernt", und das Schreiben loescht ihn — ohne dass ihn
|
|
637
|
+
jemand abgewaehlt haette. Heute rettet ihn die Schleife
|
|
638
|
+
darunter (`if (!s.on) s.on = true`), weil in allen 16
|
|
639
|
+
mitgelieferten Vorlagen die Stellzeile vor der Melderzeile
|
|
640
|
+
steht; das ist Glueck, keine Zusicherung (09.09.2026). */
|
|
641
|
+
if (x.n && S.objects[e.ziel + '.' + x.n]) { return; }
|
|
642
|
+
x.on = false;
|
|
620
643
|
});
|
|
621
644
|
}
|
|
622
645
|
e.bestandZog = true;
|
|
@@ -695,9 +718,7 @@ export function uebernehmeBestand(e) {
|
|
|
695
718
|
Schreibquelle, OPEN, SET und pct verloren sie - ein
|
|
696
719
|
Aktualisieren haette den Rollladen ueber den Alias unfahrbar
|
|
697
720
|
gemacht (Ricardo, 08.09.2026). */
|
|
698
|
-
srcW:
|
|
699
|
-
? ((c.write === true || typeof a.write === 'string') ? (q.write || '') : '')
|
|
700
|
-
: (q.write || ''),
|
|
721
|
+
srcW: schreibQuelle(o),
|
|
701
722
|
f: (typeof a.read === 'string') ? a.read : '',
|
|
702
723
|
fw: (typeof a.write === 'string') ? a.write : '',
|
|
703
724
|
caption: (nm && nm !== n) ? nm : '',
|
|
@@ -35,16 +35,13 @@ export function berechnePlaetze(e, haupt) {
|
|
|
35
35
|
Object.keys(platzVon).forEach(function (n) {
|
|
36
36
|
platzAnzahl[platzVon[n]] = (platzAnzahl[platzVon[n]] || 0) + 1;
|
|
37
37
|
});
|
|
38
|
-
|
|
38
|
+
/* Hier stand ein voller Detektorlauf gegen das info-Muster. Sein
|
|
39
|
+
Ergebnis wanderte ueber `imInfo` und `infoNamen` in einen Parameter
|
|
40
|
+
von `setzeKnoepfe`, den dort niemand liest — bei JEDEM Zeichnen,
|
|
41
|
+
also bei laufenden Werten alle 700 ms (gefunden 09.09.2026).
|
|
42
|
+
`imInfo` bleibt als leeres Objekt stehen, damit die Aufrufer
|
|
43
|
+
unveraendert bleiben. */
|
|
39
44
|
var imInfo = {};
|
|
40
|
-
if (infoFund.length && haupt && haupt.type !== 'info') {
|
|
41
|
-
infoFund[0].states.forEach(function (x) {
|
|
42
|
-
if (x.id) {
|
|
43
|
-
var kurz = x.id.slice(S.current.length + 1);
|
|
44
|
-
if (!platzVon[kurz]) { imInfo[kurz] = true; }
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
45
|
|
|
49
46
|
/* Mehrere Kanaele, die fuer sich ein Geraet ergeben? Einmal rechnen,
|
|
50
47
|
am Entwurf merken — der Hinweis in der Karte und die Knoepfe unten
|
|
@@ -460,7 +457,14 @@ export function baueListe(host, e, pl, rateKnopf, musterBlock) {
|
|
|
460
457
|
Marken, jede mit eigener Bedeutung: „geaendert" heisst, der
|
|
461
458
|
Nutzer war es; „weicht von der Vorlage ab" vergleicht mit der
|
|
462
459
|
Vorlage; diese hier mit der Datenbank (Ricardo, 06.09.2026). */
|
|
463
|
-
|
|
460
|
+
/* `e.ziel || e.kanal` — wie bei den beiden Nachbarn.
|
|
461
|
+
|
|
462
|
+
Im Aliasmodus ist `e.ziel` nie gesetzt: Man steht ja schon auf dem
|
|
463
|
+
Alias, das Ziel waehlt man erst im Quellenmodus. Die Marke „weicht vom
|
|
464
|
+
Alias ab" erschien dort also NIE — ausgerechnet dort, wo sie am
|
|
465
|
+
meisten sagt (gemessen 09.09.2026: mit `e.ziel` null Abweichungen, mit
|
|
466
|
+
`e.ziel || e.kanal` eine). */
|
|
467
|
+
var bAbw = bestandsAbweichung(s, e.ziel || e.kanal);
|
|
464
468
|
if (bAbw.length) {
|
|
465
469
|
var bv = el('span', 'geae bstabw', tr('list.aliasDiffers'));
|
|
466
470
|
bv.title = bAbw.map(function (x) {
|
package/admin/tab.html
CHANGED
|
@@ -11,10 +11,24 @@
|
|
|
11
11
|
Vorgabepalette auf. Ohne Parameter (Reiter direkt geoeffnet)
|
|
12
12
|
entscheidet die Systemeinstellung hell/dunkel. */
|
|
13
13
|
(function () {
|
|
14
|
+
/* Der Admin hat mehr Themen als vier.
|
|
15
|
+
|
|
16
|
+
`io-package.json` laesst Admin ab 7.6.20 zu, und dort gibt es
|
|
17
|
+
auch `blue` (dunkel) und `colored` (hell). Beide fielen durch
|
|
18
|
+
diese Pruefung, das Attribut blieb ungesetzt — und dann
|
|
19
|
+
entscheidet die SYSTEMeinstellung: ein dunkler Admin auf einem
|
|
20
|
+
hell eingestellten Rechner bekam die helle Palette mitten in
|
|
21
|
+
seine dunkle Umgebung. Entschieden wird deshalb nach der
|
|
22
|
+
Helligkeit, nicht nach dem Namen; unbekannte Namen fallen
|
|
23
|
+
weiterhin auf die Systemeinstellung zurueck, das ist die
|
|
24
|
+
ehrlichste Vermutung. */
|
|
25
|
+
var HELLIGKEIT = {
|
|
26
|
+
modernLight: 'modernLight', light: 'light', colored: 'modernLight',
|
|
27
|
+
modernDark: 'modernDark', dark: 'dark', blue: 'modernDark'
|
|
28
|
+
};
|
|
14
29
|
var setze = function (name) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
30
|
+
var w = HELLIGKEIT[name];
|
|
31
|
+
if (w) { document.documentElement.setAttribute('data-adminthema', w); }
|
|
18
32
|
};
|
|
19
33
|
var m = /[?&]react=([A-Za-z]+)(?:&|$)/.exec(location.search);
|
|
20
34
|
if (m) { setze(m[1]); }
|