import { createHash } from 'node:crypto' import { hauptplan, modusUndFenster } from './zeitlogik.js' /** * Oeffnungszeiten live: statische Ersatz-Tabelle und Wochenplan-Hash (Spec 4.5, rein). * Kein Appwrite, keine Config, keine Uhr. Die Tabelle steht in den Platzhaltern der Kundenseite * (ohne JavaScript und nach Abschaltung der Funktion) und zeigt nur den Wochenplan eines Plans, * keine Ausnahmen, Feiertage oder Hinweise. * * Ausgabe ist reines ASCII: Umlaute und ss-Ligatur als benannte Entities (ä ... ß), Gedankenstrich * als –, jedes andere Nicht-ASCII-Zeichen als numerische Entity (NNNN;). Kundentext (Planname) wird * zuerst HTML-maskiert (& < > " '). * * Mitternacht: Ein Fensterende '24:00' steht in der Tabelle als '00:00' ("18:00-00:00"), nie als "24:00". * Ein einziges Fenster 00:00-24:00 gilt als "rund um die Uhr". */ const WOCHENTAGE = Object.freeze(['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag']) const GEDANKENSTRICH = '–' const STANDARD_UEBERSCHRIFT = 'Öffnungszeiten' const ENTITIES = new Map([ ['&', '&'], ['<', '<'], ['>', '>'], ['"', '"'], ["'", '''], ['\u00e4', 'ä'], ['\u00f6', 'ö'], ['\u00fc', 'ü'], ['\u00c4', 'Ä'], ['\u00d6', 'Ö'], ['\u00dc', 'Ü'], ['\u00df', 'ß'], ]) /** * Kundentext -> sicheres ASCII-HTML. Steuerzeichen (auch C1) werden zu Leerzeichen (der HTML-Parser * wuerde numerische Verweise darauf umdeuten), einzelne Surrogate zu U+FFFD. */ function asciiText(roh) { let aus = '' for (const zeichen of String(roh)) { const cp = zeichen.codePointAt(0) if (cp < 0x20 || (cp >= 0x7f && cp <= 0x9f)) aus += ' ' else if (ENTITIES.has(zeichen)) aus += ENTITIES.get(zeichen) else if (cp < 0x7f) aus += zeichen else if (cp >= 0xd800 && cp <= 0xdfff) aus += '�' else aus += `${cp};` } return aus } /** Text eines Tages; modus/fenster kommen bereits bereinigt aus modusUndFenster (gueltige 'HH:MM'-Fenster). */ function tagText({ modus, fenster }) { if (modus === 'rund') return 'rund um die Uhr' if (modus === 'vereinbarung') return 'nach Vereinbarung' if (modus !== 'zeiten' || fenster.length === 0) return 'geschlossen' if (fenster.length === 1 && fenster[0][0] === '00:00' && fenster[0][1] === '24:00') return 'rund um die Uhr' return fenster .map(([von, bis]) => `${von}${GEDANKENSTRICH}${bis === '24:00' ? '00:00' : bis}`) .join(', ') } /** * Schlichte Wochentabelle des Plans:
| + | ). Ohne 'plan' gilt der Hauptplan; * gibt es keinen Plan, ist das Ergebnis '' (der Aufrufer entscheidet, z. B. 409). Kaputte Tageseintraege * (unbekannter Modus, fehlende Tage, unlesbare Fenster) zaehlen als geschlossen. */ export function ersatzHtml(daten, plan) { const p = plan && typeof plan === 'object' ? plan : hauptplan(daten) if (!p) return '' const name = typeof p.name === 'string' ? p.name.trim() : '' const zeilen = WOCHENTAGE.map((tag, i) => { const text = tagText(modusUndFenster(p.tage?.[i + 1])) return ` |
|---|---|
| ${tag} | ${text} |