Files
dogfather-universe/workspace/assets/js/tafel.js
T
DogFatherGitandClaude Opus 5 95c4599baa Anfuehrungszeichen: 199 falsche Schlusszeichen im sichtbaren Text
Deutsch oeffnet mit „ und schliesst mit “. An 199 Stellen, die ein
Mensch liest, stand als Schlusszeichen ein GERADES " -- „Nächstes"
statt „Nächstes“. Auf sechzehn oeffentlichen Seiten, in vierzig
Dateien des Workspace und in zwoelf Servermodulen, die Texte
verschicken.

Auf dem Bildschirm sieht man den Unterschied sofort. Beim Schreiben
nicht: Das gerade " liegt auf der Tastatur, die anderen nicht.

WARUM EIN ERSTER ANLAUF ZURUECKGENOMMEN WURDE

Ein gerades " ist an vielen Stellen SYNTAX und kein Schriftzeichen --
Grenze einer Zeichenkette, Grenze eines HTML-Attributs, Zeichen in
einem regulaeren Ausdruck. Wer stumpf ersetzt, macht aus

    „<a href="https://…                  ein kaputtes Attribut
    /^["'„»\s]+|["'“«.\s]+$/              einen kaputten Ausdruck
    "… nichts „mal " + "eben …"          eine kaputte Zeichenkette

DIE UNTERSCHEIDUNG LAEUFT AN MERKMALEN, NICHT AN EINER LISTE

  < > = dazwischen        -> HTML-Marke oder Attribut
  endet auf Leerzeichen   -> die Zeichenkette hoert hier auf, der
                             Satz geht in der naechsten Zeile weiter.
                             Ein deutsches Schlusszeichen steht NIE
                             hinter einem Leerzeichen.
  Rueckstrich mittendrin  -> regulaerer Ausdruck
  ${ ohne }               -> mitten in einem Ausdruck

Eine Liste erlaubter Ausnahmen waere die naechste, die niemand
pflegt. Zwoelf Stellen bleiben dadurch stehen, alle zwoelf einzeln
angesehen und alle zwoelf zu Recht -- dort steht das richtige
Schlusszeichen ohnehin weiter unten im Satz.

Fuenf davon waren allerdings ECHTE Fehler HINTER dem Link
(`…>HasiDog</a>".`) -- die erste Regel hatte nur das Attribut
gesehen, nicht den Satz danach. Gezielt nachgezogen.

Ein maskiertes `\"` in workspace-vorlagen.js (28 Hooks) wird zu “ --
ohne Rueckstrich, denn “ begrenzt nichts.

KOMMENTARE BLEIBEN, WIE SIE SIND. Dort liest es niemand ausser mir;
eine Wache, die auch Kosmetik anmahnt, wird weggeklickt. Beim ersten
Messen fielen ausserdem acht Stellen aus buehne.html faelschlich an,
weil `/* */` in HTML (in <style> und <script>) nicht ausgeblendet
war -- jetzt schon.

DIE WACHE DAZU

pruef-struktur prueft es ab sofort mit derselben Regel: 322 Dateien
mit sichtbarem Text, 0 Funde, und die zwoelf bewussten Ausnahmen
werden GEZAEHLT und genannt (erlaubt: 12). Eine Ausnahme, die niemand
sieht, waechst -- und irgendwann steht der echte Fall darin.

GEPRUEFT

  pruef-struktur   59 -> 68 Pruefungen, 0 Fehler
  node --check auf allen geaenderten JS-Dateien
  nachgemessen: 199 geaendert, 12 mit Grund stehen geblieben

  gruen geblieben: bewerbung-aufgaben 163, nachwuchs 262,
  reaktion 421, support 63, content 45, terminregel 35, treff 85

  Und nachgesehen, ob eine Pruefung noch die alte Schreibweise
  ERWARTET: 13 Fundstellen, alle dreizehn nur Text in ihrer eigenen
  Ausgabe, keine einzige ein Vergleich mit dem Seitentext.

GEGENPROBE: In reaktion.html ein Schlusszeichen zurueckgedreht ->
„workspace/reaktion.html:546 „Nächstes"", mit Datei und Zeile. Und
die Erkennung einzeln gegen HTML-Attribut, fortgesetzte
Zeichenkette, regulaeren Ausdruck und eingesetzten Wert geprueft.

Stempel gesetzt: workspace 670 Verweise in 45 Dateien, oeffentlich
554 in 48 Seiten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-01 01:56:15 +02:00

156 lines
5.8 KiB
JavaScript

/* =====================================================================
DIE SPENDENTAFEL ALS BROWSER-QUELLE (28.09.2026)
Hört am Bühnenstrom und lässt jede bestätigte Spende als Karte
laufen. Sonst tut diese Seite nichts.
---------------------------------------------------------------------
SIE MUSS TAGE LAUFEN, OHNE DASS JEMAND HINSIEHT
Das ist der ganze Unterschied zu einer Seite im Browser: Wer eine
Seite offen hat, merkt, wenn sie hängt, und lädt neu. Eine Quelle
in OBS läuft im Hintergrund, und wenn sie einmal stehenbleibt,
fällt es erst auf, wenn die erste Spende nicht erscheint — mitten
in der Sendung, vor Publikum.
Deshalb drei Dinge:
Der Browser verbindet einen abgerissenen Ereignisstrom von
selbst neu (`retry:` kommt vom Server). Darauf allein zu bauen
reicht aber nicht: Schläft der Rechner, kann der Strom in einem
Zustand stehenbleiben, aus dem der Browser nicht von selbst
herausfindet.
Also eine eigene WACHE: Kommt 70 Sekunden lang gar nichts — auch
kein Lebenszeichen, das alle 25 Sekunden gesendet wird —, wird
die Verbindung weggeworfen und neu aufgebaut.
Und ein FEHLER WIRD ANGEZEIGT, aber nur der, der etwas bedeutet:
ein falscher Schlüssel. Alles andere bleibt still, weil jede
Fläche hier im Stream zu sehen wäre.
===================================================================== */
(() => {
'use strict';
const tafel = document.getElementById('tafel');
/* ==== GROESSE UND LAGE STEHEN IN DER ADRESSE ====================
Wer in OBS eine Browser-Quelle einrichtet, hat die Adresse
ohnehin vor sich. Ein Wert, den man stattdessen im Regiepult
suchen muesste, waere ein Fensterwechsel mitten im Einrichten --
und beim Probieren aendert man zehnmal hintereinander etwas.
DIE VORGABE IST 1,4 UND NICHT 1. Im Saal steht die Karte neben
einem Chat und darf nichts zudecken; auf der Buehne deckt sie
nichts zu, was man lesen muesste, und darf deutlich groesser
sein. Eine Vorgabe, bei der man immer nachstellen muss, ist die
falsche. */
{
const p = new URLSearchParams(location.search);
const g = Number(String(p.get('g') || '').replace(',', '.'));
/* Zwischen 0,5 und 4. Darunter ist nichts mehr zu lesen, darueber
passt keine Karte mehr ins Bild -- beides waere eine Quelle,
die aussieht, als sei sie kaputt. */
const faktor = Number.isFinite(g) && g >= 0.5 && g <= 4 ? g : 1.4;
document.documentElement.style.fontSize = `${Math.round(16 * faktor)}px`;
const lagen = {
ul: ['flex-end', 'flex-start'], ur: ['flex-end', 'flex-end'],
ol: ['flex-start', 'flex-start'], or: ['flex-start', 'flex-end'],
mitte: ['center', 'center'],
};
const [senkrecht, waagerecht] = lagen[String(p.get('pos') || 'ul')] || lagen.ul;
tafel.style.justifyContent = senkrecht;
tafel.style.alignItems = waagerecht;
}
const schluessel = new URLSearchParams(location.search).get('s') || '';
function sagFehler(text) {
let k = document.querySelector('.obs-fehler');
if (!k) {
k = document.createElement('div');
k.className = 'obs-fehler';
document.body.append(k);
}
k.textContent = text;
}
if (!schluessel) {
sagFehler('Diese Adresse ist unvollständig — der Schlüssel fehlt. '
+ 'Hol sie dir im Regiepult unter „Bühne“.');
return;
}
let strom = null;
let letztes = Date.now();
function verbinden() {
try { if (strom) strom.close(); } catch { /* war schon zu */ }
strom = new EventSource(
`/workspace/api/buehne/strom?s=${encodeURIComponent(schluessel)}`);
strom.addEventListener('open', () => {
letztes = Date.now();
const k = document.querySelector('.obs-fehler');
if (k) k.remove();
});
/* JEDE NACHRICHT ZAEHLT ALS LEBENSZEICHEN -- auch die Doppelpunkt-
Zeile, die der Server alle 25 Sekunden schickt. Sie kommt hier
als „message" ohne Inhalt an. */
strom.addEventListener('message', () => { letztes = Date.now(); });
strom.addEventListener('spende', (e) => {
letztes = Date.now();
try {
const d = JSON.parse(e.data);
if (d.was === 'zeigen' && window.Spendenkarte) {
window.Spendenkarte.zeigen(tafel, d.karte);
}
} catch { /* eine kaputte Meldung darf die Quelle nicht reissen */ }
});
strom.addEventListener('error', () => {
/* KEINE MELDUNG INS BILD. Ein kurzer Abriss ist normal, und der
Browser verbindet von selbst neu. Eine rote Fläche dafür
stünde im Stream. */
if (strom.readyState === EventSource.CLOSED) {
setTimeout(verbinden, 3000);
}
});
}
/* Einmal fragen, ob der Schlüssel überhaupt stimmt. Eine Quelle,
die still bleibt, weil die Adresse falsch ist, sieht genauso aus
wie eine, bei der nur niemand gespendet hat -- und das ist die
eine Verwechslung, die hier wirklich schadet. */
fetch(`/workspace/api/buehne/lage?s=${encodeURIComponent(schluessel)}`)
.then((a) => {
if (a.status === 404) {
sagFehler('Der Schlüssel stimmt nicht mehr. '
+ 'Hol dir die Adresse neu im Regiepult unter „Bühne“.');
return;
}
verbinden();
})
.catch(() => verbinden());
/* DIE WACHE. Kommt 70 Sekunden nichts -- nicht einmal ein
Lebenszeichen --, ist die Verbindung tot, auch wenn der Browser
sie fuer offen haelt. */
setInterval(() => {
if (Date.now() - letztes > 70_000) {
letztes = Date.now();
verbinden();
}
}, 10_000);
/* WACHT DER RECHNER AUF, WIRD SOFORT NEU VERBUNDEN. Nach dem
Ruhezustand ist ein Ereignisstrom fast immer tot, und bis die
Wache greift, vergeht eine Minute. */
document.addEventListener('visibilitychange', () => {
if (!document.hidden && Date.now() - letztes > 30_000) verbinden();
});
})();