iobroker.alias-workbench 0.9.11 → 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.
package/admin/js/enums.js CHANGED
@@ -32,6 +32,11 @@ export function enumsUnbekannt() { return unbekannt; }
32
32
  var versuch = 0;
33
33
  var wiederTimer = null;
34
34
 
35
+ /* Wer wissen will, wann ein Nachversuch geklappt hat - die Felder fuer
36
+ Raum und Funktion zeichnen sich dann neu (P8). */
37
+ var geladenHoerer = [];
38
+ export function beiEnumsGeladen(fn) { geladenHoerer.push(fn); }
39
+
35
40
  export function ladeEnums(danach) {
36
41
  if (wiederTimer) { clearTimeout(wiederTimer); wiederTimer = null; }
37
42
  socket.emit('getObjectView', 'system', 'enum',
@@ -40,8 +45,8 @@ export function ladeEnums(danach) {
40
45
  /* Den alten Stand behalten. Er ist vielleicht veraltet, aber
41
46
  veraltet ist naeher an der Wahrheit als leer. */
42
47
  unbekannt = true;
43
- console.warn('[alias-workbench] Aufzaehlungen nicht geladen: ' +
44
- (err || 'keine Antwort'));
48
+ console.warn('[alias-workbench] Enumerations not loaded: ' +
49
+ (err || 'no answer'));
45
50
  if (versuch < 3) {
46
51
  versuch++;
47
52
  wiederTimer = setTimeout(function () {
@@ -59,8 +64,10 @@ export function ladeEnums(danach) {
59
64
  if (r.value && r.id) { frisch[r.id] = r.value; }
60
65
  });
61
66
  enums = frisch;
67
+ var warUnbekannt = unbekannt;
62
68
  unbekannt = false;
63
69
  versuch = 0;
70
+ if (warUnbekannt) { geladenHoerer.forEach(function (fn) { fn(); }); }
64
71
  if (typeof danach === 'function') { danach(null); }
65
72
  });
66
73
  }
@@ -1,7 +1,7 @@
1
1
  import { S } from './zustand.js';
2
2
  import './start.js';
3
3
  import { socket } from './verbindung.js';
4
- import { D, $, el, klappZeichen } from './basis.js';
4
+ import { D, $, el } from './basis.js';
5
5
  import { tr, sprachtext } from './sprache.js';
6
6
  import './einstellungen.js';
7
7
  import './enums.js';
@@ -27,13 +27,17 @@ import {
27
27
  mitNachfrage,
28
28
  geraeteDarunter,
29
29
  aliasFuer,
30
+ ausgangsQuellen,
30
31
  quellenVerteilung,
31
32
  quelleHier,
32
33
  tipptGerade,
33
34
  opt,
34
35
  vorlagenAbweichung,
35
36
  vorlageAnwenden,
36
- bestandVorrang
37
+ bestandVorrang,
38
+ kanalGruppen,
39
+ aufteilBehaelter,
40
+ rohEntwurf
37
41
  } from './entwurf.js';
38
42
  import { setzeEnums, setzeZiel, unterschiede } from './zuordnung.js';
39
43
  import './detail.js';
@@ -354,6 +358,15 @@ var warumAuf = false;
354
358
  if (!sprungZiel) {
355
359
  var vermerkt = S.objects[S.current] && S.objects[S.current].native && S.objects[S.current].native.quelle;
356
360
  if (vermerkt) { sub.appendChild(el('span', 'chip warn', tr('result.sourceGone', vermerkt))); }
361
+ /* Der Knopf bleibt sichtbar, aber gesperrt: verschwaende er, suchte
362
+ man ihn; klickbar sprang er frueher irgendwohin (D10). */
363
+ var wohin = vermerkt || (insLeere[0] && insLeere[0].srcR) || '';
364
+ if (wohin) {
365
+ sprung = el('button', 'btn schmal sprungknopf', tr('result.toSource') + ' →');
366
+ sprung.disabled = true;
367
+ sprung.title = tr('result.sourceGone', wohin);
368
+ namensZeile.appendChild(sprung);
369
+ }
357
370
  }
358
371
  } else {
359
372
  sprungZiel = aliasFuer(S.current);
@@ -505,22 +518,34 @@ var warumAuf = false;
505
518
  /* Der Vorlagenkasten: welche Vorlage gegriffen hat, warum, ihre
506
519
  Wahl und Knoepfe. Nimmt die Musterwahl bei sich auf; ohne Kasten
507
520
  bleibt sie im Kopf - bis die Leiste sie sich holt. */
508
- function baueVorlagenkasten(host, head, e, istQuelle, musterBlock) {
521
+ /* Zurueck kommt, was noch keinen Platz hat: der Musterblock (ohne
522
+ Kasten) und der Knopf fuer die Leiste ueber der Liste. Im Kasten
523
+ sitzt seit 19.09.2026 „Freie Plaetze vorschlagen"; der Knopf, der
524
+ den ganzen Bestand des Geraets laedt, wandert dafuer in die Leiste -
525
+ er aendert die Liste, nicht die Erkennung (Ricardo). */
526
+ function baueVorlagenkasten(host, head, e, istQuelle, musterBlock, rateKnopf) {
527
+ var leistenKnopf = rateKnopf;
509
528
  if (e.vorschlag || istQuelle) {
510
529
  var sg = el('div', 'suggestbox');
511
530
  if (!e.vorschlag) {
512
531
  sg.style.borderColor = 'var(--line-strong)';
513
532
  sg.style.background = 'var(--surface-2)';
514
533
  }
515
- var hd = el('div', 'hd', e.vorschlag
534
+ var hd = el('div', 'hd');
535
+ /* Der Name steht in einer eigenen Spanne, damit er sich kuerzen
536
+ laesst. Die Kopfzeile bricht nicht mehr um: vorher nahm sich der
537
+ Hinweis rechts eine eigene Zeile, sobald Marken dazukamen und der
538
+ Name lang wurde („Homematic Rollladenaktor" + von Hand gewaehlt +
539
+ erzwungen). Jetzt weicht stattdessen der Name zurueck — vollstaendig
540
+ steht er im Tooltip und im Auswahlfeld darunter (Ricardo, 17.09.2026). */
541
+ var hdText = e.vorschlag
516
542
  ? tr('tpl.identifiedAs', e.tplName)
517
- : tr('tpl.noneDetected'));
543
+ : tr('tpl.noneDetected');
544
+ var hdName = el('span', 'tname', hdText);
545
+ hdName.title = hdText;
546
+ hd.appendChild(hdName);
518
547
  if (e.vorlageVersion) {
519
- var vv2 = el('span', null, ' · ' + e.vorlage + ' v' + e.vorlageVersion);
520
- vv2.style.fontFamily = 'var(--mono)';
521
- vv2.style.fontSize = '10.5px';
522
- vv2.style.fontWeight = '400';
523
- vv2.style.color = 'var(--ink-3)';
548
+ var vv2 = el('span', 'tid', ' · ' + e.vorlage + ' v' + e.vorlageVersion);
524
549
  hd.appendChild(vv2);
525
550
  }
526
551
  if (e.vonHandGewaehlt) {
@@ -571,13 +596,22 @@ var warumAuf = false;
571
596
  Breite, die eine Liste braucht. */
572
597
  var warumKopf = null, warumLeib = null;
573
598
  if (gruende.length || weggelassen.length) {
574
- warumKopf = el('div', 'wkopf');
575
599
  /* Ohne Vorlage waere „Warum diese Vorlage" die falsche Frage —
576
600
  dann steht hier, warum keine greift. */
577
- warumKopf.appendChild(el('span', null, weggelassen.length
578
- ? tr('tpl.skippedHead', weggelassen.length)
579
- : tr(e.vorschlag ? 'tpl.whyThis' : 'tpl.whyNone')));
580
- warumKopf.appendChild(klappZeichen(warumAuf));
601
+ var warumText = tr(e.vorschlag ? 'tpl.whyThis' : 'tpl.whyNone');
602
+ warumKopf = el('div', 'wkopf' + (warumAuf ? ' auf' : ''));
603
+ /* Statt „Warum diese Vorlage +" nur ein Fragezeichen: der Satz
604
+ steht im Tooltip und spart die halbe Kopfzeile. Was weggelassen
605
+ wurde, bleibt als kurze Marke stehen — dass Punkte fehlen, soll
606
+ niemand erst suchen muessen (Ricardo, 17.09.2026). */
607
+ if (weggelassen.length) {
608
+ warumKopf.appendChild(el('span', 'chip mut',
609
+ tr('tpl.skippedChip', weggelassen.length)));
610
+ warumText = tr('tpl.skippedHead', weggelassen.length) + ' — ' + warumText;
611
+ }
612
+ warumKopf.appendChild(el('span', 'fragez', '?'));
613
+ warumKopf.title = warumText;
614
+ warumKopf.setAttribute('aria-label', warumText);
581
615
  warumKopf.tabIndex = 0;
582
616
  var um2 = function () { warumAuf = !warumAuf; zeichneErgebnis(); };
583
617
  warumKopf.addEventListener('click', um2);
@@ -634,6 +668,25 @@ var warumAuf = false;
634
668
  v2.zielOrdner = e.zielOrdner;
635
669
  v2.zielName = ausgangName(v2, selA.value);
636
670
  v2.ziel = v2.zielOrdner + '.' + v2.zielName;
671
+ /* Dieser Weg fragte bisher gar nicht nach dem Bestand -
672
+ deshalb sah derselbe Ausgang je nach Weg anders aus:
673
+ ueber den Baum kamen die Haken eines fremden Alias, hier
674
+ die Vorgaben der Vorlage. Jetzt stellen alle drei Wege
675
+ dieselbe Frage (C13, 16.09.2026).
676
+
677
+ Und die Zielzeile folgt derselben Antwort. Sie wird hier von
678
+ Hand zusammengesetzt und laeuft nicht durch `setzeZiel`;
679
+ ohne diese Zeilen zeigte sie beim Umschalten weiter auf
680
+ `…POWER2` und meldete „wird neu angelegt", waehrend
681
+ `SUNLU` daneben stand. */
682
+ var vorhA2 = aliasFuer(S.current, ausgangsQuellen(v2));
683
+ if (vorhA2) {
684
+ var t2 = vorhA2.split('.');
685
+ v2.zielName = t2.pop();
686
+ v2.zielOrdner = t2.join('.');
687
+ v2.ziel = vorhA2;
688
+ }
689
+ if (vorhA2 && S.objects[vorhA2]) { bestandVorrang(v2, vorhA2); }
637
690
  S.entwurf = v2; S.openRow = null; zeichneErgebnis();
638
691
  }
639
692
  });
@@ -650,7 +703,10 @@ var warumAuf = false;
650
703
  das Ausgangsfeld daneben blieb ungestylt in der Browserschrift. */
651
704
  var selV = el('select', 'tx');
652
705
 
653
- selV.appendChild(opt('', e.vorschlag ? tr('tpl.switch') : tr('tpl.choose')));
706
+ /* Greift eine Vorlage, ist der erste Eintrag kein Titel, sondern
707
+ eine Wahl: sie wieder loswerden. „— Vorlage wechseln —" stand
708
+ wie eine Handlung da, tat aber nichts (Ricardo, 17.09.2026). */
709
+ selV.appendChild(opt('', e.vorschlag ? tr('tpl.none') : tr('tpl.choose')));
654
710
  S.VORLAGEN.forEach(function (v) {
655
711
  var t = pruefeVorlage(v, S.current);
656
712
  /* Auch nicht passende bleiben waehlbar — mit Grund daneben. Sonst
@@ -663,7 +719,35 @@ var warumAuf = false;
663
719
  selV.appendChild(o);
664
720
  });
665
721
  selV.addEventListener('change', function () {
666
- if (!selV.value) { return; }
722
+ if (!selV.value) {
723
+ /* „keine Vorlage": den Rohentwurf nehmen. Ziel, Raum und
724
+ Funktion bleiben — die hat der Mensch eingestellt, nicht die
725
+ Vorlage. `angefasst` verhindert, dass der naechste
726
+ eintreffende Messwert die Vorlage wieder heranzieht. */
727
+ if (!e.vorschlag) { return; }
728
+ var r = rohEntwurf(S.current);
729
+ if (!r) { selV.value = e.vorlage || ''; return; }
730
+ r.ziel = e.ziel; r.zielOrdner = e.zielOrdner; r.zielName = e.zielName;
731
+ r.raum = e.raum; r.funktion = e.funktion;
732
+ r.raumHer = e.raumHer; r.funktionHer = e.funktionHer;
733
+ /* Der Ausgang wird NICHT uebernommen. Er ist keine Eigenschaft
734
+ des Geraets, sondern des `mehrfach`-Blocks der Vorlage:
735
+ `instanzenVon` gibt ohne ihn `[null]` zurueck. Wurde die
736
+ Vorlage abgewaehlt, gibt es keinen Platzhalter mehr und damit
737
+ nichts zu waehlen. Bis 17.09.2026 erbte der Rohentwurf die
738
+ Ausgaenge der alten Vorlage: der Kasten blieb stehen, und wer
739
+ ihn benutzte, holte die abgewaehlte Vorlage kommentarlos
740
+ zurueck — der Ausgang-Zweig ruft `vorschlag` mit leerem
741
+ `e.vorlage` und laesst neu erkennen (gemessen an
742
+ `hm.ZWEIKANAL`: nach dem Umschalten auf `Licht_Esstisch`
743
+ stand `hm-schaltaktor-mehrfach` wieder im Feld). Die beiden
744
+ Kanaele laufen ohne Vorlage ueber „Unterordner je Kanal". */
745
+ S.entwurf = r;
746
+ S.openRow = null;
747
+ entwurfAngefasst();
748
+ zeichneErgebnis();
749
+ return;
750
+ }
667
751
  var v = vorschlag(S.current, selV.value);
668
752
  if (v) {
669
753
  v.ziel = e.ziel;
@@ -691,7 +775,6 @@ var warumAuf = false;
691
775
  der nur die vorgeschlagenen Zeilen ein- und ausblendet. Deshalb
692
776
  steht das Geraet im Namen und der Rest im Hinweis. */
693
777
  bRoh.title = e.roh ? tr('tpl.showProposalHelp') : tr('tpl.showAllPointsHelp');
694
- if (!e.vorschlag) { bRoh.disabled = true; }
695
778
  bRoh.addEventListener('click', function () {
696
779
  var rohJetzt = !e.roh;
697
780
  /* Dieselben zwei Schritte wie in `waehle`: die Vorlage, aus der der
@@ -751,7 +834,11 @@ var warumAuf = false;
751
834
  neuE.zielName = e.zielName;
752
835
  neuE.ordnerVonHand = e.ordnerVonHand;
753
836
  if (fest) { neuE.vonHandGewaehlt = false; neuE.ausBestand = true; }
754
- if (vorh && S.objects[vorh]) { bestandVorrang(neuE, vorh); }
837
+ /* Wie beim ersten Aufbau: am Mehrfachgeraet zaehlt der Alias
838
+ DIESES Ausgangs (C13). */
839
+ var vorhW = aliasFuer(S.current, ausgangsQuellen(neuE)) ||
840
+ ((neuE.instanz === null || neuE.instanz === undefined) ? vorh : null);
841
+ if (vorhW && S.objects[vorhW]) { bestandVorrang(neuE, vorhW); }
755
842
  S.entwurf = neuE; S.openRow = null; zeichneErgebnis();
756
843
  });
757
844
  /* An der Quelle bleibt die Musterwahl im Vorlagenkasten, wo sie
@@ -782,8 +869,12 @@ var warumAuf = false;
782
869
  knoepfe.appendChild(bTpl);
783
870
  }
784
871
  }
785
- knoepfe.appendChild(bRoh);
786
- acts.appendChild(knoepfe);
872
+ if (rateKnopf) { knoepfe.appendChild(rateKnopf); }
873
+ if (knoepfe.childNodes.length) { acts.appendChild(knoepfe); }
874
+ /* Ohne Vorlagenvorschlag gibt es keinen Bestand, zu dem er
875
+ umschalten koennte - dann steht er gar nicht erst da, statt
876
+ ausgegraut in der Leiste. */
877
+ leistenKnopf = e.vorschlag ? bRoh : null;
787
878
  sg.appendChild(acts);
788
879
  host.appendChild(sg);
789
880
  }
@@ -792,7 +883,7 @@ var warumAuf = false;
792
883
  if (musterBlock) {
793
884
  head.appendChild(musterBlock);
794
885
  }
795
- return musterBlock;
886
+ return { musterBlock: musterBlock, leistenKnopf: leistenKnopf };
796
887
  }
797
888
 
798
889
  /* Die Karte „Gehoert nicht zum <Muster>" stand hier bis zum
@@ -844,7 +935,22 @@ var warumAuf = false;
844
935
  var bd = $('#btn-dry');
845
936
  var ba = $('#btn-dry-alle');
846
937
  var mehr = e.instanzen && e.instanzen.length > 1;
847
- var mehrK = !mehr && (e.kanalGeraete || []).length > 1;
938
+ /* Welche Unterordner entstuenden — nach den Haken, dieselbe Rechnung
939
+ wie der Schreibweg (C51). */
940
+ var grp = mehr ? [] : kanalGruppen(e).filter(function (g) { return !g.eigen; });
941
+ /* Zwei Kanaele mit Haken genuegen — auch wenn einer davon der
942
+ Wartungskanal ist.
943
+
944
+ Zwischenzeitlich zaehlte `.0` hier nicht mit, damit der Knopf nicht
945
+ an jedem Fuehler steht. Das ergab aber eine Unstimmigkeit, die beim
946
+ Benutzen auffaellt: fuer den INHALT zaehlte der Kanal (er bekommt
947
+ seinen Ordner), fuer den ANLASS nicht — man hakt etwas an und
948
+ nichts passiert. Ricardo am 17.09.2026: „zählt ist ja eher ne
949
+ Expertenfunktion, da weiss man schon was man macht." Also eine
950
+ Regel statt zweier. */
951
+ var typVonK = {};
952
+ (e.kanalGeraete || []).forEach(function (k) { typVonK[k.id] = k.typ; });
953
+ var mehrK = !mehr && grp.length > 1;
848
954
  /* Die Knoepfe gehoeren zum Modus, nicht nur zum Objekt: Im Modus
849
955
  „Alias anlegen" wird angelegt, nicht verwaltet — Verlegen, Quelle
850
956
  tauschen und Entfernen haben dort nichts zu suchen. Im Modus
@@ -891,13 +997,25 @@ var warumAuf = false;
891
997
  return (e.zielOrdner || '') + '.' + nm;
892
998
  }).join(String.fromCharCode(10));
893
999
  } else if (mehrK) {
894
- ba.textContent = tr('write.createPerChannel', e.kanalGeraete.length);
1000
+ ba.textContent = tr('write.createPerChannel', grp.length);
895
1001
  /* Im Hinweis stehen die Namen, die entstehen wuerden — sonst
896
1002
  klickt man auf eine Zahl und weiss nicht, was danach dasteht. */
897
- ba.title = e.kanalGeraete.map(function (k) {
1003
+ /* Dieselbe Rechnung wie der Schreibweg (schreiben.js, `ordner`):
1004
+ ein Behaelter fuer das Geraet, darunter je Kanal einer. Hier
1005
+ stand nur `zielOrdner` — also eine Ebene zu wenig. Am
1006
+ Geschirrspueler versprach der Hinweis `alias.0.Befehle`,
1007
+ entstanden waere `alias.0.Geschirrspueler.Befehle`; am
1008
+ Wohnzimmer_Dimmer las man `alias.0.1`, `alias.0.2`, `alias.0.3`
1009
+ — drei Aliase, nach blanken Kanalnummern benannt, direkt unter
1010
+ alias.0. Gefunden im Lauf vom 16.09.2026 (C15). */
1011
+ var ordnerK = aufteilBehaelter(e);
1012
+ ba.title = grp.map(function (g) {
898
1013
  /* Die Kennung, nicht den Anzeigenamen - so heisst das Objekt
899
- hinterher wirklich (G31). */
900
- return (e.zielOrdner || '') + '.' + (k.kennung || k.name) + ' (' + k.typ + ')';
1014
+ hinterher wirklich (G31). Dahinter, was hineinkommt: die
1015
+ Zahl der angehakten Zeilen — daran sieht man vor dem Klick,
1016
+ dass nichts Erfundenes dabei ist. */
1017
+ return ordnerK + '.' + (g.kennung || g.name)
1018
+ + ' (' + g.zeilen.length + (typVonK[g.id] ? ', ' + typVonK[g.id] : '') + ')';
901
1019
  }).join(String.fromCharCode(10));
902
1020
  }
903
1021
  ba.disabled = false;
@@ -967,7 +1085,9 @@ var warumAuf = false;
967
1085
 
968
1086
  var istQuelle = baueZielleiste(host, e);
969
1087
 
970
- musterBlock = baueVorlagenkasten(host, head, e, istQuelle, musterBlock);
1088
+ var kasten = baueVorlagenkasten(host, head, e, istQuelle, musterBlock, rateKnopf);
1089
+ musterBlock = kasten.musterBlock;
1090
+ rateKnopf = kasten.leistenKnopf;
971
1091
 
972
1092
  /* Gleich unter die Erkennung: was hier fehlt, entscheidet mit
973
1093
  darueber, ob eine Vorlage ueberhaupt greift. */
@@ -32,6 +32,11 @@ import { D } from './basis.js';
32
32
  export function ausdruckText(r) {
33
33
  if (!r) { return ''; }
34
34
  if (r.source !== undefined) { return r.source; }
35
+ /* Ein Objekt ohne `source` ist kein Ausdruck. `ignoreRole` kommt im
36
+ Browser als leeres `{}` an; `String({})` gab „[object Object]", und
37
+ das ist eine Zeichenklasse, die jede Rolle mit einem „e" trifft —
38
+ jeder Sollwert der Klimaanlage galt als ausgenommen (19.09.2026). */
39
+ if (typeof r === 'object') { return ''; }
35
40
  var q = String(r);
36
41
  var m = /^\/(.*)\/[a-z]*$/.exec(q);
37
42
  return m ? m[1] : q;
@@ -226,6 +231,13 @@ export function musterVon(typ) {
226
231
  statt als `electricity` nur noch als `info` erkannt. Am Bildschirm
227
232
  stand dazu nichts weiter als „passt nicht".
228
233
 
234
+ Zur Rolle selbst: `value.power.consumption` ist in der Rollenliste von
235
+ ioBroker durchgestrichen, der type-detector 6.0.x erkennt CONSUMPTION
236
+ aber NUR mit genau dieser Rolle (typePatterns.js, Muster `consumption`).
237
+ Die mitgelieferten Vorlagen behalten sie deshalb bewusst; jede traegt
238
+ den Grund im Feld `_rolle`. Role note for reviewers: the deprecated role
239
+ is kept on purpose because the type-detector requires it.
240
+
229
241
  Dieselbe Suche stand vorher zweimal im Code (detail.js und
230
242
  vorlagenblatt.js), beide Male ohne den Typ anzusehen. */
231
243
  export function plaetzeFuerRolle(musterName, rolle) {
@@ -304,6 +316,46 @@ export function typKonflikte(states, musterName) {
304
316
  return raus;
305
317
  }
306
318
 
319
+ /* Welche Pflicht eines Musters der Entwurf wirklich nicht erfuellt.
320
+
321
+ Bis 19.09.2026 nannte das Musterfeld schlicht alle `required`-Plaetze:
322
+ an der Klimaanlage stand „✕ MODE fehlt", obwohl MODE angehakt und
323
+ bestaetigt war — gefehlt hat der Sollwert. Der steht im Muster nicht
324
+ als `required`, sondern als Gruppe (`requiredOneOf: 'setpoint'`):
325
+ einer aus SET und den beiden Doppel-Sollwerten muss da sein, und die
326
+ Gruppe kam in der Auskunft gar nicht vor (U13).
327
+
328
+ Geprueft wird grob, wie der Detektor: angehakte Zeile mit passender
329
+ Rolle, schreibbar wo der Platz es verlangt, im zugelassenen Typ.
330
+ Zurueck kommen Namen — eine Gruppe als „SET / SET_HEAT / …". Findet
331
+ die Grobpruefung nichts, gibt sie [] zurueck, und der Aufrufer faellt
332
+ auf die alte Auskunft zurueck. */
333
+ export function fehlendePflicht(states, musterName) {
334
+ var mu = musterName && musterVon(musterName);
335
+ if (!mu) { return []; }
336
+ function erfuellt(pl) {
337
+ return (states || []).some(function (s) {
338
+ if (!s.on || !s.role || !rolleTrifft(pl.role, s.role)) { return false; }
339
+ if (pl.ignoreRole && rolleTrifft(pl.ignoreRole, s.role)) { return false; }
340
+ if (pl.write === true && !(s.wr || s.srcW)) { return false; }
341
+ return typPasstZuPlatz(pl, s.typ);
342
+ });
343
+ }
344
+ var raus = [], gruppen = {};
345
+ mu.states.forEach(function (pl) {
346
+ if (pl.required && !erfuellt(pl)) { raus.push(pl.name); }
347
+ if (pl.requiredOneOf) {
348
+ var g = gruppen[pl.requiredOneOf] = gruppen[pl.requiredOneOf] || { namen: [], da: false };
349
+ if (g.namen.indexOf(pl.name) === -1) { g.namen.push(pl.name); }
350
+ if (erfuellt(pl)) { g.da = true; }
351
+ }
352
+ });
353
+ Object.keys(gruppen).forEach(function (k) {
354
+ if (!gruppen[k].da) { raus.push(gruppen[k].namen.join(' / ')); }
355
+ });
356
+ return raus;
357
+ }
358
+
307
359
  /* Die Datentypen, die ein Platz zulaesst - als Liste.
308
360
 
309
361
  Drei Faelle, gemessen am type-detector 6.0.1: 622 Plaetze nennen