diff --git a/assets/js/wd-core.js b/assets/js/wd-core.js index 88687e98..8dae87c6 100644 --- a/assets/js/wd-core.js +++ b/assets/js/wd-core.js @@ -838,7 +838,7 @@ Stand haben. Die Nummer wird zusammen mit CACHE_NAME in sw.js hochgezaehlt -- beide gehoeren zusammen. */ navigator.serviceWorker - .register("/webdesign/sw.js?v=57", { scope: "/webdesign/" }) + .register("/webdesign/sw.js?v=58", { scope: "/webdesign/" }) .catch(function () { /* still schlucken: ohne SW funktioniert alles weiter */ }); }); } diff --git a/pruef-namenskollision.mjs b/pruef-namenskollision.mjs new file mode 100644 index 00000000..dda8fd21 --- /dev/null +++ b/pruef-namenskollision.mjs @@ -0,0 +1,102 @@ +/* ===================================================================== + Sucht doppelt vergebene Funktionsnamen. + + ANLASS + + Am 26.08.2026 brachte neuer Code eine Hilfsfunktion namens "kachel" + mit. Weiter oben in derselben Datei gab es seit langem eine andere + Funktion desselben Namens, die die Kacheln der Uebersicht baut. + + JavaScript kennt keine Ueberladung: Die spaetere Definition gewinnt. + Ohne Warnung, ohne Fehler, ohne dass irgendetwas anschlaegt. Die + Uebersicht zeigte danach in jeder Kachel "[object Object]". + + WARUM KEIN ANDERES WERKZEUG DAS FINDET + + - Der Browser meldet nichts: Zwei Funktionen desselben Namens sind + erlaubtes JavaScript. + - "node --check" meldet nichts: syntaktisch einwandfrei. + - Die Oberflaechen-Tests meldeten nichts: Sie pruefen, ob Kacheln DA + sind, nicht ob sinnvoller Text darin steht. + - Am tueckischsten: Der neue Code selbst funktionierte einwandfrei. + Der Schaden trat an einer ganz anderen Stelle auf, und nichts + deutete auf die Ursache hin. + + Diese Pruefung kostet Sekundenbruchteile und haette den Fehler + verhindert, bevor er ausgeliefert wurde. + ===================================================================== */ +import fs from "fs"; +import path from "path"; + +let ok = 0, fehl = 0; +const pruefe = (name, gut, info) => { + if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); } + else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); } +}; + +const WURZEL = process.cwd(); + +/* Alle Dateien mit eigenem JavaScript: die grossen Inline-Bloecke der + Seiten und die Skriptdateien. */ +const dateien = [ + ...fs.readdirSync(WURZEL).filter((f) => f.endsWith(".html")).map((f) => f), + ...fs.readdirSync(path.join(WURZEL, "webdesign")) + .filter((f) => f.endsWith(".html")).map((f) => "webdesign/" + f), + ...fs.readdirSync(path.join(WURZEL, "assets/js")) + .filter((f) => f.endsWith(".js")).map((f) => "assets/js/" + f), +]; + +console.log(`Durchsucht: ${dateien.length} Dateien\n`); + +const betroffen = []; + +for (const rel of dateien) { + const quelle = fs.readFileSync(path.join(WURZEL, rel), "utf8"); + + /* Nur Deklarationen am Zeilenanfang (mit Einrueckung). Damit bleiben + Methoden in Objekten, verschachtelte Hilfsfunktionen in anderen + Funktionen und Zeichenketten aussen vor -- die kollidieren nicht + miteinander, weil sie in eigenen Gueltigkeitsbereichen liegen. */ + const muster = /^[ \t]*function\s+([A-Za-z_$][\w$]*)\s*\(/gm; + const gesehen = new Map(); + let t; + while ((t = muster.exec(quelle)) !== null) { + const name = t[1]; + const zeile = quelle.slice(0, t.index).split("\n").length; + if (!gesehen.has(name)) gesehen.set(name, []); + gesehen.get(name).push(zeile); + } + + for (const [name, zeilen] of gesehen) { + if (zeilen.length > 1) { + betroffen.push({ datei: rel, name, zeilen }); + } + } +} + +pruefe("kein Funktionsname ist doppelt vergeben", betroffen.length === 0, + betroffen.length ? betroffen.length + " Kollision(en)" : dateien.length + " Dateien sauber"); + +for (const b of betroffen) { + console.log(`\n ${b.datei}`); + console.log(` "${b.name}" steht in Zeile ${b.zeilen.join(" und ")}`); + console.log(` Die spaetere gewinnt — die fruehere ist damit wirkungslos.`); +} + +/* Gegenprobe: Die Pruefung muss auch wirklich etwas finden koennen. + Ohne diesen Abschnitt koennte das Muster kaputt sein und immer + "sauber" melden. */ +console.log("\nGEGENPROBE"); +const beispiel = ` + function tuwas(a) { return a; } + var x = 1; + function tuwas(a, b) { return b; } +`; +const m2 = /^[ \t]*function\s+([A-Za-z_$][\w$]*)\s*\(/gm; +const treffer = [...beispiel.matchAll(m2)].map((x) => x[1]); +pruefe(" findet eine eingebaute Kollision", + treffer.filter((n) => n === "tuwas").length === 2, + treffer.join(", ")); + +console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`); +process.exit(fehl ? 1 : 0); diff --git a/pruef-vw-schmuck-computer.png b/pruef-vw-schmuck-computer.png index bd32b2e1..cd5134a9 100644 Binary files a/pruef-vw-schmuck-computer.png and b/pruef-vw-schmuck-computer.png differ diff --git a/webdesign/sw.js b/webdesign/sw.js index 4a871dce..334f5778 100644 --- a/webdesign/sw.js +++ b/webdesign/sw.js @@ -34,7 +34,7 @@ Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten Stand fest. */ -const CACHE_NAME = "dogfather-webdesign-v57"; +const CACHE_NAME = "dogfather-webdesign-v58"; /* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE HTML-Datei in dieser Liste. */ diff --git a/webdesign/verwaltung.html b/webdesign/verwaltung.html index 9b0aad95..5d39c7cb 100644 --- a/webdesign/verwaltung.html +++ b/webdesign/verwaltung.html @@ -3223,18 +3223,25 @@ } - /* Ganze Tage seit einem Zeitpunkt. Bewusst ueber den Kalendertag - gerechnet, nicht ueber 24-Stunden-Bloecke: eine Anfrage von gestern - 23:50 Uhr ist umgangssprachlich "gestern", nicht "vor 0 Tagen". */ - function tageSeit(zeit) { - if (!zeit) return 0; - var dann = new Date(zeit); - if (isNaN(dann.getTime())) return 0; - var a = new Date(dann.getFullYear(), dann.getMonth(), dann.getDate()); - var heute = new Date(); - var b = new Date(heute.getFullYear(), heute.getMonth(), heute.getDate()); - return Math.round((b - a) / 86400000); - } + /* ⚠️ HIER STAND EINE ZWEITE FUNKTION tageSeit (entfernt 26.08.2026). + + Sie rechnete dasselbe wie die weiter oben, unterschied sich aber in + einem Punkt: Bei fehlendem oder unlesbarem Datum gab sie 0 zurueck, + die obere gibt null. + + Weil in JavaScript die spaetere Definition gewinnt, war die obere + seit ihrer Einfuehrung wirkungslos -- und mit ihr die Pruefungen + "if (t === null) return ''" in altersText und dringlichkeit. Ohne + Datum stand dort seither "seit heute" statt gar nichts. + + Gefunden hat das keine Fehlermeldung, sondern pruef-namenskollision.mjs, + entstanden am selben Tag aus genau demselben Fehler an anderer + Stelle. Zwei Funktionen desselben Namens sind erlaubtes JavaScript; + kein Browser und kein Syntaxpruefer schlaegt an. + + Die beiden verbliebenen Aufrufer weiter unten vertragen null + genauso wie 0 -- geprueft: Math.max(x, null) ergibt x, und + null >= 7 ist falsch, gleich wie bei 0. */ function zeitText(tage) { if (tage <= 0) return "heute";