iobroker.alias-workbench 0.9.12 → 0.10.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Setzt nur Rollen im Entwurf; entscheiden laesst es den Detektor. */
4
4
 
5
- import { musterVon, erkenneEntwurf } from './erkennung.js';
5
+ import { musterVon, erkenneEntwurf, rolleTrifft } from './erkennung.js';
6
6
 
7
7
  /* ================== Plaetze raten ==================
8
8
 
@@ -94,6 +94,42 @@ function kernWoerter(name) {
94
94
  /* Der nicht veraendernde Vorbau des Ratens: was ist frei, was kaeme in
95
95
  Frage? Geteilt zwischen dem Raten selbst und der Frage, ob der Knopf
96
96
  ueberhaupt etwas anzubieten haette. */
97
+ /* Widerspricht die Einheit des Punkts dem Platz?
98
+
99
+ Ein Punkt in Sekunden ist keine Temperatur, einer in Prozent kein
100
+ Verbrauch. Bis 19.09.2026 gab die Einheit beim Raten nur Pluspunkte,
101
+ nie einen Ausschluss: am Geschirrspueler landete die Startverzoegerung
102
+ („0 seconds") auf der Ist-Temperatur und die Energieprognose (65 %) auf
103
+ dem Verbrauch — und weil das Thermostat damit auf mehr belegte Plaetze
104
+ kam als die Steckdose, empfahl der Hinweis „Thermostat wuerde evtl.
105
+ besser passen" (Lauf vom 19.09.2026, U33). Hinweis, Knopf und Legende
106
+ raten ueber dieselbe Funktion, die Regel gilt also fuer alle drei.
107
+
108
+ Nur ein Widerspruch schliesst aus: ohne Einheit am Punkt, oder bei
109
+ einem Platz, dessen Rolle hier nicht steht, bleibt alles wie bisher.
110
+ Die Reihenfolge zaehlt — `power.consumption` vor `power`. */
111
+ var EINHEIT_ZU_ROLLE = [
112
+ [/color\.temperature/, ['k', 'mired']],
113
+ [/temperature/, ['°c', '°f', 'k', 'c']],
114
+ [/humidity/, ['%', '%rh', 'rh']],
115
+ [/power\.consumption|energy/, ['wh', 'kwh', 'mwh']],
116
+ [/\bpower\b/, ['w', 'kw', 'mw', 'va', 'kva', 'var', 'kvar']],
117
+ [/voltage/, ['v', 'mv', 'kv']],
118
+ [/current/, ['a', 'ma']],
119
+ [/illuminance|brightness/, ['lx', 'lux']],
120
+ [/pressure/, ['hpa', 'mbar', 'bar', 'pa', 'kpa']]
121
+ ];
122
+ export function einheitPasst(rolle, einheit) {
123
+ var u = String(einheit || '').trim().toLowerCase();
124
+ if (!u || !rolle) { return true; }
125
+ for (var i = 0; i < EINHEIT_ZU_ROLLE.length; i++) {
126
+ if (EINHEIT_ZU_ROLLE[i][0].test(rolle)) {
127
+ return EINHEIT_ZU_ROLLE[i][1].indexOf(u) > -1;
128
+ }
129
+ }
130
+ return true;
131
+ }
132
+
97
133
  function rateLage(e) {
98
134
  var mu = musterVon(e.want);
99
135
  if (!mu) { return { grund: 'keinMuster', frei: [], handWeg: 0 }; }
@@ -109,6 +145,22 @@ function rateLage(e) {
109
145
  zeileBelegt[x.id.slice(e.kanal.length + 1)] = true;
110
146
  });
111
147
  }
148
+ /* Greift das Muster noch nicht, liefert der Detektor gar nichts - und
149
+ dann sah alles frei aus, auch ein MODE, das eben bestaetigt worden
150
+ war. Der zweite Vorschlag bog genau diese Zeile wieder um und legte
151
+ dieselbe Quelle obendrein auf WORKING_MODE (U24, 19.09.2026). Also
152
+ zaehlt jede angehakte Zeile, die schon den Namen eines Platzes traegt
153
+ und dessen Rolle erfuellt, als belegt - vermutet oder entschieden.
154
+ Wo das Muster greift, sagt der Detektor dasselbe; hier kommt nichts
155
+ dazu. */
156
+ mu.states.forEach(function (pl) {
157
+ if (!pl.name || platzBelegt[pl.name]) { return; }
158
+ e.states.forEach(function (st) {
159
+ if (!st.on || st.n !== pl.name || !rolleTrifft(pl.role, st.role)) { return; }
160
+ platzBelegt[pl.name] = true;
161
+ zeileBelegt[st.n] = true;
162
+ });
163
+ });
112
164
  var namenDa = {};
