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]>
274 lines
11 KiB
JavaScript
274 lines
11 KiB
JavaScript
/* =====================================================================
|
||
DIE ANRUF-PROBE (19.09.2026)
|
||
|
||
Filipe, nach einem langen Abend: „funktioniert nichts davon was
|
||
machst du?"
|
||
|
||
Zu Recht. Ich habe an diesem Abend fünf Dinge repariert, jedes
|
||
einzeln nachgewiesen -- und trotzdem klingelte bei ihm nichts. Der
|
||
Grund ist immer derselbe: Ich kann von hier aus messen, was der
|
||
SERVER tut, aber nicht, was in seinem Browser ankommt. Und genau
|
||
dort lag es jedes Mal.
|
||
|
||
Diese Seite dreht das um. Sie läuft in SEINEM Browser und beantwortet
|
||
der Reihe nach die Fragen, die ich ihm sonst stellen müsste:
|
||
|
||
1. Ist die neue Fassung überhaupt geladen? (Der häufigste Fall:
|
||
eine alte Seite aus dem Zwischenspeicher, die alte Dateien
|
||
nachlädt. Dann ist jede Reparatur unsichtbar.)
|
||
2. Darf der Browser überhaupt Ton abspielen?
|
||
3. Sind Benachrichtigungen erlaubt -- und ist ein Gerät angemeldet?
|
||
4. Steht die Verbindung zum Server, über die das Klingeln kommt?
|
||
|
||
Sie ruft niemanden an. Man kann sie so oft aufmachen, wie man will.
|
||
===================================================================== */
|
||
(() => {
|
||
const $ = (id) => document.getElementById(id);
|
||
const ziel = $('ergebnis');
|
||
if (!ziel) return;
|
||
|
||
const punkte = [];
|
||
function punkt(stand, titel, satz) {
|
||
punkte.push({ stand, titel, satz });
|
||
const k = document.createElement('div');
|
||
k.className = 'punkt';
|
||
k.dataset.stand = stand;
|
||
const z = document.createElement('span');
|
||
z.className = 'punkt__zeichen';
|
||
z.textContent = stand === 'gut' ? '✓' : stand === 'warn' ? '!' : '✗';
|
||
const t = document.createElement('div');
|
||
t.className = 'punkt__text';
|
||
const h = document.createElement('p');
|
||
h.className = 'punkt__titel';
|
||
h.textContent = titel;
|
||
const s = document.createElement('p');
|
||
s.className = 'punkt__satz';
|
||
s.textContent = satz;
|
||
t.append(h, s);
|
||
k.append(z, t);
|
||
ziel.append(k);
|
||
}
|
||
|
||
/* ---- 1. WELCHE FASSUNG IST GELADEN? -----------------------------
|
||
|
||
Der wichtigste Punkt, und der am häufigsten übersehene. Die
|
||
Anruf-Datei trägt einen Stempel im Namen (anruf.js?v=…). Steht in
|
||
der geladenen Seite ein alter Stempel, kommt aus dem
|
||
Zwischenspeicher eine alte Datei -- und jede Reparatur am Server
|
||
ist unsichtbar, egal wie oft man neu lädt. */
|
||
async function fassungPruefen() {
|
||
const skript = [...document.querySelectorAll('script[src]')]
|
||
.map((s) => s.getAttribute('src'))
|
||
.find((s) => /anruf-probe\.js/.test(s || ''));
|
||
const stempel = (skript || '').split('v=')[1] || '(keiner)';
|
||
|
||
try {
|
||
const a = await fetch(`assets/js/anruf.js?v=${stempel}`, { cache: 'no-store' });
|
||
const text = await a.text();
|
||
const hatTon = text.includes('klingelTon');
|
||
const hatOffen = text.includes('anruf/offen');
|
||
if (hatTon && hatOffen) {
|
||
punkt('gut', 'Die neue Fassung ist geladen',
|
||
`Klingelton und Nachklingeln sind da (Stand ${stempel}).`);
|
||
} else {
|
||
punkt('schlecht', 'Es läuft eine ALTE Fassung',
|
||
`Der Klingelton fehlt${hatOffen ? '' : ', das Nachklingeln auch'}. `
|
||
+ 'Die Seite kommt aus dem Zwischenspeicher. '
|
||
+ 'Hilft: die Seite ganz schließen (in der App aus dem Wechsler wischen) und neu öffnen.');
|
||
}
|
||
} catch {
|
||
punkt('warn', 'Fassung nicht prüfbar',
|
||
'Die Datei ließ sich nicht laden – bist du gerade offline?');
|
||
}
|
||
}
|
||
|
||
/* ---- 2. DARF DER BROWSER TON ABSPIELEN? -------------------------
|
||
|
||
Browser erlauben Ton erst, wenn jemand auf der Seite etwas
|
||
angeklickt hat. Wer über eine Benachrichtigung hereinkommt und
|
||
nichts berührt, hört deshalb unter Umständen nichts -- und hält
|
||
das Telefon für kaputt. */
|
||
function tonPruefen() {
|
||
const K = window.AudioContext || window.webkitAudioContext;
|
||
if (!K) {
|
||
punkt('schlecht', 'Dieser Browser kann keinen Ton erzeugen',
|
||
'Sehr selten. Ein anderer Browser hilft.');
|
||
return null;
|
||
}
|
||
const raum = new K();
|
||
if (raum.state === 'running') {
|
||
punkt('gut', 'Ton ist erlaubt', 'Der Klingelton kann abgespielt werden.');
|
||
} else {
|
||
punkt('warn', 'Ton ist noch gesperrt',
|
||
'Der Browser lässt Ton erst zu, nachdem man einmal etwas angetippt hat. '
|
||
+ 'Drück unten auf „Klingelton anhören“ – danach ist er frei, '
|
||
+ 'solange die Seite offen bleibt.');
|
||
}
|
||
return raum;
|
||
}
|
||
|
||
/* ---- 3. BENACHRICHTIGUNGEN ---------------------------------------- */
|
||
async function meldungPruefen() {
|
||
if (!('Notification' in window)) {
|
||
punkt('warn', 'Benachrichtigungen kennt dieser Browser nicht',
|
||
'Anrufe klingeln dann nur, wenn die Seite offen ist.');
|
||
return;
|
||
}
|
||
const erlaubt = Notification.permission;
|
||
if (erlaubt === 'granted') punkt('gut', 'Benachrichtigungen sind erlaubt', 'Gut.');
|
||
else if (erlaubt === 'denied') {
|
||
punkt('schlecht', 'Benachrichtigungen sind blockiert',
|
||
'Dann kommt kein Anruf an, solange die Seite zu ist. '
|
||
+ 'In den Browsereinstellungen für diese Seite wieder erlauben.');
|
||
} else {
|
||
punkt('warn', 'Benachrichtigungen sind noch nicht erlaubt',
|
||
'Im Chat oben auf die Glocke tippen und zustimmen.');
|
||
}
|
||
|
||
/* Ist auch wirklich ein Gerät beim Server angemeldet? Erlaubnis im
|
||
Browser und Anmeldung beim Server sind zwei verschiedene Dinge --
|
||
genau diese Lücke sieht man sonst nirgends. */
|
||
try {
|
||
const a = await fetch('/workspace/api/push/stand', { credentials: 'same-origin' });
|
||
if (a.ok) {
|
||
const d = await a.json();
|
||
/* `geraete` ist eine LISTE, keine Zahl -- nachgesehen in
|
||
workspace-push.js statt geraten. */
|
||
const n = Array.isArray(d.geraete) ? d.geraete.length : 0;
|
||
if (n > 0) punkt('gut', 'Dieses Konto hat ein Gerät angemeldet', `${n} Gerät(e).`);
|
||
else {
|
||
punkt('schlecht', 'Kein Gerät beim Server angemeldet',
|
||
'Der Browser darf zwar, aber es ist nichts hinterlegt. '
|
||
+ 'Im Chat auf die Glocke tippen und neu zustimmen.');
|
||
}
|
||
/* UND DIE RUHEZEIT. Sie hat gestern Abend den ganzen Anruf
|
||
verschluckt; seit heute sind Anrufe ausgenommen. Wer das
|
||
hier liest, soll es sehen, statt es zu vermuten. */
|
||
if (d.ruhe) {
|
||
const anrufArt = (d.arten || []).find((x) => x.schluessel === 'anruf');
|
||
if (anrufArt && anrufArt.an === false) {
|
||
punkt('schlecht', 'Anrufe sind bei dir abgeschaltet',
|
||
'In den Benachrichtigungen die Art „Jemand ruft an“ wieder einschalten.');
|
||
} else if (d.ruhe.jetzt) {
|
||
punkt('gut', `Es ist Ruhezeit (${d.ruhe.ab}–${d.ruhe.bis} Uhr)`,
|
||
'Erinnerungen bleiben jetzt liegen – Anrufe kommen trotzdem durch.');
|
||
}
|
||
}
|
||
}
|
||
} catch { /* die Route gibt es vielleicht nicht -- kein Grund zur Panik */ }
|
||
}
|
||
|
||
/* ---- 4. KOMMT ETWAS VOM SERVER AN? ------------------------------- */
|
||
async function serverPruefen() {
|
||
try {
|
||
const a = await fetch('/workspace/api/anruf/offen', { credentials: 'same-origin' });
|
||
if (a.status === 401) {
|
||
punkt('schlecht', 'Du bist nicht angemeldet',
|
||
'Dann kann auch nichts klingeln. Melde dich neu an.');
|
||
return;
|
||
}
|
||
if (!a.ok) {
|
||
/* NICHT NUR DIE ZAHL (19.09.2026). Alle zwoelf anderen Punkte
|
||
sagen, was zu tun ist -- dieser sagte "Antwort 503.". Und
|
||
er kann als Erster im Schlussrat landen, also ganz unten
|
||
als das, was offen bleibt. Diese Seite ist ausdruecklich
|
||
fuer jemanden gebaut, der nicht technisch ist; eine
|
||
dreistellige Zahl ist fuer ihn keine Auskunft, sondern die
|
||
Bestaetigung, dass er hier nichts ausrichten kann. */
|
||
punkt('warn', 'Der Server antwortet gerade ungewöhnlich',
|
||
'Wahrscheinlich wird er gerade neu gestartet oder ist kurz '
|
||
+ 'überlastet. Warte eine Minute und öffne diese Seite noch '
|
||
+ `einmal. Bleibt es dabei, sag DogFather Bescheid – Kennzahl ${a.status}.`);
|
||
return;
|
||
}
|
||
const d = await a.json();
|
||
punkt('gut', 'Die Verbindung zum Server steht',
|
||
(d.anrufe?.length
|
||
? `Gerade klingelt sogar etwas (${d.anrufe.length}).`
|
||
: 'Gerade klingelt nichts – das ist normal.'));
|
||
} catch {
|
||
punkt('schlecht', 'Keine Verbindung zum Server',
|
||
'Kein Netz? Ohne sie kommt kein Anruf an.');
|
||
}
|
||
}
|
||
|
||
/* ---- Der Rat am Ende --------------------------------------------- */
|
||
function ratGeben() {
|
||
const schlecht = punkte.filter((p) => p.stand === 'schlecht');
|
||
const warn = punkte.filter((p) => p.stand === 'warn');
|
||
const r = $('rat');
|
||
if (!r) return;
|
||
if (schlecht.length) {
|
||
r.textContent = 'Das Wichtigste zuerst: ' + schlecht[0].titel
|
||
+ '. ' + schlecht[0].satz;
|
||
} else if (warn.length) {
|
||
r.textContent = 'Fast alles steht. Offen ist: ' + warn[0].titel
|
||
+ '. ' + warn[0].satz;
|
||
} else {
|
||
r.textContent = 'Alles, was von hier aus prüfbar ist, steht. '
|
||
+ 'Wenn es jetzt noch klemmt, liegt es an der Verbindung zwischen '
|
||
+ 'den beiden Geräten – dann hilft nur ein echter Anruf zu zweit.';
|
||
}
|
||
}
|
||
|
||
/* ---- Der Knopf: Ton wirklich hören -------------------------------- */
|
||
let tonRaum = null;
|
||
$('ton-probe')?.addEventListener('click', () => {
|
||
try {
|
||
const K = window.AudioContext || window.webkitAudioContext;
|
||
tonRaum = tonRaum || new K();
|
||
if (tonRaum.state === 'suspended') tonRaum.resume();
|
||
const spiel = (hz, ab, lang) => {
|
||
const o = tonRaum.createOscillator();
|
||
const v = tonRaum.createGain();
|
||
o.type = 'sine'; o.frequency.value = hz;
|
||
const t = tonRaum.currentTime + ab;
|
||
v.gain.setValueAtTime(0, t);
|
||
v.gain.linearRampToValueAtTime(0.16, t + 0.03);
|
||
v.gain.linearRampToValueAtTime(0, t + lang);
|
||
o.connect(v).connect(tonRaum.destination);
|
||
o.start(t); o.stop(t + lang + 0.05);
|
||
};
|
||
/* Genau der Klingelton aus dem Anruf -- damit man hört, was man
|
||
hören würde. */
|
||
spiel(880, 0, 0.22);
|
||
spiel(660, 0.3, 0.26);
|
||
try { navigator.vibrate?.([250, 200, 250]); } catch { /* egal */ }
|
||
$('ton-probe').textContent = 'Nochmal anhören';
|
||
} catch {
|
||
$('ton-probe').textContent = 'Ton ging nicht – siehe oben';
|
||
}
|
||
});
|
||
|
||
(async () => {
|
||
await fassungPruefen();
|
||
tonPruefen();
|
||
await meldungPruefen();
|
||
await serverPruefen();
|
||
ratGeben();
|
||
})();
|
||
})();
|
||
|
||
/* ---------------------------------------------------------------------
|
||
DER RÜCKWEG FÜHRT DORTHIN, WO MAN HERKAM (19.09.2026)
|
||
|
||
Hier stand fest „‹ Zum Chat". Erreicht wird diese Seite aber auch von
|
||
der STARTSEITE aus -- über den Hinweis „Bei dir klingelt nichts".
|
||
Wer von dort kam, landete im Chat statt zurück, und das ist auf einer
|
||
Hilfeseite die falscheste Richtung überhaupt.
|
||
|
||
Jede andere Unterseite bekommt über kopf.js einen Zurück-Knopf, der
|
||
wirklich zurückführt. Diese Seite lädt kopf.js bewusst nicht (sie
|
||
soll auch dann noch stehen, wenn anderes klemmt) -- also hier
|
||
dieselbe Rechnung in vier Zeilen: Gibt es einen Verlauf, geh zurück;
|
||
sonst bleibt der Link auf die Startseite, wie er im HTML steht.
|
||
------------------------------------------------------------------- */
|
||
(function () {
|
||
var weg = document.getElementById('zurueck-weg');
|
||
if (!weg || history.length <= 1) return;
|
||
weg.addEventListener('click', function (e) {
|
||
e.preventDefault();
|
||
history.back();
|
||
});
|
||
})();
|