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

@ -21,14 +21,6 @@ window.Page_ernaehrung = (() => {
// ------------------------------------------------------------------
// Escape helper
// ------------------------------------------------------------------
function _esc(s) {
if (s == null) return '';
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
// ------------------------------------------------------------------
// LIFECYCLE
@ -156,12 +148,12 @@ window.Page_ernaehrung = (() => {
<div class="ern-field">
<label> Gewicht (kg)</label>
<input id="ern-gewicht" type="number" step="0.1" min="0.5" max="100"
value="${_esc(gewichtDefault)}" placeholder="15">
value="${UI.escape(gewichtDefault)}" placeholder="15">
</div>
<div class="ern-field">
<label>🎂 Alter (Jahre)</label>
<input id="ern-alter" type="number" step="0.5" min="0" max="25"
value="${_esc(alterDefault)}" placeholder="3">
value="${UI.escape(alterDefault)}" placeholder="3">
</div>
</div>
@ -209,7 +201,7 @@ window.Page_ernaehrung = (() => {
<div class="by-form-group" style="margin:0">
<label class="by-label">Marke / Produkt</label>
<input id="ern-prof-marke" type="text" class="by-input"
value="${_esc(_profil.marke)}" placeholder="z. B. Royal Canin">
value="${UI.escape(_profil.marke)}" placeholder="z. B. Royal Canin">
</div>
<div class="by-form-group" style="margin:0">
<label class="by-label">Portionen pro Tag</label>
@ -219,7 +211,7 @@ window.Page_ernaehrung = (() => {
<div class="by-form-group" style="margin:0">
<label class="by-label">Notizen</label>
<textarea id="ern-prof-notizen" class="by-input" rows="2"
placeholder="Besonderheiten, Allergien...">${_esc(_profil.notizen)}</textarea>
placeholder="Besonderheiten, Allergien...">${UI.escape(_profil.notizen)}</textarea>
</div>
<button class="btn btn-secondary" id="ern-prof-save-btn">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#floppy-disk"></use></svg>
@ -482,8 +474,8 @@ window.Page_ernaehrung = (() => {
<div style="display:flex;align-items:center;gap:var(--space-2)">
<span style="font-size:1.4rem">${item.emoji}</span>
<div>
<div style="font-weight:600;font-size:var(--text-sm);color:var(--c-text)">${_esc(item.name)}</div>
<div style="font-size:var(--text-xs);color:var(--c-danger)">${_esc(item.grund)}</div>
<div style="font-weight:600;font-size:var(--text-sm);color:var(--c-text)">${UI.escape(item.name)}</div>
<div style="font-size:var(--text-xs);color:var(--c-danger)">${UI.escape(item.grund)}</div>
</div>
</div>
</div>
@ -511,7 +503,7 @@ window.Page_ernaehrung = (() => {
border:1px solid var(--c-border)">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#robot"></use></svg>
Der KI-Futterberater beantwortet Ernährungsfragen für
<strong>${_esc(dog?.name || 'deinen Hund')}</strong>.
<strong>${UI.escape(dog?.name || 'deinen Hund')}</strong>.
Bei Gesundheitsfragen immer den Tierarzt zurate ziehen.
</div>
@ -524,8 +516,8 @@ window.Page_ernaehrung = (() => {
'Welche Leckerlis sind gesund?',
].map(q => `
<button class="btn btn-sm btn-secondary ern-ki-vorschlag"
data-q="${_esc(q)}"
class="text-xs">${_esc(q)}</button>
data-q="${UI.escape(q)}"
class="text-xs">${UI.escape(q)}</button>
`).join('')}
</div>
@ -577,7 +569,7 @@ window.Page_ernaehrung = (() => {
<div style="display:flex;justify-content:flex-end;margin-bottom:var(--space-2)">
<div style="background:var(--c-primary);color:#fff;border-radius:var(--radius-md);
padding:var(--space-2) var(--space-3);max-width:80%;font-size:var(--text-sm)">
${_esc(frage)}
${UI.escape(frage)}
</div>
</div>
`);
@ -617,7 +609,7 @@ window.Page_ernaehrung = (() => {
}
}
const antwortHtml = _esc(antwort)
const antwortHtml = UI.escape(antwort)
.replace(/\n\n/g, '</p><p style="margin:var(--space-1) 0">')
.replace(/\n/g, '<br>');
@ -746,7 +738,7 @@ window.Page_ernaehrung = (() => {
const dl = document.getElementById('vert-futter-datalist');
if (!dl) return;
const names = [...new Set((list || []).map(e => e.futter_name))];
dl.innerHTML = names.map(n => `<option value="${_esc(n)}">`).join('');
dl.innerHTML = names.map(n => `<option value="${UI.escape(n)}">`).join('');
}).catch(() => {});
setTimeout(() => {
@ -950,7 +942,7 @@ window.Page_ernaehrung = (() => {
<svg class="ph-icon" aria-hidden="true" style="flex-shrink:0;color:var(--c-warning,#f59e0b);margin-top:1px">
<use href="/icons/phosphor.svg#warning-circle"></use>
</svg>
<span>${_esc(data.hinweis)}</span>
<span>${UI.escape(data.hinweis)}</span>
</div>
` : '';
@ -978,7 +970,7 @@ window.Page_ernaehrung = (() => {
return `<span style="font-size:10px;font-weight:600;padding:2px 6px;
border-radius:999px;border:1px solid ${chipColor};
color:${chipColor};white-space:nowrap">
${_esc(KAT_LABELS[kat] || kat)} ×${cnt}
${UI.escape(KAT_LABELS[kat] || kat)} ×${cnt}
</span>`;
}).join('');
return `
@ -988,17 +980,17 @@ window.Page_ernaehrung = (() => {
<div style="min-width:0;flex:1">
<div style="font-weight:600;font-size:var(--text-sm);color:var(--c-text);
white-space:nowrap;overflow:hidden;text-overflow:ellipsis">
${_esc(f.name)}
${UI.escape(f.name)}
</div>
<div class="text-xs-muted">
${_esc(TYP_LABELS[f.typ] || f.typ)} &middot; ${f.mahlzeiten} Mahlzeit${f.mahlzeiten !== 1 ? 'en' : ''}
${UI.escape(TYP_LABELS[f.typ] || f.typ)} &middot; ${f.mahlzeiten} Mahlzeit${f.mahlzeiten !== 1 ? 'en' : ''}
${f.status !== 'neu' ? `&middot; <span style="color:var(--c-success,#22c55e)">+${f.positiv}</span> / <span style="color:var(--c-danger,#ef4444)">-${f.negativ}</span>` : ''}
</div>
${katChips ? `<div style="display:flex;flex-wrap:wrap;gap:4px;margin-top:4px">${katChips}</div>` : ''}
</div>
<span style="flex-shrink:0;font-size:var(--text-xs);font-weight:700;
color:${cfg.color};white-space:nowrap">
${_esc(cfg.label)}
${UI.escape(cfg.label)}
</span>
</div>
`;
@ -1085,9 +1077,9 @@ window.Page_ernaehrung = (() => {
<use href="/icons/phosphor.svg#bowl-food"></use>
</svg>
<div class="flex-1-min">
<div style="font-weight:600;font-size:var(--text-sm)">${_esc(item.futter_name)}</div>
<div style="font-weight:600;font-size:var(--text-sm)">${UI.escape(item.futter_name)}</div>
<div class="text-xs-muted">
${_esc(item.datum)} ${_esc(item.uhrzeit)}
${UI.escape(item.datum)} ${UI.escape(item.uhrzeit)}
${item.menge_g ? ` &middot; ${item.menge_g} g` : ''}
</div>
</div>
@ -1112,11 +1104,11 @@ window.Page_ernaehrung = (() => {
</svg>
<div class="flex-1-min">
<div style="font-weight:600;font-size:var(--text-sm);color:${col}">
${_esc(REAK_LABELS[item.reaktion_typ] || item.reaktion_typ)}
${UI.escape(REAK_LABELS[item.reaktion_typ] || item.reaktion_typ)}
<span style="font-weight:400;color:var(--c-text-muted)">(${item.intensitaet}/5)</span>
</div>
<div class="text-xs-muted">
${_esc(item.datum)} ${_esc(item.uhrzeit)}
${UI.escape(item.datum)} ${UI.escape(item.uhrzeit)}
</div>
</div>
<button class="btn-icon vert-del-reaktion" data-id="${item.id}"