Fix: alle funktionalen Inline-Event-Handler → addEventListener/Delegation (von CSP-Härtung 65cfa25 app-weit blockiert)

Chat (senden/öffnen/löschen/Foto), Tagebuch-Buch, KI-Berichte, Wiki-Moderation,
Events-Detail, Walks-Lightbox, Routen-Foto, Navigations-CTAs (data-page),
Presse-Copy + Züchter-Landing (externes JS). 35x UI.modal.close → data-modal-close,
28x totes event.stopPropagation entfernt. Verbleibend: kosmetische onerror/Hover. SW v1164
This commit is contained in:
rene 2026-06-04 13:59:27 +02:00
parent 152fde716c
commit 2ddd8ac350
34 changed files with 228 additions and 173 deletions

View file

@ -616,7 +616,7 @@ window.Page_dog_profile = (() => {
footer: `
<div class="w3-btn-stack">
<button class="btn btn-primary" id="chip-edit-save-btn" class="w-full">Speichern</button>
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
</div>`,
});
document.getElementById('chip-edit-save-btn').addEventListener('click', async () => {
@ -675,7 +675,7 @@ window.Page_dog_profile = (() => {
${hasPhoto ? `<button class="btn btn-primary" id="pe-save-btn" class="w-full">Speichern</button>` : ''}
<div class="flex-gap-2">
${hasPhoto ? `<button class="btn btn-danger" id="pe-delete-btn">${UI.icon('trash')} Löschen</button>` : ''}
<button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
<button class="btn btn-secondary flex-1" data-modal-close>Abbrechen</button>
</div>
</div>
`;
@ -957,7 +957,7 @@ window.Page_dog_profile = (() => {
</div>
<div id="share-list-wrap" class="mt-4"></div>`,
footer: `
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
<button class="btn btn-secondary" data-modal-close>Schließen</button>
<button class="btn btn-primary" id="share-create-btn">Link erstellen</button>`,
});
@ -1489,7 +1489,7 @@ window.Page_dog_profile = (() => {
</p>
${data.hinweis ? `<p style="font-size:var(--text-sm);color:var(--c-text-muted);margin-top:var(--space-3)">${UI.escape(data.hinweis)}</p>` : ''}
</div>`,
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
});
return;
}
@ -1608,7 +1608,7 @@ window.Page_dog_profile = (() => {
</div>`,
footer: `
<div style="display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:flex-end">
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
<button class="btn btn-secondary" data-modal-close>Schließen</button>
<a class="btn btn-secondary" href="/ausweis/${dog.id}" target="_blank" rel="noopener">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#identification-card"></use></svg>
Ausweis öffnen
@ -1832,7 +1832,7 @@ window.Page_dog_profile = (() => {
</div>`,
footer: `
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
<button class="btn btn-primary" id="pp-meta-save">Speichern</button>
</div>`,
});
@ -1896,7 +1896,7 @@ window.Page_dog_profile = (() => {
</div>`,
footer: `
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
<button class="btn btn-primary" id="pp-vacc-save">Speichern</button>
</div>`,
});
@ -1960,7 +1960,7 @@ window.Page_dog_profile = (() => {
</div>`,
footer: `
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
<button class="btn btn-primary" id="pp-med-save">Speichern</button>
</div>`,
});
@ -2017,7 +2017,7 @@ window.Page_dog_profile = (() => {
UI.modal.open({
title: 'Hundepass-Link teilen',
body: shareWrap.innerHTML,
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
});
document.getElementById('pp-sharelink-copy')?.addEventListener('click', async () => {
await navigator.clipboard.writeText(url).catch(() => {});
@ -2209,7 +2209,7 @@ window.Page_dog_profile = (() => {
? 'background:#7a4f1a;color:#f5e4c0;border-color:#7a4f1a;'
: 'background:#f5f0e8;color:#444;border-color:#e0d4b8;';
const label = y === 'alle' ? 'Alle' : y;
return `<button onclick="window._buchSetJahr('${y}')" style="
return `<button data-buch-action="year" data-buch-year="${y}" style="
border:1px solid;border-radius:8px;padding:8px 16px;
font-size:0.9rem;cursor:pointer;font-family:inherit;
${active}
@ -2239,14 +2239,14 @@ window.Page_dog_profile = (() => {
<div style="margin-bottom:20px;display:flex;flex-direction:column;gap:10px">
<label style="display:flex;align-items:center;gap:12px;cursor:pointer">
<button onclick="window._buchToggleFotos()" style="
<button data-buch-action="fotos" style="
width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;
${togStyle(nurFotos)}
">${nurFotos ? '✓' : ''}</button>
<span style="font-size:0.95rem">Nur Einträge mit Fotos</span>
</label>
<label style="display:flex;align-items:center;gap:12px;cursor:pointer">
<button onclick="window._buchToggleMeilensteine()" style="
<button data-buch-action="meilen" style="
width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;
${togStyle(nurMeilensteine)}
">${nurMeilensteine ? '✓' : ''}</button>
@ -2255,11 +2255,11 @@ window.Page_dog_profile = (() => {
</div>
<div style="display:flex;gap:10px">
<button onclick="window._buchOpen()" style="
<button data-buch-action="open" style="
flex:1;background:#7a4f1a;color:#f5e4c0;border:none;border-radius:10px;
padding:14px;font-size:1rem;font-weight:700;cursor:pointer;font-family:inherit;
">📖 Buch öffnen</button>
<button onclick="window._buchClose()" style="
<button data-buch-action="close" style="
background:#f0f0f0;color:#555;border:none;border-radius:10px;
padding:14px 18px;font-size:1rem;cursor:pointer;font-family:inherit;
"></button>
@ -2268,18 +2268,14 @@ window.Page_dog_profile = (() => {
`;
};
window._buchSetJahr = (y) => { selectedJahr = y; renderModal(); };
window._buchToggleFotos = () => { nurFotos = !nurFotos; renderModal(); };
window._buchToggleMeilensteine = () => { nurMeilensteine = !nurMeilensteine; renderModal(); };
window._buchClose = () => {
const setJahr = (y) => { selectedJahr = y; renderModal(); };
const toggleFotos = () => { nurFotos = !nurFotos; renderModal(); };
const toggleMeilen = () => { nurMeilensteine = !nurMeilensteine; renderModal(); };
const closeModal = () => {
modalEl.remove();
delete window._buchSetJahr;
delete window._buchToggleFotos;
delete window._buchToggleMeilensteine;
delete window._buchOpen;
delete window._buchClose;
document.removeEventListener('keydown', onKey);
};
window._buchOpen = () => {
const openBuch = () => {
const params = new URLSearchParams();
if (selectedJahr !== 'alle') params.set('jahr', selectedJahr);
if (nurFotos) params.set('nur_fotos', 'true');
@ -2290,10 +2286,24 @@ window.Page_dog_profile = (() => {
renderModal();
document.body.appendChild(modalEl);
modalEl.addEventListener('click', e => { if (e.target === modalEl) window._buchClose(); });
// Delegierter Click-Handler (Inline-onclick wird von der CSP blockiert);
// überlebt das Re-Rendern via renderModal().
modalEl.addEventListener('click', e => {
if (e.target === modalEl) { closeModal(); return; }
const btn = e.target.closest('[data-buch-action]');
if (!btn) return;
switch (btn.dataset.buchAction) {
case 'year': setJahr(btn.dataset.buchYear); break;
case 'fotos': toggleFotos(); break;
case 'meilen': toggleMeilen(); break;
case 'open': openBuch(); break;
case 'close': closeModal(); break;
}
});
const onKey = e => {
if (e.key === 'Escape') { window._buchClose(); document.removeEventListener('keydown', onKey); }
if (e.key === 'Escape') { closeModal(); }
};
document.addEventListener('keydown', onKey);
}
@ -2310,7 +2320,7 @@ window.Page_dog_profile = (() => {
<use href="/icons/phosphor.svg#spinner-gap"></use>
</svg>
</div>`,
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
size: 'large',
});