/* ===================================================================== pruef-nachfrage.mjs — Nichts verschwindet ohne Nachfrage. Filipe, 19.09.2026: „Es darf vor allem keine Stellen geben, an denen ein Benutzer etwas falsch machen kann, nur weil die Seite es nicht verständlich genug erklärt." Und: „Fehler möglichst verhindern, bevor sie entstehen." --------------------------------------------------------------------- WAS DAS PROBLEM WAR Das Haus hatte die richtige Bauweise — einen statt `confirm()` — und begründete sie in `aufgaben.html` sogar ausführlich. Nur stand sie IN EINER SEITE. Gemessen am 19.09.2026: **30 Stellen** in 16 Dateien griffen weiterhin zu `confirm()` oder `prompt()`. Der Unterschied ist nicht Geschmack. `confirm('Wirklich löschen?')` stellt die falsche Frage: Es fragt, ob man sicher ist, und nennt nicht, WAS passiert, was BLEIBT und ob es ZURÜCK geht. Genau das sind die drei Dinge, die man vor einem Löschen wissen will. --------------------------------------------------------------------- VIER DINGE WERDEN GEPRÜFT 1. KEIN NATIVER DIALOG mehr in einer Seiten-Datei. Ausgenommen ist allein der Notnagel in `nachfrage.js` selbst — für Browser ohne . (Ein Verbot ohne diese Ausnahme wäre falsch: Ein iPhone vor Safari 15.4 bekäme sonst gar nichts angezeigt.) 2. ANGESCHLOSSEN — jede Seite, deren Skripte `frageNach` benutzen, lädt auch `nachfrage.js`. Ohne die Datei wäre `frageNach` nicht definiert, und aus einer Rückfrage würde ein Absturz: Der Knopf täte stillschweigend nichts. ACHTUNG, hier steckte beim Bauen schon eine Falle: Die erste Fassung dieser Prüfung zählte `nachfrage.js` SELBST als Nutzer (die Datei enthält das Wort natürlich) — damit war jede Seite „Nutzer", die Prüfung immer grün und sie bewies nichts. Deshalb ist die Datei unten ausdrücklich ausgenommen. 3. GESTALTET — `.dialog` wird in einer Datei gestaltet, die JEDE Seite lädt. Vorher stand sie in `aufgaben.css`; auf `dateien.html` wäre der Dialog ein weißer Systemkasten auf dunkler Seite gewesen. Das ist keine Schönheitsfrage: Ein Kasten, der aussieht wie eine Browser-Warnung, wird weggeklickt statt gelesen. 4. VOLLSTÄNDIG — jeder Aufruf hat einen `titel` und sagt, was passiert (`was`). Ein Dialog ohne diese Angabe ist ein `confirm()` mit mehr Zeilen Code. Dazu GEGENPROBEN: Jede Prüfung muss beweisen, dass sie auch „nicht in Ordnung" sagen kann. AUFRUF: node server/pruef-nachfrage.mjs Läuft in unter zwei Sekunden, ohne Server. ===================================================================== */ import { readFileSync, readdirSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { bestaetige, nachfrageText } from "./helfer-nachfrage.mjs"; const HIER = dirname(fileURLToPath(import.meta.url)); const WURZEL = join(HIER, ".."); const SEITEN_ORDNER = join(WURZEL, "workspace"); const JS_ORDNER = join(SEITEN_ORDNER, "assets", "js"); const CSS_ORDNER = join(SEITEN_ORDNER, "assets", "css"); /* Die eine Datei, die `confirm`/`prompt` benutzen DARF -- sie ist der Notnagel fuer Browser ohne . */ const DER_NOTNAGEL = "nachfrage.js"; /** Ein winziges, gueltiges PNG (1x1) -- echte Kopfbytes, damit der * Browser wirklich ein Bild bekommt und die Vorschau etwas anzeigen * kann. Vorgetaeuschte Bytes ergaeben ein leeres ``, und die * Pruefung haette „Vorschau da" gemeldet, ohne eine zu sehen. */ const PNG_PROBE = Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk" + "YPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", "base64"); let ok = 0, fehl = 0; const pruef = (name, bedingung, zusatz = "") => { if (bedingung) { ok++; console.log(` OK ${name}`); } else { fehl++; console.log(` FEHL ${name}${zusatz ? "\n " + zusatz : ""}`); } }; const lies = (p) => readFileSync(p, "utf8"); const seiten = readdirSync(SEITEN_ORDNER).filter((f) => f.endsWith(".html")); const skripte = readdirSync(JS_ORDNER).filter((f) => f.endsWith(".js")); console.log("\n=== Nachfragen: kein nativer Dialog, und keiner ohne Erklärung ===\n"); /* --------------------------------------------------------------------- 1. KEIN NATIVER DIALOG ------------------------------------------------------------------ */ /** Entfernt Kommentare, bevor gesucht wird. Ohne das zaehlt jeder * Kommentar, der `confirm()` ERWAEHNT, als Fund -- und davon gibt es * in diesem Haus absichtlich mehrere (die Begruendungen). */ function ohneKommentare(js) { return js.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1"); } const nativ = []; for (const f of skripte) { if (f === DER_NOTNAGEL) continue; const code = ohneKommentare(lies(join(JS_ORDNER, f))); /* DAS MUSTER HATTE EINE LUECKE, UND ZWAR DIE GROESSTE MOEGLICHE (gefunden am 22.09.2026). Vorher stand hier `(?:^|[^.\w])(confirm|prompt)\s*\(`. Das `[^.\w]` sollte fremde Methoden ausschliessen -- etwas wie `angebot.prompt()` (so heisst die Installations-Aufforderung des Browsers) ist kein nativer Dialog und darf nicht als Fund zaehlen. Nur trifft dieser Ausschluss ausgerechnet die HAEUFIGSTE Schreibweise: `window.prompt(` hat einen Punkt davor. Die Pruefung war damit gruen, waehrend in teamlage.js ZWEI native prompt() standen -- einer davon seit Wochen. Genau das Muster, vor dem dieses Haus warnt: eine Pruefung, die laeuft, gruen ist und das Falsche prueft. Gefunden hat es nicht sie, sondern eine Suche von Hand. Jetzt zwei Faelle statt einem: mit `window.` davor -- oder ganz ohne Punkt. Alles andere (`irgendwas.prompt()`) bleibt aussen vor, und das ist weiterhin richtig. */ for (const m of code.matchAll(/(?:^|[^.\w])(?:window\s*\.\s*)?(confirm|prompt)\s*\(/g)) { const zeile = code.slice(0, m.index).split("\n").length; nativ.push(`${f}:${zeile} → ${m[1]}()`); } } pruef("keine Seite benutzt noch confirm() oder prompt()", nativ.length === 0, nativ.slice(0, 8).join("\n ")); /* Und der Notnagel ist da -- sonst haetten Browser ohne gar nichts. Die ZAHL gehoert in die Bedingung, nicht nur in den Text. */ const notnagel = lies(join(JS_ORDNER, DER_NOTNAGEL)); pruef("der Notnagel für alte Browser ist vorhanden", /HTMLDialogElement/.test(notnagel) && /window\.confirm\(/.test(notnagel)); /* --------------------------------------------------------------------- 2. ANGESCHLOSSEN ------------------------------------------------------------------ */ /** Welche Skripte rufen frageNach auf? OHNE die Datei, die es * DEFINIERT -- sonst ist die Antwort immer „alle". */ const nutzer = new Set(); /* EIN AUFRUF HINTER EINER `typeof`-ABFRAGE IST KEINE PFLICHT (24.09.2026 -- und die Pruefung war aus genau diesem Grund rot, ohne dass etwas kaputt war). `installieren.js` liegt auf fast jeder Seite und ruft frageNach auf -- aber ausdruecklich nur, wenn es da ist: if (typeof window.frageNach === 'function') { … } Der Kommentar daneben erklaert seit dem 22.09.2026 genau, warum: Die Datei laeuft auch auf der Anmeldewand, wo nachfrage.js mit Absicht nicht geladen wird. Die Pruefung zaehlte den Aufruf trotzdem als "diese Seite fragt nach" -- und weil installieren.js ueberall liegt, wurde damit JEDE Seite zur fragenden. Ergebnis: fuenf rote Zeilen, kein einziger echter Mangel. EINE PRUEFUNG, DIE IMMER ROT IST, IST KEINE PRUEFUNG MEHR -- genau wie eine Warnung, die immer kommt. Ab dem zweiten Mal liest man sie weg, und dann auch die echte. Die Unterscheidung ist einfach und haelt: Wer vorher fragt, ob es die Funktion gibt, kommt ohne sie aus. Wer sie einfach aufruft, braucht sie. */ const geschuetzt = (text) => /typeof\s+window\.frageNach\s*===?\s*['"]function['"]/.test(text) || /window\.frageNach\s*\?\./.test(text); const freiwillig = new Set(); for (const f of skripte) { if (f === DER_NOTNAGEL) continue; const text = ohneKommentare(lies(join(JS_ORDNER, f))); if (!/\bfrageNach\s*\(/.test(text)) continue; if (geschuetzt(text)) { freiwillig.add(f); continue; } nutzer.add(f); } pruef("es gibt überhaupt Seiten, die nachfragen", nutzer.size > 0, `gefunden: ${nutzer.size}`); /* WAS HIER HERAUSFAELLT, WIRD BENANNT. Ein stilles Aussortieren waere die bequeme Art, eine Pruefung gruen zu bekommen -- man saehe dann nicht mehr, wie gross die Ausnahme geworden ist. */ pruef("die geschützten Aufrufe sind benannt und wenige", freiwillig.size <= 3, `ohne Pflicht zur Datei (typeof-Abfrage davor): ${[...freiwillig].join(", ") || "keine"}`); /* GEGENPROBE: Ein UNGESCHUETZTER Aufruf muss weiterhin zaehlen -- sonst hiesse "alle angeschlossen" nur, dass die Regel niemanden mehr erfasst. */ pruef("Gegenprobe: ein ungeschützter Aufruf zählt weiterhin", !geschuetzt("frageNach({ titel: 'x' });") && geschuetzt("if (typeof window.frageNach === 'function') frageNach({});"), `${nutzer.size} Dateien rufen ungeschützt auf`); const ohneDatei = [], totesGewicht = [], fragendeSeiten = []; let mitNutzung = 0; for (const s of seiten) { const html = lies(join(SEITEN_ORDNER, s)); const geladen = [...html.matchAll(/