Refactor: 1167 _esc() → UI.escape() in 36 Dateien, SW by-v1113

Bündel 1 aus dem Duplikat-Audit: existierende zentrale Helper nutzen
statt lokale Duplikate.

Pure Migration ohne neuen Code:
- 1167 _esc()-Aufrufe in 36 Page-Modulen migriert auf UI.escape()
- 24 lokale _esc/_escape-Definitionen entfernt
- lost.js hatte _escape() (Variante) — 17 Aufrufe ebenfalls migriert
- jobs.js + breeder.js: tote Alias-Wrapper entfernt

UI.escape() existierte schon — wurde nur überall lokal nochmal
implementiert. Funktional identisch (gleiche 4-replace-chain für
& < > ").

Tests 19/19 grün. Frontend-LOC um ~120 Zeilen reduziert.

Hinweis: _emptyState (7 Stellen) und _icon (8 Stellen) wurden NICHT
migriert — sie haben abweichende Signaturen von UI.emptyState({...})
bzw. UI.icon(name). Eigener Sprint nötig.
This commit is contained in:
rene 2026-05-27 10:15:33 +02:00
parent e7939ce98e
commit c517c9281d
42 changed files with 1115 additions and 1341 deletions

View file

@ -17,10 +17,6 @@ window.Page_zuchthunde = (() => {
// ----------------------------------------------------------
// Hilfsfunktionen
// ----------------------------------------------------------
function _esc(s) {
return UI.escape ? UI.escape(s || '') : (s || '').replace(/[&<>"']/g, c =>
({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
function _fmtDate(iso) {
if (!iso) return '—';
@ -54,7 +50,7 @@ window.Page_zuchthunde = (() => {
else if (e === '3' || e === 'ED 3') color = '#EF4444';
}
return `<span class="zh-badge" style="background:${color}">${_esc(ergebnis || '—')}</span>`;
return `<span class="zh-badge" style="background:${color}">${UI.escape(ergebnis || '—')}</span>`;
}
function _geneticBadge(ergebnis) {
@ -63,7 +59,7 @@ window.Page_zuchthunde = (() => {
if (e === 'clear') color = '#22C55E';
if (e === 'carrier') color = '#EAB308';
if (e === 'affected') color = '#EF4444';
return `<span class="zh-badge" style="background:${color}">${_esc(ergebnis || '—')}</span>`;
return `<span class="zh-badge" style="background:${color}">${UI.escape(ergebnis || '—')}</span>`;
}
// ----------------------------------------------------------
@ -103,7 +99,7 @@ window.Page_zuchthunde = (() => {
const zwinger = _breederInfo?.zwingername || 'Mein Zwinger';
const logoUrl = _breederInfo?.logo_url || null;
const logoHtml = logoUrl
? `<img src="${_esc(logoUrl)}" alt="Logo"
? `<img src="${UI.escape(logoUrl)}" alt="Logo"
style="width:48px;height:48px;border-radius:50%;object-fit:cover;
border:2px solid rgba(196,132,58,.5);flex-shrink:0"
onerror="this.style.display='none'">`
@ -123,7 +119,7 @@ window.Page_zuchthunde = (() => {
<div class="flex-1-min">
<h2 style="margin:0 0 2px;font-size:var(--text-lg);font-weight:700;
color:var(--c-text);white-space:nowrap;overflow:hidden;
text-overflow:ellipsis;line-height:1.2">${_esc(zwinger)}</h2>
text-overflow:ellipsis;line-height:1.2">${UI.escape(zwinger)}</h2>
<div style="display:flex;align-items:center;gap:var(--space-2)">
<svg style="width:11px;height:11px;color:var(--c-primary);flex-shrink:0" viewBox="0 0 256 256">
<use href="/icons/phosphor.svg#lock-key"></use>
@ -208,7 +204,7 @@ window.Page_zuchthunde = (() => {
const el = document.getElementById('zh-list');
if (el) el.innerHTML = `
<p style="color:var(--c-danger);text-align:center;padding:var(--space-8)">
${_esc(err.message || 'Fehler beim Laden.')}
${UI.escape(err.message || 'Fehler beim Laden.')}
</p>`;
}
}
@ -228,7 +224,7 @@ window.Page_zuchthunde = (() => {
if (!filtered.length) {
el.innerHTML = _query
? `<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-6)">Keine Treffer für „${_esc(_query)}".</p>`
? `<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-6)">Keine Treffer für „${UI.escape(_query)}".</p>`
: `
<div style="text-align:center;padding:var(--space-10) var(--space-4)">
<div style="font-size:3rem;margin-bottom:var(--space-3)">${UI.icon('dog')}</div>
@ -299,12 +295,12 @@ window.Page_zuchthunde = (() => {
// Hund-Card HTML
// ----------------------------------------------------------
function _hundCardHTML(h) {
const nameLabel = h.name ? _esc(h.name) : '<em class="text-muted">Unbenannt</em>';
const rufname = h.rufname ? ` (${_esc(h.rufname)})` : '';
const nameLabel = h.name ? UI.escape(h.name) : '<em class="text-muted">Unbenannt</em>';
const rufname = h.rufname ? ` (${UI.escape(h.rufname)})` : '';
const geburtstag = h.geburtsdatum ? _fmtDate(h.geburtsdatum) : null;
const vaterLabel = h.vater_name ? `Vater: ${_esc(h.vater_name)}` : null;
const mutterLabel = h.mutter_name ? `Mutter: ${_esc(h.mutter_name)}` : null;
const vaterLabel = h.vater_name ? `Vater: ${UI.escape(h.vater_name)}` : null;
const mutterLabel = h.mutter_name ? `Mutter: ${UI.escape(h.mutter_name)}` : null;
const eltern = [vaterLabel, mutterLabel].filter(Boolean).join(' &nbsp;·&nbsp; ');
const pubLabel = h.is_public
@ -317,14 +313,14 @@ window.Page_zuchthunde = (() => {
<div class="flex-1-min">
<div class="zh-card-title">
${_genderIcon(h.geschlecht)}
${nameLabel}${_esc(rufname)}
${nameLabel}${UI.escape(rufname)}
${pubLabel}
</div>
<div class="zh-card-meta">
${h.rasse ? `${UI.icon('paw-print')} ${_esc(h.rasse)}&nbsp;&nbsp;` : ''}
${h.rasse ? `${UI.icon('paw-print')} ${UI.escape(h.rasse)}&nbsp;&nbsp;` : ''}
${geburtstag ? `${UI.icon('calendar-dots')} ${geburtstag}&nbsp;&nbsp;` : ''}
${h.chip_nr ? `${UI.icon('barcode')} ${_esc(h.chip_nr)}&nbsp;&nbsp;` : ''}
${h.zuchtbuchnummer ? `${UI.icon('book-open')} ${_esc(h.zuchtbuchnummer)}&nbsp;&nbsp;` : ''}
${h.chip_nr ? `${UI.icon('barcode')} ${UI.escape(h.chip_nr)}&nbsp;&nbsp;` : ''}
${h.zuchtbuchnummer ? `${UI.icon('book-open')} ${UI.escape(h.zuchtbuchnummer)}&nbsp;&nbsp;` : ''}
</div>
${eltern ? `<div class="zh-card-meta text-xs-secondary">${eltern}</div>` : ''}
</div>
@ -416,7 +412,7 @@ window.Page_zuchthunde = (() => {
const tests = await API.zuchthunde.healthTests(hundId);
_renderHealthSection(hundId, wrap, tests);
} catch (err) {
wrap.innerHTML = `<p style="color:var(--c-danger);font-size:var(--text-sm);padding:var(--space-2)">${_esc(err.message || 'Fehler.')}</p>`;
wrap.innerHTML = `<p style="color:var(--c-danger);font-size:var(--text-sm);padding:var(--space-2)">${UI.escape(err.message || 'Fehler.')}</p>`;
}
}
@ -425,11 +421,11 @@ window.Page_zuchthunde = (() => {
? tests.map(t => `
<div class="zh-detail-row">
<div class="zh-detail-info">
<span class="zh-detail-label">${_esc(t.test_typ || 'Sonstiges')}</span>
${t.test_name ? `<span style="color:var(--c-text-secondary);font-size:var(--text-xs)">${_esc(t.test_name)}</span>` : ''}
<span class="zh-detail-label">${UI.escape(t.test_typ || 'Sonstiges')}</span>
${t.test_name ? `<span style="color:var(--c-text-secondary);font-size:var(--text-xs)">${UI.escape(t.test_name)}</span>` : ''}
${_healthBadge(t.test_typ || '', t.ergebnis)}
${t.untersuch_am ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_fmtDate(t.untersuch_am)}</span>` : ''}
${t.labor ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(t.labor)}</span>` : ''}
${t.labor ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(t.labor)}</span>` : ''}
</div>
<button class="btn btn-ghost btn-xs zh-health-del-btn" data-tid="${t.id}" title="Löschen"
class="text-danger">${UI.icon('trash')}</button>
@ -480,7 +476,7 @@ window.Page_zuchthunde = (() => {
const tests = await API.zuchthunde.geneticTests(hundId);
_renderGeneticSection(hundId, wrap, tests);
} catch (err) {
wrap.innerHTML = `<p style="color:var(--c-danger);font-size:var(--text-sm);padding:var(--space-2)">${_esc(err.message || 'Fehler.')}</p>`;
wrap.innerHTML = `<p style="color:var(--c-danger);font-size:var(--text-sm);padding:var(--space-2)">${UI.escape(err.message || 'Fehler.')}</p>`;
}
}
@ -489,10 +485,10 @@ window.Page_zuchthunde = (() => {
? tests.map(t => `
<div class="zh-detail-row">
<div class="zh-detail-info">
<span class="zh-detail-label">${_esc(t.marker_name || '—')}</span>
<span class="zh-detail-label">${UI.escape(t.marker_name || '—')}</span>
${_geneticBadge(t.ergebnis_klasse)}
${t.getestet_am ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_fmtDate(t.getestet_am)}</span>` : ''}
${t.labor ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(t.labor)}</span>` : ''}
${t.labor ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(t.labor)}</span>` : ''}
</div>
<button class="btn btn-ghost btn-xs zh-genetic-del-btn" data-tid="${t.id}" title="Löschen"
class="text-danger">${UI.icon('trash')}</button>
@ -543,7 +539,7 @@ window.Page_zuchthunde = (() => {
const titles = await API.zuchthunde.titles(hundId);
_renderTitlesSection(hundId, wrap, titles);
} catch (err) {
wrap.innerHTML = `<p style="color:var(--c-danger);font-size:var(--text-sm);padding:var(--space-2)">${_esc(err.message || 'Fehler.')}</p>`;
wrap.innerHTML = `<p style="color:var(--c-danger);font-size:var(--text-sm);padding:var(--space-2)">${UI.escape(err.message || 'Fehler.')}</p>`;
}
}
@ -552,12 +548,12 @@ window.Page_zuchthunde = (() => {
? titles.map(t => `
<div class="zh-detail-row">
<div class="zh-detail-info">
<span class="zh-detail-label">${_esc(t.titel_name || '—')}</span>
${t.titel_typ ? `<span class="zh-badge" style="background:#6B7280">${_esc(t.titel_typ)}</span>` : ''}
<span class="zh-detail-label">${UI.escape(t.titel_name || '—')}</span>
${t.titel_typ ? `<span class="zh-badge" style="background:#6B7280">${UI.escape(t.titel_typ)}</span>` : ''}
${t.verliehen_am ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_fmtDate(t.verliehen_am)}</span>` : ''}
${t.ort ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(t.ort)}</span>` : ''}
${t.richter ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(t.richter)}</span>` : ''}
${t.formwert ? `<span class="zh-badge" style="background:#3B82F6">${_esc(t.formwert)}</span>` : ''}
${t.ort ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(t.ort)}</span>` : ''}
${t.richter ? `<span style="color:var(--c-text-muted);font-size:var(--text-xs)">${UI.escape(t.richter)}</span>` : ''}
${t.formwert ? `<span class="zh-badge" style="background:#3B82F6">${UI.escape(t.formwert)}</span>` : ''}
</div>
<button class="btn btn-ghost btn-xs zh-title-del-btn" data-tid="${t.id}" title="Löschen"
class="text-danger">${UI.icon('trash')}</button>
@ -614,13 +610,13 @@ window.Page_zuchthunde = (() => {
const vaterOptions = [
`<option value="">— kein Vater —</option>`,
...maennliche.map(h =>
`<option value="${h.id}" ${v.vater_id === h.id ? 'selected' : ''}>${_esc(h.name)}${h.rufname ? ` (${_esc(h.rufname)})` : ''}</option>`),
`<option value="${h.id}" ${v.vater_id === h.id ? 'selected' : ''}>${UI.escape(h.name)}${h.rufname ? ` (${UI.escape(h.rufname)})` : ''}</option>`),
].join('');
const mutterOptions = [
`<option value="">— keine Mutter —</option>`,
...weibliche.map(h =>
`<option value="${h.id}" ${v.mutter_id === h.id ? 'selected' : ''}>${_esc(h.name)}${h.rufname ? ` (${_esc(h.rufname)})` : ''}</option>`),
`<option value="${h.id}" ${v.mutter_id === h.id ? 'selected' : ''}>${UI.escape(h.name)}${h.rufname ? ` (${UI.escape(h.rufname)})` : ''}</option>`),
].join('');
const body = `
@ -630,12 +626,12 @@ window.Page_zuchthunde = (() => {
<div class="form-group">
<label class="form-label">Vollständiger Name <span class="text-danger">*</span></label>
<input class="form-control" type="text" name="name" required
value="${_esc(v.name || '')}" placeholder="z. B. Banyaro's Black Diamond">
value="${UI.escape(v.name || '')}" placeholder="z. B. Banyaro's Black Diamond">
</div>
<div class="form-group">
<label class="form-label">Rufname</label>
<input class="form-control" type="text" name="rufname"
value="${_esc(v.rufname || '')}" placeholder="z. B. Diamond">
value="${UI.escape(v.rufname || '')}" placeholder="z. B. Diamond">
</div>
</div>
@ -651,7 +647,7 @@ window.Page_zuchthunde = (() => {
<div class="form-group">
<label class="form-label">Geburtsdatum</label>
<input class="form-control" type="date" name="geburtsdatum"
value="${_esc(v.geburtsdatum || '')}">
value="${UI.escape(v.geburtsdatum || '')}">
</div>
</div>
@ -659,12 +655,12 @@ window.Page_zuchthunde = (() => {
<div class="form-group">
<label class="form-label">Sterbedatum</label>
<input class="form-control" type="date" name="sterbedatum"
value="${_esc(v.sterbedatum || '')}">
value="${UI.escape(v.sterbedatum || '')}">
</div>
<div class="form-group">
<label class="form-label">Farbe / Fell</label>
<input class="form-control" type="text" name="farbe"
value="${_esc(v.farbe || '')}" placeholder="z. B. schwarz-braun">
value="${UI.escape(v.farbe || '')}" placeholder="z. B. schwarz-braun">
</div>
</div>
@ -672,19 +668,19 @@ window.Page_zuchthunde = (() => {
<div class="form-group">
<label class="form-label">Chip-Nr.</label>
<input class="form-control" type="text" name="chip_nr"
value="${_esc(v.chip_nr || '')}" placeholder="15-stellig">
value="${UI.escape(v.chip_nr || '')}" placeholder="15-stellig">
</div>
<div class="form-group">
<label class="form-label">Tätowiernummer</label>
<input class="form-control" type="text" name="taetowier_nr"
value="${_esc(v.taetowier_nr || '')}">
value="${UI.escape(v.taetowier_nr || '')}">
</div>
</div>
<div class="form-group">
<label class="form-label">Zuchtbuchnummer</label>
<input class="form-control" type="text" name="zuchtbuchnummer"
value="${_esc(v.zuchtbuchnummer || '')}" placeholder="z. B. SZ 123456">
value="${UI.escape(v.zuchtbuchnummer || '')}" placeholder="z. B. SZ 123456">
</div>
<div class="grid-2">
@ -702,19 +698,19 @@ window.Page_zuchthunde = (() => {
<div class="form-group">
<label class="form-label">Züchter-Name</label>
<input class="form-control" type="text" name="zuechter_name"
value="${_esc(v.zuechter_name || '')}" placeholder="Bei Fremdzüchter">
value="${UI.escape(v.zuechter_name || '')}" placeholder="Bei Fremdzüchter">
</div>
<div class="form-group">
<label class="form-label">Eigentümer-Name</label>
<input class="form-control" type="text" name="eigentuemer_name"
value="${_esc(v.eigentuemer_name || '')}">
value="${UI.escape(v.eigentuemer_name || '')}">
</div>
</div>
<div class="form-group">
<label class="form-label">Notiz <span class="text-secondary">(intern)</span></label>
<textarea class="form-control" name="notiz" rows="2"
placeholder="Interne Anmerkungen…">${_esc(v.notiz || '')}</textarea>
placeholder="Interne Anmerkungen…">${UI.escape(v.notiz || '')}</textarea>
</div>
<div class="form-group">
@ -1121,14 +1117,14 @@ window.Page_zuchthunde = (() => {
`<option value="">-- Aus eigenen Hunden --</option>`,
..._hunde
.filter(h => h.geschlecht !== 'weiblich')
.map(h => `<option value="${h.id}">${_esc(h.name)}${h.rufname ? ` (${_esc(h.rufname)})` : ''}</option>`),
.map(h => `<option value="${h.id}">${UI.escape(h.name)}${h.rufname ? ` (${UI.escape(h.rufname)})` : ''}</option>`),
].join('');
const muetterOptions = [
`<option value="">-- Aus eigenen Hunden --</option>`,
..._hunde
.filter(h => h.geschlecht !== 'maennlich')
.map(h => `<option value="${h.id}">${_esc(h.name)}${h.rufname ? ` (${_esc(h.rufname)})` : ''}</option>`),
.map(h => `<option value="${h.id}">${UI.escape(h.name)}${h.rufname ? ` (${UI.escape(h.rufname)})` : ''}</option>`),
].join('');
const body = `
@ -1206,7 +1202,7 @@ window.Page_zuchthunde = (() => {
if (v.gen_vater != null) genInfo.push(`Gen. ${v.gen_vater} Vater`);
if (v.gen_mutter != null) genInfo.push(`Gen. ${v.gen_mutter} Mutter`);
const genStr = genInfo.length ? ` <span style="color:var(--c-text-secondary);font-size:var(--text-xs)">(${genInfo.join(' / ')})</span>` : '';
return `<li style="padding:var(--space-1) 0">${_esc(v.name || '—')}${genStr}</li>`;
return `<li style="padding:var(--space-1) 0">${UI.escape(v.name || '—')}${genStr}</li>`;
}).join('')
: `<li class="text-muted">Keine gemeinsamen Vorfahren gefunden.</li>`;
@ -1220,13 +1216,13 @@ window.Page_zuchthunde = (() => {
const wIssueHTML = (welfare.issues || []).map(i => `
<div style="display:flex;gap:8px;padding:6px 0;border-bottom:1px solid rgba(0,0,0,.06)">
<span style="color:${wColor};flex-shrink:0">${UI.icon('warning')}</span>
<span class="text-sm">${_esc(i.text)}</span>
<span class="text-sm">${UI.escape(i.text)}</span>
</div>`).join('');
const wOkHTML = (welfare.ok_points || []).map(p => `
<div style="display:flex;gap:8px;padding:4px 0">
<span style="color:#16a34a;flex-shrink:0">${UI.icon('check')}</span>
<span class="text-sm-secondary">${_esc(p)}</span>
<span class="text-sm-secondary">${UI.escape(p)}</span>
</div>`).join('');
welfareHTML = `
@ -1254,7 +1250,7 @@ window.Page_zuchthunde = (() => {
${ik.toFixed(2)} %
</div>
<div style="font-size:var(--text-sm);color:${ampelColor};font-weight:var(--weight-semibold)">
${_esc(result.ik_rating || ampelLabel)}
${UI.escape(result.ik_rating || ampelLabel)}
</div>
</div>
</div>
@ -1314,7 +1310,7 @@ window.Page_zuchthunde = (() => {
} catch (err) {
UI.modal.open({
title: `${UI.icon('sparkle')} KI-Hunde-Beschreibung`,
body: `<p class="text-danger">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
body: `<p class="text-danger">${UI.escape(err.message || 'Fehler beim Generieren.')}</p>`,
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
});
return;
@ -1322,7 +1318,7 @@ window.Page_zuchthunde = (() => {
UI.modal.open({
title: `${UI.icon('sparkle')} KI-Hunde-Beschreibung`,
body: `<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>`,
body: `<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${UI.escape(text)}</div>`,
footer: `
<button class="btn btn-secondary flex-1" id="ki-desc-copy">
${UI.icon('clipboard-text')} Kopieren
@ -1414,7 +1410,7 @@ window.Page_zuchthunde = (() => {
} catch (err) {
UI.modal.open({
title: `${UI.icon('chart-bar')} KI-Jahresbericht`,
body: `<p class="text-danger">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
body: `<p class="text-danger">${UI.escape(err.message || 'Fehler beim Generieren.')}</p>`,
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
});
return;
@ -1429,7 +1425,7 @@ window.Page_zuchthunde = (() => {
body: `
${savedId ? `<p style="font-size:var(--text-xs);color:var(--c-success);margin:0 0 var(--space-3);display:flex;align-items:center;gap:4px">
${UI.icon('check-circle')} Automatisch gespeichert</p>` : ''}
<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>`,
<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${UI.escape(text)}</div>`,
footer: `
<button class="btn btn-ghost btn-sm" id="ki-bericht-copy">
${UI.icon('clipboard-text')} Kopieren
@ -1532,7 +1528,7 @@ window.Page_zuchthunde = (() => {
} catch (err) {
UI.modal.open({
title: `${UI.icon('sparkle')} KI-Paarungsanalyse`,
body: `<p class="text-danger">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
body: `<p class="text-danger">${UI.escape(err.message || 'Fehler beim Generieren.')}</p>`,
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
});
return;
@ -1561,9 +1557,9 @@ window.Page_zuchthunde = (() => {
<div style="padding:var(--space-3);border-radius:var(--radius-md);
background:${empfehlungColor}18;border:1.5px solid ${empfehlungColor}40;
font-weight:var(--weight-semibold);color:${empfehlungColor}">
${UI.icon('check-circle')} ${_esc(empfehlungLabel)}
${UI.icon('check-circle')} ${UI.escape(empfehlungLabel)}
</div>` : ''}
<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>
<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${UI.escape(text)}</div>
</div>`,
footer: `
<button class="btn btn-secondary flex-1" id="ki-paarung-copy">
@ -1752,15 +1748,15 @@ window.Page_zuchthunde = (() => {
<div style="position:relative;border-radius:var(--radius-md);overflow:hidden;
border:${isPrimary ? '2px solid var(--c-primary)' : '1px solid var(--c-border)'};aspect-ratio:1"
data-photo-id="${ph.id}">
<a href="${_esc(ph.url || '')}" target="_blank" rel="noopener noreferrer">
<img src="${_esc(thumb)}" alt="${_esc(ph.caption || '')}"
<a href="${UI.escape(ph.url || '')}" target="_blank" rel="noopener noreferrer">
<img src="${UI.escape(thumb)}" alt="${UI.escape(ph.caption || '')}"
loading="lazy" style="width:100%;height:100%;object-fit:cover;display:block"
onerror="this.parentElement.parentElement.style.opacity='.4'">
</a>
${isPrimary ? `<span style="position:absolute;top:3px;left:3px;background:var(--c-primary);color:white;
font-size:9px;font-weight:700;border-radius:999px;padding:1px 5px">Logo</span>` : ''}
<!-- Sichtbarkeit -->
<button class="bp-vis-btn" data-photo-id="${ph.id}" data-vis="${_esc(ph.visibility)}"
<button class="bp-vis-btn" data-photo-id="${ph.id}" data-vis="${UI.escape(ph.visibility)}"
style="position:absolute;bottom:0;left:0;right:0;background:${vis.color};color:#fff;
border:none;cursor:pointer;font-size:9px;padding:2px 4px;font-weight:700">
${vis.text}
@ -1805,7 +1801,7 @@ window.Page_zuchthunde = (() => {
});
} catch (err) {
const el = document.getElementById(galleryId);
if (el) el.innerHTML = `<p class="text-danger">${_esc(err.message || 'Fehler')}</p>`;
if (el) el.innerHTML = `<p class="text-danger">${UI.escape(err.message || 'Fehler')}</p>`;
}
}