Files
dogfather-universe/workspace/assets/js/anruf-probe.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

274 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
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();
});
})();