iobroker.alias-workbench 0.8.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/LICENSE +21 -0
- package/README.md +516 -0
- package/admin/alias-workbench.png +0 -0
- package/admin/css/werkbank.css +916 -0
- package/admin/detector.js +1 -0
- package/admin/geraetewissen/tasmota-befehle.json +116 -0
- package/admin/i18n/de.json +552 -0
- package/admin/i18n/en.json +552 -0
- package/admin/js/aufzaehlungen.js +332 -0
- package/admin/js/basis.js +36 -0
- package/admin/js/baum.js +453 -0
- package/admin/js/detail.js +335 -0
- package/admin/js/einstellungen.js +40 -0
- package/admin/js/entwurf.js +908 -0
- package/admin/js/enums.js +23 -0
- package/admin/js/ergebnis.js +934 -0
- package/admin/js/erkennung.js +126 -0
- package/admin/js/katalog.js +313 -0
- package/admin/js/mqtt.js +885 -0
- package/admin/js/musternamen.js +94 -0
- package/admin/js/musterwahl.js +334 -0
- package/admin/js/objekte.js +243 -0
- package/admin/js/pruefungen.js +130 -0
- package/admin/js/quellentausch.js +570 -0
- package/admin/js/rollenwahl.js +199 -0
- package/admin/js/schreiben.js +1537 -0
- package/admin/js/sprache.js +137 -0
- package/admin/js/start.js +39 -0
- package/admin/js/verbindung.js +10 -0
- package/admin/js/vorlagen.js +572 -0
- package/admin/js/vorlagenblatt.js +1907 -0
- package/admin/js/vorschlagen.js +391 -0
- package/admin/js/werte.js +193 -0
- package/admin/js/zielleiste.js +339 -0
- package/admin/js/zuordnung.js +695 -0
- package/admin/js/zustand.js +47 -0
- package/admin/js/zustandsliste.js +515 -0
- package/admin/jsonConfig.json +249 -0
- package/admin/tab.html +148 -0
- package/admin/vorlagen/hm-dimmer.json +204 -0
- package/admin/vorlagen/hm-fensterkontakt-hm.json +176 -0
- package/admin/vorlagen/hm-fensterkontakt-hmip.json +191 -0
- package/admin/vorlagen/hm-heizgruppe.json +214 -0
- package/admin/vorlagen/hm-heizkoerper.json +236 -0
- package/admin/vorlagen/hm-rollladen.json +190 -0
- package/admin/vorlagen/hm-schaltaktor-lampe.json +197 -0
- package/admin/vorlagen/hm-schaltaktor-mehrfach.json +203 -0
- package/admin/vorlagen/hm-schaltaktor-steckdose.json +197 -0
- package/admin/vorlagen/hm-temperatur-feuchte.json +166 -0
- package/admin/vorlagen/hm-wandthermostat.json +228 -0
- package/admin/vorlagen/index.json +21 -0
- package/admin/vorlagen/messpunkt.json +225 -0
- package/admin/vorlagen/tasmota-lampe.json +263 -0
- package/admin/vorlagen/tasmota-mehrfach.json +203 -0
- package/admin/vorlagen/tasmota-rgb.json +239 -0
- package/admin/vorlagen/tasmota-steckdose.json +265 -0
- package/io-package.json +122 -0
- package/package.json +48 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Die uebersetzten Namen der Muster, angezapft beim Geraete-Adapter.
|
|
2
|
+
|
|
3
|
+
Der type-detector kennt nur rohe Typnamen (socket, thermostat). Der
|
|
4
|
+
Geraete-Adapter fuehrt fuer genau diese Typen fertige Uebersetzungen
|
|
5
|
+
in elf Sprachen - in seinem Bundle, ein Block je Sprache, und jeder
|
|
6
|
+
Block gibt sich am Eintrag "type-Device type" selbst zu erkennen.
|
|
7
|
+
Wir holen den Block der Oberflaechensprache und schlagen darin nach;
|
|
8
|
+
eine eigene Liste wuerde bei jedem type-detector-Update altern
|
|
9
|
+
(Ricardos Punkt 13, 25.08.2026 - Weg 2, wie der Raum/Funktion-Katalog).
|
|
10
|
+
|
|
11
|
+
Bricht die Kette - Adapter fehlt, Bundle umgebaut - bleibt die Karte
|
|
12
|
+
leer und ueberall steht der rohe Name. Kein Fehler, nur weniger
|
|
13
|
+
Komfort. Beide Bundle-Fassungen werden verstanden: die neue (4.x)
|
|
14
|
+
schreibt Werte in Backticks, die alte (1.x, produktiv) als
|
|
15
|
+
JSON-Strings. */
|
|
16
|
+
|
|
17
|
+
import { sprache } from './sprache.js';
|
|
18
|
+
import { holeText } from './basis.js';
|
|
19
|
+
|
|
20
|
+
var musterNamen = {};
|
|
21
|
+
var geholt = false;
|
|
22
|
+
|
|
23
|
+
/* Woran sich der gesuchte Sprachblock zu erkennen gibt. Mehr Sprachen
|
|
24
|
+
kennt die Werkbank nicht (SPRACHEN in sprache.js). */
|
|
25
|
+
var ANKER = { de: 'Ger\u00e4tetyp', en: 'Device type' };
|
|
26
|
+
|
|
27
|
+
function ziehe(src) {
|
|
28
|
+
var anker = ANKER[sprache] || ANKER.en;
|
|
29
|
+
var re = /"type-Device type":(?:`([^`]*)`|"((?:[^"\\]|\\.)*)")/g;
|
|
30
|
+
var m, start = -1;
|
|
31
|
+
while ((m = re.exec(src))) {
|
|
32
|
+
if ((m[1] !== undefined ? m[1] : m[2]) === anker) { start = m.index; break; }
|
|
33
|
+
}
|
|
34
|
+
if (start < 0) { return null; }
|
|
35
|
+
/* Der Block enthaelt nur type-Schluessel - das naechste } beendet ihn. */
|
|
36
|
+
var teil = src.slice(start, src.indexOf('}', start));
|
|
37
|
+
var raus = {};
|
|
38
|
+
var re2 = /"type-([^"]+)":(?:`([^`]*)`|"((?:[^"\\]|\\.)*)")/g;
|
|
39
|
+
var m2;
|
|
40
|
+
while ((m2 = re2.exec(teil))) {
|
|
41
|
+
var wert = m2[2] !== undefined ? m2[2] : m2[3].replace(/\\(.)/g, '$1');
|
|
42
|
+
if (m2[1] !== 'Device type') { raus[m2[1]] = wert; }
|
|
43
|
+
}
|
|
44
|
+
return Object.keys(raus).length ? raus : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function ladeMusterNamen(nachher) {
|
|
48
|
+
if (geholt) { return; }
|
|
49
|
+
geholt = true;
|
|
50
|
+
holeText('/adapter/devices/tab.html').then(function (html) {
|
|
51
|
+
/* Die tab.html nennt mehrere Bausteine (Bootstrap-Haeppchen,
|
|
52
|
+
Vendor, Hauptbundle). Die Karte steckt im Hauptbundle - das
|
|
53
|
+
heisst in beiden bekannten Fassungen index-<hash>.js, also
|
|
54
|
+
dieses zuerst, die uebrigen als Rueckfall der Reihe nach. */
|
|
55
|
+
var dateien = (html.match(/assets\/[A-Za-z0-9_\-.]+\.js/g) || [])
|
|
56
|
+
.filter(function (x, i, a) { return a.indexOf(x) === i; })
|
|
57
|
+
.sort(function (a, b) {
|
|
58
|
+
return (b.indexOf('assets/index-') === 0 ? 1 : 0)
|
|
59
|
+
- (a.indexOf('assets/index-') === 0 ? 1 : 0);
|
|
60
|
+
})
|
|
61
|
+
.slice(0, 8);
|
|
62
|
+
if (!dateien.length) { throw new Error('kein Bundle'); }
|
|
63
|
+
return dateien.reduce(function (kette, f) {
|
|
64
|
+
return kette.then(function (fertig) {
|
|
65
|
+
if (fertig) { return true; }
|
|
66
|
+
return holeText('/adapter/devices/' + f).then(function (src) {
|
|
67
|
+
var karte = ziehe(src);
|
|
68
|
+
if (karte) { musterNamen = karte; return true; }
|
|
69
|
+
return false;
|
|
70
|
+
})['catch'](function () { return false; });
|
|
71
|
+
});
|
|
72
|
+
}, Promise.resolve(false));
|
|
73
|
+
})['catch'](function () { /* still bleiben: rohe Namen genuegen */ })
|
|
74
|
+
.then(function () { if (typeof nachher === 'function') { nachher(); } });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Der Beiname eines Musters - leer nur, wenn der Katalog nichts weiss.
|
|
78
|
+
Auch die blosse Grossschreibung („Dimmer“ zu dimmer) zaehlt: sonst
|
|
79
|
+
standen in der Liste grosse Beinamen und kleine rohe Namen gemischt
|
|
80
|
+
(Ricardo, 25.08.2026). Ob der rohe Name dazugehoert, entscheidet die
|
|
81
|
+
Anzeige - sagt der Beiname nur die Schreibung, waere „Dimmer ·
|
|
82
|
+
dimmer“ doppelt gemoppelt. */
|
|
83
|
+
export function musterName(typ) {
|
|
84
|
+
return musterNamen[typ] || '';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Beiname und roher Name fuer Listen, immer als Paar: „Fenstersensor
|
|
88
|
+
· window“ und auch „Dimmer · dimmer“ - einheitlich lesbar, selbst
|
|
89
|
+
wenn Deutsch und roher Name fast gleich sind (Ricardo, 25.08.2026).
|
|
90
|
+
Nur ohne Katalogeintrag steht der rohe Name allein. */
|
|
91
|
+
export function musterZeile(typ) {
|
|
92
|
+
var n = musterNamen[typ];
|
|
93
|
+
return n ? (n + ' · ' + typ) : typ;
|
|
94
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/* Die Musterwahl: das Suchfeld mit eigener Liste, die Bewertung je
|
|
2
|
+
Muster (passt / n von m belegt) und der Knopf, der freie Plaetze
|
|
3
|
+
vorschlaegt. Gebaut wird beides hier, eingehaengt woanders: der Block
|
|
4
|
+
je nach Ansicht im Vorlagenkasten oder in der Leiste ueber der Liste,
|
|
5
|
+
der Knopf immer in der Leiste.
|
|
6
|
+
|
|
7
|
+
Herausgeloest aus zeichneErgebnis - der Abschnitt hing an `e`, dem
|
|
8
|
+
Erkennungsergebnis und sonst an nichts. */
|
|
9
|
+
|
|
10
|
+
import { S } from './zustand.js';
|
|
11
|
+
import { D, el, $ } from './basis.js';
|
|
12
|
+
import { tr, sprachtext } from './sprache.js';
|
|
13
|
+
import { VORSCHAU, musterVon, erkenneEntwurf } from './erkennung.js';
|
|
14
|
+
import { anMusterAnpassen } from './vorlagen.js';
|
|
15
|
+
import { ratePlaetze, rateZurueck, rateAnzahl, rateMoeglich } from './vorschlagen.js';
|
|
16
|
+
import { zeichneErgebnis, entwurfAngefasst, knopfFrisch } from './ergebnis.js';
|
|
17
|
+
import { musterName, musterZeile } from './musternamen.js';
|
|
18
|
+
|
|
19
|
+
export function baueMusterwahl(e, haupt, pflichtFehlt) {
|
|
20
|
+
var vw = el('div', 'verdictwrap' + (haupt && !pflichtFehlt.length ? '' : ' bad'));
|
|
21
|
+
var vlbl = el('span', 'feldlabel', tr('pattern.label'));
|
|
22
|
+
vw.appendChild(vlbl);
|
|
23
|
+
/* Musterwahl als Eingabefeld mit eigener Liste statt als <select>.
|
|
24
|
+
Ein <select> laesst sich nicht durchsuchen, und die 51 Muster
|
|
25
|
+
brauchten deshalb einen Knopf daneben, der die Liste umschaltet.
|
|
26
|
+
Jetzt ist beides im Feld: tippen sucht ueber alle Muster, und die
|
|
27
|
+
letzte Zeile schaltet zwischen den ueblichen und allen um. Kein
|
|
28
|
+
<datalist> - dessen Fenster malt das Betriebssystem, und in
|
|
29
|
+
eingebetteten Ansichten landet es neben dem Fenster. */
|
|
30
|
+
var mwrap = el('div', 'feldwrap');
|
|
31
|
+
var sel = el('input', 'tx musterwahl');
|
|
32
|
+
sel.type = 'text';
|
|
33
|
+
sel.setAttribute('autocomplete', 'off');
|
|
34
|
+
sel.placeholder = tr('pattern.search');
|
|
35
|
+
mwrap.appendChild(sel);
|
|
36
|
+
/* Kein eigenes Pfeilzeichen mehr: das Feld traegt denselben
|
|
37
|
+
SVG-Pfeil wie jedes Auswahlfeld (werkbank.css). Das Zeichen ▾
|
|
38
|
+
ist in der Schrift ein bewusst kleines Dreieck und fiel neben den
|
|
39
|
+
Selects sichtbar ab. */
|
|
40
|
+
var mlist = el('div', 'vorschlaege musterliste');
|
|
41
|
+
mlist.hidden = true;
|
|
42
|
+
mwrap.appendChild(mlist);
|
|
43
|
+
vw.appendChild(mwrap);
|
|
44
|
+
|
|
45
|
+
/* Der Knopf, der die freien Plaetze belegt.
|
|
46
|
+
|
|
47
|
+
Ein Knopf und nicht der Musterwechsel als Ausloeser: sonst haette
|
|
48
|
+
ausgerechnet ein erkanntes Geraet keinen. Ricardos Klimaanlage wird
|
|
49
|
+
als `thermostat` erkannt, man wechselt also gar nichts, und ohne
|
|
50
|
+
Knopf bliebe es dabei.
|
|
51
|
+
|
|
52
|
+
Er wird hier gebaut, weil er zum Muster gehoert - haengt aber unten
|
|
53
|
+
in der Leiste ueber der Zustandsliste. Neben dem Musterfeld stand er
|
|
54
|
+
am Rand; die Liste, die er veraendert, faengt darunter an. */
|
|
55
|
+
/* Und nur, wenn er etwas anzubieten haette: ohne freie Plaetze oder
|
|
56
|
+
freie Zeilen ist der Knopf ein leeres Versprechen — dann faellt er
|
|
57
|
+
weg, statt beim Klick „nichts gefunden" zu melden. Zuruecknehmen
|
|
58
|
+
bleibt immer moeglich, solange Vermutungen im Entwurf stehen. */
|
|
59
|
+
var rateKnopf = null;
|
|
60
|
+
if (rateAnzahl(e) || rateMoeglich(e)) {
|
|
61
|
+
/* Mit passender Vorlage ist Raten nicht der normale Weg - die
|
|
62
|
+
Zuordnung steht ja schon. Der Knopf bleibt, tritt aber leise auf
|
|
63
|
+
und fragt vor dem Lauf nach (Ricardo, 25.08.2026). */
|
|
64
|
+
var beilaeufig = !!e.vorlage && !rateAnzahl(e);
|
|
65
|
+
rateKnopf = el('button', 'btn mini' + (rateAnzahl(e) ? ' wichtig' : '')
|
|
66
|
+
+ (beilaeufig ? ' leise' : '')
|
|
67
|
+
/* Ein leiser Knopf pocht nicht - das Pochen wuerde genau die
|
|
68
|
+
Aufmerksamkeit holen, die er nicht verdient. */
|
|
69
|
+
+ (!beilaeufig && knopfFrisch('raten:' + S.current + ':' + (rateAnzahl(e) ? 'zurueck' : 'vor')) ? ' frisch' : ''),
|
|
70
|
+
rateAnzahl(e) ? tr('guess.undo') : tr('guess.button'));
|
|
71
|
+
rateKnopf.title = beilaeufig ? tr('guess.tplHint') : tr('guess.hint');
|
|
72
|
+
var rateLos = function () {
|
|
73
|
+
e.rateMeldung = ratePlaetze(e) ? null : tr('guess.none');
|
|
74
|
+
entwurfAngefasst();
|
|
75
|
+
S.openRow = null;
|
|
76
|
+
zeichneErgebnis();
|
|
77
|
+
};
|
|
78
|
+
rateKnopf.addEventListener('click', function () {
|
|
79
|
+
if (rateAnzahl(e)) {
|
|
80
|
+
rateZurueck(e); e.rateMeldung = null;
|
|
81
|
+
entwurfAngefasst();
|
|
82
|
+
S.openRow = null;
|
|
83
|
+
zeichneErgebnis();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!beilaeufig) { rateLos(); return; }
|
|
87
|
+
var vq = null;
|
|
88
|
+
S.VORLAGEN.forEach(function (v) { if (v.id === e.vorlage) { vq = v; } });
|
|
89
|
+
var body = $('#guess-body');
|
|
90
|
+
body.textContent = '';
|
|
91
|
+
body.appendChild(el('div', 'aside w',
|
|
92
|
+
tr('guess.tplAsk', vq ? sprachtext(vq.name) : e.vorlage)));
|
|
93
|
+
/* onclick statt addEventListener: der Dialog ist einer fuer alle
|
|
94
|
+
Geraete, der Griff muss beim Oeffnen den alten ersetzen. */
|
|
95
|
+
$('#btn-guess-go').onclick = function () {
|
|
96
|
+
$('#dlg-guess').close();
|
|
97
|
+
rateLos();
|
|
98
|
+
};
|
|
99
|
+
$('#dlg-guess').showModal();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Typen, nicht Schluessel: wer „blinds" waehlte, bekam einen
|
|
104
|
+
Entwurf fuer einen Typ, den der Detektor nicht kennt. */
|
|
105
|
+
var alleTypen = Object.keys(D.patterns)
|
|
106
|
+
.map(function (k) { return D.patterns[k].type || k; })
|
|
107
|
+
.filter(function (t, n, arr) { return arr.indexOf(t) === n; }).sort();
|
|
108
|
+
|
|
109
|
+
/* Die Belegung je Muster auszurechnen kostet einen Erkennungslauf.
|
|
110
|
+
Beim Tippen wird die Liste bei jedem Anschlag neu gemalt, deshalb
|
|
111
|
+
einmal gemerkt statt jedes Mal neu gerechnet. */
|
|
112
|
+
var markeCache = {};
|
|
113
|
+
function bewerte(m) {
|
|
114
|
+
if (markeCache[m] !== undefined) { return markeCache[m]; }
|
|
115
|
+
/* Die Vorschau muss vorhersagen, was das Auswaehlen wirklich tut.
|
|
116
|
+
Beim Vorschlag werden Lampe und Steckdose samt Rollen neu gebaut -
|
|
117
|
+
also auch hier gegen den umgebauten Entwurf pruefen. Sonst stuende
|
|
118
|
+
hier „SET fehlt", und nach dem Klick passte trotzdem alles. */
|
|
119
|
+
var pruefE = e;
|
|
120
|
+
if (e.vorschlag && !e.roh && m !== e.want && musterVon(m)) {
|
|
121
|
+
var kopie = JSON.parse(JSON.stringify({ kanal: e.kanal, states: e.states, want: e.want }));
|
|
122
|
+
anMusterAnpassen(kopie, m);
|
|
123
|
+
if (kopie.states.some(function (x, n) { return x.role !== e.states[n].role; })) {
|
|
124
|
+
pruefE = kopie;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var r = erkenneEntwurf(pruefE, m), mark;
|
|
128
|
+
if (!r.length) {
|
|
129
|
+
var fehlend = musterVon(m).states.filter(function (x) { return x.required; })
|
|
130
|
+
.map(function (x) { return x.name; });
|
|
131
|
+
mark = fehlend.length ? ('✕ ' + tr('pattern.missing', fehlend.join(', ')))
|
|
132
|
+
: tr('pattern.doesNotFit');
|
|
133
|
+
} else {
|
|
134
|
+
var g = r[0].states.filter(function (x) { return x.id; }).length;
|
|
135
|
+
mark = tr('pattern.slotsTaken', g, r[0].states.length);
|
|
136
|
+
/* Hier stand frueher „(Rollen werden angepasst)" hinter der
|
|
137
|
+
Belegung. Der Vermerk erschien an jeder Zeile ausser der gerade
|
|
138
|
+
gewaehlten — an einem Fensterkontakt also an sieben von acht.
|
|
139
|
+
Was fast ueberall steht, unterscheidet nichts; es verdraengte
|
|
140
|
+
nur Name und Belegung, also genau das, wonach man in der Liste
|
|
141
|
+
sucht. Dass beim Wechsel die Rollen passend gesetzt werden, ist
|
|
142
|
+
ohnehin das gewollte Verhalten und keine Abwaegung, die man vor
|
|
143
|
+
der Auswahl treffen muesste. */
|
|
144
|
+
}
|
|
145
|
+
markeCache[m] = { mark: mark, passt: !!r.length, belegt: r.length ? r[0].states.filter(function (x) { return x.id; }).length : 0 };
|
|
146
|
+
return markeCache[m];
|
|
147
|
+
}
|
|
148
|
+
function markeFuer(m) { return bewerte(m).mark; }
|
|
149
|
+
|
|
150
|
+
/* Welche Muster ohne Suchtext angeboten werden. Das erkannte und das
|
|
151
|
+
gerade gewaehlte gehoeren immer dazu - sonst fiel rgbSingle heraus,
|
|
152
|
+
sobald man einmal auf light gewechselt hatte, und der Weg zurueck
|
|
153
|
+
fuehrte nur ueber die ganze Liste. */
|
|
154
|
+
function grundListe() {
|
|
155
|
+
var l = e.alleMuster ? alleTypen.slice() : VORSCHAU.slice();
|
|
156
|
+
[e.wantAuto, e.want].forEach(function (m0) {
|
|
157
|
+
if (m0 && musterVon(m0) && l.indexOf(m0) === -1) { l.unshift(m0); }
|
|
158
|
+
});
|
|
159
|
+
return l.filter(function (m) { return !!musterVon(m); });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Gesucht wird ueber den Namen des Musters und ueber die Rollen seiner
|
|
163
|
+
Plaetze. „state" findet so auch light und window, deren Plaetze
|
|
164
|
+
state.light und state.window annehmen - der Name allein haette dazu
|
|
165
|
+
nichts gemeldet. */
|
|
166
|
+
function rollenVon(m) {
|
|
167
|
+
var mu = musterVon(m);
|
|
168
|
+
if (!mu) { return []; }
|
|
169
|
+
return mu.states.map(function (x) { return x.defaultRole || ''; })
|
|
170
|
+
.filter(Boolean).filter(function (r0, n, arr) { return arr.indexOf(r0) === n; });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
var mMarkiert = -1;
|
|
174
|
+
var mEintraege = [];
|
|
175
|
+
var mSchliessen = function () { mlist.hidden = true; mMarkiert = -1; };
|
|
176
|
+
|
|
177
|
+
var mWaehlen = function (neuTyp) {
|
|
178
|
+
if (!musterVon(neuTyp)) { return; }
|
|
179
|
+
mSchliessen();
|
|
180
|
+
/* Beim Vorschlag reicht es nicht, das Muster umzustellen - die Rollen
|
|
181
|
+
entscheiden. switch.light passt auf kein socket-SET. Also den
|
|
182
|
+
Vorschlag mit den passenden Rollen neu bauen. */
|
|
183
|
+
if (e.vorschlag && !e.roh) {
|
|
184
|
+
anMusterAnpassen(e, neuTyp);
|
|
185
|
+
} else {
|
|
186
|
+
e.want = neuTyp;
|
|
187
|
+
}
|
|
188
|
+
S.openRow = null;
|
|
189
|
+
/* Kein Fokus zurueck ins Feld: der Fokus oeffnete die Liste sofort
|
|
190
|
+
wieder, und nach der Wahl stand sie erneut offen — man musste
|
|
191
|
+
eigens daneben klicken. Die Wahl ist getroffen; wer ein anderes
|
|
192
|
+
Muster will, klickt neu ins Feld. */
|
|
193
|
+
zeichneErgebnis();
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
var mMalen = function () {
|
|
197
|
+
var such = String(sel.value || '').trim().toLowerCase();
|
|
198
|
+
var treffer = [];
|
|
199
|
+
if (!such) {
|
|
200
|
+
treffer = grundListe().map(function (m) { return { m: m, via: null }; });
|
|
201
|
+
} else {
|
|
202
|
+
/* Mit Suchtext wird immer der ganze Bestand durchsucht, nicht nur
|
|
203
|
+
die uebliche Auswahl - sonst muesste man erst umschalten, um
|
|
204
|
+
ueberhaupt finden zu koennen. */
|
|
205
|
+
alleTypen.forEach(function (m) {
|
|
206
|
+
if (!musterVon(m)) { return; }
|
|
207
|
+
/* Wer „Steckdose“ tippt, will socket finden - die Suche geht
|
|
208
|
+
ueber rohen Namen UND Beinamen. */
|
|
209
|
+
if (m.toLowerCase().indexOf(such) > -1
|
|
210
|
+
|| musterName(m).toLowerCase().indexOf(such) > -1) { treffer.push({ m: m, via: null }); return; }
|
|
211
|
+
var r0 = rollenVon(m).filter(function (x) { return x.toLowerCase().indexOf(such) > -1; });
|
|
212
|
+
if (r0.length) { treffer.push({ m: m, via: r0[0] }); }
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/* Was passt, steht oben. Ein Muster, dem eine Pflichtangabe fehlt,
|
|
216
|
+
ist keine Wahl, die man gerade trifft — es soll die Liste nicht
|
|
217
|
+
anfuehren, nur weil es alphabetisch vorn steht. Unter den
|
|
218
|
+
passenden gewinnt, wer mehr Plaetze belegt; darunter bleibt die
|
|
219
|
+
Reihenfolge, in der die Liste zusammengestellt wurde. */
|
|
220
|
+
treffer.forEach(function (t0, n) { t0.rang = n; });
|
|
221
|
+
treffer.sort(function (a, b) {
|
|
222
|
+
var pa = bewerte(a.m), pb = bewerte(b.m);
|
|
223
|
+
if (pa.passt !== pb.passt) { return pa.passt ? -1 : 1; }
|
|
224
|
+
if (pa.passt && pb.belegt !== pa.belegt) { return pb.belegt - pa.belegt; }
|
|
225
|
+
return a.rang - b.rang;
|
|
226
|
+
});
|
|
227
|
+
mEintraege = treffer;
|
|
228
|
+
|
|
229
|
+
mlist.textContent = '';
|
|
230
|
+
if (!treffer.length) {
|
|
231
|
+
var leer = el('div', 'vz zurueck');
|
|
232
|
+
leer.appendChild(el('span', null, tr('pattern.noMatch', sel.value)));
|
|
233
|
+
mlist.appendChild(leer);
|
|
234
|
+
}
|
|
235
|
+
treffer.forEach(function (t0, n) {
|
|
236
|
+
var z = el('div', 'vz' + (n === mMarkiert ? ' an' : '')
|
|
237
|
+
+ (t0.m === e.want ? ' gewaehlt' : ''));
|
|
238
|
+
z.appendChild(el('span', null, musterZeile(t0.m)));
|
|
239
|
+
z.appendChild(el('span', 'tiefer',
|
|
240
|
+
(t0.via ? tr('pattern.viaRole', t0.via) + ' ' : '') + markeFuer(t0.m)));
|
|
241
|
+
/* mousedown statt click: sonst greift der Fokusverlust zuerst und
|
|
242
|
+
die Liste ist weg, bevor der Klick ankommt. */
|
|
243
|
+
z.addEventListener('mousedown', function (ev) {
|
|
244
|
+
ev.preventDefault();
|
|
245
|
+
mWaehlen(t0.m);
|
|
246
|
+
});
|
|
247
|
+
mlist.appendChild(z);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
/* Der Umschalter, der frueher als Knopf daneben stand. Beim Suchen
|
|
251
|
+
ist er ueberfluessig - dann laeuft die Suche ohnehin ueber alles. */
|
|
252
|
+
if (!such && alleTypen.length > VORSCHAU.length) {
|
|
253
|
+
var um = el('div', 'vz mehr');
|
|
254
|
+
um.appendChild(el('span', null, (e.alleMuster ? '▴ ' : '▾ ')
|
|
255
|
+
+ (e.alleMuster ? tr('pattern.usualOnly')
|
|
256
|
+
: tr('pattern.allPatterns', alleTypen.length))));
|
|
257
|
+
um.addEventListener('mousedown', function (ev) {
|
|
258
|
+
ev.preventDefault();
|
|
259
|
+
e.alleMuster = !e.alleMuster;
|
|
260
|
+
mMarkiert = -1;
|
|
261
|
+
mMalen();
|
|
262
|
+
sel.focus();
|
|
263
|
+
});
|
|
264
|
+
mlist.appendChild(um);
|
|
265
|
+
}
|
|
266
|
+
mlist.hidden = false;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/* Solange nichts getippt ist, steht im Feld das gewaehlte Muster mit
|
|
270
|
+
seiner Belegung - dieselbe Auskunft wie frueher im zugeklappten
|
|
271
|
+
Auswahlfeld. */
|
|
272
|
+
var mBeschriften = function () {
|
|
273
|
+
/* Im Feld der Beiname, der rohe Name im Tooltip - fuer beides
|
|
274
|
+
nebeneinander ist das Feld zu schmal. */
|
|
275
|
+
sel.value = e.want ? ((musterName(e.want) || e.want) + ' ' + markeFuer(e.want)) : '';
|
|
276
|
+
sel.title = e.want || '';
|
|
277
|
+
};
|
|
278
|
+
mBeschriften();
|
|
279
|
+
|
|
280
|
+
sel.addEventListener('focus', function () {
|
|
281
|
+
sel.value = '';
|
|
282
|
+
mMarkiert = -1;
|
|
283
|
+
mMalen();
|
|
284
|
+
});
|
|
285
|
+
sel.addEventListener('input', function () { mMarkiert = -1; mMalen(); });
|
|
286
|
+
sel.addEventListener('blur', function () {
|
|
287
|
+
setTimeout(function () { mSchliessen(); mBeschriften(); }, 120);
|
|
288
|
+
});
|
|
289
|
+
sel.addEventListener('keydown', function (ev) {
|
|
290
|
+
if (ev.key === 'Escape') { mSchliessen(); mBeschriften(); sel.blur(); return; }
|
|
291
|
+
if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') {
|
|
292
|
+
if (mlist.hidden) { mMalen(); return; }
|
|
293
|
+
ev.preventDefault();
|
|
294
|
+
if (!mEintraege.length) { return; }
|
|
295
|
+
mMarkiert += (ev.key === 'ArrowDown' ? 1 : -1);
|
|
296
|
+
if (mMarkiert < 0) { mMarkiert = mEintraege.length - 1; }
|
|
297
|
+
if (mMarkiert >= mEintraege.length) { mMarkiert = 0; }
|
|
298
|
+
mMalen();
|
|
299
|
+
var akt = mlist.children[mMarkiert];
|
|
300
|
+
if (akt && akt.scrollIntoView) { akt.scrollIntoView({ block: 'nearest' }); }
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (ev.key === 'Enter') {
|
|
304
|
+
ev.preventDefault();
|
|
305
|
+
if (!mlist.hidden && mEintraege.length) {
|
|
306
|
+
mWaehlen(mEintraege[mMarkiert >= 0 ? mMarkiert : 0].m);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
if (S.fokusMusterfeld) {
|
|
311
|
+
S.fokusMusterfeld = false;
|
|
312
|
+
setTimeout(function () { sel.focus(); }, 0);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* Kein Fliesstext unter dem Auswahlfeld: die Belegung steht schon in
|
|
316
|
+
jeder Zeile des Auswahlfelds, und eine zweite Zeile verschoebe die
|
|
317
|
+
Felder daneben gegeneinander. Nur Meldungen bleiben. */
|
|
318
|
+
var meta = el('div', 'vmeta');
|
|
319
|
+
if (!haupt) {
|
|
320
|
+
meta.appendChild(el('span', 'chip bad', tr('pattern.notDetected')));
|
|
321
|
+
} else if (pflichtFehlt.length) {
|
|
322
|
+
meta.appendChild(el('span', 'chip bad',
|
|
323
|
+
tr('pattern.missing', pflichtFehlt.map(function (x) { return x.name; }).join(', '))));
|
|
324
|
+
}
|
|
325
|
+
if (e.want && e.wantAuto && e.want !== e.wantAuto) {
|
|
326
|
+
meta.appendChild(el('span', 'chip warn', tr('pattern.manualInstead', e.wantAuto)));
|
|
327
|
+
}
|
|
328
|
+
if (meta.childNodes.length) { vw.appendChild(meta); }
|
|
329
|
+
/* Der Knopf „alle 51 Muster" stand frueher neben dem Auswahlfeld und
|
|
330
|
+
schaltete die Liste um. Er sitzt jetzt als letzte Zeile in der Liste
|
|
331
|
+
selbst - dort, wo man ihn braucht, statt daneben. */
|
|
332
|
+
|
|
333
|
+
return { block: vw, rateKnopf: rateKnopf };
|
|
334
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/* Das Objektlager: Laden, Abo, Index, gezieltes Nachladen.
|
|
2
|
+
|
|
3
|
+
Was hier lebt, ist die eine Wahrheit ueber die Objektdatenbank, wie der
|
|
4
|
+
Reiter sie kennt: S.objects samt sortiertem Index. Alles Zeichnen und
|
|
5
|
+
Entwerfen liest daraus.
|
|
6
|
+
|
|
7
|
+
Die Rueckrufe nach oben (zeichnen, waehlen) kommen als Kreis-Import aus
|
|
8
|
+
werkbank.js - beim Laden der Module ist das unkritisch, weil sie erst
|
|
9
|
+
zur Laufzeit gerufen werden. */
|
|
10
|
+
|
|
11
|
+
import { S } from './zustand.js';
|
|
12
|
+
import { socket } from './verbindung.js';
|
|
13
|
+
import './basis.js';
|
|
14
|
+
import './sprache.js';
|
|
15
|
+
import { ladeEinstellungen } from './einstellungen.js';
|
|
16
|
+
import { zeigeObjektzahl } from './vorlagen.js';
|
|
17
|
+
import { enums, ladeEnums } from './enums.js';
|
|
18
|
+
import { ladeEnumVorlagen } from './katalog.js';
|
|
19
|
+
import { zeichneBaum } from './baum.js';
|
|
20
|
+
import { waehle , tipptGerade } from './entwurf.js';
|
|
21
|
+
import { zeichneErgebnis, neuZeichnenOderAufbauen } from './ergebnis.js';
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/* ================== Objekte holen ================== */
|
|
25
|
+
export var ARTEN = ['device', 'channel', 'folder', 'state'];
|
|
26
|
+
|
|
27
|
+
/* Nach dem Neuladen soll der Bericht stimmen. Die Erkennung haengt an
|
|
28
|
+
den vorhandenen Objekten — legt man einen cmnd-Punkt an, kann eine
|
|
29
|
+
Vorlage greifen, die vorher nicht griff. Vorher zeichnete das Laden
|
|
30
|
+
nur den Baum neu und liess rechts den alten Entwurf stehen. */
|
|
31
|
+
export function ladeObjekte(danach) {
|
|
32
|
+
var offen = ARTEN.length;
|
|
33
|
+
S.objects = {};
|
|
34
|
+
ARTEN.forEach(function (art) {
|
|
35
|
+
socket.emit('getObjectView', 'system', art, { startkey: '', endkey: '香' }, function (err, doc) {
|
|
36
|
+
if (!err && doc && doc.rows) {
|
|
37
|
+
doc.rows.forEach(function (r) {
|
|
38
|
+
if (r.value && r.id) { S.objects[r.id] = r.value; }
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (--offen === 0) {
|
|
42
|
+
S.keysSorted = Object.keys(S.objects).sort();
|
|
43
|
+
S.kleinIndex = {};
|
|
44
|
+
ladeEnums();
|
|
45
|
+
/* Erst die Einstellungen, dann die Vorlagen - sonst holt die
|
|
46
|
+
Werkbank sie auch dann, wenn sie abgeschaltet sind. */
|
|
47
|
+
ladeEinstellungen(function () {
|
|
48
|
+
/* Kam der Katalog spaeter als das erste Zeichnen, fehlte dem
|
|
49
|
+
Feld der Vorschlag - nachzeichnen, sobald er da ist. */
|
|
50
|
+
ladeEnumVorlagen(function () {
|
|
51
|
+
if (S.current) { zeichneErgebnis(); }
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
/* Kopfzeile nachziehen - je nach Ladereihenfolge stand dort
|
|
55
|
+
noch die Null von vor dem Einlesen. */
|
|
56
|
+
zeigeObjektzahl();
|
|
57
|
+
zeichneBaum();
|
|
58
|
+
if (S.current && S.baumModus !== 'vorlagen') { waehle(S.current); }
|
|
59
|
+
horcheAufObjekte();
|
|
60
|
+
if (typeof danach === 'function') { danach(); }
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Objekte entstehen auch ohne Zutun der Werkbank. Der uebliche Weg ist
|
|
67
|
+
genau der: cmnd.POWER anlegen, einmal schalten — und mqtt-client legt
|
|
68
|
+
stat.POWER an. Ohne Abo zeigte die Werkbank danach weiter „stat.POWER
|
|
69
|
+
fehlt", bis man den Reiter neu lud. Man erreichte den Zustand, in dem
|
|
70
|
+
die Vorlage greift, mit dem Werkzeug allein nie.
|
|
71
|
+
|
|
72
|
+
Nachgezogen wird immer, neu gezeichnet nur, wenn es den gerade
|
|
73
|
+
gewaehlten Zweig betrifft und kein Dialog offensteht — sonst
|
|
74
|
+
verschwindet einem die Zeile unter dem Finger. */
|
|
75
|
+
var horcht = false, nachziehTimer = null, nachziehBaum = false, enumTimer = null;
|
|
76
|
+
|
|
77
|
+
/* Wer selbst frisch nachgeladen hat, macht den Nachzieher des Abos
|
|
78
|
+
ueberfluessig. */
|
|
79
|
+
export function nachziehenErledigt() {
|
|
80
|
+
if (nachziehTimer) { clearTimeout(nachziehTimer); nachziehTimer = null; }
|
|
81
|
+
nachziehBaum = false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function horcheAufObjekte() {
|
|
85
|
+
if (horcht) { return; }
|
|
86
|
+
horcht = true;
|
|
87
|
+
socket.emit('subscribeObjects', '*');
|
|
88
|
+
socket.on('objectChange', function (id, obj) {
|
|
89
|
+
if (!id) { return; }
|
|
90
|
+
/* Nur was zaehlt, nicht jeder Schreibvorgang: ts und from aendern
|
|
91
|
+
sich bei jedem setObject, auch wenn inhaltlich alles gleich
|
|
92
|
+
bleibt. Ohne diesen Vergleich zeichnete die Werkbank ein zweites
|
|
93
|
+
Mal neu, nachdem sie selbst geschrieben und schon nachgezogen
|
|
94
|
+
hatte — der Baum sprang sichtbar zweimal. */
|
|
95
|
+
var kern = function (o) {
|
|
96
|
+
return o ? JSON.stringify([o.type, o.common, o.native]) : null;
|
|
97
|
+
};
|
|
98
|
+
var vorher = kern(S.objects[id]);
|
|
99
|
+
var nachher = kern(obj);
|
|
100
|
+
if (vorher === nachher) { return; }
|
|
101
|
+
|
|
102
|
+
/* Aufzaehlungen gehen einen eigenen Weg.
|
|
103
|
+
|
|
104
|
+
Sie gehoeren nicht in `objects` - dort stuenden sie im Geraetebaum,
|
|
105
|
+
wo sie nichts zu suchen haben. Nachziehen muss man sie aber, und
|
|
106
|
+
das war der Fehler: Die Werkbank las sie einmal beim Start und nie
|
|
107
|
+
wieder. Nach dem ersten Schreiben arbeitete sie mit einem
|
|
108
|
+
veralteten Stand weiter, kannte die eben gesetzte Zuordnung nicht
|
|
109
|
+
und trug sie beim naechsten Mal nicht aus. Die neue Funktion kam
|
|
110
|
+
hinzu, statt die alte abzuloesen - ein Geraet stand am Ende in
|
|
111
|
+
zweien. Gemessen an `alias.0.FK_Badezimmer`, das in „Licht“ und
|
|
112
|
+
„Verschluss“ gleichzeitig hing. */
|
|
113
|
+
if (id.indexOf('enum.') === 0) {
|
|
114
|
+
if (kern(enums[id]) === nachher) { return; }
|
|
115
|
+
if (obj) { enums[id] = obj; } else { delete enums[id]; }
|
|
116
|
+
if (enumTimer) { clearTimeout(enumTimer); }
|
|
117
|
+
var enumNachziehen = function () {
|
|
118
|
+
enumTimer = null;
|
|
119
|
+
/* Wer gerade im Trockenlauf steht oder tippt, wird nicht
|
|
120
|
+
unterbrochen - gleich nochmal. */
|
|
121
|
+
if (document.querySelector('dialog[open]') || tipptGerade()) {
|
|
122
|
+
enumTimer = setTimeout(enumNachziehen, 400);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (S.current) { zeichneErgebnis(); }
|
|
126
|
+
};
|
|
127
|
+
enumTimer = setTimeout(enumNachziehen, 300);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
var gabEs = S.objects[id] !== undefined;
|
|
132
|
+
if (obj) { S.objects[id] = obj; } else { delete S.objects[id]; }
|
|
133
|
+
if (gabEs !== (S.objects[id] !== undefined)) { nachziehBaum = true; }
|
|
134
|
+
|
|
135
|
+
var betrifft = S.current && (id === S.current || id.indexOf(S.current + '.') === 0);
|
|
136
|
+
if (!betrifft && !nachziehBaum) { return; }
|
|
137
|
+
if (nachziehTimer) { clearTimeout(nachziehTimer); }
|
|
138
|
+
var nachziehen = function () {
|
|
139
|
+
nachziehTimer = null;
|
|
140
|
+
/* Offener Dialog: spaeter nochmal, nicht ersatzlos fallenlassen.
|
|
141
|
+
|
|
142
|
+
Vorher stand hier ein blankes `return`. Wer einen Alias ueber den
|
|
143
|
+
Loeschdialog entfernte, bekam waehrend der offenen Meldung die
|
|
144
|
+
`objectChange`-Meldungen: `objects` verlor die Eintraege,
|
|
145
|
+
`keysSorted` behielt sie, und der Nachzieher, der beides wieder
|
|
146
|
+
in Deckung gebracht haette, wurde verworfen. Der naechste
|
|
147
|
+
Baumaufbau griff dann auf ein Objekt zu, das es nicht mehr gab -
|
|
148
|
+
`Cannot read properties of undefined (reading 'type')`. */
|
|
149
|
+
if (document.querySelector('dialog[open]')) {
|
|
150
|
+
nachziehTimer = setTimeout(nachziehen, 400);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
/* Wer gerade tippt, behaelt seine Eingabe — spaeter nochmal. */
|
|
154
|
+
if (tipptGerade()) { nachziehTimer = setTimeout(nachziehen, 400); return; }
|
|
155
|
+
if (nachziehBaum || indexStimmtNicht()) {
|
|
156
|
+
indexNeu();
|
|
157
|
+
nachziehBaum = false;
|
|
158
|
+
zeichneBaum();
|
|
159
|
+
}
|
|
160
|
+
neuZeichnenOderAufbauen();
|
|
161
|
+
};
|
|
162
|
+
nachziehTimer = setTimeout(nachziehen, 600);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Nach einer eigenen Aktion nur nachziehen, was sich geaendert hat.
|
|
167
|
+
Vorher rief jede angelegte cmnd-Zeile ladeObjekte() und las die ganze
|
|
168
|
+
Datenbank neu — auf einer gewachsenen Anlage sind das 24 500 Objekte
|
|
169
|
+
fuer einen einzigen Datenpunkt, und der Baum sprang zweimal. */
|
|
170
|
+
/* Sobald jemand am Entwurf etwas eingestellt hat, darf ihn kein
|
|
171
|
+
Nachladen mehr wegwerfen. Vorher genuegte ein angelegter Datenpunkt,
|
|
172
|
+
und Zielordner, Haken und von Hand angelegte Punkte waren weg. */
|
|
173
|
+
|
|
174
|
+
export function uebernimmObjekt(id, o) {
|
|
175
|
+
var gabEs = S.objects[id] !== undefined;
|
|
176
|
+
if (o) { S.objects[id] = o; } else { delete S.objects[id]; }
|
|
177
|
+
return gabEs !== (S.objects[id] !== undefined);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Der Baum liest aus S.keysSorted, nicht aus S.objects. Wer einen Punkt
|
|
181
|
+
hinzufuegt oder entfernt, muss den Index neu bauen — sonst kennt der
|
|
182
|
+
Baum ihn nicht, egal wie oft man zeichnet. */
|
|
183
|
+
export function indexNeu() {
|
|
184
|
+
S.keysSorted = Object.keys(S.objects).sort();
|
|
185
|
+
S.kleinIndex = {};
|
|
186
|
+
zeigeObjektzahl();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Stimmt die Zahl der Schluessel nicht mit dem Index ueberein, hat
|
|
190
|
+
jemand anders an S.objects gearbeitet — das Abo etwa, das eintraegt und
|
|
191
|
+
das Aufraeumen seinem Zeitgeber ueberlaesst. Ohne diese Probe fiel
|
|
192
|
+
genau der uebliche Fall durch: Abo traegt ein, das gezielte Nachladen
|
|
193
|
+
sieht keine Aenderung mehr, bricht den Zeitgeber ab, und der Baum
|
|
194
|
+
bleibt auf altem Stand. */
|
|
195
|
+
export function indexStimmtNicht() {
|
|
196
|
+
return S.keysSorted.length !== Object.keys(S.objects).length;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function holeObjekt(id, danach) {
|
|
200
|
+
socket.emit('getObject', id, function (err, o) {
|
|
201
|
+
var anders = uebernimmObjekt(id, err ? null : o);
|
|
202
|
+
if (anders || nachziehBaum || indexStimmtNicht()) {
|
|
203
|
+
indexNeu();
|
|
204
|
+
zeichneBaum();
|
|
205
|
+
}
|
|
206
|
+
/* Das Abo hat auf dieselbe Aenderung schon einen Nachzieher
|
|
207
|
+
angesetzt — der ist jetzt erledigt. Sonst zeichnet die Werkbank
|
|
208
|
+
ein zweites Mal, eine halbe Sekunde spaeter, und alles springt. */
|
|
209
|
+
nachziehenErledigt();
|
|
210
|
+
neuZeichnenOderAufbauen(danach);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Nach einer Abfrage am Geraet weiss man nicht, welche Punkte dabei
|
|
215
|
+
entstanden sind — dann eben der ganze Zweig, aber nicht mehr. */
|
|
216
|
+
export function holeZweig(praefix, danach) {
|
|
217
|
+
/* `baumNeu` ist ein lokales Flag - nicht der Baum in S. Beim Umzug in
|
|
218
|
+
das Zustandsmodul geriet es einmal faelschlich unter die Raeder,
|
|
219
|
+
weil es in einer Komma-Deklaration versteckt war. */
|
|
220
|
+
var offen = ARTEN.length, baumNeu = false;
|
|
221
|
+
ARTEN.forEach(function (art) {
|
|
222
|
+
socket.emit('getObjectView', 'system', art,
|
|
223
|
+
{ startkey: praefix, endkey: praefix + '香' }, function (err, doc) {
|
|
224
|
+
if (!err && doc && doc.rows) {
|
|
225
|
+
doc.rows.forEach(function (r) {
|
|
226
|
+
if (r.value && r.id && uebernimmObjekt(r.id, r.value)) { baumNeu = true; }
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (--offen === 0) {
|
|
230
|
+
if (baumNeu || nachziehBaum || indexStimmtNicht()) {
|
|
231
|
+
indexNeu();
|
|
232
|
+
zeichneBaum();
|
|
233
|
+
}
|
|
234
|
+
nachziehenErledigt();
|
|
235
|
+
neuZeichnenOderAufbauen(danach);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* ================== Baum ================== */
|
|
242
|
+
/* Aus den flachen IDs einen Baum bauen. Nur Zweige, die irgendwo
|
|
243
|
+
Zustaende enthalten — der Rest ist fuer uns Laerm. */
|