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

@ -633,7 +633,7 @@ window.Page_uebungen = (() => {
border-radius:var(--radius-full,999px);
background:var(--c-primary-subtle);color:var(--c-primary);
font-size:var(--text-xs);font-weight:var(--weight-semibold)">
${b.icon || '🏅'} ${_esc(b.name || b.badge_id)}
${b.icon || '🏅'} ${UI.escape(b.name || b.badge_id)}
</span>
`).join('');
const more = rest > 0
@ -715,8 +715,8 @@ window.Page_uebungen = (() => {
text-transform:uppercase;letter-spacing:.04em">${cfg.label}</span>
</div>
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
color:var(--c-text);line-height:1.3">${_esc(r.exercise_name)}</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.4">${_esc(r.reason)}</div>
color:var(--c-text);line-height:1.3">${UI.escape(r.exercise_name)}</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.4">${UI.escape(r.reason)}</div>
<div style="display:flex;flex-direction:column;gap:var(--space-2);margin-top:auto;padding-top:var(--space-1)">
<div>
<span class="text-xs-secondary">${r.suggested_reps}× empfohlen</span>
@ -724,7 +724,7 @@ window.Page_uebungen = (() => {
${prognose}
</div>
<button class="ueb-trainer-btn btn btn-primary"
data-tab="${_esc(r.tab)}" data-name="${_esc(r.exercise_name)}" data-reps="${r.suggested_reps}"
data-tab="${UI.escape(r.tab)}" data-name="${UI.escape(r.exercise_name)}" data-reps="${r.suggested_reps}"
style="font-size:var(--text-xs);padding:4px 10px;width:100%">
Üben
</button>
@ -817,25 +817,25 @@ window.Page_uebungen = (() => {
${ALL.map(g => `
<div style="font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--c-text-secondary);
text-transform:uppercase;letter-spacing:.05em;padding:var(--space-3) 0 var(--space-1)">
${_esc(g.group)}
${UI.escape(g.group)}
</div>
${g.items.map(u => {
const key = _progressKey(g.tab, u.name);
const cur = _getStatus(g.tab, u.name);
return `
<div style="display:flex;align-items:center;gap:var(--space-2);
padding:var(--space-2) 0;border-bottom:1px solid var(--c-border)" data-key="${_esc(key)}">
padding:var(--space-2) 0;border-bottom:1px solid var(--c-border)" data-key="${UI.escape(key)}">
<span style="flex:1;font-size:var(--text-sm);color:var(--c-text);min-width:0;
overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${_esc(u.name)}</span>
overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${UI.escape(u.name)}</span>
<div style="display:flex;gap:3px;flex-shrink:0">
${STATUS_QUICK.map(s => `
<button class="qs-btn" data-key="${_esc(key)}" data-val="${s.id === null ? '' : _esc(s.id)}"
<button class="qs-btn" data-key="${UI.escape(key)}" data-val="${s.id === null ? '' : UI.escape(s.id)}"
style="font-size:9px;font-weight:700;padding:3px 6px;border-radius:999px;cursor:pointer;
white-space:nowrap;transition:all .15s;
background:${cur === s.id ? s.bg : 'var(--c-surface-2)'};
color:${cur === s.id ? s.color : 'var(--c-text-secondary)'};
border:1px solid ${cur === s.id ? s.color + '66' : 'var(--c-border)'}">
${_esc(s.label)}
${UI.escape(s.label)}
</button>`).join('')}
</div>
</div>`;
@ -899,7 +899,7 @@ window.Page_uebungen = (() => {
${TABS.map(t => `
<button class="by-tab${t.id === _activeTab ? ' active' : ''}"
data-tab="${t.id}">
${_esc(t.label)}
${UI.escape(t.label)}
</button>
`).join('')}
</div>
@ -922,18 +922,18 @@ window.Page_uebungen = (() => {
return `
<div style="background:${c.bg};border:1px solid ${c.border};border-radius:var(--radius-md);
padding:var(--space-3) var(--space-4);display:flex;gap:var(--space-3);align-items:flex-start;cursor:pointer"
data-action-tab="${_esc(s.action_tab || '')}"
data-action-name="${_esc(s.action_name || '')}"
data-action-tab="${UI.escape(s.action_tab || '')}"
data-action-name="${UI.escape(s.action_name || '')}"
class="ueb-suggestion-card">
<svg class="ph-icon" style="width:20px;height:20px;flex-shrink:0;color:${c.text};margin-top:2px" aria-hidden="true">
<use href="/icons/phosphor.svg#${_esc(s.icon)}"></use>
<use href="/icons/phosphor.svg#${UI.escape(s.icon)}"></use>
</svg>
<div style="min-width:0">
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);color:${c.text};margin-bottom:2px">
${_esc(s.title)}
${UI.escape(s.title)}
</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.5">
${_esc(s.text)}
${UI.escape(s.text)}
</div>
</div>
</div>
@ -1074,7 +1074,7 @@ window.Page_uebungen = (() => {
);
if (!allExercises.length) {
return `<div style="padding:var(--space-8);text-align:center;color:var(--c-text-muted)">
${UI.icon('magnifying-glass')} Keine Übungen gefunden für ${_esc(q)}"
${UI.icon('magnifying-glass')} Keine Übungen gefunden für ${UI.escape(q)}"
</div>`;
}
return `<div style="padding:var(--space-4);display:flex;flex-direction:column;gap:var(--space-4)">
@ -1082,7 +1082,7 @@ window.Page_uebungen = (() => {
<div>
<div style="font-size:var(--text-xs);font-weight:var(--weight-semibold);
color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.05em;
margin-bottom:var(--space-1);padding:0 var(--space-2)">${_esc(u.kategorie)}</div>
margin-bottom:var(--space-1);padding:0 var(--space-2)">${UI.escape(u.kategorie)}</div>
${_renderCard(u, i)}
</div>`).join('')}
</div>`;
@ -1114,25 +1114,25 @@ window.Page_uebungen = (() => {
const hasBody = (u.schritte?.length > 0) || (u.fehler?.length > 0) || !!u.steigerung || !!u.tipp;
return `
<div class="card" style="padding:0;overflow:hidden" data-exercise-name="${_esc(u.name)}" data-exercise-id="${_esc(_progressKey(_activeTab, u.name))}">
<div class="card" style="padding:0;overflow:hidden" data-exercise-name="${UI.escape(u.name)}" data-exercise-id="${UI.escape(_progressKey(_activeTab, u.name))}">
<!-- Header -->
<div style="padding:var(--space-4) var(--space-4) var(--space-3)">
<!-- Zeile 1: Name + Schwierigkeits-Badge -->
<div style="display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-2);margin-bottom:var(--space-2)">
<span style="font-size:var(--text-base);font-weight:var(--weight-semibold);color:var(--c-text);line-height:1.3">
${_esc(u.name)}
${UI.escape(u.name)}
</span>
<span style="font-size:var(--text-xs);font-weight:var(--weight-semibold);white-space:nowrap;
padding:2px var(--space-2);border-radius:var(--radius-sm);flex-shrink:0;
background:${diff.color}22;color:${diff.color}">
${_esc(diff.label)}
${UI.escape(diff.label)}
</span>
</div>
<!-- Zeile 2: Aktions-Buttons -->
<div style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-2);flex-wrap:wrap">
<button class="ueb-log-btn"
data-tab="${_esc(_activeTab)}"
data-name="${_esc(u.name)}"
data-tab="${UI.escape(_activeTab)}"
data-name="${UI.escape(u.name)}"
title="Trainingseinheit loggen"
style="background:var(--c-primary-subtle);border:1px solid var(--c-primary-light);
color:var(--c-primary);cursor:pointer;padding:3px 9px;
@ -1146,8 +1146,8 @@ window.Page_uebungen = (() => {
</button>
${_sessionStatsChip(_activeTab, u.name)}
<button class="ueb-notiz-btn"
data-tab="${_esc(_activeTab)}"
data-name="${_esc(u.name)}"
data-tab="${UI.escape(_activeTab)}"
data-name="${UI.escape(u.name)}"
title="Notiz hinzufügen"
style="background:none;border:1px solid var(--c-border);cursor:pointer;
padding:3px 7px;border-radius:var(--radius-sm);
@ -1159,9 +1159,9 @@ window.Page_uebungen = (() => {
Notiz
</button>
<button class="ueb-status-btn"
data-tab="${_esc(_activeTab)}"
data-name="${_esc(u.name)}"
title="${_esc(sm.label)}"
data-tab="${UI.escape(_activeTab)}"
data-name="${UI.escape(u.name)}"
title="${UI.escape(sm.label)}"
style="background:none;border:none;cursor:pointer;padding:2px;
display:flex;align-items:center;gap:4px;
font-size:var(--text-xs);color:${sm.color};
@ -1170,7 +1170,7 @@ window.Page_uebungen = (() => {
<use href="/icons/phosphor.svg#${sm.icon}"></use>
</svg>
<span style="font-size:10px;font-weight:var(--weight-semibold);white-space:nowrap">
${_esc(sm.label)}
${UI.escape(sm.label)}
</span>
</button>
</div>
@ -1178,20 +1178,20 @@ window.Page_uebungen = (() => {
<div style="display:flex;flex-wrap:wrap;gap:var(--space-2);margin-bottom:${u.beschreibung ? 'var(--space-3)' : '0'}">
<span class="text-xs-secondary">
<svg class="ph-icon" style="width:12px;height:12px" aria-hidden="true"><use href="/icons/phosphor.svg#clock"></use></svg>
${_esc(u.dauer)}
${UI.escape(u.dauer)}
</span>
<span class="text-xs-secondary">
<svg class="ph-icon" style="width:12px;height:12px" aria-hidden="true"><use href="/icons/phosphor.svg#dog"></use></svg>
${_esc(u.alter)}
${UI.escape(u.alter)}
</span>
<span class="text-xs-secondary">
<svg class="ph-icon" style="width:12px;height:12px" aria-hidden="true"><use href="/icons/phosphor.svg#package"></use></svg>
${_esc(u.material)}
${UI.escape(u.material)}
</span>
</div>
${u.beschreibung ? `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);line-height:1.5;margin:0">
${_esc(u.beschreibung)}
${UI.escape(u.beschreibung)}
</p>
` : ''}
${u.hinweis ? `
@ -1200,7 +1200,7 @@ window.Page_uebungen = (() => {
font-size:var(--text-xs);color:var(--c-text);line-height:1.4;
display:flex;align-items:flex-start;gap:var(--space-2)">
<svg class="ph-icon" style="width:13px;height:13px;flex-shrink:0;margin-top:1px;color:var(--c-warning)" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg>
<span>${_esc(u.hinweis)}</span>
<span>${UI.escape(u.hinweis)}</span>
</div>
` : ''}
</div>
@ -1225,7 +1225,7 @@ window.Page_uebungen = (() => {
</p>
<ol style="margin:0 0 var(--space-4);padding-left:var(--space-5);display:flex;flex-direction:column;gap:var(--space-2)">
${u.schritte.map(s => `
<li style="font-size:var(--text-sm);color:var(--c-text);line-height:1.5">${_esc(s)}</li>
<li style="font-size:var(--text-sm);color:var(--c-text);line-height:1.5">${UI.escape(s)}</li>
`).join('')}
</ol>
` : ''}
@ -1237,7 +1237,7 @@ window.Page_uebungen = (() => {
</p>
<ul style="margin:0 0 var(--space-4);padding-left:var(--space-5);display:flex;flex-direction:column;gap:var(--space-2)">
${u.fehler.map(f => `
<li style="font-size:var(--text-sm);color:var(--c-text-secondary);line-height:1.5">${_esc(f)}</li>
<li style="font-size:var(--text-sm);color:var(--c-text-secondary);line-height:1.5">${UI.escape(f)}</li>
`).join('')}
</ul>
` : ''}
@ -1247,14 +1247,14 @@ window.Page_uebungen = (() => {
<svg class="ph-icon" style="width:14px;height:14px;color:var(--c-primary)" aria-hidden="true">
<use href="/icons/phosphor.svg#arrow-right"></use>
</svg>
<strong>Steigerung:</strong> ${_esc(u.steigerung)}
<strong>Steigerung:</strong> ${UI.escape(u.steigerung)}
</div>
` : ''}
${u.tipp ? `
<div style="margin-top:var(--space-3);padding:var(--space-2) var(--space-3);
background:var(--c-primary-subtle);border-radius:var(--radius-sm);
font-size:var(--text-xs);color:var(--c-text-secondary)">
💡 ${_esc(u.tipp)}
💡 ${UI.escape(u.tipp)}
</div>` : ''}
</div>
` : ''}
@ -1281,7 +1281,7 @@ window.Page_uebungen = (() => {
<svg class="ph-icon" style="width:18px;height:18px;flex-shrink:0" aria-hidden="true">
<use href="/icons/phosphor.svg#${sm.icon}"></use>
</svg>
${next ? `<span style="font-size:10px;font-weight:var(--weight-semibold);white-space:nowrap">${_esc(sm.label)}</span>` : ''}
${next ? `<span style="font-size:10px;font-weight:var(--weight-semibold);white-space:nowrap">${UI.escape(sm.label)}</span>` : ''}
`;
});
});
@ -1344,7 +1344,7 @@ window.Page_uebungen = (() => {
<h3 style="font-size:var(--text-base);font-weight:var(--weight-semibold);color:var(--c-text);
margin:0 0 var(--space-4);text-align:center">
Notiz: ${_esc(exerciseName)}
Notiz: ${UI.escape(exerciseName)}
</h3>
<div style="display:flex;flex-direction:column;gap:var(--space-4)">
@ -1359,7 +1359,7 @@ window.Page_uebungen = (() => {
border-radius:var(--radius-md);font-size:var(--text-sm);
font-family:var(--font-sans);background:var(--c-surface);
color:var(--c-text);resize:vertical;outline:none;
line-height:1.5">${_esc(noteText)}</textarea>
line-height:1.5">${UI.escape(noteText)}</textarea>
</div>
<!-- Erfolgsquote -->
@ -1569,7 +1569,7 @@ window.Page_uebungen = (() => {
<h3 style="font-size:var(--text-base);font-weight:var(--weight-semibold);color:var(--c-text);
margin:0 0 var(--space-4);text-align:center">
Einheit loggen: ${_esc(exerciseName)}
Einheit loggen: ${UI.escape(exerciseName)}
</h3>
<form id="${formId}" style="display:flex;flex-direction:column;gap:var(--space-4)">
@ -1620,7 +1620,7 @@ window.Page_uebungen = (() => {
border-radius:var(--radius-md);border:1.5px solid var(--c-border);
background:var(--c-surface-2);cursor:pointer;transition:all 0.15s">
${emoji}
<span style="font-size:9px;color:var(--c-text-secondary)">${_esc(val)}</span>
<span style="font-size:9px;color:var(--c-text-secondary)">${UI.escape(val)}</span>
</button>
`).join('')}
</div>
@ -2103,7 +2103,7 @@ window.Page_uebungen = (() => {
: '';
const noteHtml = s.notiz
? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:3px;
line-height:1.4;font-style:italic">${_esc(s.notiz)}</div>`
line-height:1.4;font-style:italic">${UI.escape(s.notiz)}</div>`
: '';
return `
<div style="display:flex;align-items:flex-start;gap:var(--space-3);
@ -2113,7 +2113,7 @@ window.Page_uebungen = (() => {
<div class="flex-1-min">
<div style="display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap">
<span style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
color:var(--c-text)">${_esc(s.exercise_name)}</span>
color:var(--c-text)">${UI.escape(s.exercise_name)}</span>
${topBadge}
</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);margin-top:1px">
@ -2146,7 +2146,7 @@ window.Page_uebungen = (() => {
<div style="font-size:var(--text-xs);font-weight:var(--weight-semibold);
color:var(--c-text-secondary);text-transform:uppercase;
letter-spacing:.05em;padding:var(--space-2) 0 var(--space-1)">
${_esc(label)}
${UI.escape(label)}
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-1)">
${sessions.map(_sessionRow).join('')}
@ -2216,7 +2216,7 @@ window.Page_uebungen = (() => {
<div style="display:flex;align-items:center;gap:var(--space-2);
padding:4px var(--space-2);border-radius:var(--radius-sm);
font-size:var(--text-xs);color:var(--c-text-secondary)">
<span style="flex-shrink:0;min-width:52px">${_esc(dateLabel)}</span>
<span style="flex-shrink:0;min-width:52px">${UI.escape(dateLabel)}</span>
<span style="flex-shrink:0">${erfolg}</span>
<span style="flex-shrink:0">${s.erfolgsquote}%${top}</span>
<span class="flex-1-min">${s.wiederholungen}× Wdh.${stimmung ? ' ' + stimmung : ''}</span>
@ -2244,12 +2244,12 @@ window.Page_uebungen = (() => {
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
color:var(--c-text);line-height:1.3;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis">
${_esc(ex.name)}
${UI.escape(ex.name)}
</div>
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);margin-top:2px">
${ex.sessions.length} Einheit${ex.sessions.length !== 1 ? 'en' : ''}
${ex.topCount ? ' · ' + ex.topCount + '× TOP' : ''}
· ${_esc(lastLabel)}
· ${UI.escape(lastLabel)}
</div>
</div>
<!-- Chevron -->
@ -2350,8 +2350,8 @@ window.Page_uebungen = (() => {
['Ablenkungstraining', 'Wieder häufiger (höhere Anforderung)'],
].map(([p, b], i) => `
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
<td style="padding:var(--space-2) var(--space-3);color:var(--c-text);border-bottom:1px solid var(--c-border)">${_esc(p)}</td>
<td style="padding:var(--space-2) var(--space-3);color:var(--c-text-secondary);border-bottom:1px solid var(--c-border)">${_esc(b)}</td>
<td style="padding:var(--space-2) var(--space-3);color:var(--c-text);border-bottom:1px solid var(--c-border)">${UI.escape(p)}</td>
<td style="padding:var(--space-2) var(--space-3);color:var(--c-text-secondary);border-bottom:1px solid var(--c-border)">${UI.escape(b)}</td>
</tr>
`).join('')}
</tbody>
@ -2391,9 +2391,9 @@ window.Page_uebungen = (() => {
].map(([s, b, c], i) => `
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
<td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">
<span style="font-weight:var(--weight-semibold);color:${c}">${_esc(s)}</span>
<span style="font-weight:var(--weight-semibold);color:${c}">${UI.escape(s)}</span>
</td>
<td style="padding:var(--space-2) var(--space-3);color:var(--c-text-secondary);border-bottom:1px solid var(--c-border)">${_esc(b)}</td>
<td style="padding:var(--space-2) var(--space-3);color:var(--c-text-secondary);border-bottom:1px solid var(--c-border)">${UI.escape(b)}</td>
</tr>
`).join('')}
</tbody>
@ -2425,7 +2425,7 @@ window.Page_uebungen = (() => {
color:${ok ? 'var(--c-success)' : 'var(--c-danger)'}" aria-hidden="true">
<use href="/icons/phosphor.svg#${ok ? 'check-circle' : 'x-circle'}"></use>
</svg>
<span style="font-size:var(--text-sm);color:var(--c-text);line-height:1.5">${_esc(text)}</span>
<span style="font-size:var(--text-sm);color:var(--c-text);line-height:1.5">${UI.escape(text)}</span>
</li>
`).join('')}
</ul>
@ -2438,16 +2438,7 @@ window.Page_uebungen = (() => {
// ----------------------------------------------------------
// HELPER
// ----------------------------------------------------------
function _esc(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
// ----------------------------------------------------------
// ----------------------------------------------------------
// PUBLIC
// ----------------------------------------------------------
return { init, refresh, onDogChange };