iobroker.alias-workbench 0.8.3 → 0.9.0
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 +69 -3
- package/admin/css/werkbank.css +57 -8
- package/admin/detector.js +1 -1
- package/admin/i18n/de.json +13 -0
- package/admin/i18n/en.json +13 -0
- package/admin/js/baum.js +27 -4
- package/admin/js/detail.js +58 -6
- package/admin/js/entwurf.js +140 -4
- package/admin/js/ergebnis.js +118 -31
- package/admin/js/erkennung.js +54 -5
- package/admin/js/musternamen.js +91 -26
- package/admin/js/sprache.js +3 -0
- package/admin/js/vorlagen.js +22 -2
- package/admin/js/vorlagenblatt.js +2 -3
- package/admin/js/zuordnung.js +41 -8
- package/admin/js/zustand.js +7 -1
- package/admin/js/zustandsliste.js +143 -16
- package/admin/tab.html +8 -0
- package/io-package.json +14 -1
- package/package.json +1 -1
package/admin/i18n/de.json
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
"app.disconnected": "getrennt",
|
|
5
5
|
"app.objectsCount": "{0} Objekte",
|
|
6
6
|
"app.patternsCount": "{0} Muster",
|
|
7
|
+
"app.subsCount": "{0} Abos",
|
|
8
|
+
"app.subsCount1": "ein Abo",
|
|
9
|
+
"app.subsHint": "Ein Abo ist ein ganzer Zweig, kein einzelner Wert — ein Muster mit Stern deckt alle Punkte darunter ab. Beim Wechsel des Knotens werden alle alten abgemeldet; steigt die Zahl mit der Zeit, bleibt eines hängen. Gerade beobachtet:",
|
|
7
10
|
"app.templatesCount": "{0} Vorlagen",
|
|
8
11
|
"app.title": "Alias-Werkbank",
|
|
9
12
|
"catalog.missing": "Katalog des Admin nicht erreichbar — nur Bestand",
|
|
@@ -163,7 +166,9 @@
|
|
|
163
166
|
"help.sources": "Links ein Gerät oder einen Kanal wählen. Die Werkbank erkennt, worum es sich handelt, und schlägt einen fertigen Alias vor. Gibt es ihn schon, wird er stattdessen aktualisiert.",
|
|
164
167
|
"help.templates": "Links eine Vorlage wählen. Mitgelieferte lassen sich ansehen und duplizieren, eigene bearbeiten. Der Probelauf zeigt, welche Geräte sie trifft.",
|
|
165
168
|
"info.countedSeparately": "für die meisten ohne Folgen",
|
|
169
|
+
"info.dlgTitle": "Gehört nicht zum {0}-Muster",
|
|
166
170
|
"info.explain": "funktionieren ganz normal und liegen da, wo du sie angelegt hast. Es entsteht dadurch kein zusätzliches Objekt — der Alias bleibt, wie er ist. Sie gehören nur nicht zu dem, was ioBroker unter einem {0} versteht; dafür passt ihre Rolle nicht. Bedeutung hat das erst, wenn du den Alias an Alexa, Google Home oder eine Gerätesteuerung hängst: die sehen dann ein {0} ohne diese Punkte. In VIS, in Skripten und im Verlauf benutzt du sie wie jeden anderen Datenpunkt.",
|
|
171
|
+
"info.more": "Was das bedeutet",
|
|
167
172
|
"info.notPartOf": "Gehört nicht zum {0}",
|
|
168
173
|
"info.sameDatapoints": "{0} — ",
|
|
169
174
|
"leave.body": "An {0} sind Änderungen offen, die noch nicht geschrieben wurden. Wechselst du jetzt, sind sie weg.",
|
|
@@ -187,9 +192,12 @@
|
|
|
187
192
|
"list.onlyPattern": "nur was ins Muster passt",
|
|
188
193
|
"list.onlyPatternHint": "Hakt genau die Punkte an, die einen Platz im gewählten Muster bekommen — alles andere wird abgehakt und nicht mit angelegt.",
|
|
189
194
|
"list.onlyWithValue": "nur mit Wert",
|
|
195
|
+
"list.otherSource": "aus {0}",
|
|
196
|
+
"list.otherSourceHint": "Dieser Punkt liest nicht aus {0}, sondern aus {1}.",
|
|
190
197
|
"list.reset": "zurücksetzen",
|
|
191
198
|
"list.resetHint": "Verwirft alle Änderungen dieser Sitzung — Haken, Rollen, Formeln, Quellen — und zeigt wieder den Stand, wie er im System steht.",
|
|
192
199
|
"list.sendPointMissing": "Sendepunkt fehlt noch",
|
|
200
|
+
"list.sourceHint": "Dieser Punkt liest aus {0}.",
|
|
193
201
|
"list.sourceMissing": "Quelle fehlt",
|
|
194
202
|
"list.tpl": "Vorlage",
|
|
195
203
|
"list.tplDiffers": "weicht von der Vorlage ab",
|
|
@@ -277,6 +285,7 @@
|
|
|
277
285
|
"pattern.missing": "{0} fehlt",
|
|
278
286
|
"pattern.noMatch": "kein Muster passt zu „{0}“",
|
|
279
287
|
"pattern.noPlaceIn": "kein Platz im {0}-Muster",
|
|
288
|
+
"pattern.noPlaceLegend": "So hinterlegte Zeilen haben keinen Platz im {0}-Muster",
|
|
280
289
|
"pattern.noPlaceLong": "kein Platz im {0}-Muster — der Punkt wird trotzdem angelegt und ist voll benutzbar, nur Alexa & Co. zählen ihn separat",
|
|
281
290
|
"pattern.notDetected": "nicht erkannt",
|
|
282
291
|
"pattern.required": "PFLICHT — fehlt",
|
|
@@ -292,7 +301,10 @@
|
|
|
292
301
|
"result.header": "Was der type-detector daraus macht",
|
|
293
302
|
"result.newlyCreated": "wird neu angelegt",
|
|
294
303
|
"result.noObjectShort": "kein Objekt",
|
|
304
|
+
"result.pickSource": "Wohin springen?",
|
|
295
305
|
"result.pointsFromBranches": "{0} Datenpunkte aus {1} Zweigen. ",
|
|
306
|
+
"result.sourceCount": "{0} Quellen",
|
|
307
|
+
"result.sourceCountHint": "Die Punkte dieses Alias lesen aus mehr als einem Knoten. „zur Quelle\" führt zu dem, aus dem die meisten stammen; ein Klick zeigt alle zur Auswahl.",
|
|
296
308
|
"result.sourceGone": "Die Quelle {0} gibt es nicht mehr.",
|
|
297
309
|
"result.states": "{0} Zustände",
|
|
298
310
|
"result.statesDirect": "{0} Zustände ({1} direkt)",
|
|
@@ -454,6 +466,7 @@
|
|
|
454
466
|
"tpls.wissen": "+ {0} weitere Punkte als Rollenwissen — greift in der Ansicht „Alle Datenpunkte des Geräts“",
|
|
455
467
|
"tree.aliases": "Alias bearbeiten",
|
|
456
468
|
"tree.collapseAll": "alles zuklappen",
|
|
469
|
+
"tree.emptyNode": "keine Datenpunkte — der Knoten steht in der Datenbank, ist aber leer",
|
|
457
470
|
"tree.expandAll": "alles aufklappen",
|
|
458
471
|
"tree.filter": "filtern …",
|
|
459
472
|
"tree.filterAria": "Objekte filtern",
|
package/admin/i18n/en.json
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
"app.disconnected": "disconnected",
|
|
5
5
|
"app.objectsCount": "{0} objects",
|
|
6
6
|
"app.patternsCount": "{0} patterns",
|
|
7
|
+
"app.subsCount": "{0} subscriptions",
|
|
8
|
+
"app.subsCount1": "one subscription",
|
|
9
|
+
"app.subsHint": "A subscription covers a whole branch, not a single value — one pattern with a star catches every point below it. Switching nodes unsubscribes all previous ones; if the number keeps growing, one is being left behind. Currently watching:",
|
|
7
10
|
"app.templatesCount": "{0} templates",
|
|
8
11
|
"app.title": "Alias Workbench",
|
|
9
12
|
"catalog.missing": "Admin catalogue unavailable — existing entries only",
|
|
@@ -163,7 +166,9 @@
|
|
|
163
166
|
"help.sources": "Pick a device or channel on the left. The workbench recognises what it is and proposes a finished alias. If one already exists, it is updated instead.",
|
|
164
167
|
"help.templates": "Pick a template on the left. Bundled ones can be viewed and duplicated, your own edited. The trial run shows which devices it matches.",
|
|
165
168
|
"info.countedSeparately": "no consequences for most setups",
|
|
169
|
+
"info.dlgTitle": "Not part of the {0} pattern",
|
|
166
170
|
"info.explain": "work perfectly normally and stay where you created them. Nothing extra is written — the alias stays as it is. They simply are not part of what ioBroker considers a {0}; their role does not fit any of its slots. It only starts to matter once you hand the alias to Alexa, Google Home or a device UI: those will see a {0} without these points. In VIS, in scripts and in history you use them like any other datapoint.",
|
|
171
|
+
"info.more": "What this means",
|
|
167
172
|
"info.notPartOf": "Not part of the {0}",
|
|
168
173
|
"info.sameDatapoints": "{0} — ",
|
|
169
174
|
"leave.body": "There are changes on {0} that have not been written yet. Switching now discards them.",
|
|
@@ -187,9 +192,12 @@
|
|
|
187
192
|
"list.onlyPattern": "only what fits the pattern",
|
|
188
193
|
"list.onlyPatternHint": "Checks exactly the points that get a slot in the chosen pattern — everything else is unchecked and not created.",
|
|
189
194
|
"list.onlyWithValue": "only with a value",
|
|
195
|
+
"list.otherSource": "from {0}",
|
|
196
|
+
"list.otherSourceHint": "This point does not read from {0} but from {1}.",
|
|
190
197
|
"list.reset": "reset",
|
|
191
198
|
"list.resetHint": "Discards every change made here — checkboxes, roles, formulas, sources — and shows the state as stored again.",
|
|
192
199
|
"list.sendPointMissing": "send datapoint not created yet",
|
|
200
|
+
"list.sourceHint": "This point reads from {0}.",
|
|
193
201
|
"list.sourceMissing": "source missing",
|
|
194
202
|
"list.tpl": "template",
|
|
195
203
|
"list.tplDiffers": "differs from the template",
|
|
@@ -277,6 +285,7 @@
|
|
|
277
285
|
"pattern.missing": "{0} missing",
|
|
278
286
|
"pattern.noMatch": "no pattern matches \"{0}\"",
|
|
279
287
|
"pattern.noPlaceIn": "no slot in the {0} pattern",
|
|
288
|
+
"pattern.noPlaceLegend": "Rows shaded like this have no slot in the {0} pattern",
|
|
280
289
|
"pattern.noPlaceLong": "no slot in the {0} pattern — the point is still created and fully usable, only Alexa and friends count it separately",
|
|
281
290
|
"pattern.notDetected": "not detected",
|
|
282
291
|
"pattern.required": "REQUIRED — missing",
|
|
@@ -292,7 +301,10 @@
|
|
|
292
301
|
"result.header": "What the type-detector makes of it",
|
|
293
302
|
"result.newlyCreated": "will be created",
|
|
294
303
|
"result.noObjectShort": "no object",
|
|
304
|
+
"result.pickSource": "Jump where?",
|
|
295
305
|
"result.pointsFromBranches": "{0} datapoints from {1} branches. ",
|
|
306
|
+
"result.sourceCount": "{0} sources",
|
|
307
|
+
"result.sourceCountHint": "The points of this alias read from more than one node. \"to the source\" leads to the one most of them come from; a click shows all of them.",
|
|
296
308
|
"result.sourceGone": "The source {0} no longer exists.",
|
|
297
309
|
"result.states": "{0} states",
|
|
298
310
|
"result.statesDirect": "{0} states ({1} directly)",
|
|
@@ -454,6 +466,7 @@
|
|
|
454
466
|
"tpls.wissen": "+ {0} more states as role knowledge — used in the “All data points of the device” view",
|
|
455
467
|
"tree.aliases": "Edit alias",
|
|
456
468
|
"tree.collapseAll": "collapse all",
|
|
469
|
+
"tree.emptyNode": "no datapoints — the node exists in the database but is empty",
|
|
457
470
|
"tree.expandAll": "expand all",
|
|
458
471
|
"tree.filter": "filter …",
|
|
459
472
|
"tree.filterAria": "Filter objects",
|
package/admin/js/baum.js
CHANGED
|
@@ -281,7 +281,22 @@ function renderKinder(knoten, filter, tiefe) {
|
|
|
281
281
|
|
|
282
282
|
reihen.forEach(function (reihe) {
|
|
283
283
|
var k = reihe.k;
|
|
284
|
-
|
|
284
|
+
/* Leere Knoten ausgrauen statt weglassen.
|
|
285
|
+
|
|
286
|
+
Ein Kanal ohne Datenpunkte gibt keinen Alias her - deshalb blieb
|
|
287
|
+
er bisher ganz weg. Nur sieht das nicht nach „leer" aus, sondern
|
|
288
|
+
nach „gibt es nicht": Ricardos Heizungsgruppen standen im Admin
|
|
289
|
+
mit sieben Kanaelen, in der Werkbank mit zweien, und es sah wie
|
|
290
|
+
ein Anzeigefehler aus, waehrend in Wahrheit hm-rpc die
|
|
291
|
+
Datenpunkte nicht angelegt hatte (08.09.2026). Wer den Bestand
|
|
292
|
+
sucht, soll die Luecke sehen.
|
|
293
|
+
|
|
294
|
+
Nur echte Objekte, keine blossen Kennungsknoten: `channel`,
|
|
295
|
+
`device`, `folder`. Waehlbar sind sie nicht (`istWaehlbar` steigt
|
|
296
|
+
bei `_zust === 0` aus), also fuehrt auch kein Klick ins Leere.
|
|
297
|
+
Produktiv sind das 194 Zeilen zu 3432 - gut sechs Prozent. */
|
|
298
|
+
var leer = !k._zust;
|
|
299
|
+
if (leer && k.art !== 'channel' && k.art !== 'device' && k.art !== 'folder') { return; }
|
|
285
300
|
/* Zustaende zeigt die rechte Seite, nicht der Baum - es sei denn,
|
|
286
301
|
unter dem Zustand liegt noch etwas. Dann ist er ein Behaelter und
|
|
287
302
|
muss sichtbar sein, sonst hilft es nichts, dass `istWaehlbar` ihn
|
|
@@ -299,15 +314,20 @@ function renderKinder(knoten, filter, tiefe) {
|
|
|
299
314
|
|
|
300
315
|
/* Der Filter sucht auch im Namen. Wer „Badezimmer" tippt, meint
|
|
301
316
|
FK_Badezimmer — und nicht die Seriennummer, die er nicht kennt. */
|
|
302
|
-
|
|
303
|
-
|
|
317
|
+
/* Beim Filtern zaehlen leere Knoten nicht als Treffer - sonst
|
|
318
|
+
findet die Suche Zeilen, mit denen sich nichts anfangen laesst. */
|
|
319
|
+
var passt = !filter
|
|
320
|
+
? true
|
|
321
|
+
: (!leer && (k.id.toLowerCase().indexOf(filter) !== -1 ||
|
|
322
|
+
(reihe.zus && reihe.zus.toLowerCase().indexOf(filter) !== -1)));
|
|
304
323
|
var kinderUl = renderKinder(k, filter, tiefe + 1);
|
|
305
324
|
var kinderPassen = kinderUl.childNodes.length > 0;
|
|
306
325
|
if (!passt && !kinderPassen) { return; }
|
|
307
326
|
|
|
308
327
|
var li = el('li');
|
|
309
328
|
var waehlbar = istWaehlbar(k);
|
|
310
|
-
var d = el('div', 'node' + (waehlbar ? ' pick' : ' grp') + (
|
|
329
|
+
var d = el('div', 'node' + (waehlbar ? ' pick' : ' grp') + (leer ? ' leer' : '') +
|
|
330
|
+
(k.id === S.current ? ' sel' : ''));
|
|
311
331
|
|
|
312
332
|
var hatKinder = kinderUl.childNodes.length > 0;
|
|
313
333
|
var vorgabe = (S.alleKlapp === null) ? (tiefe <= 1) : S.alleKlapp;
|
|
@@ -342,6 +362,9 @@ function renderKinder(knoten, filter, tiefe) {
|
|
|
342
362
|
d.appendChild(nm2);
|
|
343
363
|
d.title = k.id + ' · ' + reihe.zus;
|
|
344
364
|
}
|
|
365
|
+
/* Der Hinweis kommt hinter den Zusatznamen, nicht davor - sonst
|
|
366
|
+
ueberschreibt ihn die Zeile darueber wieder. */
|
|
367
|
+
if (leer) { d.title = (d.title ? d.title + ' · ' : '') + tr('tree.emptyNode'); }
|
|
345
368
|
if (waehlbar) {
|
|
346
369
|
if (wohlGeraet(k)) {
|
|
347
370
|
var pk = el('span', 'geraetepunkt', '●');
|
package/admin/js/detail.js
CHANGED
|
@@ -5,7 +5,7 @@ import { socket } from './verbindung.js';
|
|
|
5
5
|
import { el } from './basis.js';
|
|
6
6
|
import { tr } from './sprache.js';
|
|
7
7
|
import { wertVon, jsonFelder, feldAusFormel, feldFormel, feldWert, jsonVon } from './werte.js';
|
|
8
|
-
import { musterVon, erkenneEntwurf } from './erkennung.js';
|
|
8
|
+
import { musterVon, erkenneEntwurf, rolleTrifft } from './erkennung.js';
|
|
9
9
|
import { musterName } from './musternamen.js';
|
|
10
10
|
import { rollenFeld } from './rollenwahl.js';
|
|
11
11
|
import { opt , quellenAuswahl, vorlagenAbweichung, bestandsAbweichung } from './entwurf.js';
|
|
@@ -28,6 +28,58 @@ export function detailZeile(e, s, idx) {
|
|
|
28
28
|
keine mehr und faellt beim Zuruecknehmen nicht mehr weg. */
|
|
29
29
|
function neu() { s.geaendert = true; rateBehalten(s); entwurfAngefasst(); zeichneErgebnis(); }
|
|
30
30
|
|
|
31
|
+
/* ---- Die Auswahl der Datenpunkte ---------------------------------
|
|
32
|
+
|
|
33
|
+
Im Eintrag stehen die ersten beiden Glieder nicht: eine volle
|
|
34
|
+
Kennung wird hier bis zu 78 Zeichen lang (gemessen am Testsystem,
|
|
35
|
+
Median 36) und passte in kein Feld dieser Spalte. Ein `select`
|
|
36
|
+
kuerzt hinten ab — weg waere also gerade das, was den Punkt
|
|
37
|
+
unterscheidet.
|
|
38
|
+
|
|
39
|
+
Nur: ohne den Instanzteil sehen `Solar.Balkon.Einspeisung.Deckel`
|
|
40
|
+
(ein Alias-Punkt) und `SmartHome.Solar_Balkon.tele.SENSOR` (die
|
|
41
|
+
echte Quelle) gleich aus, und wer den ersten waehlt, baut aus
|
|
42
|
+
Versehen einen Alias, der aus einem Alias liest (Ricardo,
|
|
43
|
+
08.09.2026). Deshalb traegt jede Gruppe ihre Instanz als
|
|
44
|
+
Ueberschrift, und `alias.0` steht ganz unten: daraus zu lesen ist
|
|
45
|
+
der Sonderfall, nicht der Regelfall. */
|
|
46
|
+
function quellenIn(sel, ids) {
|
|
47
|
+
var gruppen = {}, folge = [];
|
|
48
|
+
ids.forEach(function (id) {
|
|
49
|
+
var inst = id.split('.').slice(0, 2).join('.');
|
|
50
|
+
if (!gruppen[inst]) { gruppen[inst] = []; folge.push(inst); }
|
|
51
|
+
gruppen[inst].push(id);
|
|
52
|
+
});
|
|
53
|
+
folge.sort(function (a, b) {
|
|
54
|
+
var aa = a.indexOf('alias.') === 0, bb = b.indexOf('alias.') === 0;
|
|
55
|
+
return aa === bb ? 0 : (aa ? 1 : -1);
|
|
56
|
+
});
|
|
57
|
+
folge.forEach(function (inst) {
|
|
58
|
+
var g = document.createElement('optgroup');
|
|
59
|
+
g.label = inst;
|
|
60
|
+
gruppen[inst].forEach(function (id) {
|
|
61
|
+
var o = opt(id, id.split('.').slice(2).join('.'));
|
|
62
|
+
o.title = id;
|
|
63
|
+
g.appendChild(o);
|
|
64
|
+
});
|
|
65
|
+
sel.appendChild(g);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Und weil die Ueberschrift nur im aufgeklappten Zustand zu sehen ist
|
|
70
|
+
— ein `select` zeigt zugeklappt allein den Text seiner Option —,
|
|
71
|
+
steht die volle Kennung als eigene Zeile unter dem Feld. Dieselbe
|
|
72
|
+
Bauart wie „Die Vorlage will hier: …": sie darf umbrechen und hat
|
|
73
|
+
damit keine Breitengrenze. */
|
|
74
|
+
function mitVollId(felder, id) {
|
|
75
|
+
var w = el('div', 'feldstapel');
|
|
76
|
+
w.appendChild(felder);
|
|
77
|
+
var v = el('div', 'vollid');
|
|
78
|
+
if (id) { v.textContent = id; v.title = id; }
|
|
79
|
+
w.appendChild(v);
|
|
80
|
+
return w;
|
|
81
|
+
}
|
|
82
|
+
|
|
31
83
|
/* ---- Wo die Vorlage etwas anderes will --------------------------
|
|
32
84
|
|
|
33
85
|
Die zugeklappte Zeile sagt nur, DASS etwas abweicht. Welches Feld,
|
|
@@ -165,7 +217,7 @@ export function detailZeile(e, s, idx) {
|
|
|
165
217
|
var selR = el('select', 'tx');
|
|
166
218
|
selR.style.width = '260px';
|
|
167
219
|
selR.appendChild(opt('', quellen.length ? tr('detail.pickOne') : tr('detail.noSource')));
|
|
168
|
-
|
|
220
|
+
quellenIn(selR, quellen);
|
|
169
221
|
if (s.srcR && quellen.indexOf(s.srcR) === -1) { selR.appendChild(opt(s.srcR, s.srcR + ' ' + tr('detail.elsewhere'))); }
|
|
170
222
|
selR.appendChild(opt('__frei__', tr('detail.otherObject')));
|
|
171
223
|
selR.value = s.srcR || '';
|
|
@@ -215,7 +267,7 @@ export function detailZeile(e, s, idx) {
|
|
|
215
267
|
lF.appendChild(selF);
|
|
216
268
|
qf.appendChild(lF);
|
|
217
269
|
}
|
|
218
|
-
zeigeBeide(zeile(tr('detail.readsFrom'), qf), ['srcR']);
|
|
270
|
+
zeigeBeide(zeile(tr('detail.readsFrom'), mitVollId(qf, s.srcR)), ['srcR']);
|
|
219
271
|
|
|
220
272
|
if (s.frei) {
|
|
221
273
|
var iFrei = el('input', 'tx w');
|
|
@@ -238,7 +290,7 @@ export function detailZeile(e, s, idx) {
|
|
|
238
290
|
ein, damit im Feld steht, worauf es hinausgeht. */
|
|
239
291
|
selW.appendChild(opt('', tr('detail.noWrite')));
|
|
240
292
|
if (s.srcR && s.srcW !== s.srcR) { selW.appendChild(opt('=lesen', tr('detail.sameAsRead'))); }
|
|
241
|
-
|
|
293
|
+
quellenIn(selW, quellen);
|
|
242
294
|
if (s.srcW && quellen.indexOf(s.srcW) === -1) { selW.appendChild(opt(s.srcW, s.srcW)); }
|
|
243
295
|
selW.value = s.srcW || '';
|
|
244
296
|
selW.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
@@ -255,7 +307,7 @@ export function detailZeile(e, s, idx) {
|
|
|
255
307
|
if (s.srcW && s.srcW !== s.srcR) {
|
|
256
308
|
wf.appendChild(el('div', 'sugg', tr('detail.separateHint')));
|
|
257
309
|
}
|
|
258
|
-
zeigeBeide(zeile(tr('detail.writesTo'), wf), ['srcW']);
|
|
310
|
+
zeigeBeide(zeile(tr('detail.writesTo'), mitVollId(wf, s.srcW)), ['srcW']);
|
|
259
311
|
|
|
260
312
|
/* --- Leseformel --- */
|
|
261
313
|
var fb = el('div');
|
|
@@ -311,7 +363,7 @@ export function detailZeile(e, s, idx) {
|
|
|
311
363
|
if (must) {
|
|
312
364
|
var treffer = null;
|
|
313
365
|
must.states.forEach(function (p) {
|
|
314
|
-
if (!treffer && p.role &&
|
|
366
|
+
if (!treffer && p.role && rolleTrifft(p.role, s.role)) { treffer = p; }
|
|
315
367
|
});
|
|
316
368
|
var hin = el('div', 'sugg');
|
|
317
369
|
if (treffer) {
|
package/admin/js/entwurf.js
CHANGED
|
@@ -10,7 +10,7 @@ import './enums.js';
|
|
|
10
10
|
import { zusatzName, setzeModus, zeichneBaum, merkeKlappstand } from './baum.js';
|
|
11
11
|
import { kindZustaende, aliasQuellen, holeWerte } from './werte.js';
|
|
12
12
|
import { erkenneEntwurf } from './erkennung.js';
|
|
13
|
-
import { vorschlag , pruefeVorlage } from './vorlagen.js';
|
|
13
|
+
import { vorschlag , pruefeVorlage, zeigeAbozahl } from './vorlagen.js';
|
|
14
14
|
|
|
15
15
|
import { zeichneErgebnis } from './ergebnis.js';
|
|
16
16
|
|
|
@@ -215,6 +215,120 @@ export function knotenDa(id) {
|
|
|
215
215
|
return kindZustaende(id).length > 0;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/* Aus welchen Knoten liest dieser Alias, und wie oft aus jedem?
|
|
219
|
+
|
|
220
|
+
Dieselbe Zaehlung wie in `quelleVon` - nur dass die dort das Ergebnis
|
|
221
|
+
auf einen Sieger eindampft. Fuer die Anzeige zaehlt aber gerade das,
|
|
222
|
+
was dabei verlorengeht: dass es mehr als einen gibt. Ricardos
|
|
223
|
+
`alias.0.Solar.Netz` liest drei Punkte aus `0_userdata` und zwei aus
|
|
224
|
+
`mqtt-client`; der Sprungknopf fuehrte zur Mehrheit und sagte kein
|
|
225
|
+
Wort ueber den Rest (Ricardo, 08.09.2026).
|
|
226
|
+
|
|
227
|
+
Absteigend nach Anzahl, bei Gleichstand alphabetisch - sonst
|
|
228
|
+
wechselte die Reihenfolge bei jedem Neuzeichnen. */
|
|
229
|
+
/* Zu welcher Quelle des Alias gehoert der Knoten, an dem ich stehe?
|
|
230
|
+
|
|
231
|
+
Die Antwort ist nicht „die Mehrheit", sondern „die hier". Sie traegt
|
|
232
|
+
zwei Dinge: den Rueckweg zum Alias (er gehoert an jede Quelle, nicht
|
|
233
|
+
nur an die groesste) und den Bezugspunkt der Zeilenmarken - markiert
|
|
234
|
+
wird, was NICHT von hier kommt (Ricardo, 08.09.2026).
|
|
235
|
+
|
|
236
|
+
Getroffen wird ein Eintrag der Verteilung auf drei Weisen: genau er,
|
|
237
|
+
ein Knoten unter ihm (`…Stromzaehler.stat` unter der zusammengefassten
|
|
238
|
+
Sparte) oder sein unmittelbarer Elternknoten - dort steht man am
|
|
239
|
+
Geraet, waehrend die Quelle eine Ebene tiefer liegt. Nur eine Ebene:
|
|
240
|
+
sonst bekaeme `mqtt-client.0.SmartHome` den Knopf und jede Ebene
|
|
241
|
+
darueber gleich mit. */
|
|
242
|
+
export function quelleHier(aliasId, knoten) {
|
|
243
|
+
if (!aliasId || !knoten) { return null; }
|
|
244
|
+
var treffer = null;
|
|
245
|
+
quellenVerteilung(aliasId).forEach(function (q) {
|
|
246
|
+
if (treffer) { return; }
|
|
247
|
+
if (q.id === knoten) { treffer = q.id; return; }
|
|
248
|
+
if (knoten.indexOf(q.id + '.') === 0) { treffer = q.id; return; }
|
|
249
|
+
var t = q.id.split('.');
|
|
250
|
+
t.pop();
|
|
251
|
+
if (t.join('.') === knoten) { treffer = q.id; }
|
|
252
|
+
});
|
|
253
|
+
return treffer;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function quellenVerteilung(aliasId) {
|
|
257
|
+
var zaehler = {};
|
|
258
|
+
kindZustaende(aliasId).forEach(function (id) {
|
|
259
|
+
var q = aliasQuellen(S.objects[id]);
|
|
260
|
+
/* Einmal je DATENPUNKT, nicht je Quellangabe. `quelleVon` zaehlt
|
|
261
|
+
Lese- und Schreibquelle getrennt - fuer den Mehrheitsentscheid
|
|
262
|
+
gleichgueltig, fuer eine Anzeige nicht: an einem Alias mit fuenf
|
|
263
|
+
Punkten stand „6 und 4" statt „3 und 2". Wo Lesen und Schreiben
|
|
264
|
+
auseinandergehen, zaehlt die Lesequelle: aus ihr kommt der Wert,
|
|
265
|
+
den man sieht. */
|
|
266
|
+
var x = q.read || q.write;
|
|
267
|
+
if (!x) { return; }
|
|
268
|
+
var t = String(x).split('.');
|
|
269
|
+
t.pop();
|
|
270
|
+
var eltern = t.join('.');
|
|
271
|
+
if (eltern) { zaehler[eltern] = (zaehler[eltern] || 0) + 1; }
|
|
272
|
+
});
|
|
273
|
+
var sortiere = function (liste) {
|
|
274
|
+
return liste.sort(function (a, b) { return (b.n - a.n) || (a.id < b.id ? -1 : 1); });
|
|
275
|
+
};
|
|
276
|
+
var liste = sortiere(Object.keys(zaehler).map(function (k) { return { id: k, n: zaehler[k] }; }));
|
|
277
|
+
|
|
278
|
+
/* Sparten desselben Geraets sind keine zweite Quelle.
|
|
279
|
+
|
|
280
|
+
Ein Tasmota-Alias liest aus `…Licht.stat` und `…Licht.tele` - das
|
|
281
|
+
sind zwei Knoten, aber ein Geraet, und „2 Quellen" waere dort
|
|
282
|
+
schlicht falsch. Alles, was unterhalb der Hauptquelle liegt, zaehlt
|
|
283
|
+
deshalb zu ihr. Uebrig bleiben nur eigene Zweige - bei Ricardos
|
|
284
|
+
`alias.0.Solar.Netz` `0_userdata` und `mqtt-client` (08.09.2026). */
|
|
285
|
+
var haupt = quelleVon(aliasId);
|
|
286
|
+
if (!haupt) { return liste; }
|
|
287
|
+
var raus = [], hauptN = 0;
|
|
288
|
+
liste.forEach(function (q) {
|
|
289
|
+
if (q.id === haupt || q.id.indexOf(haupt + '.') === 0) { hauptN += q.n; return; }
|
|
290
|
+
raus.push(q);
|
|
291
|
+
});
|
|
292
|
+
if (hauptN) { raus.push({ id: haupt, n: hauptN }); }
|
|
293
|
+
|
|
294
|
+
/* Dasselbe noch einmal unter den Nebenquellen.
|
|
295
|
+
|
|
296
|
+
Der Schritt darueber fasst nur gegen die HAUPTquelle zusammen. Bei
|
|
297
|
+
`alias.0.Solar.Balkon.Einspeisung` liegen aber zwei der Nebenquellen
|
|
298
|
+
ebenfalls beieinander - `…Solar_Balkon.stat` und `…Solar_Balkon.tele`
|
|
299
|
+
sind ein Tasmota, und es standen „4 Quellen" statt drei (Ricardo,
|
|
300
|
+
08.09.2026).
|
|
301
|
+
|
|
302
|
+
Zusammengefasst wird nur, was wirklich zusammengehoert: mehrere
|
|
303
|
+
Knoten unter demselben Elternknoten, und das auch nur, wenn sie
|
|
304
|
+
blosse Kennungsebenen ohne eigenes Objekt sind (so legt MQTT seine
|
|
305
|
+
Sparten an) oder ihr Elternknoten ein `device` ist (so liegen die
|
|
306
|
+
Kanaele bei Homematic). Zwei `channel` in einem Ordner - etwa
|
|
307
|
+
`0_userdata.0.Solar.Netz` und `…Solar.Balkon` - bleiben getrennt:
|
|
308
|
+
das sind zwei Geraete, keine zwei Sparten. */
|
|
309
|
+
var nachEltern = {};
|
|
310
|
+
raus.forEach(function (q) {
|
|
311
|
+
var t = q.id.split('.');
|
|
312
|
+
t.pop();
|
|
313
|
+
var eltern = t.join('.');
|
|
314
|
+
(nachEltern[eltern] = nachEltern[eltern] || []).push(q);
|
|
315
|
+
});
|
|
316
|
+
var zusammen = [];
|
|
317
|
+
Object.keys(nachEltern).forEach(function (eltern) {
|
|
318
|
+
var gruppe = nachEltern[eltern];
|
|
319
|
+
var ohneObjekt = gruppe.every(function (q) { return !S.objects[q.id]; });
|
|
320
|
+
var elternGeraet = !!(S.objects[eltern] && S.objects[eltern].type === 'device');
|
|
321
|
+
if (gruppe.length < 2 || !eltern || (!ohneObjekt && !elternGeraet)) {
|
|
322
|
+
zusammen = zusammen.concat(gruppe);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
var summe = 0;
|
|
326
|
+
gruppe.forEach(function (q) { summe += q.n; });
|
|
327
|
+
zusammen.push({ id: eltern, n: summe });
|
|
328
|
+
});
|
|
329
|
+
return sortiere(zusammen);
|
|
330
|
+
}
|
|
331
|
+
|
|
218
332
|
export function quelleVon(aliasId) {
|
|
219
333
|
var o = S.objects[aliasId];
|
|
220
334
|
var q = o && o.native && o.native.quelle;
|
|
@@ -573,9 +687,31 @@ export function waehle(id, fertig) {
|
|
|
573
687
|
sel.scrollIntoView({ block: 'nearest' });
|
|
574
688
|
}
|
|
575
689
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
690
|
+
/* Abonniert wird, was auch angezeigt wird.
|
|
691
|
+
|
|
692
|
+
Bisher nur `<knoten>.*`. An einer Quelle stimmt das - die Werte in
|
|
693
|
+
der Liste kommen von dort. Am Alias nicht: dort stehen die Werte der
|
|
694
|
+
QUELLEN, und die bekamen kein Ereignis. Die Zahlen wurden einmal
|
|
695
|
+
beim Auswaehlen geholt und standen dann still; an
|
|
696
|
+
`alias.0.Solar.Balkon.Energie`, wo sich im Sekundentakt etwas
|
|
697
|
+
aendert, fiel es auf (Ricardo, 08.09.2026).
|
|
698
|
+
|
|
699
|
+
Deshalb am Alias zusaetzlich seine Quellen - `quellenVerteilung`
|
|
700
|
+
weiss ja, welche das sind. Und deshalb eine Liste: sonst bliebe
|
|
701
|
+
beim naechsten Wechsel die Haelfte abonniert und die Werkbank
|
|
702
|
+
bekaeme mit der Zeit Ereignisse fuer das halbe System. Die Zahl der
|
|
703
|
+
laufenden Abos steht in der Kopfleiste - dort sieht man sofort, ob
|
|
704
|
+
sich etwas ansammelt. */
|
|
705
|
+
(S.abo || []).forEach(function (m) { socket.emit('unsubscribe', m); });
|
|
706
|
+
var muster = [id + '.*'];
|
|
707
|
+
if (id.indexOf('alias.') === 0) {
|
|
708
|
+
quellenVerteilung(id).forEach(function (q) {
|
|
709
|
+
if (muster.indexOf(q.id + '.*') === -1) { muster.push(q.id + '.*'); }
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
S.abo = muster;
|
|
713
|
+
muster.forEach(function (m) { socket.emit('subscribe', m); });
|
|
714
|
+
zeigeAbozahl();
|
|
579
715
|
|
|
580
716
|
/* Erst die Werte, dann der Vorschlag. Die Erkennung schaut in den
|
|
581
717
|
Rohwert von tele.SENSOR hinein — ohne Werte haelt sie ein Geraet
|
package/admin/js/ergebnis.js
CHANGED
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
mitNachfrage,
|
|
28
28
|
geraeteDarunter,
|
|
29
29
|
aliasFuer,
|
|
30
|
+
quellenVerteilung,
|
|
31
|
+
quelleHier,
|
|
30
32
|
tipptGerade,
|
|
31
33
|
opt,
|
|
32
34
|
vorlagenAbweichung,
|
|
@@ -331,18 +333,123 @@ var warumAuf = false;
|
|
|
331
333
|
erschien dadurch an jeder Sparte und jedem Unterkanal, also an
|
|
332
334
|
`…RGB.stat` und `…RGB.tele` ebenso wie am Geraet. Der Knopf
|
|
333
335
|
gehoert nur dorthin, wo der Alias auch wirklich herkommt. */
|
|
334
|
-
|
|
336
|
+
/* Rueckprobe, aber nicht zu streng.
|
|
337
|
+
|
|
338
|
+
Frueher galt nur: `quelleVon(alias) === S.current`. Bei einem
|
|
339
|
+
Alias aus zwei Zweigen fiel der Rueckweg damit weg - wer ueber
|
|
340
|
+
die neue Quellenauswahl auf `…Stromzaehler.stat` sprang, stand
|
|
341
|
+
dort ohne Knopf zurueck (Ricardo, 08.09.2026).
|
|
342
|
+
|
|
343
|
+
Jetzt gilt der Knopf auch an einer Nebenquelle - aber nur, wenn
|
|
344
|
+
sie GENAU in der Verteilung steht und einen eigenen Zweig
|
|
345
|
+
bildet. Sparten unterhalb der Hauptquelle bleiben damit weiter
|
|
346
|
+
aussen vor: `…Licht.stat` und `…Licht.tele` liegen beide unter
|
|
347
|
+
`…Bastelzimmer_Licht`, und der Knopf gehoert ans Geraet, nicht
|
|
348
|
+
an jede Sparte. Knoten weiter oben (`…Strom`) stehen gar nicht
|
|
349
|
+
erst in der Verteilung. */
|
|
350
|
+
/* Der Rueckweg gehoert an jede Quelle des Alias - an die groesste
|
|
351
|
+
wie an jede andere, und auch an das Geraet ueber einer Sparte.
|
|
352
|
+
`quelleHier` sagt, ob dieser Knoten zu einer von ihnen gehoert;
|
|
353
|
+
steht er zu weit oben oder gar nicht dabei, bleibt der Knopf
|
|
354
|
+
weg (Ricardo, 08.09.2026). */
|
|
355
|
+
if (sprungZiel && !quelleHier(sprungZiel, S.current)) { sprungZiel = null; }
|
|
335
356
|
sprungModus = 'aliase';
|
|
336
357
|
}
|
|
358
|
+
/* Liest der Alias aus mehr als einem Knoten, gehoert das in den Kopf.
|
|
359
|
+
|
|
360
|
+
`quelleVon` entscheidet nach Mehrheit und wirft den Rest weg. Das
|
|
361
|
+
ist als Sprungziel richtig, als Auskunft aber die halbe Wahrheit:
|
|
362
|
+
an `alias.0.Solar.Netz` - drei Punkte aus `0_userdata`, zwei aus
|
|
363
|
+
`mqtt-client` - stand nirgends, dass es zwei Quellen sind. Man
|
|
364
|
+
musste jede Zeile aufklappen, um es zu sehen (Ricardo,
|
|
365
|
+
08.09.2026). */
|
|
366
|
+
/* Der Chip gilt fuer den Alias, gleich von welcher Seite man ihn
|
|
367
|
+
ansieht: am Alias selbst und an jeder seiner Quellen. Wer an einer
|
|
368
|
+
Nebenquelle steht, soll sehen, dass er nur einen Teil vor sich
|
|
369
|
+
hat. */
|
|
370
|
+
var quellAlias = (S.current.indexOf('alias.') === 0) ? S.current
|
|
371
|
+
: (sprungZiel && sprungModus === 'aliase' ? sprungZiel : null);
|
|
372
|
+
var verteilung = quellAlias ? quellenVerteilung(quellAlias) : [];
|
|
373
|
+
var mehrQuellen = verteilung.length > 1;
|
|
337
374
|
if (knotenDa(sprungZiel)) {
|
|
338
375
|
var zumAlias = (sprungModus === 'aliase');
|
|
339
376
|
sprung = el('button', 'btn schmal sprungknopf',
|
|
340
377
|
(zumAlias ? tr('result.toAlias') : tr('result.toSource')) + ' \u2192');
|
|
341
378
|
sprung.title = (zumAlias ? tr('result.toAliasHint') : tr('result.toSourceHint'))
|
|
342
|
-
+
|
|
379
|
+
+ String.fromCharCode(10) + sprungZiel;
|
|
343
380
|
var ziel0 = sprungZiel, modus0 = sprungModus;
|
|
344
|
-
|
|
345
|
-
|
|
381
|
+
/* Die Auswahl gehoert nur an „zur Quelle" - dort gibt es mehrere
|
|
382
|
+
Ziele. „zum Alias" hat genau eines; eine Liste von QUELLEN unter
|
|
383
|
+
diesem Knopf ist sinnlos (Ricardo, 08.09.2026). Der Chip bleibt
|
|
384
|
+
an beiden Stellen: er sagt etwas ueber den Alias, nicht ueber
|
|
385
|
+
den Sprung. */
|
|
386
|
+
if (mehrQuellen && sprungModus === 'quellen') {
|
|
387
|
+
/* Die Auswahl haengt am Body, nicht am Kopf.
|
|
388
|
+
|
|
389
|
+
Erst stand sie neben dem Knopf - und war sofort wieder weg:
|
|
390
|
+
bei `alias.0.Solar.Netz` laeuft im Sekundentakt ein neuer
|
|
391
|
+
Messwert ein, jeder zeichnet die rechte Seite neu, und mit ihr
|
|
392
|
+
verschwand die eben geoeffnete Liste. Am Body ueberlebt sie
|
|
393
|
+
das; ihre Stelle bekommt sie aus der Lage des Knopfes.
|
|
394
|
+
|
|
395
|
+
`mousedown` statt `click`, wie bei den Vorschlagslisten der
|
|
396
|
+
Zielleiste: der Mausdruck kommt vor dem Neuzeichnen. */
|
|
397
|
+
var listeQ = null;
|
|
398
|
+
var zuQ = function () {
|
|
399
|
+
if (listeQ && listeQ.parentNode) { listeQ.parentNode.removeChild(listeQ); }
|
|
400
|
+
listeQ = null;
|
|
401
|
+
};
|
|
402
|
+
sprung.addEventListener('mousedown', function (ev) {
|
|
403
|
+
ev.preventDefault();
|
|
404
|
+
ev.stopPropagation();
|
|
405
|
+
if (listeQ) { zuQ(); return; }
|
|
406
|
+
listeQ = el('div', 'vorschlaege sprungliste');
|
|
407
|
+
listeQ.appendChild(el('div', 'vzkopf', tr('result.pickSource')));
|
|
408
|
+
verteilung.forEach(function (q) {
|
|
409
|
+
/* Keine Zeile hervorheben. Die Hervorhebung meinte „das ist
|
|
410
|
+
die wichtigste" - eine Aussage, die bei drei zu drei
|
|
411
|
+
niemand treffen kann und die auch sonst niemand braucht:
|
|
412
|
+
alle Quellen des Alias sind gleichberechtigt (Ricardo,
|
|
413
|
+
08.09.2026). */
|
|
414
|
+
var z = el('div', 'vz');
|
|
415
|
+
z.appendChild(el('span', null, q.id));
|
|
416
|
+
z.appendChild(el('span', 'zq', ' \u00b7 ' + q.n));
|
|
417
|
+
z.addEventListener('mousedown', function (ev2) {
|
|
418
|
+
ev2.preventDefault();
|
|
419
|
+
ev2.stopPropagation();
|
|
420
|
+
zuQ();
|
|
421
|
+
springeZu(q.id, modus0);
|
|
422
|
+
});
|
|
423
|
+
listeQ.appendChild(z);
|
|
424
|
+
});
|
|
425
|
+
var r = sprung.getBoundingClientRect();
|
|
426
|
+
listeQ.style.position = 'fixed';
|
|
427
|
+
listeQ.style.left = Math.round(r.left) + 'px';
|
|
428
|
+
listeQ.style.top = Math.round(r.bottom + 2) + 'px';
|
|
429
|
+
listeQ.hidden = false;
|
|
430
|
+
document.body.appendChild(listeQ);
|
|
431
|
+
});
|
|
432
|
+
/* Woanders hin gedrueckt heisst: doch nicht. Einmal registriert,
|
|
433
|
+
nicht bei jedem Neuzeichnen - sonst haeuft sich der Zuhoerer. */
|
|
434
|
+
if (!S.sprungZuhoerer) {
|
|
435
|
+
S.sprungZuhoerer = true;
|
|
436
|
+
document.addEventListener('mousedown', function () {
|
|
437
|
+
var offen = document.querySelector('.sprungliste');
|
|
438
|
+
if (offen && offen.parentNode) { offen.parentNode.removeChild(offen); }
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
namensZeile.appendChild(sprung);
|
|
442
|
+
} else {
|
|
443
|
+
sprung.addEventListener('click', function () { springeZu(ziel0, modus0); });
|
|
444
|
+
namensZeile.appendChild(sprung);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (mehrQuellen) {
|
|
448
|
+
var chQ = el('span', 'chip mut quellenzahl', tr('result.sourceCount', verteilung.length));
|
|
449
|
+
chQ.title = tr('result.sourceCountHint') + String.fromCharCode(10, 10) +
|
|
450
|
+
verteilung.map(function (q) { return q.id + ' \u00b7 ' + q.n; })
|
|
451
|
+
.join(String.fromCharCode(10));
|
|
452
|
+
namensZeile.appendChild(chQ);
|
|
346
453
|
}
|
|
347
454
|
links.appendChild(sub);
|
|
348
455
|
head.appendChild(links);
|
|
@@ -609,31 +716,13 @@ var warumAuf = false;
|
|
|
609
716
|
return musterBlock;
|
|
610
717
|
}
|
|
611
718
|
|
|
612
|
-
/*
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
unsere Woerter: „info" ist der interne Name des Sammeltyps, und
|
|
620
|
-
wer den Detektor nicht kennt, weiss nach dem Satz weniger als
|
|
621
|
-
vorher. Jetzt steht da, worum es geht - und der Zusatz sagt
|
|
622
|
-
gleich, ob man handeln muss. */
|
|
623
|
-
ich.appendChild(el('span', 'typ', tr('info.notPartOf', e.want || '')));
|
|
624
|
-
ich.appendChild(el('span', 'chip mut', tr('info.countedSeparately')));
|
|
625
|
-
ic.appendChild(ich);
|
|
626
|
-
var ie = el('div', 'infoerkl');
|
|
627
|
-
ie.style.padding = '10px 15px 12px';
|
|
628
|
-
ie.style.fontSize = '11.5px';
|
|
629
|
-
ie.style.color = 'var(--ink-3)';
|
|
630
|
-
ie.style.lineHeight = '1.6';
|
|
631
|
-
ie.appendChild(el('b', null, tr('info.sameDatapoints', infoNamen.join(', '))));
|
|
632
|
-
ie.appendChild(document.createTextNode(tr('info.explain', e.want || '')));
|
|
633
|
-
ic.appendChild(ie);
|
|
634
|
-
host.appendChild(ic);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
719
|
+
/* Die Karte „Gehoert nicht zum <Muster>" stand hier bis zum
|
|
720
|
+
08.09.2026 — weit unten, mit den Namen der betroffenen Punkte und
|
|
721
|
+
der langen Erklaerung. Sie ist weg: dieselbe Auskunft haengt jetzt
|
|
722
|
+
am Hinweis der Legende, und die steht dort, wo die getoenten Zeilen
|
|
723
|
+
stehen. Zweimal dasselbe, einmal oben und einmal ganz unten, war
|
|
724
|
+
eine zu viel (Ricardo, 08.09.2026). Die Texte `info.explain` und
|
|
725
|
+
`info.notPartOf` bleiben in Gebrauch — die Legende nutzt sie. */
|
|
637
726
|
|
|
638
727
|
/* Was sich gegenueber dem gespeicherten Stand aendern wuerde. */
|
|
639
728
|
function baueDiffKarte(host, e) {
|
|
@@ -802,9 +891,7 @@ var warumAuf = false;
|
|
|
802
891
|
rateKnopf = null;
|
|
803
892
|
musterBlock = null;
|
|
804
893
|
|
|
805
|
-
/* --- info-Karte --- */
|
|
806
894
|
var infoNamen = Object.keys(imInfo);
|
|
807
|
-
baueInfoKarte(host, e, infoNamen);
|
|
808
895
|
|
|
809
896
|
/* --- Änderungen gegenüber dem, was gespeichert ist --- */
|
|
810
897
|
baueDiffKarte(host, e);
|