Öffnungszeiten: Fixrunde Website-Skript und Editor (JSON-LD, Kopf beim Scrollen, späte Platzhalter, Links, Bestätigung)
- JSON-LD wird ohne "<" geschrieben (<), ein </script> im Firmennamen bricht keinen Block mehr - verschiebe(): ist die Hinweis-Leiste weggescrollt, bekommen feste Elemente ihren eigenen Inline-top zurück (Köpfe, die per Klasse beim Scrollen wegfahren, funktionieren wieder) - Späte Platzhalter (Einzelseiten-Apps): entprellter MutationObserver auf body zeichnet neue oder wieder eingehängte Platzhalter nach - sichererLink lehnt Zugangsdaten in der Adresse ab; die Adresse der Schnittstelle bestimmt nur das eigene Script-Tag, kein Platzhalter mehr (der siteKey am Platzhalter bleibt laut Spec 4.2) - Editor: die Bestätigung klebt unter der Kopfleiste und bleibt nach dem Speichern eines unteren Abschnitts sichtbar Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ecf67d6978
commit
aa9ee35ab6
6 files changed
+221
-12
No files matched your search
+69
-9
@@ -49,6 +49,8 @@
|
||||
var RELOAD_MS = 600000 // hoechstens alle 10 Minuten ein Abruf
|
||||
var ALTER_MAX_MS = 24 * 60 * 60 * 1000 // aeltere Daten gelten nicht mehr
|
||||
var ABRUF_TIMEOUT_MS = 15000
|
||||
var BEOBACHTER_MS = 200 // Entprellung: nach Aenderungen der Seite hoechstens alle 200 ms nach neuen Platzhaltern sehen
|
||||
var GETRENNT_MAX = 20
|
||||
|
||||
// @rein-anfang (reine Funktionen: kein Zugriff auf Seite oder Uhr; test/zeitenEmbedStatic.test.js fuehrt sie einzeln in einem leeren Kontext aus)
|
||||
var UHR_TOLERANZ_MS = 5 * 60 * 1000 // groessere Abweichung der Uhr laut Date-Kopf: Serverzeit
|
||||
@@ -221,11 +223,12 @@
|
||||
'Store BikeStore BookStore ClothingStore ComputerStore ConvenienceStore DepartmentStore ElectronicsStore Florist FurnitureStore GardenStore GroceryStore HardwareStore HobbyShop HomeGoodsStore JewelryStore LiquorStore MensClothingStore MobilePhoneStore MovieRentalStore MusicStore OfficeEquipmentStore OutletStore PawnShop PetStore ShoeStore SportingGoodsStore TireShop ToyStore WholesaleStore ' +
|
||||
'TelevisionStation TouristInformationCenter TravelAgency '
|
||||
|
||||
// Eine Adresse aus der Schnittstelle: nur https mit Host, ohne Leer- und Steuerzeichen (sonst ''). Es steht keine feste Webadresse im Skript,
|
||||
// nur diese Pruefung des Schemas.
|
||||
// Eine Adresse aus der Schnittstelle: nur https mit Host, ohne Leer- und Steuerzeichen und ohne Zugangsdaten (sonst ''; wie der Server und
|
||||
// linkOk des Portals). Ein @ vor dem ersten /, ?, # oder \ gehoert zu Benutzer und Kennwort (die Rueckwaertsschraege zaehlt wie / im Browser).
|
||||
// Es steht keine feste Webadresse im Skript, nur diese Pruefung des Schemas.
|
||||
function sichererLink (url) {
|
||||
if (typeof url !== 'string' || url.length > 2000 || /[\s\u0000-\u001f\u007f]/.test(url)) return ''
|
||||
return /^https:\/\/[^\/?#]+/i.test(url) ? url : ''
|
||||
return /^https:\/\/[^\/?#\\@]+(?:[\/?#]|$)/i.test(url) ? url : ''
|
||||
}
|
||||
|
||||
// Knopf eines Hinweises { art: 'link'|'anruf', ziel, text } als { art, href, text } oder null. Links: nur https; Anruf: tel: nur ueber telefonLink.
|
||||
@@ -336,6 +339,10 @@
|
||||
}
|
||||
return erg
|
||||
}
|
||||
|
||||
// JSON-LD als Text fuer ein <script type="application/ld+json">: ohne "<" (\u003c), damit ein </script> im Firmennamen oder in einem schon
|
||||
// vorhandenen Block den Block nie beendet, wenn die Seite serialisiert wird (Suchroboter, "Seite speichern"). JSON.parse liest es zurueck.
|
||||
function jsonLdText (wert) { return JSON.stringify(wert).replace(/</g, '\\u003c') }
|
||||
// @rein-ende
|
||||
|
||||
// @telefon-anfang (reine Funktion, test/zeitenEmbedStatic.test.js fuehrt sie einzeln aus; dieselbe Regel wie in public/embed/termine.js; fuer Kontakt-Leiste und Hinweis-Knoepfe)
|
||||
@@ -385,6 +392,7 @@
|
||||
var laeuft = false
|
||||
var timerId = 0
|
||||
var platzhalter = []
|
||||
var getrennt = [] // Platzhalter, die gerade nicht mehr auf der Seite stehen (hoechstens GETRENNT_MAX): kommt derselbe Knoten zurueck, gilt er wieder
|
||||
var siteKey = ''
|
||||
var api = ''
|
||||
var HANDY_MQ = '(max-width: 768px)' // Kontakt-Leiste nur auf schmalen Schirmen
|
||||
@@ -632,9 +640,31 @@
|
||||
|
||||
function fuelleSicher (p, d, t) { sicher(function () { fuelle(p, d, t) }) }
|
||||
|
||||
// Entfernte Platzhalter (Seitenwechsel einer Einzelseiten-App) wandern in `getrennt`; ein wieder eingehaengter Knoten kommt mit seinem
|
||||
// Ersatz-HTML zurueck und wird neu gezeichnet (sig leer), denn die Seite hat seinen Inhalt unterdessen vielleicht zurueckgesetzt
|
||||
function pflegePlatzhalter () {
|
||||
var i, p
|
||||
var bleibt = []
|
||||
for (i = 0; i < platzhalter.length; i++) {
|
||||
p = platzhalter[i]
|
||||
if (p.root.isConnected !== false) bleibt.push(p)
|
||||
else getrennt.push(p)
|
||||
}
|
||||
platzhalter = bleibt
|
||||
var rest = []
|
||||
for (i = 0; i < getrennt.length; i++) {
|
||||
p = getrennt[i]
|
||||
if (p.root.isConnected !== false) {
|
||||
p.sig = ''
|
||||
platzhalter.push(p)
|
||||
} else rest.push(p)
|
||||
}
|
||||
getrennt = rest.slice(-GETRENNT_MAX)
|
||||
}
|
||||
|
||||
function render () {
|
||||
sammle()
|
||||
platzhalter = platzhalter.filter(function (p) { return p.root.isConnected !== false })
|
||||
pflegePlatzhalter()
|
||||
var d = frisch() ? daten : null
|
||||
var t = jetzt()
|
||||
for (var i = 0; i < platzhalter.length; i++) fuelleSicher(platzhalter[i], d, t)
|
||||
@@ -760,11 +790,18 @@
|
||||
return liste
|
||||
}
|
||||
|
||||
// Feste Elemente um max(0, Leistenhoehe - Scrollposition) nach unten ruecken (Inline-top, hat Vorrang vor den Regeln der Seite)
|
||||
// Feste Elemente um max(0, Leistenhoehe - Scrollposition) nach unten ruecken (Inline-top, hat Vorrang vor den Regeln der Seite). Ist die
|
||||
// Leiste weggescrollt (s = 0), bekommt das Element seinen eigenen Inline-top zurueck (oder keinen): Koepfe, die per Klasse oder Skript beim
|
||||
// Scrollen weg- und wieder einfahren (top: -80px), arbeiten dann wie auf der Seite gedacht.
|
||||
function verschiebe (l) {
|
||||
var y = window.pageYOffset || document.documentElement.scrollTop || 0
|
||||
var s = Math.max(0, l.hoehe - Math.max(0, y))
|
||||
for (var i = 0; i < l.fest.length; i++) l.fest[i].el.style.setProperty('top', (l.fest[i].basis + s) + 'px', 'important')
|
||||
for (var i = 0; i < l.fest.length; i++) {
|
||||
var f = l.fest[i]
|
||||
if (s > 0) f.el.style.setProperty('top', (f.basis + s) + 'px', 'important')
|
||||
else if (f.wert) f.el.style.setProperty('top', f.wert, f.prio)
|
||||
else f.el.style.removeProperty('top')
|
||||
}
|
||||
}
|
||||
|
||||
function setzeFesteZurueck (l) {
|
||||
@@ -985,7 +1022,7 @@
|
||||
try { bloecke.push(JSON.parse(skripte[i].textContent)) } catch (e) { bloecke.push(null) }
|
||||
}
|
||||
var erg = jsonLdEinbauen(bloecke, jd, seite)
|
||||
for (i = 0; i < erg.geaendert.length; i++) skripte[erg.geaendert[i]].textContent = JSON.stringify(bloecke[erg.geaendert[i]])
|
||||
for (i = 0; i < erg.geaendert.length; i++) skripte[erg.geaendert[i]].textContent = jsonLdText(bloecke[erg.geaendert[i]])
|
||||
if (erg.neu) {
|
||||
if (!ldEigenes) {
|
||||
ldEigenes = document.createElement('script')
|
||||
@@ -993,7 +1030,7 @@
|
||||
ldEigenes.setAttribute('data-wkz-jsonld', '')
|
||||
;(document.head || document.documentElement).appendChild(ldEigenes)
|
||||
}
|
||||
ldEigenes.textContent = JSON.stringify(erg.neu)
|
||||
ldEigenes.textContent = jsonLdText(erg.neu)
|
||||
} else if (ldEigenes) {
|
||||
if (ldEigenes.parentNode) ldEigenes.parentNode.removeChild(ldEigenes)
|
||||
ldEigenes = null
|
||||
@@ -1039,8 +1076,8 @@
|
||||
}
|
||||
|
||||
function bestimmeApi (skript) {
|
||||
// nur das eigene Script-Tag (oder dessen Herkunft) bestimmt die Adresse: ein Platzhalter der Seite kann die Anfragen nicht umleiten
|
||||
var a = skript ? attr(skript, 'data-api') : ''
|
||||
for (var i = 0; !a && i < platzhalter.length; i++) a = attr(platzhalter[i].root, 'data-api')
|
||||
a = a.replace(/\/+$/, '')
|
||||
if (a) return a
|
||||
if (skript && skript.src) {
|
||||
@@ -1049,6 +1086,28 @@
|
||||
return ''
|
||||
}
|
||||
|
||||
// Einzelseiten-Apps (Vite/React) haengen Platzhalter erst nach einem Seitenwechsel ein. Ein Beobachter auf body (childList, subtree) sieht
|
||||
// einen neuen (noch nicht eingetragenen) oder wieder eingehaengten Platzhalter und zeichnet nach, hoechstens alle BEOBACHTER_MS; er bleibt
|
||||
// fuer die Lebensdauer der Seite (die eigenen Aenderungen des Skripts loesen nur diese billige Pruefung aus, nie ein Zeichnen).
|
||||
function pruefeNeue () {
|
||||
var neu = document.querySelector(SELEKTOR + ':not([data-wkz-init])')
|
||||
for (var i = 0; !neu && i < getrennt.length; i++) if (getrennt[i].root.isConnected !== false) neu = getrennt[i].root
|
||||
if (neu) render()
|
||||
}
|
||||
|
||||
function beobachteSeite () {
|
||||
if (typeof MutationObserver !== 'function' || !document.body) return
|
||||
var wartet = false
|
||||
new MutationObserver(function () {
|
||||
if (wartet) return
|
||||
wartet = true
|
||||
timer(function () {
|
||||
wartet = false
|
||||
sicher(pruefeNeue)
|
||||
}, BEOBACHTER_MS)
|
||||
}).observe(document.body, { childList: true, subtree: true })
|
||||
}
|
||||
|
||||
function start () {
|
||||
sammle()
|
||||
var skript = eigenesSkript()
|
||||
@@ -1067,6 +1126,7 @@
|
||||
}
|
||||
tick()
|
||||
})
|
||||
beobachteSeite()
|
||||
lade()
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
/* Editor: Zeitplaene, Feiertage, Ausnahmen (zeiten-editor.js, zeiten-plaene.js, zeiten-ausnahmen.js) */
|
||||
.zt-editor > * + * { margin-top: 16px; }
|
||||
.zt-editor > .tm-meldung:empty, .zt-editor > .tm-box[hidden] { margin: 0; }
|
||||
/* Die Bestätigung (erstes Kind des Editors) klebt unter der Kopfleiste: nach dem Speichern eines unteren Abschnitts springt der Fokus auf den Knopf,
|
||||
und „gespeichert“ läge sonst ausser Sicht. Undurchsichtig, damit der Inhalt darunter nicht durchscheint; leer klebt nichts. */
|
||||
.zt-editor > .tm-meldung:not(:empty) { position: sticky; top: calc(var(--kb-appbar-h, 0px) + 8px); z-index: 5; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45); }
|
||||
.zt-editor > .tm-meldung-ok:not(:empty) { background: linear-gradient(rgba(74, 222, 128, 0.1), rgba(74, 222, 128, 0.1)), #0c0c0e; }
|
||||
.zt-editor > .tm-meldung-fehler:not(:empty) { background: linear-gradient(rgba(248, 113, 113, 0.1), rgba(248, 113, 113, 0.1)), #0c0c0e; }
|
||||
.zt-kopfbereich:empty { display: none; }
|
||||
.zt-spaeter { margin: 0 4px; line-height: 1.5; }
|
||||
.zt-plaene { display: grid; gap: 12px; }
|
||||
|
||||
Reference in new issue
Block a user