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

@ -286,8 +286,8 @@ window.Page_lost = (() => {
const marker = UI.map.svgMarker(r.lat, r.lon, html, { size: 34, anchorY: 17 })
.addTo(_map)
.bindPopup(`
<b>🔍 ${_escape(r.name)}</b><br>
${r.rasse ? _escape(r.rasse) + '<br>' : ''}
<b>🔍 ${UI.escape(r.name)}</b><br>
${r.rasse ? UI.escape(r.rasse) + '<br>' : ''}
${distStr ? `<small>📍 ${distStr} entfernt</small><br>` : ''}
${r._isPending ? '<small>⏳ Sync ausstehend</small><br>' : ''}
<small>📅 ${_fmtDate(r.created_at)}</small>
@ -382,10 +382,10 @@ window.Page_lost = (() => {
<div style="display:flex;align-items:center;gap:var(--space-2);
margin-bottom:var(--space-1);flex-wrap:wrap">
<span style="font-weight:var(--weight-semibold);font-size:var(--text-base)">
${_escape(r.name)}
${UI.escape(r.name)}
</span>
${r.rasse
? `<span class="badge">${_escape(r.rasse)}</span>`
? `<span class="badge">${UI.escape(r.rasse)}</span>`
: ''}
${isOwn
? '<span class="badge badge-warning">Meine Meldung</span>'
@ -399,11 +399,11 @@ window.Page_lost = (() => {
</div>
<p style="margin:0 0 var(--space-1);font-size:var(--text-sm);
color:var(--c-text)">
${_escape(r.beschreibung.slice(0, 120))}${r.beschreibung.length > 120 ? '…' : ''}
${UI.escape(r.beschreibung.slice(0, 120))}${r.beschreibung.length > 120 ? '…' : ''}
</p>
<div class="text-xs-secondary">
Gemeldet ${_fmtDate(r.created_at)}
${r.melder_name ? '· ' + _escape(r.melder_name.split(' ')[0]) : ''}
${r.melder_name ? '· ' + UI.escape(r.melder_name.split(' ')[0]) : ''}
</div>
${r._isPending
? `<div style="margin-top:var(--space-2);display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap">
@ -418,7 +418,7 @@ window.Page_lost = (() => {
: (_appState.user ? `<div class="mt-2">
<button class="btn btn-ghost btn-xs lost-note-btn"
data-lost-note-id="${r.id}"
data-lost-note-name="${_escape(r.name)}"
data-lost-note-name="${UI.escape(r.name)}"
title="Notiz" onclick="event.stopPropagation()">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz
</button>
@ -447,19 +447,19 @@ window.Page_lost = (() => {
: ''}
<div style="display:flex;gap:var(--space-2);flex-wrap:wrap;margin-bottom:var(--space-3)">
<span class="badge badge-danger">🐕 ${_escape(r.name)}</span>
${r.rasse ? `<span class="badge">${_escape(r.rasse)}</span>` : ''}
<span class="badge badge-danger">🐕 ${UI.escape(r.name)}</span>
${r.rasse ? `<span class="badge">${UI.escape(r.rasse)}</span>` : ''}
</div>
<p style="white-space:pre-wrap;margin-bottom:var(--space-3)">
${_escape(r.beschreibung)}
${UI.escape(r.beschreibung)}
</p>
<div style="font-size:var(--text-sm);color:var(--c-text-secondary);
margin-bottom:var(--space-4);line-height:1.8">
<div>📍 ${r.lat.toFixed(5)}, ${r.lon.toFixed(5)}${distStr ? ' (' + distStr + ' entfernt)' : ''}</div>
<div>📅 Gemeldet: ${_fmtDate(r.created_at)}</div>
${r.melder_name ? `<div>👤 Gemeldet von: ${_escape(r.melder_name.split(' ')[0])}</div>` : ''}
${r.melder_name ? `<div>👤 Gemeldet von: ${UI.escape(r.melder_name.split(' ')[0])}</div>` : ''}
</div>
<div style="display:flex;gap:var(--space-2);flex-wrap:wrap">
@ -473,7 +473,7 @@ window.Page_lost = (() => {
</div>
`;
UI.modal.open({ title: `🔍 ${_escape(r.name)} wird vermisst`, body });
UI.modal.open({ title: `🔍 ${UI.escape(r.name)} wird vermisst`, body });
document.getElementById('detail-lost-map')?.addEventListener('click', () => {
UI.modal.close();
@ -511,10 +511,10 @@ window.Page_lost = (() => {
// ----------------------------------------------------------
function _showFoundDialog(r) {
UI.modal.open({
title: `🎉 ${_escape(r.name)} gefunden?`,
title: `🎉 ${UI.escape(r.name)} gefunden?`,
body: `
<p style="color:var(--c-text-secondary);margin-bottom:var(--space-4)">
Wurde ${_escape(r.name)} wiedergefunden? Die Meldung wird als
Wurde ${UI.escape(r.name)} wiedergefunden? Die Meldung wird als
abgeschlossen markiert und aus der Liste entfernt.
</p>
`,
@ -555,7 +555,7 @@ window.Page_lost = (() => {
const dogs = _appState.dogs || [];
const dogOpts = dogs.length > 0
? `<option value="">— kein registrierter Hund —</option>` +
dogs.map(d => `<option value="${d.id}">${_escape(d.name)}${d.rasse ? ' (' + _escape(d.rasse) + ')' : ''}</option>`).join('')
dogs.map(d => `<option value="${d.id}">${UI.escape(d.name)}${d.rasse ? ' (' + UI.escape(d.rasse) + ')' : ''}</option>`).join('')
: '';
const body = `
@ -790,17 +790,7 @@ window.Page_lost = (() => {
day: '2-digit', month: '2-digit', year: 'numeric'
});
}
function _escape(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function _emptyState(icon, title, text, cta = '') {
function _emptyState(icon, title, text, cta = '') {
return `<div class="empty-state">
<svg class="ph-icon empty-state-icon" aria-hidden="true">
<use href="/icons/phosphor.svg#${icon}"></use>
@ -829,7 +819,7 @@ window.Page_lost = (() => {
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
<div>
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${_escape(parentLabel)}</div>
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
</div>
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
</div>