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

@ -15,21 +15,16 @@ window.Page_playdate = (() => {
// ------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------
function _esc(s) {
return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function _fmtDate(iso) {
function _fmtDate(iso) {
if (!iso) return '';
const d = new Date(iso.replace(' ', 'T'));
return d.toLocaleDateString('de-DE');
}
function _dogAvatar(foto_url, name, size = 48) {
const initials = _esc((name || '?').charAt(0).toUpperCase());
const initials = UI.escape((name || '?').charAt(0).toUpperCase());
if (foto_url) {
return `<img src="${_esc(foto_url)}" alt="${initials}"
return `<img src="${UI.escape(foto_url)}" alt="${initials}"
style="width:${size}px;height:${size}px;border-radius:50%;object-fit:cover;display:block;"
onerror="this.outerHTML='<div style=\'width:${size}px;height:${size}px;border-radius:50%;background:var(--c-primary-subtle);display:flex;align-items:center;justify-content:center;font-size:${Math.round(size*0.45)}px;font-weight:700;color:var(--c-primary);\'>${initials}</div>'">`;
}
@ -250,29 +245,29 @@ window.Page_playdate = (() => {
${_dogAvatar(d.foto_url, d.dog_name, 56)}
<div class="flex-1-min">
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base);
color:var(--c-text)">${_esc(d.dog_name)}</div>
${d.rasse ? `<div class="text-sm-secondary">${_esc(d.rasse)}</div>` : ''}
${d.alter ? `<div class="text-xs-muted">${_esc(d.alter)}</div>` : ''}
color:var(--c-text)">${UI.escape(d.dog_name)}</div>
${d.rasse ? `<div class="text-sm-secondary">${UI.escape(d.rasse)}</div>` : ''}
${d.alter ? `<div class="text-xs-muted">${UI.escape(d.alter)}</div>` : ''}
</div>
</div>
<div style="display:flex;gap:var(--space-3);margin-bottom:var(--space-3);flex-wrap:wrap">
<span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--c-text-secondary)">
${UI.icon('map-pin')}
${d.ort_name ? _esc(d.ort_name) + ' · ' : ''}${d.entfernung_km} km entfernt
${d.ort_name ? UI.escape(d.ort_name) + ' · ' : ''}${d.entfernung_km} km entfernt
</span>
${d.geschlecht ? `<span class="text-xs-muted">${_esc(d.geschlecht)}</span>` : ''}
${d.geschlecht ? `<span class="text-xs-muted">${UI.escape(d.geschlecht)}</span>` : ''}
</div>
${d.beschreibung ? `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);
margin:0 0 var(--space-3);line-height:1.5">
${_esc(d.beschreibung)}
${UI.escape(d.beschreibung)}
</p>` : ''}
<button class="btn btn-primary btn-sm playdate-anfrage-btn"
data-dog-id="${d.dog_id}"
data-dog-name="${_esc(d.dog_name)}">
data-dog-name="${UI.escape(d.dog_name)}">
${UI.icon('paw-print')} Spielkamerad anfragen
</button>
</div>
@ -393,8 +388,8 @@ window.Page_playdate = (() => {
<div style="display:flex;gap:var(--space-3);align-items:center;margin-bottom:var(--space-3)">
${_dogAvatar(dog.foto_url, dog.name, 44)}
<div class="flex-1-min">
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">${_esc(dog.name)}</div>
${dog.rasse ? `<div class="text-xs-secondary">${_esc(dog.rasse)}</div>` : ''}
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">${UI.escape(dog.name)}</div>
${dog.rasse ? `<div class="text-xs-secondary">${UI.escape(dog.rasse)}</div>` : ''}
</div>
<span style="font-size:var(--text-xs);font-weight:600;
padding:2px 10px;border-radius:999px;
@ -407,12 +402,12 @@ window.Page_playdate = (() => {
${isAktiv ? `
<div style="font-size:var(--text-sm);color:var(--c-text-secondary);margin-bottom:var(--space-3)">
${UI.icon('map-pin')}
${listing.ort_name ? _esc(listing.ort_name) + ' · ' : ''}
${listing.ort_name ? UI.escape(listing.ort_name) + ' · ' : ''}
Radius: ${listing.radius_km} km
</div>
${listing.beschreibung ? `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);
margin:0 0 var(--space-3);line-height:1.5">${_esc(listing.beschreibung)}</p>` : ''}
margin:0 0 var(--space-3);line-height:1.5">${UI.escape(listing.beschreibung)}</p>` : ''}
` : `
<p style="font-size:var(--text-sm);color:var(--c-text-muted);margin:0 0 var(--space-3)">
Noch kein Inserat trage dich ein, damit andere dich finden können.
@ -445,7 +440,7 @@ window.Page_playdate = (() => {
<div class="flex-gap-2">
<input type="text" id="listing-ort" class="form-control"
placeholder="z.B. München"
value="${_esc(existing?.ort_name || '')}">
value="${UI.escape(existing?.ort_name || '')}">
<button type="button" class="btn btn-ghost btn-sm" id="listing-gps-btn"
title="GPS-Standort ermitteln">
${UI.icon('crosshair')}
@ -472,7 +467,7 @@ window.Page_playdate = (() => {
<div class="form-group">
<label class="form-label">Beschreibung (optional)</label>
<textarea id="listing-beschreibung" class="form-control" rows="3" maxlength="400"
placeholder="Erzähl etwas über deinen Hund und was ihr sucht…">${_esc(existing?.beschreibung || '')}</textarea>
placeholder="Erzähl etwas über deinen Hund und was ihr sucht…">${UI.escape(existing?.beschreibung || '')}</textarea>
</div>
</form>
`,
@ -635,11 +630,11 @@ window.Page_playdate = (() => {
<div style="display:flex;gap:var(--space-3);align-items:flex-start;margin-bottom:var(--space-3)">
${_dogAvatar(r.from_dog_foto, r.from_dog_name, 44)}
<div class="flex-1-min">
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">${_esc(r.from_dog_name)}</div>
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">${UI.escape(r.from_dog_name)}</div>
<div class="text-xs-secondary">
${r.from_dog_rasse ? _esc(r.from_dog_rasse) + ' · ' : ''}
${r.alter ? _esc(r.alter) + ' · ' : ''}
von ${_esc(r.from_user_name)}
${r.from_dog_rasse ? UI.escape(r.from_dog_rasse) + ' · ' : ''}
${r.alter ? UI.escape(r.alter) + ' · ' : ''}
von ${UI.escape(r.from_user_name)}
</div>
<div class="text-xs-muted">${_fmtDate(r.created_at)}</div>
</div>
@ -651,7 +646,7 @@ window.Page_playdate = (() => {
background:var(--c-surface-2);border-radius:var(--radius-md);
padding:var(--space-2) var(--space-3);margin-bottom:var(--space-3);
line-height:1.5">
"${_esc(r.nachricht)}"
"${UI.escape(r.nachricht)}"
</div>` : ''}
${isPending ? `
@ -680,10 +675,10 @@ window.Page_playdate = (() => {
<div style="display:flex;gap:var(--space-3);align-items:flex-start;margin-bottom:var(--space-3)">
${_dogAvatar(r.to_dog_foto, r.to_dog_name, 44)}
<div class="flex-1-min">
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">${_esc(r.to_dog_name)}</div>
<div style="font-weight:var(--weight-semibold);color:var(--c-text)">${UI.escape(r.to_dog_name)}</div>
<div class="text-xs-secondary">
${r.to_dog_rasse ? _esc(r.to_dog_rasse) + ' · ' : ''}
von ${_esc(r.to_user_name)}
${r.to_dog_rasse ? UI.escape(r.to_dog_rasse) + ' · ' : ''}
von ${UI.escape(r.to_user_name)}
</div>
<div class="text-xs-muted">${_fmtDate(r.created_at)}</div>
</div>
@ -692,7 +687,7 @@ window.Page_playdate = (() => {
${r.nachricht ? `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0 0 var(--space-3)">
"${_esc(r.nachricht)}"
"${UI.escape(r.nachricht)}"
</p>` : ''}
${r.status === 'accepted' ? `