Some checks failed
Deploy Development / deploy (push) Successful in 36s
Test Suite / pytest-backend (push) Successful in 6s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Failing after 28s
- Added new utility functions for handling exercise focus areas, style directions, and training types, improving data presentation. - Refactored ExercisesListPage to utilize new card layouts and improved visibility labels for exercises. - Updated ClubsPage and SkillsPage to implement a consistent tab navigation style, enhancing user experience. - Enhanced CSS styles for better responsiveness and visual consistency across various components. - Improved loading states and accessibility features for better user feedback and interaction.
53 lines
1.4 KiB
JavaScript
53 lines
1.4 KiB
JavaScript
/**
|
|
* Reduziert HTML aus Übungs-Kurztexten auf eine kleine erlaubte Menge von Tags (ohne Attribute).
|
|
* Für Anzeige mit dangerouslySetInnerHTML.
|
|
*/
|
|
const ALLOWED_TAGS = new Set(['b', 'strong', 'i', 'em', 'br', 'p', 'span', 'ul', 'ol', 'li'])
|
|
|
|
function cleanTree(parent) {
|
|
const nodes = Array.from(parent.childNodes)
|
|
for (const node of nodes) {
|
|
if (node.nodeType === Node.TEXT_NODE) continue
|
|
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
parent.removeChild(node)
|
|
continue
|
|
}
|
|
const tag = node.tagName.toLowerCase()
|
|
if (!ALLOWED_TAGS.has(tag)) {
|
|
while (node.firstChild) {
|
|
parent.insertBefore(node.firstChild, node)
|
|
}
|
|
parent.removeChild(node)
|
|
continue
|
|
}
|
|
while (node.attributes.length > 0) {
|
|
node.removeAttribute(node.attributes[0].name)
|
|
}
|
|
cleanTree(node)
|
|
}
|
|
}
|
|
|
|
export function sanitizeExerciseRichText(html) {
|
|
if (html == null || typeof html !== 'string') return ''
|
|
const trimmed = html.trim()
|
|
if (!trimmed) return ''
|
|
|
|
const tpl = document.createElement('template')
|
|
tpl.innerHTML = trimmed
|
|
cleanTree(tpl.content)
|
|
return tpl.innerHTML
|
|
}
|
|
|
|
export function coerceApiNameList(value) {
|
|
if (Array.isArray(value)) return value.map(String).filter((s) => s.trim())
|
|
if (typeof value === 'string') {
|
|
try {
|
|
const p = JSON.parse(value)
|
|
if (Array.isArray(p)) return p.map(String).filter((s) => s.trim())
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
return []
|
|
}
|