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

@ -242,13 +242,13 @@ window.Page_social = (() => {
🌙 Noch nicht gezeigt:
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:6px">
${_unusedBreeds.map(b =>
`<button class="sm-breed-chip" data-id="${b.id}" data-name="${_esc(b.name)}"
`<button class="sm-breed-chip" data-id="${b.id}" data-name="${UI.escape(b.name)}"
style="padding:5px 12px;border-radius:var(--radius-full);
border:1.5px solid var(--c-border);
background:var(--c-surface-2);color:var(--c-text);
font-size:12px;cursor:pointer;font-family:inherit;
transition:all var(--transition-fast)">
${_esc(b.name)}</button>`).join('')}
${UI.escape(b.name)}</button>`).join('')}
</div>
</div>` : ''}
<input id="sm-breed-search" list="sm-breed-list"
@ -259,7 +259,7 @@ window.Page_social = (() => {
padding:9px 12px;font-size:var(--text-sm);
font-family:inherit;box-sizing:border-box">
<datalist id="sm-breed-list">
${_breeds.map(b => `<option value="${_esc(b.name)}" data-id="${b.id}">`).join('')}
${_breeds.map(b => `<option value="${UI.escape(b.name)}" data-id="${b.id}">`).join('')}
</datalist>
<input type="hidden" id="sm-breed-id">
</div>
@ -414,9 +414,9 @@ window.Page_social = (() => {
<span style="font-size:1.4em;flex-shrink:0;line-height:1.2">${idea.emoji||'💡'}</span>
<div class="flex-1-min">
<div style="font-weight:600;font-size:var(--text-sm);margin-bottom:4px;
line-height:1.3">${_esc(idea.thema)}</div>
line-height:1.3">${UI.escape(idea.thema)}</div>
<div style="font-size:11px;color:var(--c-text-secondary);margin-bottom:6px;
line-height:1.4">🎓 ${_esc(idea.warum)}</div>
line-height:1.4">🎓 ${UI.escape(idea.warum)}</div>
<div style="display:flex;gap:6px">
<span style="font-size:10px;background:var(--c-surface-2);
padding:2px 6px;border-radius:4px">${_FL[idea.format]||idea.format}</span>
@ -427,16 +427,16 @@ window.Page_social = (() => {
<div style="display:flex;flex-direction:column;gap:5px;flex-shrink:0">
<button class="btn btn-primary btn-sm sm-use"
style="font-size:11px;padding:6px 10px;min-height:34px;white-space:nowrap"
data-thema="${_esc(idea.thema)}"
data-thema="${UI.escape(idea.thema)}"
data-format="${idea.format||'post'}"
data-platform="${idea.platform||'both'}">
Nutzen </button>
<button class="btn btn-secondary btn-sm sm-save-idea"
style="font-size:10px;padding:4px 8px;min-height:26px;white-space:nowrap"
data-thema="${_esc(idea.thema)}"
data-thema="${UI.escape(idea.thema)}"
data-format="${idea.format||'post'}"
data-platform="${idea.platform||'both'}"
data-category="${_esc(idea.category||'')}">
data-category="${UI.escape(idea.category||'')}">
<svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#push-pin"></use></svg> Merken</button>
</div>
</div>
@ -522,15 +522,15 @@ window.Page_social = (() => {
margin-bottom:6px;display:flex;align-items:center;gap:10px">
<div class="flex-1-min">
<div style="font-size:var(--text-sm);font-weight:600;color:var(--c-text)">
${_esc(e.name)}</div>
${UI.escape(e.name)}</div>
<div style="font-size:10px;color:var(--c-text-muted)">
${_esc(e.schwierigkeit||'')} · ${_esc(e.alter_ab||'')} · ${_esc(e.dauer||'')}</div>
${UI.escape(e.schwierigkeit||'')} · ${UI.escape(e.alter_ab||'')} · ${UI.escape(e.dauer||'')}</div>
</div>
<div style="display:flex;align-items:center;gap:6px;flex-shrink:0">
${e.posts_count > 0 ? `<span style="font-size:10px;background:#10b981;
color:#fff;padding:1px 6px;border-radius:4px"><svg class="ph-icon" aria-hidden="true" style="width:12px;height:12px"><use href="/icons/phosphor.svg#check"></use></svg> ${e.posts_count}x</span>` : ''}
<button class="btn btn-sm btn-primary sm-ex-use"
data-id="${e.exercise_id}" data-name="${_esc(e.name)}"
data-id="${e.exercise_id}" data-name="${UI.escape(e.name)}"
style="font-size:11px;padding:4px 10px;min-height:28px">
Nutzen</button>
</div>
@ -580,9 +580,9 @@ window.Page_social = (() => {
<span style="font-size:2.2em;flex-shrink:0">🎾</span>
<div>
<div style="font-size:11px;color:#4ade80;font-weight:600;margin-bottom:2px">
Trainingstipp · ${_esc(data.exercise_kat||'')} · ${stilLabel}</div>
Trainingstipp · ${UI.escape(data.exercise_kat||'')} · ${stilLabel}</div>
<div style="font-weight:700;font-size:var(--text-base);color:#15803d">
${_esc(data.exercise_name||'')}</div>
${UI.escape(data.exercise_name||'')}</div>
</div>
</div>
${_renderResult(data, null)}`;
@ -592,7 +592,7 @@ window.Page_social = (() => {
} catch(e) {
clearInterval(interval.bar); clearInterval(interval.msg);
res.innerHTML = `<div style="color:var(--c-danger);padding:var(--space-3)">
😬 ${_esc(e.message||String(e))}</div>`;
😬 ${UI.escape(e.message||String(e))}</div>`;
} finally {
btn.disabled = false;
}
@ -620,10 +620,10 @@ window.Page_social = (() => {
<span style="font-size:2.2em;flex-shrink:0">🛁</span>
<div>
<div style="font-size:11px;color:#c084fc;font-weight:600;margin-bottom:2px">
Pflegetipp · ${_esc(data.pflege_kat||'')}
${data.rasse_name ? ` · speziell für ${_esc(data.rasse_name)}` : ''}</div>
Pflegetipp · ${UI.escape(data.pflege_kat||'')}
${data.rasse_name ? ` · speziell für ${UI.escape(data.rasse_name)}` : ''}</div>
<div style="font-weight:700;font-size:var(--text-base);color:#7c3aed">
${_esc(data.pflege_titel||'')}</div>
${UI.escape(data.pflege_titel||'')}</div>
</div>
</div>
${_renderResult(data, null)}`;
@ -633,7 +633,7 @@ window.Page_social = (() => {
} catch(e) {
clearInterval(interval.bar); clearInterval(interval.msg);
res.innerHTML = `<div style="color:var(--c-danger);padding:var(--space-3)">
😬 ${_esc(e.message||String(e))}</div>`;
😬 ${UI.escape(e.message||String(e))}</div>`;
} finally { btn.disabled = false; }
});
@ -663,7 +663,7 @@ window.Page_social = (() => {
<div style="font-size:11px;color:var(--c-primary);font-weight:600;margin-bottom:2px">
Rasse des Tages</div>
<div style="font-weight:700;font-size:var(--text-base);color:var(--c-primary-dark)">
${_esc(data.topic?.replace('Rasse des Tages: ',''))}</div>
${UI.escape(data.topic?.replace('Rasse des Tages: ',''))}</div>
</div>
</div>
${_renderResult(data, mediaUrl)}`;
@ -675,7 +675,7 @@ window.Page_social = (() => {
} catch(e) {
clearInterval(interval.bar); clearInterval(interval.msg);
res.innerHTML = `<div style="color:var(--c-danger);padding:var(--space-3)">
😬 ${_esc(e.message||String(e))}</div>`;
😬 ${UI.escape(e.message||String(e))}</div>`;
} finally {
btn.disabled = false;
}
@ -719,7 +719,7 @@ window.Page_social = (() => {
clearInterval(interval);
res.innerHTML = `<div style="color:var(--c-danger);padding:var(--space-3);
border-radius:8px;background:var(--c-surface-2)">
😬 Ups: ${_esc(e.message||String(e))}</div>`;
😬 Ups: ${UI.escape(e.message||String(e))}</div>`;
} finally {
btn.disabled = false;
btn.innerHTML = '<svg class="ph-icon" aria-hidden="true" style="width:16px;height:16px"><use href="/icons/phosphor.svg#sparkle"></use></svg> Los geht\'s!';
@ -816,7 +816,7 @@ window.Page_social = (() => {
<div>
<div style="font-size:11px;font-weight:700;color:var(--c-primary);margin-bottom:4px;
text-transform:uppercase;letter-spacing:.5px">Luna sagt:</div>
<div style="font-size:var(--text-sm);line-height:1.6;color:var(--c-text)">${_esc(data.coaching)}</div>
<div style="font-size:var(--text-sm);line-height:1.6;color:var(--c-text)">${UI.escape(data.coaching)}</div>
</div>
</div>
</div>` : ''}
@ -903,9 +903,9 @@ window.Page_social = (() => {
${data.hook ? `<div class="sm-label">🎣 Hook</div>
<div style="font-size:var(--text-sm);font-style:italic;margin-bottom:var(--space-3);
line-height:1.6">
"${_esc(data.hook)}"</div>` : ''}
"${UI.escape(data.hook)}"</div>` : ''}
${data.cta ? `<div class="sm-label">📣 Call-to-Action</div>
<div style="font-size:var(--text-sm);line-height:1.6">${_esc(data.cta)}</div>` : ''}
<div style="font-size:var(--text-sm);line-height:1.6">${UI.escape(data.cta)}</div>` : ''}
</div>` : ''}
${_resultBlock('📸 Was du filmen/fotografieren solltest', data.visual_brief, false)}
${data.script ? `
@ -914,7 +914,7 @@ window.Page_social = (() => {
margin-bottom:var(--space-3);box-shadow:var(--shadow-xs)">
<div class="sm-label">🎬 Video-Aufbau</div>
<div style="font-size:var(--text-sm);white-space:pre-wrap;
line-height:1.7">${_esc(data.script)}</div>
line-height:1.7">${UI.escape(data.script)}</div>
</div>` : ''}
${(data.image_prompt||data.canva_notes||unsplash) ? `
<div style="background:var(--c-surface);border:1px solid var(--c-border);
@ -926,12 +926,12 @@ window.Page_social = (() => {
DALL-E / Midjourney:</div>
<div style="font-size:11px;background:var(--c-surface-2);padding:10px;
border-radius:var(--radius-md);font-family:monospace;word-break:break-word;
margin-bottom:var(--space-3);line-height:1.5">${_esc(data.image_prompt)}</div>
margin-bottom:var(--space-3);line-height:1.5">${UI.escape(data.image_prompt)}</div>
${_copyBtn(data.image_prompt)}` : ''}
${data.canva_notes ? `
<div style="font-size:11px;color:var(--c-text-muted);margin:var(--space-3) 0 6px">Canva:</div>
<div style="font-size:var(--text-sm);margin-bottom:var(--space-3);
line-height:1.6">${_esc(data.canva_notes)}</div>` : ''}
line-height:1.6">${UI.escape(data.canva_notes)}</div>` : ''}
${unsplash ? `<a href="${unsplash}" target="_blank" rel="noopener"
style="font-size:var(--text-sm);color:var(--c-primary);display:inline-block">
🔍 Kostenlose Fotos auf Unsplash </a>` : ''}
@ -945,14 +945,14 @@ window.Page_social = (() => {
margin-bottom:var(--space-3);box-shadow:var(--shadow-xs)">
<div class="sm-label">${label}</div>
<div style="font-size:var(--text-sm);white-space:pre-wrap;line-height:1.7;
margin-bottom:${copyable?'var(--space-3)':'0'}">${_esc(text)}</div>
margin-bottom:${copyable?'var(--space-3)':'0'}">${UI.escape(text)}</div>
${copyable ? _copyBtn(text) : ''}
</div>`;
}
function _copyBtn(text) {
return `<button class="btn btn-sm btn-secondary sm-copy"
data-copy="${_esc(text)}"
data-copy="${UI.escape(text)}"
style="font-size:11px;padding:5px 14px;min-height:32px;
border-radius:var(--radius-full)">
📋 Kopieren</button>`;
@ -1012,7 +1012,7 @@ window.Page_social = (() => {
<div style="text-align:center;padding:8px;color:var(--c-success);
font-weight:600;font-size:var(--text-sm)">
🎉 Super! Post als veröffentlicht markiert.
${url ? `<br><a href="${_esc(url)}" target="_blank" rel="noopener"
${url ? `<br><a href="${UI.escape(url)}" target="_blank" rel="noopener"
style="font-size:11px;color:var(--c-primary)">Post ansehen </a>` : ''}
</div>`;
// Stats aktualisieren
@ -1073,7 +1073,7 @@ window.Page_social = (() => {
<div style="font-size:11px;color:#000;line-height:1.4;
display:-webkit-box;-webkit-line-clamp:3;
-webkit-box-orient:vertical;overflow:hidden">
${_esc((item.caption||'').substring(0,150))}${(item.caption||'').length>150?'…':''}</div>
${UI.escape((item.caption||'').substring(0,150))}${(item.caption||'').length>150?'…':''}</div>
${item.hashtags ? `<div style="font-size:10px;color:#00376b;margin-top:4px;
word-break:break-word;display:-webkit-box;-webkit-line-clamp:2;
-webkit-box-orient:vertical;overflow:hidden">
@ -1156,11 +1156,11 @@ window.Page_social = (() => {
${c.ai_score ? `<span style="font-size:10px">${'⭐'.repeat(c.ai_score)}</span>` : ''}
</div>
<div style="font-weight:600;font-size:var(--text-sm);margin-bottom:3px;
line-height:1.3">${_esc(c.topic)}</div>
line-height:1.3">${UI.escape(c.topic)}</div>
${c.hook ? `<div style="font-size:11px;color:var(--c-text-secondary);
font-style:italic;margin-bottom:2px">🎣 ${_esc(c.hook)}</div>` : ''}
font-style:italic;margin-bottom:2px">🎣 ${UI.escape(c.hook)}</div>` : ''}
${c.post_url
? `<a href="${_esc(c.post_url)}" target="_blank" rel="noopener"
? `<a href="${UI.escape(c.post_url)}" target="_blank" rel="noopener"
style="font-size:10px;color:var(--c-primary);display:inline-flex;
align-items:center;gap:3px;margin-top:2px">
<svg class="ph-icon" aria-hidden="true" style="width:14px;height:14px"><use href="/icons/phosphor.svg#link-simple"></use></svg> Post ansehen</a>`
@ -1195,10 +1195,10 @@ window.Page_social = (() => {
border-top:1px solid var(--c-border);padding-top:10px">
${c.coaching ? `<div style="background:var(--c-surface-2);border-radius:8px;
padding:10px;margin-bottom:10px;font-size:11px;line-height:1.5">
🌙 ${_esc(c.coaching)}</div>` : ''}
🌙 ${UI.escape(c.coaching)}</div>` : ''}
${c.caption ? `<div style="font-size:11px;color:var(--c-text-muted);margin-bottom:2px">Caption:</div>
<div style="font-size:var(--text-sm);white-space:pre-wrap;line-height:1.5;
margin-bottom:8px">${_esc(c.caption)}</div>
margin-bottom:8px">${UI.escape(c.caption)}</div>
${_copyBtn(c.caption)}` : ''}
${c.hashtags ? `<div style="font-size:11px;color:var(--c-primary);margin-top:8px;
word-break:break-word">
@ -1375,7 +1375,7 @@ window.Page_social = (() => {
<div style="font-size:11px;font-weight:700;color:var(--c-primary);margin-bottom:4px;
text-transform:uppercase;letter-spacing:.5px">
Lunas Feedback:</div>
<div style="font-size:var(--text-sm);line-height:1.6">${_esc(data.notes)}</div>
<div style="font-size:var(--text-sm);line-height:1.6">${UI.escape(data.notes)}</div>
</div>
</div>
</div>` : ''}
@ -1384,7 +1384,7 @@ window.Page_social = (() => {
API.get('/social/stats').then(s => { _stats = s; });
} catch(e) {
clearInterval(interval);
res.innerHTML = `<div class="text-danger">😬 Fehler: ${_esc(e.message||String(e))}</div>`;
res.innerHTML = `<div class="text-danger">😬 Fehler: ${UI.escape(e.message||String(e))}</div>`;
} finally {
btn.disabled = false;
btn.innerHTML = '🔍 Luna, schau mal drüber!';
@ -1404,12 +1404,6 @@ window.Page_social = (() => {
return picked;
}
function _esc(s) {
if (!s) return '';
return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;')
.replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
// CSS
const style = document.createElement('style');
style.textContent = `