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,1907 @@
|
|
|
1
|
+
/* Die dritte Sicht: Vorlagen ansehen, bearbeiten, speichern, Probelauf.
|
|
2
|
+
|
|
3
|
+
Paketvorlagen sind nur zu lesen - wer eine aendern will, dupliziert
|
|
4
|
+
sie, und die Kopie verdeckt das Original. */
|
|
5
|
+
|
|
6
|
+
import { S } from './zustand.js';
|
|
7
|
+
import { socket } from './verbindung.js';
|
|
8
|
+
import { D, $, el, kurz } from './basis.js';
|
|
9
|
+
import { tr, sprachtext, sprache } from './sprache.js';
|
|
10
|
+
import { katalogFehlt } from './katalog.js';
|
|
11
|
+
import { funktionsAuswahl, kennungZuFunktion } from './aufzaehlungen.js';
|
|
12
|
+
import { musterVon } from './erkennung.js';
|
|
13
|
+
import { musterZeile, musterName } from './musternamen.js';
|
|
14
|
+
import { ladeVorlagen, aendereVorlagen, setzeMeta, pruefeVorlage, INSTANZ_ID } from './vorlagen.js';
|
|
15
|
+
import { kindZustaende, hatPunkt, feldAusFormel } from './werte.js';
|
|
16
|
+
import { opt } from './entwurf.js';
|
|
17
|
+
import { zeichneErgebnis } from './ergebnis.js';
|
|
18
|
+
import { delKnopf } from './schreiben.js';
|
|
19
|
+
|
|
20
|
+
import { rollenFeld } from './rollenwahl.js';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
/* ================== Vorlagen verwalten ==================
|
|
24
|
+
Dritte Sicht neben Quellen und Aliasen: links die Vorlagen, rechts
|
|
25
|
+
die gewaehlte mit Probelauf. Paketvorlagen sind nur zu lesen —
|
|
26
|
+
wer eine aendern will, dupliziert sie, und die Kopie verdeckt das
|
|
27
|
+
Original. Damit bleibt das Paket immer der Auslieferungszustand. */
|
|
28
|
+
|
|
29
|
+
var vGewaehlt = null; /* id der gewaehlten Vorlage */
|
|
30
|
+
var vEinlesen = null; /* eingelesene, noch nicht gespeicherte Vorlage */
|
|
31
|
+
var vEntwurf = null; /* Arbeitskopie beim Bearbeiten */
|
|
32
|
+
var vLoeschFrage = false;
|
|
33
|
+
var vJsonAuf = false;
|
|
34
|
+
var vOffeneZeile = null;
|
|
35
|
+
|
|
36
|
+
export function vorlageMitId(id) {
|
|
37
|
+
var t = null;
|
|
38
|
+
S.VORLAGEN.forEach(function (v) { if (v.id === id) { t = v; } });
|
|
39
|
+
return t;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function ohneUnterstrich(v) {
|
|
43
|
+
var k = {};
|
|
44
|
+
Object.keys(v).forEach(function (f) { if (f.charAt(0) !== '_') { k[f] = v[f]; } });
|
|
45
|
+
/* Halbfertige Ersatzquellen nicht mitspeichern: eine Zeile ohne
|
|
46
|
+
Datenpunkt ist beim Bearbeiten nuetzlich, in der Vorlage waere sie
|
|
47
|
+
nur Ballast. */
|
|
48
|
+
if (Array.isArray(k.zustaende)) {
|
|
49
|
+
k.zustaende = k.zustaende.map(function (z) {
|
|
50
|
+
if (!z || !z.lesenSonst) { return z; }
|
|
51
|
+
var liste = ersatzListe(z).filter(function (x) { return x && x.punkt; });
|
|
52
|
+
var kopie = {};
|
|
53
|
+
Object.keys(z).forEach(function (f) { kopie[f] = z[f]; });
|
|
54
|
+
if (!liste.length) { delete kopie.lesenSonst; }
|
|
55
|
+
else { kopie.lesenSonst = (liste.length === 1) ? liste[0] : liste; }
|
|
56
|
+
return kopie;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return k;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function zeichneVorlagenListe() {
|
|
63
|
+
var host = $('#tree');
|
|
64
|
+
host.textContent = '';
|
|
65
|
+
var filter = ($('#q').value || '').trim().toLowerCase();
|
|
66
|
+
|
|
67
|
+
var liste = S.VORLAGEN.filter(function (v) {
|
|
68
|
+
if (!filter) { return true; }
|
|
69
|
+
return (v.id + ' ' + sprachtext(v.name)).toLowerCase().indexOf(filter) > -1;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (vEinlesen) {
|
|
73
|
+
host.appendChild(vListenZeile(vEinlesen, true));
|
|
74
|
+
}
|
|
75
|
+
if (!liste.length && !vEinlesen) {
|
|
76
|
+
host.appendChild(el('div', 'empty', tr('tree.nothingFound')));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
liste.forEach(function (v) { host.appendChild(vListenZeile(v, false)); });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function vListenZeile(v, eingelesen) {
|
|
83
|
+
/* Zwei verschiedene Dinge, die im Objektbaum zwei verschiedene Klassen
|
|
84
|
+
tragen: `pick` heisst waehlbar (Mauszeiger), `sel` heisst gewaehlt
|
|
85
|
+
(Akzentfarbe). Diese Liste setzte nur `pick` — und `pick` faerbt
|
|
86
|
+
nichts. Die angeklickte Vorlage stand deshalb unmarkiert da, waehrend
|
|
87
|
+
rechts ihr Blatt aufgeschlagen war (26.08.2026). */
|
|
88
|
+
var gewaehlt = (!eingelesen && v.id === vGewaehlt && !vEinlesen) ||
|
|
89
|
+
(eingelesen && vEinlesen);
|
|
90
|
+
var d = el('div', 'node pick' + (gewaehlt ? ' sel' : ''));
|
|
91
|
+
d.style.cursor = 'pointer';
|
|
92
|
+
d.style.gap = '6px';
|
|
93
|
+
var eigen = v._quelle === 'benutzer';
|
|
94
|
+
d.appendChild(el('span', 'chip ' + (eingelesen ? 'warn' : (eigen ? 'ok' : 'mut')),
|
|
95
|
+
eingelesen ? tr('tv.imported') : (eigen ? tr('tpl.own') : tr('tv.package'))));
|
|
96
|
+
var nm = el('span', 'nm', sprachtext(v.name));
|
|
97
|
+
d.appendChild(nm);
|
|
98
|
+
var meta = el('span', null, v.id + ' v' + (v.version || 1));
|
|
99
|
+
meta.style.fontFamily = 'var(--mono)';
|
|
100
|
+
meta.style.fontSize = '10px';
|
|
101
|
+
meta.style.color = 'var(--ink-3)';
|
|
102
|
+
meta.style.marginLeft = 'auto';
|
|
103
|
+
d.appendChild(meta);
|
|
104
|
+
d.addEventListener('click', function () {
|
|
105
|
+
if (!eingelesen) { vEinlesen = null; vGewaehlt = v.id; }
|
|
106
|
+
vEntwurf = null;
|
|
107
|
+
vLoeschFrage = false;
|
|
108
|
+
vOffeneZeile = null;
|
|
109
|
+
zeichneVorlagenListe();
|
|
110
|
+
zeichneErgebnis();
|
|
111
|
+
});
|
|
112
|
+
return d;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Beim Bearbeiten wird auf einer Kopie gearbeitet — erst Speichern
|
|
116
|
+
macht sie echt. Gleiche Regel wie beim Entwurf eines Geraets. */
|
|
117
|
+
function vArbeitsstand() {
|
|
118
|
+
if (vEinlesen) { return vEinlesen; }
|
|
119
|
+
if (vEntwurf) { return vEntwurf; }
|
|
120
|
+
return vorlageMitId(vGewaehlt);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function vAendern(feld, wert) {
|
|
124
|
+
if (!vEinlesen && !vEntwurf) {
|
|
125
|
+
var o = vorlageMitId(vGewaehlt);
|
|
126
|
+
if (!o) { return; }
|
|
127
|
+
vEntwurf = JSON.parse(JSON.stringify(ohneUnterstrich(o)));
|
|
128
|
+
vEntwurf._quelle = o._quelle;
|
|
129
|
+
}
|
|
130
|
+
feld(vEinlesen || vEntwurf, wert);
|
|
131
|
+
zeichneErgebnis();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function zeichneVorlagenBlatt(host) {
|
|
135
|
+
/* Die Knoepfe unten gehoeren zum Geraet, nicht zur Vorlage. In dieser
|
|
136
|
+
Sicht waeren sie sonst noch auf das Geraet scharf, das vor dem
|
|
137
|
+
Umschalten gewaehlt war. */
|
|
138
|
+
/* Nicht nur sperren, sondern verstecken — ein grauer Knopf „Alias
|
|
139
|
+
erzeugen" gehoert nicht unter eine Vorlage. */
|
|
140
|
+
['#btn-dry', '#btn-tpl', '#btn-checks'].forEach(function (w) {
|
|
141
|
+
var b = $(w);
|
|
142
|
+
if (b) { b.disabled = true; b.hidden = true; }
|
|
143
|
+
});
|
|
144
|
+
var ba0 = $('#btn-dry-alle');
|
|
145
|
+
if (ba0) { ba0.hidden = true; }
|
|
146
|
+
delKnopf(false);
|
|
147
|
+
|
|
148
|
+
var v = vArbeitsstand();
|
|
149
|
+
if (!v) {
|
|
150
|
+
/* Dieselbe Erklaerung wie in den anderen beiden Modi. */
|
|
151
|
+
var lh = el('div', 'empty leerhilfe');
|
|
152
|
+
lh.appendChild(el('div', 'lh1', tr('help.templates')));
|
|
153
|
+
host.appendChild(lh);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
var eigen = v._quelle === 'benutzer' || !!vEinlesen;
|
|
157
|
+
var vorhanden = vorlageMitId(v.id);
|
|
158
|
+
var geaendert = !!vEntwurf || !!vEinlesen;
|
|
159
|
+
|
|
160
|
+
/* --- Kopf --- */
|
|
161
|
+
var head = el('div', 'reshead');
|
|
162
|
+
var links = el('div');
|
|
163
|
+
links.appendChild(el('div', 'ziel', sprachtext(v.name)));
|
|
164
|
+
var unter = el('div', 'unter');
|
|
165
|
+
unter.appendChild(el('span', 'chip ' + (eigen ? 'ok' : 'mut'),
|
|
166
|
+
vEinlesen ? tr('tv.imported') : (eigen ? tr('tpl.own') : tr('tv.package'))));
|
|
167
|
+
unter.appendChild(el('span', null, v.id + ' v' + (v.version || 1) +
|
|
168
|
+
' · ' + tr('tv.rank', v.rang || 0)));
|
|
169
|
+
links.appendChild(unter);
|
|
170
|
+
head.appendChild(links);
|
|
171
|
+
host.appendChild(head);
|
|
172
|
+
|
|
173
|
+
/* --- Was das Einlesen bewirken wuerde --- */
|
|
174
|
+
if (vEinlesen) {
|
|
175
|
+
var iw = el('div', 'aside w');
|
|
176
|
+
iw.style.marginBottom = '11px';
|
|
177
|
+
iw.appendChild(el('b', null, tr('tv.importTitle')));
|
|
178
|
+
var was;
|
|
179
|
+
if (!vorhanden) { was = tr('tv.importNew', v.id); }
|
|
180
|
+
else if (vorhanden._quelle === 'benutzer') {
|
|
181
|
+
was = tr('tv.importReplaces', v.id, vorhanden.version || 1, v.version || 1);
|
|
182
|
+
} else { was = tr('tv.importShadows', v.id); }
|
|
183
|
+
iw.appendChild(document.createTextNode(' ' + was));
|
|
184
|
+
host.appendChild(iw);
|
|
185
|
+
} else if (!eigen) {
|
|
186
|
+
var pw = el('div', 'aside');
|
|
187
|
+
pw.style.marginBottom = '11px';
|
|
188
|
+
pw.appendChild(el('b', null, tr('tv.packageTitle')));
|
|
189
|
+
pw.appendChild(document.createTextNode(' ' + tr('tv.packageText')));
|
|
190
|
+
host.appendChild(pw);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* --- Knoepfe. Ganz oben, weil man dort zuerst hinschaut. --- */
|
|
194
|
+
var bar = el('div');
|
|
195
|
+
bar.style.display = 'flex';
|
|
196
|
+
bar.style.gap = '8px';
|
|
197
|
+
bar.style.flexWrap = 'wrap';
|
|
198
|
+
bar.style.alignItems = 'center';
|
|
199
|
+
bar.style.margin = '0 0 12px';
|
|
200
|
+
|
|
201
|
+
if (geaendert) {
|
|
202
|
+
var bs = el('button', 'btn primary', vEinlesen ? tr('tv.importAndSave') : tr('tv.save'));
|
|
203
|
+
bs.addEventListener('click', vSpeichern);
|
|
204
|
+
bar.appendChild(bs);
|
|
205
|
+
var bv = el('button', 'btn', tr('tv.discard'));
|
|
206
|
+
bv.addEventListener('click', function () {
|
|
207
|
+
vEinlesen = null; vEntwurf = null; vLoeschFrage = false;
|
|
208
|
+
zeichneVorlagenListe(); zeichneErgebnis();
|
|
209
|
+
});
|
|
210
|
+
bar.appendChild(bv);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Zwei verschiedene Absichten, deshalb zwei Beschriftungen: eine
|
|
214
|
+
mitgelieferte Vorlage will man meist *aendern* — dann behaelt die
|
|
215
|
+
Kopie die Kennung und verdeckt das Original. Eine eigene Vorlage
|
|
216
|
+
will man meist *abwandeln* — dann braucht die Kopie eine neue
|
|
217
|
+
Kennung, sonst ueberschriebe sie das Vorbild. */
|
|
218
|
+
var bd2 = el('button', 'btn', eigen ? tr('tv.duplicate') : tr('tv.makeOwn'));
|
|
219
|
+
bd2.addEventListener('click', function () { vDuplizieren(v, !eigen); });
|
|
220
|
+
bar.appendChild(bd2);
|
|
221
|
+
|
|
222
|
+
var ba = el('button', 'btn', tr('tv.export'));
|
|
223
|
+
ba.addEventListener('click', function () { vAusgeben(v); });
|
|
224
|
+
bar.appendChild(ba);
|
|
225
|
+
|
|
226
|
+
if (eigen && !vEinlesen && vorhanden && vorhanden._quelle === 'benutzer') {
|
|
227
|
+
var bl = el('button', 'btn', vLoeschFrage ? tr('tv.deleteSure') : tr('tv.delete'));
|
|
228
|
+
if (vLoeschFrage) {
|
|
229
|
+
bl.style.background = 'var(--bad)';
|
|
230
|
+
bl.style.borderColor = 'var(--bad)';
|
|
231
|
+
bl.style.color = '#fff';
|
|
232
|
+
}
|
|
233
|
+
bl.addEventListener('click', function () {
|
|
234
|
+
if (!vLoeschFrage) { vLoeschFrage = true; zeichneErgebnis(); return; }
|
|
235
|
+
vLoeschen(v.id);
|
|
236
|
+
});
|
|
237
|
+
bar.appendChild(bl);
|
|
238
|
+
/* Verdeckt sie eine mitgelieferte, kommt die wieder zum Vorschein. */
|
|
239
|
+
if (vLoeschFrage) { bar.appendChild(el('span', 'hint', tr('tv.deleteHint'))); }
|
|
240
|
+
}
|
|
241
|
+
host.appendChild(bar);
|
|
242
|
+
|
|
243
|
+
/* --- Kopfdaten --- */
|
|
244
|
+
var kk = el('div', 'card');
|
|
245
|
+
kk.style.marginBottom = '11px';
|
|
246
|
+
var kh = el('div', 'ch');
|
|
247
|
+
kh.appendChild(el('span', 'typ', tr('tpls.head')));
|
|
248
|
+
if (geaendert) { kh.appendChild(el('span', 'chip warn', tr('tv.unsaved'))); }
|
|
249
|
+
|
|
250
|
+
kk.appendChild(kh);
|
|
251
|
+
var kb = el('div');
|
|
252
|
+
kb.style.padding = '9px 12px 11px';
|
|
253
|
+
kb.style.display = 'flex';
|
|
254
|
+
kb.style.flexWrap = 'wrap';
|
|
255
|
+
kb.style.gap = '9px 14px';
|
|
256
|
+
|
|
257
|
+
function feld(label, wert, setzen, breit) {
|
|
258
|
+
var l = el('label', 'feld');
|
|
259
|
+
l.appendChild(el('span', 'feldlabel', label));
|
|
260
|
+
var i = document.createElement('input');
|
|
261
|
+
i.className = 'tx';
|
|
262
|
+
i.type = 'text';
|
|
263
|
+
i.value = wert === undefined || wert === null ? '' : String(wert);
|
|
264
|
+
i.disabled = !eigen;
|
|
265
|
+
if (breit) { i.style.minWidth = breit; }
|
|
266
|
+
i.addEventListener('change', function () { vAendern(setzen, i.value); });
|
|
267
|
+
l.appendChild(i);
|
|
268
|
+
return l;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
kb.appendChild(feld(tr('tpls.nameDe'), textIn(v.name, 'de'),
|
|
272
|
+
function (x, w) { if (typeof x.name === 'string') { x.name = { de: x.name, en: x.name }; } x.name.de = w; }, '140px'));
|
|
273
|
+
kb.appendChild(feld(tr('tpls.nameEn'), textIn(v.name, 'en'),
|
|
274
|
+
function (x, w) { if (typeof x.name === 'string') { x.name = { de: x.name, en: x.name }; } x.name.en = w; }, '140px'));
|
|
275
|
+
kb.appendChild(feld(tr('tpls.rank'), v.rang || 0,
|
|
276
|
+
function (x, w) { x.rang = Number(w) || 0; }, '60px'));
|
|
277
|
+
var rh = el('span', 'hint', eigen ? tr('tv.rankOwn') : tr('tv.rankPackage'));
|
|
278
|
+
rh.style.flexBasis = '100%';
|
|
279
|
+
kb.appendChild(rh);
|
|
280
|
+
|
|
281
|
+
var lt = el('label', 'feld');
|
|
282
|
+
lt.appendChild(el('span', 'feldlabel', tr('tpls.deviceType')));
|
|
283
|
+
var st = el('select', 'tx');
|
|
284
|
+
st.disabled = !eigen;
|
|
285
|
+
st.appendChild(opt('', tr('tpls.noType')));
|
|
286
|
+
(D ? Object.keys(D.patterns).sort() : []).forEach(function (m) {
|
|
287
|
+
var o = opt(m, musterZeile(m));
|
|
288
|
+
if (m === v.geraetetyp) { o.selected = true; }
|
|
289
|
+
st.appendChild(o);
|
|
290
|
+
});
|
|
291
|
+
st.addEventListener('change', function () {
|
|
292
|
+
vAendern(function (x, w) { x.geraetetyp = w || undefined; }, st.value);
|
|
293
|
+
});
|
|
294
|
+
lt.appendChild(st);
|
|
295
|
+
kb.appendChild(lt);
|
|
296
|
+
|
|
297
|
+
/* Wozu das Geraet zaehlt.
|
|
298
|
+
|
|
299
|
+
Nicht dasselbe wie der Geraetetyp: `socket` sagt nur, dass etwas
|
|
300
|
+
geschaltet wird - ob eine Steckdose, eine Lampe oder eine
|
|
301
|
+
Druckerleiste, weiss allein die Vorlage. Gespeichert wird die
|
|
302
|
+
Kennung aus dem Katalog des Admin, nicht der Anzeigename; daraus
|
|
303
|
+
loest die Werkbank beides auf, den vorhandenen Eintrag und die
|
|
304
|
+
Vorlage samt Uebersetzung und Bild. Leer heisst: nichts
|
|
305
|
+
vorschlagen. */
|
|
306
|
+
var lf = el('label', 'feld');
|
|
307
|
+
lf.appendChild(el('span', 'feldlabel', tr('tpls.function')));
|
|
308
|
+
var sf = el('select', 'tx');
|
|
309
|
+
sf.disabled = !eigen;
|
|
310
|
+
sf.appendChild(opt('', tr('tpls.noFunction')));
|
|
311
|
+
funktionsAuswahl(v.funktion).forEach(function (x) {
|
|
312
|
+
var o = opt(x.wert, x.text);
|
|
313
|
+
if (x.gewaehlt) { o.selected = true; }
|
|
314
|
+
sf.appendChild(o);
|
|
315
|
+
});
|
|
316
|
+
sf.addEventListener('change', function () {
|
|
317
|
+
vAendern(function (x, w) { x.funktion = w || undefined; }, sf.value);
|
|
318
|
+
});
|
|
319
|
+
lf.appendChild(sf);
|
|
320
|
+
if (katalogFehlt) { lf.appendChild(el('span', 'hint', tr('catalog.missing'))); }
|
|
321
|
+
kb.appendChild(lf);
|
|
322
|
+
|
|
323
|
+
kk.appendChild(kb);
|
|
324
|
+
|
|
325
|
+
var nb = el('div');
|
|
326
|
+
nb.style.padding = '0 12px 11px';
|
|
327
|
+
nb.appendChild(feld(tr('tpls.nameHint'), (v.erkennung || {}).namenshinweis || '',
|
|
328
|
+
function (x, w) { x.erkennung = x.erkennung || {}; if (w) { x.erkennung.namenshinweis = w; } else { delete x.erkennung.namenshinweis; } }, '260px'));
|
|
329
|
+
nb.appendChild(el('div', 'hint', tr('tv.nameHintShort')));
|
|
330
|
+
kk.appendChild(nb);
|
|
331
|
+
host.appendChild(kk);
|
|
332
|
+
|
|
333
|
+
/* --- Erkennung --- */
|
|
334
|
+
var ek = el('div', 'card');
|
|
335
|
+
ek.style.marginBottom = '11px';
|
|
336
|
+
var eh = el('div', 'ch');
|
|
337
|
+
eh.appendChild(el('span', 'typ', tr('tv.detection')));
|
|
338
|
+
var erf = (v.erkennung || {}).erforderlich || [];
|
|
339
|
+
eh.appendChild(el('span', 'chip mut', tr('tv.requiredCount', erf.length)));
|
|
340
|
+
ek.appendChild(eh);
|
|
341
|
+
var eb = el('div');
|
|
342
|
+
eb.style.padding = '9px 12px 11px';
|
|
343
|
+
var listenFeld = function (label, wert, setzen, hilfe) {
|
|
344
|
+
var w = el('div');
|
|
345
|
+
w.style.marginBottom = '9px';
|
|
346
|
+
w.appendChild(feld(label, wert, setzen, '380px'));
|
|
347
|
+
if (hilfe) { w.appendChild(el('div', 'hint', hilfe)); }
|
|
348
|
+
return w;
|
|
349
|
+
};
|
|
350
|
+
var alsListe = function (text) {
|
|
351
|
+
return String(text).split(',').map(function (t) { return t.trim(); })
|
|
352
|
+
.filter(function (t) { return !!t; });
|
|
353
|
+
};
|
|
354
|
+
eb.appendChild(listenFeld(tr('tv.required2'), erf.join(', '), function (x, wert) {
|
|
355
|
+
x.erkennung = x.erkennung || {};
|
|
356
|
+
x.erkennung.erforderlich = alsListe(wert);
|
|
357
|
+
}, tr('tv.requiredHelp')));
|
|
358
|
+
if (!erf.length) {
|
|
359
|
+
eb.appendChild(el('div', 'hint', tr('tpls.noRequired')));
|
|
360
|
+
}
|
|
361
|
+
var inh = (v.erkennung || {}).inhalt || {};
|
|
362
|
+
Object.keys(inh).forEach(function (punkt) {
|
|
363
|
+
var z = el('div', 'hint', tr('tpls.contentRow', punkt, inh[punkt]));
|
|
364
|
+
z.style.fontFamily = 'var(--mono)';
|
|
365
|
+
eb.appendChild(z);
|
|
366
|
+
});
|
|
367
|
+
eb.appendChild(listenFeld(tr('tv.forbidden'), ((v.erkennung || {}).verboten || []).join(', '),
|
|
368
|
+
function (x, wert) {
|
|
369
|
+
x.erkennung = x.erkennung || {};
|
|
370
|
+
var l = alsListe(wert);
|
|
371
|
+
if (l.length) { x.erkennung.verboten = l; } else { delete x.erkennung.verboten; }
|
|
372
|
+
}, tr('tv.forbiddenHelp')));
|
|
373
|
+
if (v.mehrfach) {
|
|
374
|
+
eb.appendChild(el('div', 'hint', tr('tv.multi', v.kanalname || 'POWER%N%')));
|
|
375
|
+
}
|
|
376
|
+
ek.appendChild(eb);
|
|
377
|
+
host.appendChild(ek);
|
|
378
|
+
|
|
379
|
+
/* --- Datenpunkte --- */
|
|
380
|
+
var zk = el('div', 'card');
|
|
381
|
+
zk.style.marginBottom = '11px';
|
|
382
|
+
var zh = el('div', 'ch');
|
|
383
|
+
zh.appendChild(el('span', 'typ', tr('tpls.states')));
|
|
384
|
+
zh.appendChild(el('span', 'chip mut', tr('tpls.statesCount', (v.zustaende || []).length)));
|
|
385
|
+
if (eigen) {
|
|
386
|
+
zh.appendChild(el('span', 'luecke'));
|
|
387
|
+
zh.appendChild(el('span', 'hint', tr('tv.clickRow')));
|
|
388
|
+
}
|
|
389
|
+
zk.appendChild(zh);
|
|
390
|
+
(v.zustaende || []).forEach(function (z, nr) {
|
|
391
|
+
var auf = (vOffeneZeile === nr);
|
|
392
|
+
var r = el('div', 'slot' + (eigen ? ' vklick' : '') + (auf ? ' offen' : ''));
|
|
393
|
+
r.style.gridTemplateColumns = '1fr 1.1fr 1.2fr 90px 15px';
|
|
394
|
+
r.appendChild(el('span', 'sn', z.name));
|
|
395
|
+
r.appendChild(el('span', 'rx', z.rolle || '\u2014'));
|
|
396
|
+
var pw = el('span', 'rx');
|
|
397
|
+
pw.style.fontFamily = 'var(--mono)';
|
|
398
|
+
pw.appendChild(document.createTextNode(z.lesen + (z.feld ? ' \u00b7 ' + z.feld : '')));
|
|
399
|
+
/* Der Schreibpfad ist ein Pfad wie der Lesepfad davor, keine
|
|
400
|
+
Statusmarke - als Chip las er sich wie \u201eVorgabe" daneben. */
|
|
401
|
+
if (z.schreiben) { pw.appendChild(el('span', 'leise', ' \u2192 ' + z.schreiben)); }
|
|
402
|
+
if (z.absolut) { pw.appendChild(el('span', 'chip warn', tr('tpls.absolute'))); }
|
|
403
|
+
r.appendChild(pw);
|
|
404
|
+
/* Dieselben zwei Angaben wie im Speichern-Dialog, gleiche Richtung
|
|
405
|
+
und dieselben Woerter — vorher stand hier „Pflicht/freiwillig"
|
|
406
|
+
und dort „Vorgabe / muss da sein", fuer genau dieselbe Sache. */
|
|
407
|
+
var mm = el('span', 'rx');
|
|
408
|
+
if (!z.vorgabeAus) { mm.appendChild(el('span', 'chip mut', tr('tpls.colDefault'))); }
|
|
409
|
+
if (istPflichtpunkt(v, z)) { mm.appendChild(el('span', 'chip ok', tr('tpls.colRequired'))); }
|
|
410
|
+
r.appendChild(mm);
|
|
411
|
+
r.appendChild(el('span', 'ca2', eigen ? (auf ? '\u25be' : '\u25b8') : ''));
|
|
412
|
+
if (eigen) {
|
|
413
|
+
r.addEventListener('click', function () {
|
|
414
|
+
vOffeneZeile = auf ? null : nr;
|
|
415
|
+
zeichneErgebnis();
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
zk.appendChild(r);
|
|
419
|
+
if (eigen && auf) { zk.appendChild(vZeilenDetail(z, nr)); }
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
if (eigen) {
|
|
423
|
+
var np = el('div');
|
|
424
|
+
np.style.padding = '9px 12px 11px';
|
|
425
|
+
var nKnopf = el('button', 'btn schmal', tr('tv.addState'));
|
|
426
|
+
nKnopf.addEventListener('click', function () {
|
|
427
|
+
vAendern(function (x) {
|
|
428
|
+
x.zustaende = x.zustaende || [];
|
|
429
|
+
x.zustaende.push({ name: 'NEU', typ: 'mixed', lesen: '', optional: true });
|
|
430
|
+
vOffeneZeile = x.zustaende.length - 1;
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
np.appendChild(nKnopf);
|
|
434
|
+
zk.appendChild(np);
|
|
435
|
+
}
|
|
436
|
+
/* Das Rollenwissen ist kein Zeilenbestand - nur ein Vermerk, dass es
|
|
437
|
+
da ist und wo es greift. */
|
|
438
|
+
if (v.weiterePunkte && Object.keys(v.weiterePunkte).length) {
|
|
439
|
+
var ww = el('div', 'hint');
|
|
440
|
+
ww.style.padding = '7px 12px 10px';
|
|
441
|
+
ww.textContent = tr('tpls.wissen', Object.keys(v.weiterePunkte).length);
|
|
442
|
+
zk.appendChild(ww);
|
|
443
|
+
}
|
|
444
|
+
host.appendChild(zk);
|
|
445
|
+
|
|
446
|
+
/* --- Probelauf --- */
|
|
447
|
+
var pk = el('div', 'card');
|
|
448
|
+
pk.style.marginBottom = '11px';
|
|
449
|
+
var ph2 = el('div', 'ch');
|
|
450
|
+
ph2.appendChild(el('span', 'typ', tr('tpls.tryout')));
|
|
451
|
+
if (erf.length) { probeWerteHolen(ohneUnterstrich(v), zeichneErgebnis); }
|
|
452
|
+
var treffer = erf.length
|
|
453
|
+
? probelauf(ohneUnterstrich(v), v._quelle === 'benutzer') : [];
|
|
454
|
+
var gew = treffer.filter(function (t) { return t.gewinnt; }).length;
|
|
455
|
+
ph2.appendChild(el('span', 'chip ' + (gew ? 'ok' : 'warn'), tr('tpls.hits', treffer.length, gew)));
|
|
456
|
+
pk.appendChild(ph2);
|
|
457
|
+
var pb2 = el('div');
|
|
458
|
+
pb2.style.padding = '9px 12px 11px';
|
|
459
|
+
if (!treffer.length) {
|
|
460
|
+
pb2.appendChild(el('div', 'hint', erf.length ? tr('tpls.noHits') : tr('tpls.noRequired')));
|
|
461
|
+
} else {
|
|
462
|
+
treffer.slice(0, 40).forEach(function (t) {
|
|
463
|
+
var r = el('div', 'slot');
|
|
464
|
+
r.style.gridTemplateColumns = '1fr 110px 150px';
|
|
465
|
+
var idl = el('span', 'sn', t.id);
|
|
466
|
+
idl.style.fontFamily = 'var(--mono)';
|
|
467
|
+
r.appendChild(idl);
|
|
468
|
+
r.appendChild(el('span', 'rx', tr('tpls.evidence', t.eigen.belege)));
|
|
469
|
+
r.appendChild(el('span', 'chip ' + (t.gewinnt ? 'ok' : 'bad'),
|
|
470
|
+
t.gewinnt ? (t.gegner ? tr('tpls.beats', textIn(t.gegner.vorlage.name, sprache)) : tr('tpls.onlyOne'))
|
|
471
|
+
: tr('tpls.losesTo', textIn(t.gegner.vorlage.name, sprache))));
|
|
472
|
+
pb2.appendChild(r);
|
|
473
|
+
});
|
|
474
|
+
if (treffer.length > 40) {
|
|
475
|
+
pb2.appendChild(el('div', 'hint', tr('tpls.andMore', treffer.length - 40)));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
pk.appendChild(pb2);
|
|
479
|
+
host.appendChild(pk);
|
|
480
|
+
|
|
481
|
+
/* --- das JSON, zugeklappt: bei 16 Datenpunkten sind das 200 Zeilen,
|
|
482
|
+
und darunter faende niemand mehr die Knoepfe --- */
|
|
483
|
+
var jk = el('div', 'card');
|
|
484
|
+
jk.style.marginBottom = '11px';
|
|
485
|
+
var jh = el('div', 'ch');
|
|
486
|
+
jh.appendChild(el('span', 'typ', tr('tv.json')));
|
|
487
|
+
jh.appendChild(el('span', 'luecke'));
|
|
488
|
+
var jb = el('button', 'btn schmal', vJsonAuf ? tr('tv.hide') : tr('tv.show'));
|
|
489
|
+
jb.addEventListener('click', function () { vJsonAuf = !vJsonAuf; zeichneErgebnis(); });
|
|
490
|
+
jh.appendChild(jb);
|
|
491
|
+
jk.appendChild(jh);
|
|
492
|
+
if (vJsonAuf) {
|
|
493
|
+
var pre = el('div', 'raw');
|
|
494
|
+
pre.style.margin = '9px 12px 11px';
|
|
495
|
+
pre.style.whiteSpace = 'pre';
|
|
496
|
+
pre.style.maxHeight = 'none';
|
|
497
|
+
pre.textContent = JSON.stringify(ohneUnterstrich(v), null, 2);
|
|
498
|
+
jk.appendChild(pre);
|
|
499
|
+
}
|
|
500
|
+
host.appendChild(jk);
|
|
501
|
+
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* Ein Datenpunkt zum Aufklappen. Bewusst ohne Werte und ohne
|
|
505
|
+
Detektor-Bericht: hier gibt es kein Geraet, an dem man das messen
|
|
506
|
+
koennte. Was die Aenderung bewirkt, zeigt der Probelauf darunter. */
|
|
507
|
+
/* ============ Bausteine, die beide Detailansichten teilen ============
|
|
508
|
+
Der Block am Geraet und der an der Vorlage sollen gleich aussehen und
|
|
509
|
+
gleich bedienbar sein. Wo sie dasselbe zeigen, bauen sie es jetzt aus
|
|
510
|
+
denselben Funktionen — sonst laufen die beiden auseinander, sobald man
|
|
511
|
+
an einer Stelle etwas verbessert. */
|
|
512
|
+
|
|
513
|
+
/* Eine Zeile Beschriftung + Inhalt, wie im Detail am Geraet. */
|
|
514
|
+
export function dtZeile(d, k, inhalt, cls) {
|
|
515
|
+
var r = el('div', 'dr');
|
|
516
|
+
r.appendChild(el('div', 'k', k));
|
|
517
|
+
if (inhalt === undefined || inhalt === null) {
|
|
518
|
+
r.appendChild(el('div', cls || null, '\u2014'));
|
|
519
|
+
} else if (typeof inhalt === 'string' || typeof inhalt === 'number') {
|
|
520
|
+
r.appendChild(el('div', cls || null, String(inhalt)));
|
|
521
|
+
} else {
|
|
522
|
+
r.appendChild(inhalt);
|
|
523
|
+
}
|
|
524
|
+
d.appendChild(r);
|
|
525
|
+
return r;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function dtFeld(beschriftung, element) {
|
|
529
|
+
var l = el('label', 'fld');
|
|
530
|
+
l.appendChild(el('span', null, beschriftung));
|
|
531
|
+
l.appendChild(element);
|
|
532
|
+
return l;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/* Rollenauswahl samt Begruendung. geraetetyp darf leer sein — dann
|
|
536
|
+
entfaellt nur der Hinweis, welchen Platz die Rolle traefe. */
|
|
537
|
+
function rollenBlock(rolle, geraetetyp, setzen) {
|
|
538
|
+
var rb = el('div');
|
|
539
|
+
/* Dasselbe durchsuchbare Feld wie am Geraet - ein Aussehen fuer alle
|
|
540
|
+
Rollenwahlen. Ohne Belegt-Vermerk: in der Vorlage gibt es keinen
|
|
541
|
+
Entwurf, gegen den man Plaetze zaehlen koennte. */
|
|
542
|
+
rb.appendChild(rollenFeld({
|
|
543
|
+
wert: rolle || '',
|
|
544
|
+
muster: geraetetyp,
|
|
545
|
+
leerErlaubt: true,
|
|
546
|
+
breite: '300px',
|
|
547
|
+
beiWahl: function (r) { setzen(r); }
|
|
548
|
+
}));
|
|
549
|
+
|
|
550
|
+
var must = geraetetyp && musterVon(geraetetyp);
|
|
551
|
+
if (must) {
|
|
552
|
+
var treffer = null;
|
|
553
|
+
must.states.forEach(function (pp) {
|
|
554
|
+
if (!treffer && pp.role && rolle &&
|
|
555
|
+
new RegExp(pp.role.source ? pp.role.source : pp.role).test(rolle)) { treffer = pp; }
|
|
556
|
+
});
|
|
557
|
+
var hin = el('div', 'sugg');
|
|
558
|
+
if (treffer) {
|
|
559
|
+
hin.appendChild(document.createTextNode(tr('pattern.fitsOn')));
|
|
560
|
+
hin.appendChild(el('b', null, String(treffer.role)));
|
|
561
|
+
hin.appendChild(document.createTextNode(tr('pattern.toSlot', treffer.name, musterName(geraetetyp) || geraetetyp)));
|
|
562
|
+
} else {
|
|
563
|
+
hin.textContent = tr('pattern.noPlaceLong', musterName(geraetetyp) || geraetetyp);
|
|
564
|
+
}
|
|
565
|
+
rb.appendChild(hin);
|
|
566
|
+
}
|
|
567
|
+
return rb;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/* Typ, Einheit, Nachkommastellen, Beschriftung — in dieser Reihenfolge
|
|
571
|
+
und mit denselben Auswahlmoeglichkeiten wie am Geraet. */
|
|
572
|
+
function anzeigeBlock(hole, setze, platzhalter) {
|
|
573
|
+
var af = el('div', 'fields');
|
|
574
|
+
|
|
575
|
+
var selT = el('select', 'tx');
|
|
576
|
+
[['', '\u2014'], ['boolean', 'boolean'], ['number', 'number'],
|
|
577
|
+
['string', 'string'], ['mixed', 'mixed']].forEach(function (o) {
|
|
578
|
+
selT.appendChild(opt(o[0], o[1]));
|
|
579
|
+
});
|
|
580
|
+
selT.value = hole('typ') || '';
|
|
581
|
+
selT.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
582
|
+
selT.addEventListener('change', function () { setze('typ', selT.value); });
|
|
583
|
+
af.appendChild(dtFeld(tr('detail.type'), selT));
|
|
584
|
+
|
|
585
|
+
var iU = el('input', 'tx');
|
|
586
|
+
iU.type = 'text';
|
|
587
|
+
iU.style.width = '70px';
|
|
588
|
+
iU.value = hole('einheit') || '';
|
|
589
|
+
iU.placeholder = '\u2014';
|
|
590
|
+
iU.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
591
|
+
iU.addEventListener('change', function () { setze('einheit', iU.value); });
|
|
592
|
+
af.appendChild(dtFeld(tr('detail.unit'), iU));
|
|
593
|
+
|
|
594
|
+
var selD = el('select', 'tx');
|
|
595
|
+
[['', tr('detail.asDelivered')], ['0', '0'], ['1', '1'], ['2', '2'], ['3', '3']]
|
|
596
|
+
.forEach(function (o) { selD.appendChild(opt(o[0], o[1])); });
|
|
597
|
+
var dec = hole('dec');
|
|
598
|
+
selD.value = (dec === undefined || dec === null) ? '' : String(dec);
|
|
599
|
+
selD.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
600
|
+
selD.addEventListener('change', function () { setze('dec', selD.value); });
|
|
601
|
+
af.appendChild(dtFeld(tr('detail.decimals'), selD));
|
|
602
|
+
|
|
603
|
+
var iC = el('input', 'tx');
|
|
604
|
+
iC.type = 'text';
|
|
605
|
+
iC.style.width = '150px';
|
|
606
|
+
iC.value = hole('caption') || '';
|
|
607
|
+
iC.placeholder = platzhalter || '';
|
|
608
|
+
iC.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
609
|
+
iC.addEventListener('change', function () { setze('caption', iC.value); });
|
|
610
|
+
af.appendChild(dtFeld(tr('detail.caption'), iC));
|
|
611
|
+
|
|
612
|
+
return af;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* Zaehlt dieser Datenpunkt fuer die Erkennung? Nicht aus optional
|
|
616
|
+
abgelesen, sondern aus der Pflichtliste — die ist die Wahrheit. */
|
|
617
|
+
function istPflichtpunkt(v, z) {
|
|
618
|
+
var erf = (v.erkennung || {}).erforderlich || [];
|
|
619
|
+
return !!z.lesen && erf.indexOf(z.lesen) > -1;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/* lesenSonst darf ein Punktname, ein Objekt oder eine Liste sein — alle
|
|
623
|
+
drei Formen kommen in Vorlagen vor. Nach innen immer eine Liste. */
|
|
624
|
+
function ersatzListe(z) {
|
|
625
|
+
var s0 = z.lesenSonst;
|
|
626
|
+
if (!s0) { return []; }
|
|
627
|
+
var arr = Array.isArray(s0) ? s0 : [s0];
|
|
628
|
+
return arr.map(function (x) {
|
|
629
|
+
return (typeof x === 'string') ? { punkt: x } : x;
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Im Arbeitsstand bleibt eine noch leere Zeile stehen — sonst koennte
|
|
634
|
+
man nie eine anlegen, weil sie im selben Atemzug wieder verschwaende.
|
|
635
|
+
Weggeworfen wird sie erst beim Ablegen, in ohneUnterstrich. */
|
|
636
|
+
function schreibeErsatz(z, liste) {
|
|
637
|
+
if (!liste.length) { delete z.lesenSonst; return; }
|
|
638
|
+
z.lesenSonst = (liste.length === 1) ? liste[0] : liste;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function vZeilenDetail(z, nr) {
|
|
642
|
+
var v = vArbeitsstand();
|
|
643
|
+
var d = el('div', 'detail');
|
|
644
|
+
|
|
645
|
+
/* Genau der Aufbau des Detailblocks am Geraet: Name, liest aus,
|
|
646
|
+
schreibt nach, Formel, Schreibformel, Rolle, Anzeige. Nur was ein
|
|
647
|
+
Geraet braucht, fehlt hier — Rohwert, Ergebnis und die Auswahl aus
|
|
648
|
+
vorhandenen Objekten. Es gibt kein Geraet, an dem man das messen
|
|
649
|
+
koennte. */
|
|
650
|
+
var setz = function (feldname, wandeln) {
|
|
651
|
+
return function (x, wert) {
|
|
652
|
+
var zz = x.zustaende[nr];
|
|
653
|
+
var w = wandeln ? wandeln(wert) : wert;
|
|
654
|
+
if (w === '' || w === undefined || w === null) { delete zz[feldname]; }
|
|
655
|
+
else { zz[feldname] = w; }
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
var setzeFeld = function (feldname, wert, wandeln) {
|
|
659
|
+
vAendern(setz(feldname, wandeln), wert);
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
var eingabe = function (wert, feldname, breite, platzhalter, wandeln) {
|
|
663
|
+
var i = el('input', 'tx');
|
|
664
|
+
i.type = 'text';
|
|
665
|
+
i.value = wert === undefined || wert === null ? '' : String(wert);
|
|
666
|
+
if (breite) { i.style.width = breite; }
|
|
667
|
+
if (platzhalter) { i.placeholder = platzhalter; }
|
|
668
|
+
i.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
669
|
+
i.addEventListener('change', function () { setzeFeld(feldname, i.value, wandeln); });
|
|
670
|
+
return i;
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
dtZeile(d, tr('detail.name'), eingabe(z.name, 'name', '260px', tr('detail.namePlaceholder')));
|
|
674
|
+
|
|
675
|
+
/* --- liest aus, mit JSON-Feld daneben --- */
|
|
676
|
+
var qf = el('div', 'fields');
|
|
677
|
+
qf.appendChild(dtFeld(tr('detail.datapoint'),
|
|
678
|
+
eingabe(z.lesen, 'lesen', '260px', z.absolut ? '0_userdata.0.\u2026' : 'stat.POWER')));
|
|
679
|
+
qf.appendChild(dtFeld(tr('detail.jsonField'), eingabe(z.feld, 'feld', '150px', '\u2014')));
|
|
680
|
+
dtZeile(d, tr('detail.readsFrom'), qf);
|
|
681
|
+
|
|
682
|
+
/* --- Ersatzquellen ---
|
|
683
|
+
Dasselbe steht bei Tasmota oft an mehreren Stellen, und welche ein
|
|
684
|
+
Geraet gerade fuehrt, weiss man vorher nicht: die IP liegt in
|
|
685
|
+
tele.INFO2, in tele.STATE oder in stat.STATUS5. Die Werkbank nimmt
|
|
686
|
+
den ersten Weg, bei dem Objekt und Feld wirklich da sind. Ohne
|
|
687
|
+
dieses Feld liess sich das nur in der ausgegebenen Datei aendern. */
|
|
688
|
+
var sonstListe = ersatzListe(z);
|
|
689
|
+
|
|
690
|
+
var sb = el('div');
|
|
691
|
+
sonstListe.forEach(function (w, wi) {
|
|
692
|
+
var zeile = el('div', 'fields');
|
|
693
|
+
zeile.style.marginBottom = '5px';
|
|
694
|
+
|
|
695
|
+
var setzeErsatz = function (teil, wert) {
|
|
696
|
+
vAendern(function (x) {
|
|
697
|
+
var zz = x.zustaende[nr];
|
|
698
|
+
var liste = ersatzListe(zz);
|
|
699
|
+
if (!liste[wi]) { return; }
|
|
700
|
+
if (wert === '') { delete liste[wi][teil]; } else { liste[wi][teil] = wert; }
|
|
701
|
+
if (!liste[wi].punkt) { liste.splice(wi, 1); }
|
|
702
|
+
schreibeErsatz(zz, liste);
|
|
703
|
+
}, wert);
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
var ip1 = el('input', 'tx');
|
|
707
|
+
ip1.type = 'text'; ip1.value = w.punkt || ''; ip1.style.width = '210px';
|
|
708
|
+
ip1.placeholder = 'tele.STATE';
|
|
709
|
+
ip1.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
710
|
+
ip1.addEventListener('change', function () { setzeErsatz('punkt', ip1.value); });
|
|
711
|
+
zeile.appendChild(dtFeld(tr('detail.datapoint'), ip1));
|
|
712
|
+
|
|
713
|
+
var ip2 = el('input', 'tx');
|
|
714
|
+
ip2.type = 'text'; ip2.value = w.feld || ''; ip2.style.width = '150px';
|
|
715
|
+
ip2.placeholder = '—';
|
|
716
|
+
ip2.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
717
|
+
ip2.addEventListener('change', function () { setzeErsatz('feld', ip2.value); });
|
|
718
|
+
zeile.appendChild(dtFeld(tr('detail.jsonField'), ip2));
|
|
719
|
+
|
|
720
|
+
var ip3 = el('input', 'tx');
|
|
721
|
+
ip3.type = 'text'; ip3.value = w.leseformel || ''; ip3.style.width = '230px';
|
|
722
|
+
ip3.placeholder = tr('detail.formulaPlaceholder');
|
|
723
|
+
ip3.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
724
|
+
ip3.addEventListener('change', function () { setzeErsatz('leseformel', ip3.value); });
|
|
725
|
+
zeile.appendChild(dtFeld(tr('detail.formula'), ip3));
|
|
726
|
+
|
|
727
|
+
var x1 = el('button', 'btn schmal', tr('tv.removeFallback'));
|
|
728
|
+
x1.type = 'button';
|
|
729
|
+
x1.addEventListener('click', function (ev) {
|
|
730
|
+
ev.stopPropagation();
|
|
731
|
+
vAendern(function (x) {
|
|
732
|
+
var zz = x.zustaende[nr];
|
|
733
|
+
var liste = ersatzListe(zz);
|
|
734
|
+
liste.splice(wi, 1);
|
|
735
|
+
schreibeErsatz(zz, liste);
|
|
736
|
+
}, null);
|
|
737
|
+
});
|
|
738
|
+
zeile.appendChild(dtFeld(' ', x1));
|
|
739
|
+
|
|
740
|
+
sb.appendChild(zeile);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
var plus = el('button', 'btn schmal', tr('tv.addFallback'));
|
|
744
|
+
plus.type = 'button';
|
|
745
|
+
plus.addEventListener('click', function (ev) {
|
|
746
|
+
ev.stopPropagation();
|
|
747
|
+
vAendern(function (x) {
|
|
748
|
+
var zz = x.zustaende[nr];
|
|
749
|
+
var liste = ersatzListe(zz);
|
|
750
|
+
liste.push({ punkt: '' });
|
|
751
|
+
schreibeErsatz(zz, liste);
|
|
752
|
+
}, null);
|
|
753
|
+
});
|
|
754
|
+
sb.appendChild(plus);
|
|
755
|
+
sb.appendChild(el('div', 'sugg', tr('tv.fallbackHint')));
|
|
756
|
+
dtZeile(d, tr('tv.fallback'), sb);
|
|
757
|
+
|
|
758
|
+
dtZeile(d, tr('detail.writesTo'),
|
|
759
|
+
eingabe(z.schreiben, 'schreiben', '260px', tr('detail.noWrite')));
|
|
760
|
+
|
|
761
|
+
var fb = el('div');
|
|
762
|
+
fb.appendChild(eingabe(z.leseformel, 'leseformel', '100%', tr('detail.formulaPlaceholder')));
|
|
763
|
+
if ((z.typ || '') === 'boolean') {
|
|
764
|
+
fb.appendChild(el('div', 'sugg', tr('detail.formulaHint')));
|
|
765
|
+
}
|
|
766
|
+
dtZeile(d, tr('detail.formula'), fb);
|
|
767
|
+
|
|
768
|
+
if (z.schreiben) {
|
|
769
|
+
var wb = el('div');
|
|
770
|
+
wb.appendChild(eingabe(z.schreibformel, 'schreibformel', '100%', tr('detail.writeFormulaPlaceholder')));
|
|
771
|
+
wb.appendChild(el('div', 'sugg', tr('detail.writeFormulaHint')));
|
|
772
|
+
dtZeile(d, tr('detail.writeFormula'), wb);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
dtZeile(d, tr('detail.role'), rollenBlock(z.rolle, v.geraetetyp, function (neu) {
|
|
776
|
+
setzeFeld('rolle', neu);
|
|
777
|
+
}));
|
|
778
|
+
|
|
779
|
+
dtZeile(d, tr('detail.display'), anzeigeBlock(
|
|
780
|
+
function (was) {
|
|
781
|
+
if (was === 'einheit') { return z.einheit; }
|
|
782
|
+
if (was === 'dec') { return z.nachkommastellen; }
|
|
783
|
+
if (was === 'caption') { return z.beschriftung; }
|
|
784
|
+
return z.typ;
|
|
785
|
+
},
|
|
786
|
+
function (was, wert) {
|
|
787
|
+
if (was === 'einheit') { return setzeFeld('einheit', wert); }
|
|
788
|
+
if (was === 'dec') { return setzeFeld('nachkommastellen', wert, function (w) { return w === '' ? '' : Number(w); }); }
|
|
789
|
+
if (was === 'caption') { return setzeFeld('beschriftung', wert); }
|
|
790
|
+
setzeFeld('typ', wert);
|
|
791
|
+
}, z.name));
|
|
792
|
+
|
|
793
|
+
var haken = function (label, an, tut) {
|
|
794
|
+
var l = el('label', 'fld');
|
|
795
|
+
l.style.flexDirection = 'row';
|
|
796
|
+
l.style.alignItems = 'center';
|
|
797
|
+
l.style.gap = '5px';
|
|
798
|
+
var c = document.createElement('input');
|
|
799
|
+
c.type = 'checkbox';
|
|
800
|
+
c.checked = an;
|
|
801
|
+
c.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
|
802
|
+
c.addEventListener('change', function () { vAendern(function (x) { tut(x.zustaende[nr], c.checked); }); });
|
|
803
|
+
l.appendChild(c);
|
|
804
|
+
l.appendChild(el('span', null, label));
|
|
805
|
+
return l;
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
var zeile2 = el('div', 'fields');
|
|
809
|
+
/* Beide Haken laufen in dieselbe Richtung wie im Speichern-Dialog:
|
|
810
|
+
angehakt heisst „ja". Vorher stand hier „freiwillig" und „Vorgabe
|
|
811
|
+
aus" — also zweimal die Umkehrung, was niemand zusammenbringt. */
|
|
812
|
+
zeile2.appendChild(haken(tr('tpls.colDefault'), !z.vorgabeAus,
|
|
813
|
+
function (zz, an) { if (an) { delete zz.vorgabeAus; } else { zz.vorgabeAus = true; } }));
|
|
814
|
+
|
|
815
|
+
/* Ein Pflichtpunkt ist zweierlei in einem: er darf beim Anwenden
|
|
816
|
+
nicht fehlen (optional) und er entscheidet ueber die Erkennung
|
|
817
|
+
(erforderlich). Der Haken haelt beides zusammen — sonst koennten
|
|
818
|
+
Liste und Zeile Verschiedenes behaupten. */
|
|
819
|
+
zeile2.appendChild(haken(tr('tpls.colRequired'), istPflichtpunkt(v, z), function (zz, an) {
|
|
820
|
+
var w = vArbeitsstand();
|
|
821
|
+
w.erkennung = w.erkennung || {};
|
|
822
|
+
var liste = (w.erkennung.erforderlich || []).slice();
|
|
823
|
+
var pfad = zz.lesen;
|
|
824
|
+
var i = liste.indexOf(pfad);
|
|
825
|
+
if (an) {
|
|
826
|
+
delete zz.optional;
|
|
827
|
+
if (pfad && i === -1) { liste.push(pfad); }
|
|
828
|
+
if (zz.schreiben && liste.indexOf(zz.schreiben) === -1) { liste.push(zz.schreiben); }
|
|
829
|
+
} else {
|
|
830
|
+
zz.optional = true;
|
|
831
|
+
if (i > -1) { liste.splice(i, 1); }
|
|
832
|
+
var j = zz.schreiben ? liste.indexOf(zz.schreiben) : -1;
|
|
833
|
+
if (j > -1) { liste.splice(j, 1); }
|
|
834
|
+
}
|
|
835
|
+
w.erkennung.erforderlich = liste;
|
|
836
|
+
}));
|
|
837
|
+
|
|
838
|
+
zeile2.appendChild(haken(tr('tpls.absolute'), !!z.absolut,
|
|
839
|
+
function (zz, an) { if (an) { zz.absolut = true; } else { delete zz.absolut; } }));
|
|
840
|
+
var wKnopf = el('button', 'btn schmal', tr('tv.removeState'));
|
|
841
|
+
wKnopf.addEventListener('click', function () {
|
|
842
|
+
vAendern(function (x) { x.zustaende.splice(nr, 1); vOffeneZeile = null; });
|
|
843
|
+
});
|
|
844
|
+
zeile2.appendChild(wKnopf);
|
|
845
|
+
dtZeile(d, tr('tv.inTemplate'), zeile2);
|
|
846
|
+
|
|
847
|
+
var hw = el('div', 'sugg');
|
|
848
|
+
hw.textContent = tr('tv.flagsHelp');
|
|
849
|
+
dtZeile(d, '', hw);
|
|
850
|
+
return d;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function vDuplizieren(v, gleicheKennung) {
|
|
854
|
+
var k = JSON.parse(JSON.stringify(ohneUnterstrich(v)));
|
|
855
|
+
if (gleicheKennung) {
|
|
856
|
+
/* Uebernehmen statt abwandeln: gleiche Kennung, eine Version
|
|
857
|
+
weiter — damit verdeckt sie die mitgelieferte. */
|
|
858
|
+
k.version = (k.version || 1) + 1;
|
|
859
|
+
} else {
|
|
860
|
+
k.id = freierSchluessel(k.id + '-kopie');
|
|
861
|
+
k.version = 1;
|
|
862
|
+
k.rang = eigenerMaxRang() + 10;
|
|
863
|
+
k.name = { de: textIn(k.name, 'de') + ' (Kopie)', en: textIn(k.name, 'en') + ' (copy)' };
|
|
864
|
+
}
|
|
865
|
+
if (typeof k.name === 'string') { k.name = { de: k.name, en: k.name }; }
|
|
866
|
+
vEinlesen = null;
|
|
867
|
+
vEntwurf = k;
|
|
868
|
+
vEntwurf._quelle = 'benutzer';
|
|
869
|
+
vGewaehlt = k.id;
|
|
870
|
+
vLoeschFrage = false;
|
|
871
|
+
zeichneVorlagenListe();
|
|
872
|
+
zeichneErgebnis();
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/* Ausgeben als Datei — dasselbe Format wie admin/vorlagen/*.json, damit
|
|
876
|
+
eine gut gewordene Vorlage ohne Umbau ins Paket wandern kann. */
|
|
877
|
+
function vAusgeben(v) {
|
|
878
|
+
var text = JSON.stringify(ohneUnterstrich(v), null, 2) + '\n';
|
|
879
|
+
var a = document.createElement('a');
|
|
880
|
+
a.href = URL.createObjectURL(new Blob([text], { type: 'application/json' }));
|
|
881
|
+
a.download = v.id + '.json';
|
|
882
|
+
document.body.appendChild(a);
|
|
883
|
+
a.click();
|
|
884
|
+
setTimeout(function () { URL.revokeObjectURL(a.href); a.remove(); }, 2000);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
export function vEinlesenDatei(datei) {
|
|
888
|
+
var leser = new FileReader();
|
|
889
|
+
leser.onload = function () {
|
|
890
|
+
var roh;
|
|
891
|
+
try { roh = JSON.parse(String(leser.result)); } catch { roh = null; }
|
|
892
|
+
var fehler = vPruefeEingelesen(roh);
|
|
893
|
+
if (fehler) {
|
|
894
|
+
vEinlesen = null;
|
|
895
|
+
var host = $('#res');
|
|
896
|
+
host.textContent = '';
|
|
897
|
+
var w = el('div', 'aside w');
|
|
898
|
+
w.appendChild(el('b', null, tr('tv.importBad')));
|
|
899
|
+
w.appendChild(document.createTextNode(' ' + fehler));
|
|
900
|
+
host.appendChild(w);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
vEinlesen = ohneUnterstrich(roh);
|
|
904
|
+
vEinlesen._quelle = 'benutzer';
|
|
905
|
+
vEntwurf = null;
|
|
906
|
+
vLoeschFrage = false;
|
|
907
|
+
zeichneVorlagenListe();
|
|
908
|
+
zeichneErgebnis();
|
|
909
|
+
};
|
|
910
|
+
leser.readAsText(datei);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function vPruefeEingelesen(o) {
|
|
914
|
+
if (!o || typeof o !== 'object' || Array.isArray(o)) { return tr('tv.badNotObject'); }
|
|
915
|
+
if (!o.id || typeof o.id !== 'string') { return tr('tv.badNoId'); }
|
|
916
|
+
if (!Array.isArray(o.zustaende) || !o.zustaende.length) { return tr('tv.badNoStates'); }
|
|
917
|
+
var schlecht = o.zustaende.filter(function (z) { return !z || !z.name || !z.lesen; });
|
|
918
|
+
if (schlecht.length) { return tr('tv.badState', schlecht.length); }
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function vSpeichern() {
|
|
923
|
+
var v = vArbeitsstand();
|
|
924
|
+
if (!v) { return; }
|
|
925
|
+
var rein = ohneUnterstrich(v);
|
|
926
|
+
|
|
927
|
+
/* Aendert sich der Inhalt, zaehlt die Version weiter. Sonst traegt
|
|
928
|
+
jedes Geraet fuer immer „vorlageVersion: 1" und gilt als aktuell —
|
|
929
|
+
ein spaeteres Nachziehen haette nie etwas zu melden. Das Duplizieren
|
|
930
|
+
zaehlte schon hoch, das Speichern nicht. */
|
|
931
|
+
var alt = vorlageMitId(rein.id);
|
|
932
|
+
if (alt) {
|
|
933
|
+
var ohneV = function (x) {
|
|
934
|
+
var k = JSON.parse(JSON.stringify(ohneUnterstrich(x)));
|
|
935
|
+
delete k.version;
|
|
936
|
+
return JSON.stringify(k);
|
|
937
|
+
};
|
|
938
|
+
/* Nie unter das fallen, was der Arbeitsstand schon traegt: beim
|
|
939
|
+
Duplizieren einer mitgelieferten Vorlage steht dort bereits eine
|
|
940
|
+
Version hoeher, damit die Kopie das Original verdeckt. Wuerde man
|
|
941
|
+
stumpf die gespeicherte uebernehmen, faende die Kopie zurueck auf
|
|
942
|
+
die Version des Pakets. */
|
|
943
|
+
var vorschlag = Number(rein.version) || 1;
|
|
944
|
+
var altV = Number(alt.version) || 1;
|
|
945
|
+
rein.version = (ohneV(alt) !== ohneV(rein))
|
|
946
|
+
? Math.max(vorschlag, altV + 1)
|
|
947
|
+
: Math.max(vorschlag, altV);
|
|
948
|
+
}
|
|
949
|
+
aendereVorlagen(rein.id, rein, function (err) {
|
|
950
|
+
if (err) { return vMeldung(tr('tpls.failed') + ' ' + err, true); }
|
|
951
|
+
ladeVorlagen(function () {
|
|
952
|
+
vEinlesen = null; vEntwurf = null; vGewaehlt = rein.id;
|
|
953
|
+
setzeMeta();
|
|
954
|
+
zeichneVorlagenListe();
|
|
955
|
+
zeichneErgebnis();
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function vLoeschen(id) {
|
|
961
|
+
aendereVorlagen(id, null, function (err) {
|
|
962
|
+
if (err) { return vMeldung(tr('tpls.failed') + ' ' + err, true); }
|
|
963
|
+
ladeVorlagen(function () {
|
|
964
|
+
vEinlesen = null; vEntwurf = null; vLoeschFrage = false;
|
|
965
|
+
vGewaehlt = vorlageMitId(id) ? id : null;
|
|
966
|
+
setzeMeta();
|
|
967
|
+
zeichneVorlagenListe();
|
|
968
|
+
zeichneErgebnis();
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function vMeldung(text, schlecht) {
|
|
974
|
+
var host = $('#res');
|
|
975
|
+
host.textContent = '';
|
|
976
|
+
var w = el('div', 'aside' + (schlecht ? ' w' : ''));
|
|
977
|
+
w.appendChild(el('b', null, text));
|
|
978
|
+
host.appendChild(w);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/* ================== Aus einem Geraet eine Vorlage machen ==================
|
|
982
|
+
Die Gegenrichtung zu wendeAn. Ableitbar ist, welche Punkte benutzt
|
|
983
|
+
werden und wie sie gelesen werden. Nicht ableitbar ist, welche davon
|
|
984
|
+
ein Geraet zu diesem Geraetetyp machen und woran man es am Namen
|
|
985
|
+
erkennt — danach fragt der Dialog. */
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
export function textIn(n, l) {
|
|
989
|
+
if (!n) { return ''; }
|
|
990
|
+
if (typeof n === 'string') { return n; }
|
|
991
|
+
return n[l] || n.en || n.de || '';
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function relZu(id, kanal) {
|
|
995
|
+
if (id && id.indexOf(kanal + '.') === 0) { return { p: kurz(id, kanal), abs: false }; }
|
|
996
|
+
return { p: id || '', abs: true };
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/* stat.POWER1 → stat.POWER%N%. Ersetzt wird nur am Ende eines
|
|
1000
|
+
Abschnitts und nur, wenn davor noch etwas steht — sonst wuerde aus
|
|
1001
|
+
ENERGY.Power beim Speichern Unsinn. */
|
|
1002
|
+
function zuPlatzhalter(pfad, inst) {
|
|
1003
|
+
var i = (inst === null || inst === undefined) ? '' : String(inst);
|
|
1004
|
+
if (!i) { return pfad; }
|
|
1005
|
+
return String(pfad).split('.').map(function (t) {
|
|
1006
|
+
return (t.length > i.length && t.slice(-i.length) === i) ? t.slice(0, -i.length) + '%N%' : t;
|
|
1007
|
+
}).join('.');
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/* Welcher Pfad steht am Ende in der Vorlage? Bei einem Mehrfachgeraet
|
|
1011
|
+
tritt der Platzhalter an die Stelle der Ausgangsnummer. */
|
|
1012
|
+
function zPfad(r, k, schreibend) {
|
|
1013
|
+
var roh = schreibend ? r.schreibenRoh : r.lesenRoh;
|
|
1014
|
+
var abs = schreibend ? r.schreibenAbs : r.lesenAbs;
|
|
1015
|
+
if (!roh) { return ''; }
|
|
1016
|
+
if (!k.mehrfach || abs || !k.instanz) { return roh; }
|
|
1017
|
+
return zuPlatzhalter(roh, k.instanz);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/* Endet die Nummer eines Ausgangs in den Pfaden? Haeufigste Zahl am
|
|
1021
|
+
Ende eines Abschnitts gewinnt — cmnd.POWER1, stat.POWER1 sagen 1. */
|
|
1022
|
+
function rateInstanz(zeilen, geraetName) {
|
|
1023
|
+
var zaehler = {};
|
|
1024
|
+
zeilen.forEach(function (r) {
|
|
1025
|
+
if (r.lesenAbs) { return; }
|
|
1026
|
+
String(r.lesenRoh).split('.').forEach(function (t) {
|
|
1027
|
+
var m = /^(.*[^0-9])([0-9]{1,2})$/.exec(t);
|
|
1028
|
+
if (m) { zaehler[m[2]] = (zaehler[m[2]] || 0) + 1; }
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
var mn = /^(.*[^0-9])([0-9]{1,2})$/.exec(geraetName || '');
|
|
1032
|
+
if (mn) { zaehler[mn[2]] = (zaehler[mn[2]] || 0) + 1; }
|
|
1033
|
+
var beste = '', wie = 0;
|
|
1034
|
+
Object.keys(zaehler).forEach(function (n) {
|
|
1035
|
+
if (zaehler[n] > wie || (zaehler[n] === wie && Number(n) < Number(beste))) {
|
|
1036
|
+
beste = n; wie = zaehler[n];
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
return beste;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/* Eine Mehrfachvorlage beschreibt genau einen Ausgang — die anderen
|
|
1043
|
+
entstehen daraus. Zeigt der Entwurf noch auf POWER2, POWER3 …, waere
|
|
1044
|
+
die Vorlage an dieses eine Geraet gefesselt. Also einsammeln und
|
|
1045
|
+
sagen, statt still etwas Falsches zu speichern. */
|
|
1046
|
+
function andereAusgaenge(zeilen, instanz) {
|
|
1047
|
+
var raus = { punkte: [], nummern: [] };
|
|
1048
|
+
if (!instanz) { return raus; }
|
|
1049
|
+
zeilen.forEach(function (r) {
|
|
1050
|
+
if (r.weg || r.lesenAbs) { return; }
|
|
1051
|
+
var fremd = null;
|
|
1052
|
+
String(r.lesenRoh).split('.').forEach(function (t) {
|
|
1053
|
+
var m = /^(.*[^0-9])([0-9]{1,2})$/.exec(t);
|
|
1054
|
+
if (m && m[2] !== String(instanz)) { fremd = m[2]; }
|
|
1055
|
+
});
|
|
1056
|
+
if (fremd) {
|
|
1057
|
+
raus.punkte.push(r.name);
|
|
1058
|
+
if (raus.nummern.indexOf(fremd) === -1) { raus.nummern.push(fremd); }
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
raus.nummern.sort(function (a, b) { return Number(a) - Number(b); });
|
|
1062
|
+
return raus;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
function schluesselAus(text) {
|
|
1066
|
+
return String(text || '').toLowerCase()
|
|
1067
|
+
.split('ä').join('ae').split('ö').join('oe').split('ü').join('ue').split('ß').join('ss')
|
|
1068
|
+
.replace(/[^a-z0-9]+/g, '-').replace(/^-+/, '').replace(/-+$/, '') || 'vorlage';
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/* Der Rang entscheidet nur zwischen Vorlagen derselben Herkunft — der
|
|
1072
|
+
Zuschlag fuer „eigene" liegt in der Bewertung ueber jedem moeglichen
|
|
1073
|
+
Rang. Deshalb wird beim Vergeben auch nur im eigenen Zahlenraum
|
|
1074
|
+
gezaehlt: sonst schoebe eine mitgelieferte Vorlage mit hohem Rang die
|
|
1075
|
+
Nummern des Benutzers mit jedem Update vor sich her, ohne dass sich am
|
|
1076
|
+
Verhalten irgendetwas aenderte. */
|
|
1077
|
+
function eigenerMaxRang() {
|
|
1078
|
+
var m = 0;
|
|
1079
|
+
S.VORLAGEN.forEach(function (v) {
|
|
1080
|
+
if (v._quelle === 'benutzer' && (v.rang || 0) > m) { m = v.rang || 0; }
|
|
1081
|
+
});
|
|
1082
|
+
return m;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function freierSchluessel(basis) {
|
|
1086
|
+
var da = {};
|
|
1087
|
+
S.VORLAGEN.forEach(function (v) { da[v.id] = 1; });
|
|
1088
|
+
if (!da[basis]) { return basis; }
|
|
1089
|
+
for (var i = 2; i < 99; i++) { if (!da[basis + '-' + i]) { return basis + '-' + i; } }
|
|
1090
|
+
return basis + '-neu';
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
export function vorlagenVorbereiten(e) {
|
|
1094
|
+
var quelle = null;
|
|
1095
|
+
S.VORLAGEN.forEach(function (v) { if (v.id === e.vorlage) { quelle = v; } });
|
|
1096
|
+
var maxRang = eigenerMaxRang();
|
|
1097
|
+
|
|
1098
|
+
var mehrfach = !!(quelle && quelle.mehrfach);
|
|
1099
|
+
var inst = mehrfach ? e.instanz : null;
|
|
1100
|
+
|
|
1101
|
+
var zeilen = [];
|
|
1102
|
+
e.states.forEach(function (s) {
|
|
1103
|
+
if (!s.n || !s.srcR) { return; }
|
|
1104
|
+
var r = relZu(s.srcR, e.kanal);
|
|
1105
|
+
var w = (s.srcW && s.srcW !== s.srcR) ? relZu(s.srcW, e.kanal) : null;
|
|
1106
|
+
var zv = null;
|
|
1107
|
+
if (quelle && s.ausVorlage) {
|
|
1108
|
+
(quelle.zustaende || []).forEach(function (z) { if (z.name === s.ausVorlage) { zv = z; } });
|
|
1109
|
+
}
|
|
1110
|
+
zeilen.push({
|
|
1111
|
+
s: s,
|
|
1112
|
+
name: s.n,
|
|
1113
|
+
/* Roh heisst: relativ zum Geraet, aber noch mit der konkreten
|
|
1114
|
+
Ausgangsnummer. Der Platzhalter wird erst gesetzt, wenn er
|
|
1115
|
+
gebraucht wird — sonst liesse sich der Haken „mehrere
|
|
1116
|
+
Ausgaenge" nicht mehr umlegen. */
|
|
1117
|
+
lesenRoh: r.p,
|
|
1118
|
+
lesenAbs: r.abs,
|
|
1119
|
+
schreibenRoh: w ? w.p : '',
|
|
1120
|
+
schreibenAbs: w ? w.abs : false,
|
|
1121
|
+
aus: !s.on,
|
|
1122
|
+
/* Vorschlag: was einen Schreibweg hat, traegt das Geraet. Was aus
|
|
1123
|
+
einer Vorlage kam, erbt deren Einstufung. Von Hand ergaenzte
|
|
1124
|
+
Punkte bleiben freiwillig — jeder Pflichtpunkt macht die
|
|
1125
|
+
Vorlage genauer und zugleich sproeder. */
|
|
1126
|
+
pflicht: r.abs ? false : (zv ? !zv.optional : !!w)
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
var inhalte = [];
|
|
1131
|
+
zeilen.forEach(function (z) {
|
|
1132
|
+
var f = feldAusFormel(z.s.f);
|
|
1133
|
+
if (!f || z.lesenAbs) { return; }
|
|
1134
|
+
var oben = f.split('.')[0];
|
|
1135
|
+
var da = false;
|
|
1136
|
+
inhalte.forEach(function (x) { if (x.punkt === z.lesenRoh && x.feld === oben) { da = true; } });
|
|
1137
|
+
if (!da) { inhalte.push({ punkt: z.lesenRoh, feld: oben, an: false }); }
|
|
1138
|
+
});
|
|
1139
|
+
if (quelle && quelle.erkennung && quelle.erkennung.inhalt) {
|
|
1140
|
+
Object.keys(quelle.erkennung.inhalt).forEach(function (p) {
|
|
1141
|
+
var da = false;
|
|
1142
|
+
inhalte.forEach(function (x) {
|
|
1143
|
+
if (x.punkt === p) { da = true; x.an = true; x.feld = quelle.erkennung.inhalt[p]; }
|
|
1144
|
+
});
|
|
1145
|
+
if (!da) { inhalte.push({ punkt: p, feld: quelle.erkennung.inhalt[p], an: true }); }
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
var geraetName = (e.kanal || '').split('.').pop();
|
|
1150
|
+
return {
|
|
1151
|
+
e: e,
|
|
1152
|
+
zeilen: zeilen,
|
|
1153
|
+
inhalte: inhalte,
|
|
1154
|
+
kopf: {
|
|
1155
|
+
modus: quelle ? 'update' : 'neu',
|
|
1156
|
+
quelle: quelle,
|
|
1157
|
+
id: quelle ? quelle.id : freierSchluessel(schluesselAus(geraetName)),
|
|
1158
|
+
nameDe: quelle ? textIn(quelle.name, 'de') : geraetName,
|
|
1159
|
+
nameEn: quelle ? textIn(quelle.name, 'en') : geraetName,
|
|
1160
|
+
geraetetyp: e.want || (quelle ? quelle.geraetetyp : '') || '',
|
|
1161
|
+
/* Was die Vorlage schon sagt, gilt; sonst das, was am Geraet
|
|
1162
|
+
eingestellt ist. Wer eine Vorlage aus einem Geraet baut, hat die
|
|
1163
|
+
Funktion dort meist gerade gesetzt - sie noch einmal auswaehlen
|
|
1164
|
+
zu muessen waere Arbeit ohne Gewinn. */
|
|
1165
|
+
funktion: (quelle && quelle.funktion) || kennungZuFunktion(e.funktion) || '',
|
|
1166
|
+
namenshinweis: quelle ? ((quelle.erkennung || {}).namenshinweis || '') : '',
|
|
1167
|
+
rang: quelle ? (quelle.rang || 0) : maxRang + 10,
|
|
1168
|
+
maxRang: maxRang,
|
|
1169
|
+
mehrfach: mehrfach,
|
|
1170
|
+
instanz: mehrfach ? String(inst === null || inst === undefined ? '' : inst)
|
|
1171
|
+
: rateInstanz(zeilen, geraetName),
|
|
1172
|
+
kanalname: (quelle && quelle.kanalname) ? quelle.kanalname : 'POWER%N%',
|
|
1173
|
+
verboten: quelle ? (((quelle.erkennung || {}).verboten) || []).join(', ') : '',
|
|
1174
|
+
geraetName: geraetName
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/* Der Entwurf als Vorlagenobjekt — genau das, was gespeichert wird. */
|
|
1180
|
+
export function baueVorlage(z) {
|
|
1181
|
+
var k = z.kopf;
|
|
1182
|
+
var v = {
|
|
1183
|
+
id: k.id,
|
|
1184
|
+
version: k.modus === 'update' && k.quelle ? (k.quelle.version || 1) + 1 : 1,
|
|
1185
|
+
rang: Number(k.rang) || 0,
|
|
1186
|
+
name: { de: k.nameDe || k.id, en: k.nameEn || k.nameDe || k.id },
|
|
1187
|
+
geraetetyp: k.geraetetyp || undefined,
|
|
1188
|
+
funktion: k.funktion || undefined,
|
|
1189
|
+
erkennung: {}
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
var erf = [];
|
|
1193
|
+
z.zeilen.forEach(function (r) {
|
|
1194
|
+
if (r.weg || !r.pflicht || r.lesenAbs) { return; }
|
|
1195
|
+
var lp = zPfad(r, k, false);
|
|
1196
|
+
var sp = zPfad(r, k, true);
|
|
1197
|
+
if (erf.indexOf(lp) === -1) { erf.push(lp); }
|
|
1198
|
+
if (sp && !r.schreibenAbs && erf.indexOf(sp) === -1) { erf.push(sp); }
|
|
1199
|
+
});
|
|
1200
|
+
v.erkennung.erforderlich = erf;
|
|
1201
|
+
|
|
1202
|
+
var verb = String(k.verboten || '').split(',').map(function (x) { return x.trim(); })
|
|
1203
|
+
.filter(function (x) { return !!x; });
|
|
1204
|
+
/* Ein Punkt kann nicht zugleich Pflicht und verboten sein — so eine
|
|
1205
|
+
Vorlage passt auf gar nichts. Der Widerspruch entsteht leicht: man
|
|
1206
|
+
traegt unten „darf es nicht geben" ein und vergisst, dass derselbe
|
|
1207
|
+
Punkt oben schon „muss da sein" ist. */
|
|
1208
|
+
v.widerspruch = verb.filter(function (x) { return erf.indexOf(x) !== -1; });
|
|
1209
|
+
if (verb.length) { v.erkennung.verboten = verb; }
|
|
1210
|
+
|
|
1211
|
+
var inh = {};
|
|
1212
|
+
z.inhalte.forEach(function (x) { if (x.an) { inh[x.punkt] = x.feld; } });
|
|
1213
|
+
if (Object.keys(inh).length) { v.erkennung.inhalt = inh; }
|
|
1214
|
+
if (k.namenshinweis) { v.erkennung.namenshinweis = k.namenshinweis; }
|
|
1215
|
+
|
|
1216
|
+
if (k.mehrfach && k.instanz) {
|
|
1217
|
+
v.mehrfach = { platzhalter: '%N%' };
|
|
1218
|
+
v.kanalname = k.kanalname || 'POWER%N%';
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
v.zustaende = z.zeilen.filter(function (r) { return !r.weg; }).map(function (r) {
|
|
1222
|
+
var s = r.s;
|
|
1223
|
+
var f = feldAusFormel(s.f);
|
|
1224
|
+
var zu = {
|
|
1225
|
+
name: r.name,
|
|
1226
|
+
rolle: s.role || undefined,
|
|
1227
|
+
typ: s.typ || undefined,
|
|
1228
|
+
einheit: s.unit || undefined,
|
|
1229
|
+
lesen: zPfad(r, k, false)
|
|
1230
|
+
};
|
|
1231
|
+
if (r.lesenAbs) { zu.absolut = true; }
|
|
1232
|
+
if (r.schreibenRoh) { zu.schreiben = zPfad(r, k, true); }
|
|
1233
|
+
if (f) { zu.feld = f; } else if (s.f) { zu.leseformel = s.f; }
|
|
1234
|
+
if (s.fw && r.schreibenRoh) { zu.schreibformel = s.fw; }
|
|
1235
|
+
if (!r.pflicht) { zu.optional = true; }
|
|
1236
|
+
if (r.aus) { zu.vorgabeAus = true; }
|
|
1237
|
+
if (s.caption) { zu.beschriftung = s.caption; }
|
|
1238
|
+
if (s.dec !== undefined && s.dec !== '') { zu.nachkommastellen = Number(s.dec); }
|
|
1239
|
+
if (s.states) { zu.werteliste = s.states; }
|
|
1240
|
+
if (s.hinweis) { zu.hinweis = s.hinweis; }
|
|
1241
|
+
/* Was die Vorlage sonst noch als Lesequelle kannte. Der gerade
|
|
1242
|
+
genommene Weg steht schon in `lesen` und faellt hier weg. */
|
|
1243
|
+
if (s.wege && s.wege.length > 1) {
|
|
1244
|
+
var rest = s.wege.filter(function (w) { return w.punkt !== zu.lesen; })
|
|
1245
|
+
.map(function (w) {
|
|
1246
|
+
var o = { punkt: w.punkt };
|
|
1247
|
+
if (w.feld) { o.feld = w.feld; }
|
|
1248
|
+
if (w.formel) { o.formel = w.formel; }
|
|
1249
|
+
return o;
|
|
1250
|
+
});
|
|
1251
|
+
if (rest.length) { zu.lesenSonst = (rest.length === 1) ? rest[0] : rest; }
|
|
1252
|
+
}
|
|
1253
|
+
return zu;
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
/* Das Rollenwissen: alles, was am Geraet liegt und keine Zeile der
|
|
1257
|
+
Vorlage ist, wandert als Pfad→Rolle mit. Der Vorschlag ignoriert
|
|
1258
|
+
es; die Ansicht „Alle Datenpunkte des Geraets" liest daraus die
|
|
1259
|
+
Rollen, die hm-rpc am Geraet leer laesst. Rangfolge je Punkt: was
|
|
1260
|
+
die aktuelle Zeile sagt (Handarbeit im Entwurf), dann das
|
|
1261
|
+
gepflegte Wissen der Ursprungsvorlage, zuletzt die Geraeterolle.
|
|
1262
|
+
Ohne Rolle kein Eintrag - leeres Wissen hilft niemandem. */
|
|
1263
|
+
var zeilenQuellen = {};
|
|
1264
|
+
z.zeilen.forEach(function (r) {
|
|
1265
|
+
if (r.weg) { return; }
|
|
1266
|
+
if (r.lesenRoh && !r.lesenAbs) { zeilenQuellen[r.lesenRoh] = true; }
|
|
1267
|
+
if (r.schreibenRoh && !r.schreibenAbs) { zeilenQuellen[r.schreibenRoh] = true; }
|
|
1268
|
+
});
|
|
1269
|
+
var altesWissen = (k.modus === 'update' && k.quelle && k.quelle.weiterePunkte) || {};
|
|
1270
|
+
var wissen = {};
|
|
1271
|
+
kindZustaende(z.e.kanal).forEach(function (id) {
|
|
1272
|
+
var rel = relZu(id, z.e.kanal);
|
|
1273
|
+
if (rel.abs || zeilenQuellen[rel.p]) { return; }
|
|
1274
|
+
var zeilenRolle = '';
|
|
1275
|
+
z.e.states.forEach(function (s0) {
|
|
1276
|
+
if (!zeilenRolle && s0.srcR === id && s0.role) { zeilenRolle = s0.role; }
|
|
1277
|
+
});
|
|
1278
|
+
var o0 = S.objects[id];
|
|
1279
|
+
var rolle = zeilenRolle || altesWissen[rel.p] ||
|
|
1280
|
+
((o0 && o0.common && o0.common.role) || '');
|
|
1281
|
+
if (rolle) { wissen[rel.p] = rolle; }
|
|
1282
|
+
});
|
|
1283
|
+
/* Gepflegtes Wissen zu Punkten, die dieses eine Geraet gerade nicht
|
|
1284
|
+
hat, bleibt erhalten - andere Geraete des Typs haben sie. */
|
|
1285
|
+
Object.keys(altesWissen).forEach(function (pf) {
|
|
1286
|
+
if (!zeilenQuellen[pf] && !wissen[pf]) { wissen[pf] = altesWissen[pf]; }
|
|
1287
|
+
});
|
|
1288
|
+
if (Object.keys(wissen).length) { v.weiterePunkte = wissen; }
|
|
1289
|
+
|
|
1290
|
+
/* Was die Ursprungsvorlage sonst noch mitbringt — etwa
|
|
1291
|
+
nebenwirkungen — bleibt erhalten. Sonst fiele beim Aktualisieren
|
|
1292
|
+
still unter den Tisch, was der Dialog gar nicht anzeigt. */
|
|
1293
|
+
var bekannt = { id: 1, version: 1, rang: 1, name: 1, geraetetyp: 1,
|
|
1294
|
+
funktion: 1, erkennung: 1, mehrfach: 1, kanalname: 1,
|
|
1295
|
+
zustaende: 1, weiterePunkte: 1 };
|
|
1296
|
+
if (k.modus === 'update' && k.quelle) {
|
|
1297
|
+
Object.keys(k.quelle).forEach(function (feld) {
|
|
1298
|
+
if (feld.charAt(0) === '_' || bekannt[feld]) { return; }
|
|
1299
|
+
v[feld] = k.quelle[feld];
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return JSON.parse(JSON.stringify(v)); /* undefined weg */
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/* Kandidaten fuer den Probelauf. Nicht jeder Knoten im Baum — nur die,
|
|
1307
|
+
unter denen der erste Pflichtpunkt ueberhaupt vorkommt. Sonst muesste
|
|
1308
|
+
hatPunkt fuer tausende Knoten die Kleinschreibkarte aufbauen, und das
|
|
1309
|
+
dauert auf einer grossen Anlage sichtbar lange. */
|
|
1310
|
+
function kandidatenFuer(v) {
|
|
1311
|
+
var erk = v.erkennung || {};
|
|
1312
|
+
var erf = erk.erforderlich || [];
|
|
1313
|
+
if (!erf.length) { return []; }
|
|
1314
|
+
var ph = v.mehrfach ? (v.mehrfach.platzhalter || '%N%') : null;
|
|
1315
|
+
var raus = {};
|
|
1316
|
+
|
|
1317
|
+
function sammle(pfad) {
|
|
1318
|
+
var teile = ph ? String(pfad).split(ph) : [String(pfad)];
|
|
1319
|
+
var vorn = teile[0];
|
|
1320
|
+
if (vorn) {
|
|
1321
|
+
var suche = ('.' + vorn).toLowerCase();
|
|
1322
|
+
S.keysSorted.forEach(function (id) {
|
|
1323
|
+
if (id.indexOf('alias.') === 0) { return; }
|
|
1324
|
+
if (!S.objects[id] || S.objects[id].type !== 'state') { return; }
|
|
1325
|
+
var i = id.toLowerCase().indexOf(suche);
|
|
1326
|
+
if (i < 1) { return; }
|
|
1327
|
+
var kanal = id.slice(0, i);
|
|
1328
|
+
if (kanal.split('.').length >= 3) { raus[kanal] = 1; }
|
|
1329
|
+
});
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/* Der Platzhalter kann auch ganz vorn stehen. Eine Tasmota nennt
|
|
1334
|
+
ihre Ausgaenge cmnd.POWER%N% — da bleibt ein Vorspann uebrig, an
|
|
1335
|
+
dem sich der Kanal abschneiden laesst. Ein Homematic-Doppelaktor
|
|
1336
|
+
nennt sie %N%.STATE, und dann ist der Vorspann leer: der Kanal
|
|
1337
|
+
ist die Nummer selbst. Frueher stieg die Suche hier aus und die
|
|
1338
|
+
Vorlage fand kein einziges Geraet. Jetzt laeuft sie ueber den
|
|
1339
|
+
Nachspann und nimmt das Segment davor als Ausgangsnummer. */
|
|
1340
|
+
var nach = (teile[1] || '').toLowerCase();
|
|
1341
|
+
if (!nach) { return; }
|
|
1342
|
+
S.keysSorted.forEach(function (id) {
|
|
1343
|
+
if (id.indexOf('alias.') === 0) { return; }
|
|
1344
|
+
if (!S.objects[id] || S.objects[id].type !== 'state') { return; }
|
|
1345
|
+
if (id.toLowerCase().slice(-nach.length) !== nach) { return; }
|
|
1346
|
+
var t = id.slice(0, id.length - nach.length).split('.');
|
|
1347
|
+
t.pop();
|
|
1348
|
+
if (t.length >= 3) { raus[t.join('.')] = 1; }
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
sammle(erf[0]);
|
|
1353
|
+
/* Auch ueber die Inhaltspruefung suchen. Ein Geraet, das seine
|
|
1354
|
+
Ausgaenge nur in tele/STATE meldet und noch keinen cmnd-Punkt hat,
|
|
1355
|
+
waere sonst kein Kandidat — der Probelauf einer Mehrfach-Vorlage
|
|
1356
|
+
fand ein Geraet statt vier. Was am Ende wirklich passt, entscheidet
|
|
1357
|
+
`pruefeVorlage`; hier geht es nur darum, niemanden zu uebersehen. */
|
|
1358
|
+
Object.keys(erk.inhalt || {}).forEach(sammle);
|
|
1359
|
+
|
|
1360
|
+
return Object.keys(raus).sort();
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
/* Der Probelauf braucht Werte, nicht nur Objekte.
|
|
1364
|
+
`pruefeVorlage` prueft auch `erkennung.inhalt` und schaut dafuer in
|
|
1365
|
+
den Wert des Punktes — steht dort nichts, gilt die Vorlage als
|
|
1366
|
+
unpassend. Geladen werden die Werte aber nur fuer den gewaehlten
|
|
1367
|
+
Kanal, und im Vorlagen-Tab ist keiner gewaehlt. Also meldete jede
|
|
1368
|
+
Vorlage mit `inhalt` „0 Geraete getroffen", auch die, die im
|
|
1369
|
+
Quellen-Tab zwanzig Geraete erkennt.
|
|
1370
|
+
|
|
1371
|
+
Hier werden die fehlenden Werte einmal nachgeladen und danach neu
|
|
1372
|
+
gezeichnet. Was einmal vergeblich abgefragt wurde, wird nicht wieder
|
|
1373
|
+
abgefragt — sonst laedt ein Punkt ohne Wert endlos. */
|
|
1374
|
+
var probeGeholt = {};
|
|
1375
|
+
|
|
1376
|
+
function probeWerteHolen(v, danach) {
|
|
1377
|
+
var erk = v.erkennung || {};
|
|
1378
|
+
if (!erk.inhalt) { return false; }
|
|
1379
|
+
var ph = v.mehrfach ? (v.mehrfach.platzhalter || '%N%') : null;
|
|
1380
|
+
var fehlt = [];
|
|
1381
|
+
kandidatenFuer(v).forEach(function (kanal) {
|
|
1382
|
+
Object.keys(erk.inhalt).forEach(function (p0) {
|
|
1383
|
+
var pfad = ph ? String(p0).split(ph).join('1') : p0;
|
|
1384
|
+
var id = hatPunkt(kanal, pfad);
|
|
1385
|
+
if (id && S.werte[id] === undefined && !probeGeholt[id]) { fehlt.push(id); }
|
|
1386
|
+
});
|
|
1387
|
+
});
|
|
1388
|
+
if (!fehlt.length) { return false; }
|
|
1389
|
+
var offen = fehlt.length;
|
|
1390
|
+
fehlt.forEach(function (id) {
|
|
1391
|
+
probeGeholt[id] = 1;
|
|
1392
|
+
socket.emit('getState', id, function (err, st) {
|
|
1393
|
+
if (!err && st) { S.werte[id] = st; }
|
|
1394
|
+
if (--offen === 0) { danach(); }
|
|
1395
|
+
});
|
|
1396
|
+
});
|
|
1397
|
+
return true;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/* Was faengt diese Vorlage, und wer gewinnt dort heute? */
|
|
1401
|
+
export function probelauf(vorlage, alsEigene) {
|
|
1402
|
+
/* Nie am Original arbeiten — sonst stuende hinterher _quelle im
|
|
1403
|
+
gespeicherten JSON. */
|
|
1404
|
+
var neu = JSON.parse(JSON.stringify(vorlage));
|
|
1405
|
+
/* Nur rechnen, was auch zutrifft. Eine eigene Vorlage bekommt in der
|
|
1406
|
+
Bewertung 1000 Punkte Vorsprung; setzte man das hier immer, meldete
|
|
1407
|
+
das Blatt einer mitgelieferten Vorlage Siege, die sie nie erringt —
|
|
1408
|
+
Steckdose 18 und Lampe 23 „gewonnen" bei 26 Geraeten, obwohl je
|
|
1409
|
+
Geraet nur eine gewinnen kann. Im Dialog „Als Vorlage speichern"
|
|
1410
|
+
stimmt es dagegen: dort wird sie gerade zu einer eigenen. */
|
|
1411
|
+
if (alsEigene) { neu._quelle = 'benutzer'; }
|
|
1412
|
+
var raus = [];
|
|
1413
|
+
kandidatenFuer(neu).forEach(function (p) {
|
|
1414
|
+
var t = pruefeVorlage(neu, p);
|
|
1415
|
+
if (!t.passt) { return; }
|
|
1416
|
+
|
|
1417
|
+
/* Wer gewinnt hier heute, mit dem Bestand wie er ist? Beim
|
|
1418
|
+
Aktualisieren ist das oft die Vorlage selbst — dann wechselt
|
|
1419
|
+
nichts, auch wenn sie den Vergleich gegen alle anderen gewinnt. */
|
|
1420
|
+
var jetzt = null, andere = null;
|
|
1421
|
+
S.VORLAGEN.forEach(function (v) {
|
|
1422
|
+
var x = pruefeVorlage(v, p);
|
|
1423
|
+
if (!x.passt) { return; }
|
|
1424
|
+
if (!jetzt || x.punkte > jetzt.punkte) { jetzt = x; }
|
|
1425
|
+
if (v.id !== neu.id && (!andere || x.punkte > andere.punkte)) { andere = x; }
|
|
1426
|
+
});
|
|
1427
|
+
raus.push({
|
|
1428
|
+
id: p, eigen: t, gegner: andere,
|
|
1429
|
+
gewinnt: !andere || t.punkte > andere.punkte,
|
|
1430
|
+
wechsel: !!(jetzt && jetzt.vorlage.id !== neu.id &&
|
|
1431
|
+
(!andere || t.punkte > andere.punkte))
|
|
1432
|
+
});
|
|
1433
|
+
});
|
|
1434
|
+
return raus;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
export function tplKnopf(an) {
|
|
1438
|
+
var b = $('#btn-tpl');
|
|
1439
|
+
if (b) {
|
|
1440
|
+
b.disabled = !an;
|
|
1441
|
+
b.title = an ? '' : tr('tpls.pickDeviceFirst');
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
export function zeigeVorlagenDialog() {
|
|
1446
|
+
if (!S.entwurf || !S.current) { return; }
|
|
1447
|
+
S.tplZ = vorlagenVorbereiten(S.entwurf);
|
|
1448
|
+
var b = $('#btn-tpl-save');
|
|
1449
|
+
if (b) { b.hidden = false; b.disabled = false; }
|
|
1450
|
+
zeichneVorlagenDialog();
|
|
1451
|
+
$('#dlg-tpl').showModal();
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
function tplFeld(label, wert, aendern, breit) {
|
|
1455
|
+
var l = el('label', 'feld');
|
|
1456
|
+
l.appendChild(el('span', 'feldlabel', label));
|
|
1457
|
+
var i = document.createElement('input');
|
|
1458
|
+
i.className = 'tx';
|
|
1459
|
+
i.type = 'text';
|
|
1460
|
+
i.value = wert === undefined || wert === null ? '' : String(wert);
|
|
1461
|
+
if (breit) { i.style.minWidth = breit; }
|
|
1462
|
+
i.addEventListener('input', function () { aendern(i.value); });
|
|
1463
|
+
i.addEventListener('change', function () { aendern(i.value); zeichneVorlagenDialog(); });
|
|
1464
|
+
l.appendChild(i);
|
|
1465
|
+
return l;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
export function zeichneVorlagenDialog() {
|
|
1469
|
+
var z = S.tplZ;
|
|
1470
|
+
if (!z) { return; }
|
|
1471
|
+
var k = z.kopf;
|
|
1472
|
+
var body = $('#tpl-body');
|
|
1473
|
+
body.textContent = '';
|
|
1474
|
+
|
|
1475
|
+
/* --- aktualisieren oder neu --- */
|
|
1476
|
+
if (k.quelle) {
|
|
1477
|
+
var wahl = el('div', 'card');
|
|
1478
|
+
wahl.style.marginBottom = '11px';
|
|
1479
|
+
var wh = el('div', 'ch');
|
|
1480
|
+
wh.appendChild(el('span', 'typ', tr('tpls.whatToDo')));
|
|
1481
|
+
wahl.appendChild(wh);
|
|
1482
|
+
var wb = el('div');
|
|
1483
|
+
wb.style.padding = '9px 12px 11px';
|
|
1484
|
+
[['update', tr('tpls.updateIt', textIn(k.quelle.name, sprache), (k.quelle.version || 1) + 1),
|
|
1485
|
+
k.quelle._quelle === 'paket' ? tr('tpls.shadowsPackage') : ''],
|
|
1486
|
+
['neu', tr('tpls.saveAsNew'), tr('tpls.saveAsNewHint')]].forEach(function (w) {
|
|
1487
|
+
var r = el('label');
|
|
1488
|
+
r.style.display = 'block';
|
|
1489
|
+
r.style.margin = '3px 0';
|
|
1490
|
+
r.style.fontSize = '12.5px';
|
|
1491
|
+
var rb = document.createElement('input');
|
|
1492
|
+
rb.type = 'radio';
|
|
1493
|
+
rb.name = 'tplmodus';
|
|
1494
|
+
rb.checked = k.modus === w[0];
|
|
1495
|
+
rb.addEventListener('change', function () {
|
|
1496
|
+
if (!rb.checked) { return; }
|
|
1497
|
+
k.modus = w[0];
|
|
1498
|
+
if (w[0] === 'neu') {
|
|
1499
|
+
k.id = freierSchluessel(schluesselAus(k.nameDe || k.geraetName));
|
|
1500
|
+
k.rang = k.maxRang + 10;
|
|
1501
|
+
} else {
|
|
1502
|
+
k.id = k.quelle.id;
|
|
1503
|
+
k.rang = k.quelle.rang || 0;
|
|
1504
|
+
}
|
|
1505
|
+
zeichneVorlagenDialog();
|
|
1506
|
+
});
|
|
1507
|
+
r.appendChild(rb);
|
|
1508
|
+
r.appendChild(document.createTextNode(' ' + w[1]));
|
|
1509
|
+
if (w[2]) {
|
|
1510
|
+
var hn = el('span', 'chip mut', w[2]);
|
|
1511
|
+
hn.style.marginLeft = '6px';
|
|
1512
|
+
r.appendChild(hn);
|
|
1513
|
+
}
|
|
1514
|
+
wb.appendChild(r);
|
|
1515
|
+
});
|
|
1516
|
+
wahl.appendChild(wb);
|
|
1517
|
+
body.appendChild(wahl);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
/* --- Kopfdaten --- */
|
|
1521
|
+
var kk = el('div', 'card');
|
|
1522
|
+
kk.style.marginBottom = '11px';
|
|
1523
|
+
var kh = el('div', 'ch');
|
|
1524
|
+
kh.appendChild(el('span', 'typ', tr('tpls.head')));
|
|
1525
|
+
kh.appendChild(el('span', 'chip mut', k.id));
|
|
1526
|
+
kk.appendChild(kh);
|
|
1527
|
+
var kb = el('div');
|
|
1528
|
+
kb.style.padding = '9px 12px 11px';
|
|
1529
|
+
kb.style.display = 'flex';
|
|
1530
|
+
kb.style.flexWrap = 'wrap';
|
|
1531
|
+
kb.style.gap = '9px 14px';
|
|
1532
|
+
|
|
1533
|
+
kb.appendChild(tplFeld(tr('tpls.nameDe'), k.nameDe, function (x) { k.nameDe = x; }, '150px'));
|
|
1534
|
+
kb.appendChild(tplFeld(tr('tpls.nameEn'), k.nameEn, function (x) { k.nameEn = x; }, '150px'));
|
|
1535
|
+
kb.appendChild(tplFeld(tr('tpls.id'), k.id, function (x) { k.id = schluesselAus(x); }, '150px'));
|
|
1536
|
+
|
|
1537
|
+
var lt = el('label', 'feld');
|
|
1538
|
+
lt.appendChild(el('span', 'feldlabel', tr('tpls.deviceType')));
|
|
1539
|
+
var st = el('select', 'tx');
|
|
1540
|
+
st.appendChild(opt('', tr('tpls.noType')));
|
|
1541
|
+
(D ? Object.keys(D.patterns).sort() : []).forEach(function (m) {
|
|
1542
|
+
var o = opt(m, musterZeile(m));
|
|
1543
|
+
if (m === k.geraetetyp) { o.selected = true; }
|
|
1544
|
+
st.appendChild(o);
|
|
1545
|
+
});
|
|
1546
|
+
st.addEventListener('change', function () { k.geraetetyp = st.value; zeichneVorlagenDialog(); });
|
|
1547
|
+
lt.appendChild(st);
|
|
1548
|
+
kb.appendChild(lt);
|
|
1549
|
+
|
|
1550
|
+
/* Die Funktion gehoert in die Vorlage, nicht nur ans einzelne Geraet.
|
|
1551
|
+
|
|
1552
|
+
Der Geraetetyp sagt, *wie* etwas gebaut ist - `socket` heisst
|
|
1553
|
+
geschaltet, mehr nicht. Ob eine Steckdose, eine Waschmaschine oder
|
|
1554
|
+
eine Druckerleiste daran haengt, weiss allein die Vorlage. Sie traegt
|
|
1555
|
+
die Funktion, und der naechste Alias aus dieser Vorlage bekommt sie
|
|
1556
|
+
vorgeschlagen. Bisher liess sie sich nur nachtraeglich im
|
|
1557
|
+
Vorlagenblatt setzen - beim Speichern gab es das Feld nicht. */
|
|
1558
|
+
var lf2 = el('label', 'feld');
|
|
1559
|
+
lf2.appendChild(el('span', 'feldlabel', tr('tpls.function')));
|
|
1560
|
+
var sf2 = el('select', 'tx');
|
|
1561
|
+
sf2.appendChild(opt('', tr('tpls.noFunction')));
|
|
1562
|
+
funktionsAuswahl(k.funktion).forEach(function (x) {
|
|
1563
|
+
var o = opt(x.wert, x.text);
|
|
1564
|
+
if (x.gewaehlt) { o.selected = true; }
|
|
1565
|
+
sf2.appendChild(o);
|
|
1566
|
+
});
|
|
1567
|
+
sf2.addEventListener('change', function () { k.funktion = sf2.value; });
|
|
1568
|
+
lf2.appendChild(sf2);
|
|
1569
|
+
if (katalogFehlt) { lf2.appendChild(el('span', 'hint', tr('catalog.missing'))); }
|
|
1570
|
+
kb.appendChild(lf2);
|
|
1571
|
+
|
|
1572
|
+
kb.appendChild(tplFeld(tr('tpls.rank'), k.rang, function (x) { k.rang = x; }, '60px'));
|
|
1573
|
+
var rh2 = el('div', 'hint', tr('tv.rankOwn'));
|
|
1574
|
+
rh2.style.flexBasis = '100%';
|
|
1575
|
+
kb.appendChild(rh2);
|
|
1576
|
+
kk.appendChild(kb);
|
|
1577
|
+
|
|
1578
|
+
var nh = el('div');
|
|
1579
|
+
nh.style.padding = '0 12px 11px';
|
|
1580
|
+
var nf = tplFeld(tr('tpls.nameHint'), k.namenshinweis, function (x) { k.namenshinweis = x; }, '260px');
|
|
1581
|
+
nh.appendChild(nf);
|
|
1582
|
+
var nt = el('div', 'hint', tr('tpls.nameHintHelp', k.geraetName));
|
|
1583
|
+
nt.style.marginTop = '4px';
|
|
1584
|
+
nh.appendChild(nt);
|
|
1585
|
+
kk.appendChild(nh);
|
|
1586
|
+
|
|
1587
|
+
/* --- mehrere Ausgaenge --- */
|
|
1588
|
+
var mf = el('div');
|
|
1589
|
+
mf.style.padding = '0 12px 12px';
|
|
1590
|
+
var ml = el('label');
|
|
1591
|
+
ml.style.display = 'block';
|
|
1592
|
+
ml.style.fontSize = '12.5px';
|
|
1593
|
+
var mcb = document.createElement('input');
|
|
1594
|
+
mcb.type = 'checkbox';
|
|
1595
|
+
mcb.checked = !!k.mehrfach;
|
|
1596
|
+
mcb.addEventListener('change', function () {
|
|
1597
|
+
k.mehrfach = mcb.checked;
|
|
1598
|
+
if (k.mehrfach && !k.instanz) { k.instanz = rateInstanz(z.zeilen, k.geraetName); }
|
|
1599
|
+
zeichneVorlagenDialog();
|
|
1600
|
+
});
|
|
1601
|
+
ml.appendChild(mcb);
|
|
1602
|
+
ml.appendChild(document.createTextNode(' ' + tr('tpls.multiple')));
|
|
1603
|
+
mf.appendChild(ml);
|
|
1604
|
+
|
|
1605
|
+
if (k.mehrfach) {
|
|
1606
|
+
var mz = el('div');
|
|
1607
|
+
mz.style.display = 'flex';
|
|
1608
|
+
mz.style.flexWrap = 'wrap';
|
|
1609
|
+
mz.style.gap = '9px 14px';
|
|
1610
|
+
mz.style.marginTop = '6px';
|
|
1611
|
+
mz.appendChild(tplFeld(tr('tpls.whichNumber'), k.instanz,
|
|
1612
|
+
function (x) { k.instanz = String(x).trim(); }, '60px'));
|
|
1613
|
+
mz.appendChild(tplFeld(tr('tpls.channelName'), k.kanalname,
|
|
1614
|
+
function (x) { k.kanalname = x; }, '140px'));
|
|
1615
|
+
mf.appendChild(mz);
|
|
1616
|
+
mf.appendChild(el('div', 'hint',
|
|
1617
|
+
k.instanz ? tr('tpls.multipleHelp', k.instanz) : tr('tpls.multipleNoNumber')));
|
|
1618
|
+
var fremd = andereAusgaenge(z.zeilen, k.instanz);
|
|
1619
|
+
if (fremd.punkte.length) {
|
|
1620
|
+
var fw2 = el('div', 'aside w');
|
|
1621
|
+
fw2.style.margin = '9px 0 0';
|
|
1622
|
+
fw2.appendChild(el('b', null, tr('tpls.otherOutputs', fremd.punkte.length, fremd.nummern.join(', '))));
|
|
1623
|
+
fw2.appendChild(document.createTextNode(' ' + tr('tpls.otherOutputsText')));
|
|
1624
|
+
/* Nicht nur schimpfen, sondern es koennen: die fremden Zeilen
|
|
1625
|
+
fliegen aus der Vorlage — das Geraet bleibt unangetastet. */
|
|
1626
|
+
var fb = el('button', 'btn schmal', tr('tpls.dropOthers'));
|
|
1627
|
+
fb.style.marginTop = '8px';
|
|
1628
|
+
fb.addEventListener('click', function () {
|
|
1629
|
+
z.zeilen.forEach(function (r) {
|
|
1630
|
+
if (fremd.punkte.indexOf(r.name) > -1) { r.weg = true; }
|
|
1631
|
+
});
|
|
1632
|
+
zeichneVorlagenDialog();
|
|
1633
|
+
});
|
|
1634
|
+
fw2.appendChild(el('div', null, '')).appendChild(fb);
|
|
1635
|
+
mf.appendChild(fw2);
|
|
1636
|
+
}
|
|
1637
|
+
var weg = z.zeilen.filter(function (r) { return r.weg; });
|
|
1638
|
+
if (weg.length) {
|
|
1639
|
+
var wz = el('div', 'hint');
|
|
1640
|
+
wz.style.marginTop = '7px';
|
|
1641
|
+
wz.appendChild(document.createTextNode(tr('tpls.droppedCount', weg.length) + ' '));
|
|
1642
|
+
var zb = el('button', 'btn schmal', tr('tpls.undrop'));
|
|
1643
|
+
zb.addEventListener('click', function () {
|
|
1644
|
+
z.zeilen.forEach(function (r) { r.weg = false; });
|
|
1645
|
+
zeichneVorlagenDialog();
|
|
1646
|
+
});
|
|
1647
|
+
wz.appendChild(zb);
|
|
1648
|
+
mf.appendChild(wz);
|
|
1649
|
+
}
|
|
1650
|
+
} else {
|
|
1651
|
+
mf.appendChild(el('div', 'hint', tr('tpls.multipleOff')));
|
|
1652
|
+
}
|
|
1653
|
+
kk.appendChild(mf);
|
|
1654
|
+
|
|
1655
|
+
/* --- Punkte, die es nicht geben darf --- */
|
|
1656
|
+
var vf = el('div');
|
|
1657
|
+
vf.style.padding = '0 12px 12px';
|
|
1658
|
+
vf.appendChild(tplFeld(tr('tv.forbidden'), k.verboten, function (x) { k.verboten = x; }, '280px'));
|
|
1659
|
+
vf.appendChild(el('div', 'hint', tr('tv.forbiddenHelp')));
|
|
1660
|
+
kk.appendChild(vf);
|
|
1661
|
+
|
|
1662
|
+
body.appendChild(kk);
|
|
1663
|
+
|
|
1664
|
+
/* --- Zustaende --- */
|
|
1665
|
+
var zk = el('div', 'card');
|
|
1666
|
+
zk.style.marginBottom = '11px';
|
|
1667
|
+
var zh = el('div', 'ch');
|
|
1668
|
+
var drin = z.zeilen.filter(function (r) { return !r.weg; });
|
|
1669
|
+
zh.appendChild(el('span', 'typ', tr('tpls.states')));
|
|
1670
|
+
zh.appendChild(el('span', 'chip mut', tr('tpls.statesCount', drin.length)));
|
|
1671
|
+
zk.appendChild(zh);
|
|
1672
|
+
|
|
1673
|
+
/* Drei verschiedene Fragen je Zeile, deshalb drei Spalten. Frueher
|
|
1674
|
+
stand nur ein Haken links, und der bedeutete "muss vorhanden sein" —
|
|
1675
|
+
an dieser Stelle liest aber jeder "ist ausgewaehlt", weil er genau
|
|
1676
|
+
das in jeder anderen Liste dieses Adapters bedeutet. */
|
|
1677
|
+
var erkl = el('div', 'hint');
|
|
1678
|
+
erkl.style.padding = '8px 12px 0';
|
|
1679
|
+
erkl.appendChild(document.createTextNode(tr('tpls.statesHelp')));
|
|
1680
|
+
zk.appendChild(erkl);
|
|
1681
|
+
|
|
1682
|
+
var abgewaehlt = z.zeilen.filter(function (r) { return !r.weg && r.aus; });
|
|
1683
|
+
if (abgewaehlt.length) {
|
|
1684
|
+
var az = el('div', 'hint');
|
|
1685
|
+
az.style.padding = '6px 12px 0';
|
|
1686
|
+
az.appendChild(document.createTextNode(tr('tpls.someOff', abgewaehlt.length) + ' '));
|
|
1687
|
+
var ab = el('button', 'btn schmal', tr('tpls.dropOff'));
|
|
1688
|
+
ab.addEventListener('click', function () {
|
|
1689
|
+
abgewaehlt.forEach(function (r) { r.weg = true; r.pflicht = false; });
|
|
1690
|
+
zeichneVorlagenDialog();
|
|
1691
|
+
});
|
|
1692
|
+
az.appendChild(ab);
|
|
1693
|
+
zk.appendChild(az);
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
var kopfz = el('div', 'slot vzeile vkopf');
|
|
1697
|
+
[tr('tpls.colInTemplate'), tr('tpls.colDefault'), tr('tpls.colRequired'),
|
|
1698
|
+
tr('tpls.colName'), tr('tpls.colPath')]
|
|
1699
|
+
.forEach(function (t) { kopfz.appendChild(el('span', null, t)); });
|
|
1700
|
+
zk.appendChild(kopfz);
|
|
1701
|
+
|
|
1702
|
+
var absDa = false;
|
|
1703
|
+
z.zeilen.forEach(function (r) {
|
|
1704
|
+
if (!r.weg && r.lesenAbs) { absDa = true; }
|
|
1705
|
+
var row = el('div', 'slot vzeile');
|
|
1706
|
+
if (r.weg) { row.style.opacity = '.45'; }
|
|
1707
|
+
|
|
1708
|
+
var haken = function (an, gesperrt, titel, tut) {
|
|
1709
|
+
var w = el('span');
|
|
1710
|
+
var c = document.createElement('input');
|
|
1711
|
+
c.type = 'checkbox';
|
|
1712
|
+
c.checked = an;
|
|
1713
|
+
c.disabled = !!gesperrt;
|
|
1714
|
+
c.title = titel;
|
|
1715
|
+
c.addEventListener('change', function () { tut(c.checked); });
|
|
1716
|
+
w.appendChild(c);
|
|
1717
|
+
return w;
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
/* 1. Kommt der Punkt ueberhaupt in die Vorlage? */
|
|
1721
|
+
row.appendChild(haken(!r.weg, false, tr('tpls.colInTemplate'), function (an) {
|
|
1722
|
+
r.weg = !an;
|
|
1723
|
+
if (r.weg) { r.pflicht = false; }
|
|
1724
|
+
zeichneVorlagenDialog();
|
|
1725
|
+
}));
|
|
1726
|
+
|
|
1727
|
+
/* 2. Ist er beim naechsten Geraet vorgehakt? Kommt vom Haken am
|
|
1728
|
+
Geraet, laesst sich hier aber noch drehen. */
|
|
1729
|
+
row.appendChild(haken(!r.aus, r.weg, tr('tpls.colDefault'), function (an) {
|
|
1730
|
+
r.aus = !an;
|
|
1731
|
+
zeichneVorlagenDialog();
|
|
1732
|
+
}));
|
|
1733
|
+
|
|
1734
|
+
/* 3. Muss er da sein, damit die Vorlage ueberhaupt greift? */
|
|
1735
|
+
row.appendChild(haken(r.pflicht, r.lesenAbs || r.weg, tr('tpls.colRequired'), function (an) {
|
|
1736
|
+
r.pflicht = an;
|
|
1737
|
+
zeichneVorlagenDialog();
|
|
1738
|
+
}));
|
|
1739
|
+
|
|
1740
|
+
row.appendChild(el('span', 'sn', r.name));
|
|
1741
|
+
|
|
1742
|
+
var pw = el('span', 'rx pf');
|
|
1743
|
+
pw.style.fontFamily = 'var(--mono)';
|
|
1744
|
+
pw.title = zPfad(r, k, false) + (r.schreibenRoh ? ' → ' + zPfad(r, k, true) : '');
|
|
1745
|
+
pw.appendChild(document.createTextNode(zPfad(r, k, false)));
|
|
1746
|
+
if (r.schreibenRoh) { pw.appendChild(el('span', 'chip mut', '→ ' + zPfad(r, k, true))); }
|
|
1747
|
+
if (r.lesenAbs) { pw.appendChild(el('span', 'chip warn', tr('tpls.absolute'))); }
|
|
1748
|
+
row.appendChild(pw);
|
|
1749
|
+
|
|
1750
|
+
zk.appendChild(row);
|
|
1751
|
+
});
|
|
1752
|
+
body.appendChild(zk);
|
|
1753
|
+
|
|
1754
|
+
if (absDa) {
|
|
1755
|
+
var aw = el('div', 'aside w');
|
|
1756
|
+
aw.style.marginBottom = '11px';
|
|
1757
|
+
aw.appendChild(el('b', null, tr('tpls.absoluteTitle')));
|
|
1758
|
+
aw.appendChild(document.createTextNode(tr('tpls.absoluteText')));
|
|
1759
|
+
body.appendChild(aw);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
/* --- Inhaltspruefungen --- */
|
|
1763
|
+
if (z.inhalte.length) {
|
|
1764
|
+
var ik = el('div', 'card');
|
|
1765
|
+
ik.style.marginBottom = '11px';
|
|
1766
|
+
var ih = el('div', 'ch');
|
|
1767
|
+
ih.appendChild(el('span', 'typ', tr('tpls.content')));
|
|
1768
|
+
ik.appendChild(ih);
|
|
1769
|
+
var ib = el('div');
|
|
1770
|
+
ib.style.padding = '9px 12px 11px';
|
|
1771
|
+
z.inhalte.forEach(function (x) {
|
|
1772
|
+
var r = el('label');
|
|
1773
|
+
r.style.display = 'block';
|
|
1774
|
+
r.style.margin = '3px 0';
|
|
1775
|
+
r.style.fontSize = '12.5px';
|
|
1776
|
+
var cb = document.createElement('input');
|
|
1777
|
+
cb.type = 'checkbox';
|
|
1778
|
+
cb.checked = x.an;
|
|
1779
|
+
cb.addEventListener('change', function () { x.an = cb.checked; zeichneVorlagenDialog(); });
|
|
1780
|
+
r.appendChild(cb);
|
|
1781
|
+
var t = el('span', null, ' ' + tr('tpls.contentRow', x.punkt, x.feld));
|
|
1782
|
+
t.style.fontFamily = 'var(--mono)';
|
|
1783
|
+
r.appendChild(t);
|
|
1784
|
+
ib.appendChild(r);
|
|
1785
|
+
});
|
|
1786
|
+
ib.appendChild(el('div', 'hint', tr('tpls.contentHelp')));
|
|
1787
|
+
ik.appendChild(ib);
|
|
1788
|
+
body.appendChild(ik);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
/* --- Probelauf --- */
|
|
1792
|
+
var neu = baueVorlage(z);
|
|
1793
|
+
var pk = el('div', 'card');
|
|
1794
|
+
pk.style.marginBottom = '11px';
|
|
1795
|
+
var ph = el('div', 'ch');
|
|
1796
|
+
ph.appendChild(el('span', 'typ', tr('tpls.tryout')));
|
|
1797
|
+
var treffer = [];
|
|
1798
|
+
var fehlerErk = !neu.erkennung.erforderlich.length;
|
|
1799
|
+
if (!fehlerErk) {
|
|
1800
|
+
probeWerteHolen(neu, zeichneVorlagenDialog);
|
|
1801
|
+
treffer = probelauf(neu, true);
|
|
1802
|
+
}
|
|
1803
|
+
var gew = treffer.filter(function (t) { return t.gewinnt; }).length;
|
|
1804
|
+
var wech = treffer.filter(function (t) { return t.wechsel; }).length;
|
|
1805
|
+
ph.appendChild(el('span', 'chip ' + (gew ? 'ok' : 'warn'), tr('tpls.hits', treffer.length, gew)));
|
|
1806
|
+
if (wech) { ph.appendChild(el('span', 'chip warn', tr('tpls.takesOver', wech))); }
|
|
1807
|
+
pk.appendChild(ph);
|
|
1808
|
+
var pb = el('div');
|
|
1809
|
+
pb.style.padding = '9px 12px 11px';
|
|
1810
|
+
if (fehlerErk) {
|
|
1811
|
+
pb.appendChild(el('div', 'hint', tr('tpls.noRequired')));
|
|
1812
|
+
} else if (!treffer.length) {
|
|
1813
|
+
pb.appendChild(el('div', 'hint', tr('tpls.noHits')));
|
|
1814
|
+
} else {
|
|
1815
|
+
treffer.slice(0, 40).forEach(function (t) {
|
|
1816
|
+
var r = el('div', 'slot');
|
|
1817
|
+
r.style.gridTemplateColumns = '1fr 150px 130px';
|
|
1818
|
+
var idl = el('span', 'sn', t.id);
|
|
1819
|
+
idl.style.fontFamily = 'var(--mono)';
|
|
1820
|
+
if (t.id === (z.e.kanal || '')) { idl.style.fontWeight = '700'; }
|
|
1821
|
+
r.appendChild(idl);
|
|
1822
|
+
r.appendChild(el('span', 'rx', tr('tpls.evidence', t.eigen.belege)));
|
|
1823
|
+
var w = el('span', 'chip ' + (t.gewinnt ? 'ok' : 'bad'),
|
|
1824
|
+
t.gewinnt ? (t.gegner ? tr('tpls.beats', textIn(t.gegner.vorlage.name, sprache)) : tr('tpls.onlyOne'))
|
|
1825
|
+
: tr('tpls.losesTo', textIn(t.gegner.vorlage.name, sprache)));
|
|
1826
|
+
r.appendChild(w);
|
|
1827
|
+
pb.appendChild(r);
|
|
1828
|
+
});
|
|
1829
|
+
if (treffer.length > 40) {
|
|
1830
|
+
pb.appendChild(el('div', 'hint', tr('tpls.andMore', treffer.length - 40)));
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
pk.appendChild(pb);
|
|
1834
|
+
body.appendChild(pk);
|
|
1835
|
+
|
|
1836
|
+
/* --- das fertige JSON --- */
|
|
1837
|
+
var jk = el('div', 'card');
|
|
1838
|
+
var jh = el('div', 'ch');
|
|
1839
|
+
jh.appendChild(el('span', 'typ', tr('tpls.json')));
|
|
1840
|
+
jh.appendChild(el('span', 'chip mut', 'v' + neu.version));
|
|
1841
|
+
jk.appendChild(jh);
|
|
1842
|
+
var pre = el('div', 'raw');
|
|
1843
|
+
pre.style.margin = '9px 12px 11px';
|
|
1844
|
+
pre.style.whiteSpace = 'pre';
|
|
1845
|
+
pre.style.maxHeight = 'none';
|
|
1846
|
+
pre.textContent = JSON.stringify(neu, null, 2);
|
|
1847
|
+
jk.appendChild(pre);
|
|
1848
|
+
body.appendChild(jk);
|
|
1849
|
+
|
|
1850
|
+
var b = $('#btn-tpl-save');
|
|
1851
|
+
if (b) {
|
|
1852
|
+
b.disabled = fehlerErk || !k.id ||
|
|
1853
|
+
!z.zeilen.filter(function (r) { return !r.weg; }).length;
|
|
1854
|
+
b.textContent = k.modus === 'update' ? tr('tpls.saveUpdate') : tr('tpls.saveNew');
|
|
1855
|
+
}
|
|
1856
|
+
var tt = $('#tpl-titel');
|
|
1857
|
+
if (tt) { tt.textContent = tr('tpls.title', z.e.kanal.split('.').pop()); }
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
export function speichereVorlageAusDialog() {
|
|
1861
|
+
if (!S.tplZ) { return; }
|
|
1862
|
+
var neu = baueVorlage(S.tplZ);
|
|
1863
|
+
|
|
1864
|
+
/* Widerspricht sie sich, wird nicht gespeichert — eine Vorlage, die
|
|
1865
|
+
auf nichts passen kann, hilft niemandem und ist spaeter schwer zu
|
|
1866
|
+
durchschauen. */
|
|
1867
|
+
if (neu.widerspruch && neu.widerspruch.length) {
|
|
1868
|
+
var body0 = $('#tpl-body');
|
|
1869
|
+
var alt0 = body0.querySelector('.widerspruch');
|
|
1870
|
+
if (alt0) { alt0.remove(); }
|
|
1871
|
+
var w0 = el('div', 'aside w widerspruch');
|
|
1872
|
+
w0.appendChild(el('b', null, tr('tpls.contradiction')));
|
|
1873
|
+
w0.appendChild(document.createTextNode(' ' + tr('tpls.contradictionText',
|
|
1874
|
+
neu.widerspruch.join(', '))));
|
|
1875
|
+
body0.insertBefore(w0, body0.firstChild);
|
|
1876
|
+
body0.scrollTop = 0;
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
delete neu.widerspruch;
|
|
1880
|
+
|
|
1881
|
+
var b = $('#btn-tpl-save');
|
|
1882
|
+
if (b) { b.disabled = true; b.textContent = tr('tpls.saving'); }
|
|
1883
|
+
|
|
1884
|
+
aendereVorlagen(neu.id, neu, function (err) {
|
|
1885
|
+
var body = $('#tpl-body');
|
|
1886
|
+
if (err) {
|
|
1887
|
+
body.textContent = '';
|
|
1888
|
+
var w = el('div', 'aside w');
|
|
1889
|
+
w.appendChild(el('b', null, tr('tpls.failed')));
|
|
1890
|
+
w.appendChild(document.createTextNode(' ' + err));
|
|
1891
|
+
body.appendChild(w);
|
|
1892
|
+
if (b) { b.disabled = false; b.textContent = tr('tpls.saveNew'); }
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
ladeVorlagen(function () {
|
|
1896
|
+
setzeMeta();
|
|
1897
|
+
body.textContent = '';
|
|
1898
|
+
var m = el('div');
|
|
1899
|
+
m.style.fontSize = '13px';
|
|
1900
|
+
m.appendChild(el('b', null, tr('tpls.saved', neu.id, neu.version)));
|
|
1901
|
+
m.appendChild(el('div', null, tr('tpls.savedWhere', INSTANZ_ID)));
|
|
1902
|
+
body.appendChild(m);
|
|
1903
|
+
if (b) { b.hidden = true; }
|
|
1904
|
+
if (S.current) { zeichneErgebnis(); }
|
|
1905
|
+
});
|
|
1906
|
+
});
|
|
1907
|
+
}
|