113
165
  e.states.forEach(function (st) { namenDa[st.n] = true; });
114
166
 
@@ -161,19 +213,19 @@ function rateLage(e) {
161
213
  dann haette derselbe Quellpunkt aber zwei Plaetze, und die achte
162
214
  Pruefung („Kein Punkt doppelt") meldete es zu Recht. Also bleibt der
163
215
  Platz frei. Die Meldung unter der Leiste sagt, dass und warum. */
164
- var handWeg = 0;
216
+ var handWeg = 0, handNamen = [];
165
217
  var freieZeilen = e.states.filter(function (st) {
166
218
  if (!st.n || !st.srcR || zeileBelegt[st.n]) { return false; }
167
219
  if (quelleBelegt[st.srcR]) { return false; }
168
220
  if (st.srcW && quelleBelegt[st.srcW]) { return false; }
169
- if (st.geaendert) { handWeg++; return false; }
221
+ if (st.geaendert) { handWeg++; handNamen.push(st.n); return false; }
170
222
  return true;
171
223
  });
172
224
  if (!freieZeilen.length) {
173
- return { grund: 'vergeben', frei: freiNamen, handWeg: handWeg };
225
+ return { grund: 'vergeben', frei: freiNamen, handWeg: handWeg, handNamen: handNamen };
174
226
  }
175
227
  return { grund: null, mu: mu, namenDa: namenDa, freiePlaetze: freiePlaetze,
176
- freieZeilen: freieZeilen, frei: freiNamen, handWeg: handWeg };
228
+ freieZeilen: freieZeilen, frei: freiNamen, handWeg: handWeg, handNamen: handNamen };
177
229
  }
178
230
 
179
231
  /* Der Rateschritt - mit `trocken` rechnet er nur, ohne den Entwurf
@@ -196,7 +248,7 @@ function rateLage(e) {
196
248
  export function ratePlaetze(e, trocken) {
197
249
  var lage = rateLage(e);
198
250
  if (lage.grund) {
199
- return { anzahl: 0, grund: lage.grund, frei: lage.frei, handWeg: lage.handWeg };
251
+ return { anzahl: 0, grund: lage.grund, frei: lage.frei, handWeg: lage.handWeg, handNamen: lage.handNamen };
200
252
  }
201
253
  var namenDa = lage.namenDa;
202
254
  var freiePlaetze = lage.freiePlaetze, freieZeilen = lage.freieZeilen;
@@ -262,6 +314,7 @@ export function ratePlaetze(e, trocken) {
262
314
  /* Was unmoeglich ist, kommt gar nicht erst in die Bewertung. */
263
315
  if (pl.write === true && !st.srcW && !partnerVon[st.n]) { return -1; }
264
316
  if (!typPasst(pl, st)) { return -1; }
317
+ if (!einheitPasst(pl.defaultRole, st.unit)) { return -1; }
265
318
 
266
319
  var w = kernWoerter(st.n);
267
320
  var wSet = {};
@@ -388,7 +441,7 @@ export function ratePlaetze(e, trocken) {
388
441
  }
389
442
  });
390
443
  return { anzahl: gesetzt, grund: gesetzt ? null : 'keinTreffer',
391
- frei: lage.frei, handWeg: lage.handWeg };
444
+ frei: lage.frei, handWeg: lage.handWeg, handNamen: lage.handNamen };
392
445
  }
393
446
 
394
447
  /* Eine einzelne Vermutung zuruecknehmen. */
package/admin/js/werte.js CHANGED
@@ -42,7 +42,7 @@ export function jsonVon(id) {
42
42
  }
43
43
 
44
44
  export function feldWert(obj, pfad) {
45
- var t = pfad.split('.');
45
+ var t = pfadTeile(pfad);
46
46
  var x = obj;
47
47
  for (var i = 0; i < t.length; i++) {
48
48
  if (x === null || x === undefined || typeof x !== 'object') { return undefined; }
@@ -171,7 +171,11 @@ export function zeileAusAlias(o, zeilenname) {
171
171
  aber als Lesequelle gilt sie nicht: sonst zeigte „Alias bearbeiten"
172
172
  eine Quelle, die „Alias anlegen" nie eingetragen hat, und beim
173
173
  naechsten Aktualisieren kippte `read` still auf `true` zurueck. */
174
- srcR: (c.read === false && q.einfach) ? '' : (q.read || ''),
174
+ /* Seit 19.09.2026 auch beim Taster: `read: false` am Alias haengt an
175
+ der Rolle, nicht an einer fehlenden Lesequelle (`knopfZeile`). Ein
176
+ alter Taster-Alias mit einfacher `alias.id` liest also aus seinem
177
+ Schreibziel - genau das, was ioBroker mit ihm ohnehin tut. */
178
+ srcR: q.read || '',
175
179
  srcW: schreibQuelle(o),
176
180
  f: (typeof a.read === 'string') ? a.read : '',
177
181
  fw: (typeof a.write === 'string') ? a.write : '',
@@ -375,6 +379,49 @@ var FELDTEIL = "\\??\\.(?:[A-Za-z_$][\\w$]*|\\[(?:'(?:[^'\\\\]|\\\\.)*'|\"(?:[^\
375
379
  var FELDPFAD = new RegExp('^JSON\\.parse\\(val\\)((?:' + FELDTEIL + ')+)(?:\\s*\\?\\?\\s*null)?$');
376
380
  var FELDSTUECK = new RegExp(FELDTEIL, 'g');
377
381
 
382
+ /* Der Feldpfad als Text und zurueck (X12, 19.09.2026).
383
+
384
+ Gewoehnlich sind die Stuecke durch Punkte getrennt: `ENERGY.Power`,
385
+ `service.storage./dev/shm.free`. Das bleibt zeichengleich - so stehen
386
+ die Pfade in gespeicherten Vorlagen, und die sollen weiter gelten.
387
+ Nur ein Schluessel, der SELBST einen Punkt enthaelt, steht in Klammern
388
+ ohne Punkt davor: `cpu_usages['frigate.full_system'].cpu`. Vorher
389
+ wurde er an jedem Punkt zerteilt, die Formel suchte
390
+ `?.frigate?.full_system` und ergab null. Frigate hat so einen
391
+ Schluessel in `stats`. Ein Stueck, das mit `[` anfaengt, kommt aus
392
+ demselben Grund in Klammern. */
393
+ export function pfadAus(teile) {
394
+ var raus = '';
395
+ teile.forEach(function (t, n) {
396
+ t = String(t);
397
+ if (t.indexOf('.') > -1 || t.charAt(0) === '[') {
398
+ raus += "['" + t.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "']";
399
+ } else {
400
+ raus += (n ? '.' : '') + t;
401
+ }
402
+ });
403
+ return raus;
404
+ }
405
+
406
+ export function pfadTeile(pfad) {
407
+ var s = String(pfad || ''), teile = [], i = 0;
408
+ while (i < s.length) {
409
+ if (s.charAt(i) === '.') { i++; continue; }
410
+ if (s.slice(i, i + 2) === "['") {
411
+ var j = i + 2, t = '';
412
+ while (j < s.length && !(s.charAt(j) === "'" && s.charAt(j + 1) === ']')) {
413
+ if (s.charAt(j) === '\\' && j + 1 < s.length) { j++; }
414
+ t += s.charAt(j); j++;
415
+ }
416
+ teile.push(t); i = j + 2; continue;
417
+ }
418
+ var k = i;
419
+ while (k < s.length && s.charAt(k) !== '.' && s.slice(k, k + 2) !== "['") { k++; }
420
+ teile.push(s.slice(i, k)); i = k;
421
+ }
422
+ return teile;
423
+ }
424
+
378
425
  export function feldAusFormel(f) {
379
426
  var m = FELDPFAD.exec(f || '');
380
427
  if (!m) { return ''; }
@@ -386,21 +433,19 @@ export function feldAusFormel(f) {
386
433
  if (s.charAt(0) === '[') {
387
434
  /* Nur die Fluchtzeichen zurueckdrehen, die feldSegment setzt. */
388
435
  s = s.slice(2, -2).replace(/\\(.)/g, '$1');
389
- /* Ein Punkt im Schluessel selbst laesst sich im Pfadformat von
390
- `jsonFelder` nicht wieder auseinanderhalten — dann lieber
391
- „eigene Formel" zeigen als einen Pfad, der nicht zurueckfuehrt. */
392
- if (s.indexOf('.') > -1) { return ''; }
393
436
  }
394
437
  teile.push(s);
395
438
  }
396
- return teile.join('.');
439
+ /* Ein Punkt im Schluessel selbst kommt seit X12 in Klammern zurueck
440
+ (`pfadAus`); vorher hiess das hier „eigene Formel". */
441
+ return pfadAus(teile);
397
442
  }
398
443
 
399
444
  /* Der abgesicherte Zugriff auf ein JSON-Feld. An einer Stelle, damit die
400
445
  Vorlagen-Abkuerzung und die Feldauswahl im Detail dasselbe bauen. */
401
446
  export function feldFormel(feld) {
402
447
  if (!feld) { return ''; }
403
- return 'JSON.parse(val)' + String(feld).split('.').map(feldSegment).join('') + ' ?? null';
448
+ return 'JSON.parse(val)' + pfadTeile(feld).map(feldSegment).join('') + ' ?? null';
404
449
  }
405
450
 
406
451
  /* ---- Feldpfade: Ende ---- */
@@ -412,18 +457,49 @@ export function jsonFelder(id) {
412
457
  try { o = JSON.parse(st.val); } catch { return null; }
413
458
  if (!o || typeof o !== 'object') { return null; }
414
459
  var raus = [];
415
- (function geh(x, pfad, tiefe) {
460
+ (function geh(x, weg, tiefe) {
416
461
  if (tiefe > 3) { return; }
417
462
  Object.keys(x).forEach(function (k) {
418
- var p = pfad ? pfad + '.' + k : k;
419
- if (x[k] && typeof x[k] === 'object' && !Array.isArray(x[k])) { geh(x[k], p, tiefe + 1); }
420
- else { raus.push(p); }
463
+ var w = weg.concat([k]);
464
+ if (x[k] && typeof x[k] === 'object' && !Array.isArray(x[k])) { geh(x[k], w, tiefe + 1); }
465
+ else { raus.push(pfadAus(w)); }
421
466
  });
422
- })(o, '', 0);
467
+ })(o, [], 0);
423
468
  return raus;
424
469
  }
425
470
 
426
471
 
472
+ /* Ist das Objekt ein Taster - ein Punkt ohne Zustand?
473
+
474
+ `read: false` allein reicht nicht (siehe `zielKann`). Dazu muss er
475
+ schreibbar sein und sich als Tastendruck ausweisen: Homematic ueber
476
+ `native.TYPE: ACTION`, alle anderen ueber eine Rolle `button…`. Die
477
+ eine Stelle fuer diese Frage - der Entwurf fragt sie auch. */
478
+ export function istTaster(o) {
479
+ var c = (o && o.common) || {};
480
+ var nat = (o && o.native) || {};
481
+ return c.read === false && !!c.write &&
482
+ (nat.TYPE === 'ACTION' || /^button/.test(String(c.role || '')));
483
+ }
484
+
485
+ /* Wird der Alias-Datenpunkt ein Knopf?
486
+
487
+ Die eine Regel fuer `common.read` am Alias (Ricardo, 19.09.2026): Rolle
488
+ `button…` UND ein Schreibziel -> Knopf, `read: false`. Alles andere
489
+ `read: true`. So steht es in der Rollenliste von ioBroker: „Buttons
490
+ (booleans, write-only) … common.write=true, common.read=false"; der
491
+ Tastensensor (`button.press`, `button.long` ohne Schreiben) liest.
492
+
493
+ Nicht aus der Quelle abgeleitet: deren `read` setzt jeder Adapter
494
+ anders. hm-rpc schreibt `read: false` an `BOOST_MODE`, einen Schalter,
495
+ der seinen Zustand meldet - uebernommen, zeichneten vis, Alexa und
496
+ matter den Boost als Knopf. Und nicht aus einer fehlenden Lesequelle:
497
+ gemessen am Testsystem liest ein Alias mit `read: false` trotzdem aus
498
+ seiner Lesequelle, jeder Druck kommt an. */
499
+ export function knopfZeile(s) {
500
+ return !!(s && s.srcW && /^button/.test(String(s.role || '')));
501
+ }
502
+
427
503
  /* ---- Was das Ziel kann ---------------------------------------------
428
504
 
429
505
  Die Werkbank schreibt auf fremde Punkte und liest aus fremden Punkten.
@@ -440,8 +516,15 @@ export function jsonFelder(id) {
440
516
  Schreibvorgang trotzdem durch — gemessen —, der Adapter
441
517
  dahinter reicht ihn aber nicht ans Geraet weiter.
442
518
  Ein stiller Blindgaenger.
443
- `meldetNichts` Die Lesequelle traegt `read: false`. Ein Tastendruck
444
- hat keinen Zustand; gelesen wird dort nie etwas.
519
+ Bis 19.09.2026 stand an dritter Stelle `meldetNichts`: jede Lesequelle
520
+ mit `read: false` bekam die Marke „aendert sich nie". Gemessen am
521
+ Produktivsystem traf das 1371 Punkte, 620 davon keine Taster, und 76
522
+ dieser 620 hatten ihren Wert in den letzten 30 Tagen geaendert -
523
+ `BOOST_MODE` an den Heizungen etwa. hm-rpc setzt `read: false`, sobald
524
+ der Wert nicht *abfragbar* ist (OPERATIONS ohne Bit 1); gemeldet wird er
525
+ trotzdem. Aus dem Feld allein laesst sich nicht ablesen, ob ein Punkt
526
+ lebt, und wer der Marke folgte, nahm eine funktionierende Lesequelle
527
+ heraus (Ricardo).
445
528
 
446
529
  Gibt eine Liste zurueck, nicht ein Urteil — wer sie anzeigt, entscheidet
447
530
  ueber die Schaerfe. */
@@ -463,10 +546,5 @@ export function zielKann(s) {
463
546
  }
464
547
  }
465
548
 
466
- if (s.srcR) {
467
- var r = S.objects[s.srcR];
468
- var rc = (r && r.common) || null;
469
- if (rc && rc.read === false) { raus.push({ art: 'meldetNichts', punkt: s.srcR }); }
470
- }
471
549
  return raus;
472
550
  }
@@ -4,7 +4,7 @@
4
4
  import { S } from './zustand.js';
5
5
  import { el } from './basis.js';
6
6
  import { tr, txt } from './sprache.js';
7
- import { enums, enumsUnbekannt } from './enums.js';
7
+ import { enums, enumsUnbekannt, beiEnumsGeladen } from './enums.js';
8
8
  import {
9
9
  enumListe,
10
10
  enumsVon,
@@ -238,6 +238,15 @@ export function enumZeile(host, e) {
238
238
  ex.title = tr('enums.clear');
239
239
  ex.setAttribute('aria-label', tr('enums.clear'));
240
240
  ex.hidden = !ei.value;
241
+ /* Aufzaehlungen nicht geladen: dann wird Raum und Funktion nicht
242
+ geschrieben, also laesst sich hier auch nichts waehlen. Vorher
243
+ stand das nur im Trockenlauf, und die Felder sahen aus wie immer
244
+ (P8, 19.09.2026). */
245
+ if (enumsUnbekannt()) {
246
+ ei.disabled = true;
247
+ ex.disabled = true;
248
+ ei.title = tr('enums.unknown');
249
+ }
241
250
  ex.addEventListener('mousedown', function (ev) {
242
251
  ev.preventDefault();
243
252
  e[spec.feld] = '';
@@ -270,7 +279,7 @@ export function enumZeile(host, e) {
270
279
  if (spec.art === 'rooms' && e.zuletzt === 'raum' && e.ziel && !aliasFuer(e.kanal)) {
271
280
  var ro2 = enumAlsOrdner(e.raum);
272
281
  if (ro2 && e.zielOrdner !== 'alias.0.' + ro2) {
273
- var oz = el('div', 'uebernahme' +
282
+ var oz = el('div', 'uebernahme raumangebot' +
274
283
  (angebotFrisch('raum:' + ro2) ? ' frisch' : ''));
275
284
  oz.appendChild(el('span', 'zq', tr('enums.alsoFolder', ro2)));
276
285
  var ob = el('button', 'btn mini wichtig', tr('target.yes'));
@@ -341,15 +350,7 @@ export function enumZeile(host, e) {
341
350
  : (x.anzahl ? tr('enums.members', x.anzahl) : tr('enums.unused'))));
342
351
  z.addEventListener('mousedown', function (ev) {
343
352
  ev.preventDefault();
344
- e[spec.feld] = x.id;
345
- e[spec.her] = 'hand';
346
- if (spec.art === 'rooms') { e.zuletzt = 'raum'; }
347
- /* Das Icon jetzt holen, nicht erst beim Schreiben: dort ist
348
- kein Warten mehr moeglich, der Objektbau laeuft synchron. */
349
- if (x.vorlage && x.vorlage.icon) { holeIkon(spec.art, x.vorlage.icon); }
350
- entwurfAngefasst();
351
- zu();
352
- zeichneErgebnis();
353
+ nimm(x);
353
354
  });
354
355
  el2.appendChild(z);
355
356
  });
@@ -366,6 +367,23 @@ export function enumZeile(host, e) {
366
367
  el2.hidden = false;
367
368
  };
368
369
 
370
+ /* Einen Eintrag der Liste uebernehmen — fuer Mausklick und Enter
371
+ dieselbe Stelle. Bis 19.09.2026 waren es zwei, und der Enter-Weg
372
+ vergass zweierlei: `e.zuletzt`, an dem das Angebot „Den Alias auch
373
+ unter … ablegen?“ haengt, und das Bild einer neuen Aufzaehlung aus
374
+ den Vorlagen des Admin (Lauf vom 19.09.2026, S3). */
375
+ function nimm(x) {
376
+ e[spec.feld] = x.id;
377
+ e[spec.her] = 'hand';
378
+ if (spec.art === 'rooms') { e.zuletzt = 'raum'; }
379
+ /* Das Icon jetzt holen, nicht erst beim Schreiben: dort ist
380
+ kein Warten mehr moeglich, der Objektbau laeuft synchron. */
381
+ if (x.vorlage && x.vorlage.icon) { holeIkon(spec.art, x.vorlage.icon); }
382
+ entwurfAngefasst();
383
+ zu();
384
+ zeichneErgebnis();
385
+ }
386
+
369
387
  ei.addEventListener('focus', function () { ei.value = ''; mMark = -1; malen(); });
370
388
  ei.addEventListener('input', function () { mMark = -1; malen(); });
371
389
  ei.addEventListener('blur', function () {
@@ -387,18 +405,23 @@ export function enumZeile(host, e) {
387
405
  }
388
406
  if (ev.key === 'Enter' && !el2.hidden && mListe.length) {
389
407
  ev.preventDefault();
390
- e[spec.feld] = mListe[mMark >= 0 ? mMark : 0].id;
391
- e[spec.her] = 'hand';
392
- entwurfAngefasst();
393
- zu();
394
- zeichneErgebnis();
408
+ nimm(mListe[mMark >= 0 ? mMark : 0]);
395
409
  }
396
410
  });
397
411
  });
398
412
  if (enumAngebot) { ebar.appendChild(enumAngebot); }
399
413
  host.appendChild(ebar);
414
+ if (enumsUnbekannt()) {
415
+ var eh = el('div', 'aside w', tr('enums.notLoadedField'));
416
+ eh.style.margin = '4px 0 8px';
417
+ host.appendChild(eh);
418
+ }
400
419
  }
401
420
 
421
+ /* Klappt ein Nachversuch, verschwindet der Hinweis von selbst - sonst
422
+ stuende er bis zum naechsten Klick da, obwohl alles wieder geht. */
423
+ beiEnumsGeladen(function () { if (S.current) { zeichneErgebnis(); } });
424
+
402
425
  /* Welcher Raum, welche Funktion - und woher.
403
426
 
404
427
  Gibt es den Alias schon, gilt, was dort steht: seine Zuordnung ist eine
@@ -14,6 +14,11 @@ export var S = {
14
14
  objects: {},
15
15
  keysSorted: [],
16
16
  kleinIndex: {},
17
+ /* Zaehlt jede Aenderung an S.objects. Wer sich etwas aus den Objekten
18
+ merkt (die Karte Quelle -> Alias in entwurf.js), vergleicht hiermit
19
+ statt mit der Zahl der Schluessel — beim Verlegen verschwinden genau
20
+ so viele, wie dazukommen (T22). */
21
+ objektStand: 0,
17
22
  /* Alias-Kanal -> Zahl der Punkte, deren Quelle es nicht (mehr) gibt.
18
23
  Einmal beim Laden gerechnet; der Baum schlaegt nur nach. */
19
24
  aliasKaputt: {},
@@ -44,7 +49,6 @@ export var S = {
44
49
  loeschListe: [],
45
50
  loeschZiel: null,
46
51
  loeschAlleAusgaenge: false,
47
- verlegeZiel: null,
48
52
  /* Einmal registrierter Schliesser fuer die Quellenauswahl am
49
53
  Sprungknopf - siehe ergebnis.js. */
50
54
  sprungZuhoerer: false,