// Assemble player HTML and inline runtime scripts from the server-side templates. const fs = require('fs'); const path = require('path'); const announcementIcons = require('#src/data/announcement-icons'); function mediaKind(mediaPath) { const ext = path.extname(mediaPath || '').toLowerCase(); if (['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'].includes(ext)) { return 'image'; } if (['.mp4', '.webm', '.ogg'].includes(ext)) { return 'video'; } if (ext === '.pdf') { return 'pdf'; } return 'file'; } function escapeHtml(value) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function normalizeStyleAttributeValue(value) { return String(value || '') .replace(/"/g, '"') .replace(/'/g, "'") .replace(/"/g, '"') .replace(/'/g, "'"); } function sanitizeFontFamily(value) { return String(value || '').replace(/[^a-zA-Z0-9 ,"-]/g, '').trim(); } function sanitizeFontSize(value) { return Math.max(8, Number(value || 0) || 24); } function sanitizeTextColor(value, fallback) { const raw = String(value || '').trim(); if (/^#[0-9a-fA-F]{6}$/.test(raw) || /^#[0-9a-fA-F]{3}$/.test(raw)) { return raw; } return fallback || '#000000'; } const ALLOWED_RICH_TEXT_TAGS = ['a', 'b', 'blockquote', 'br', 'code', 'col', 'colgroup', 'div', 'em', 'figure', 'figcaption', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'th', 'thead', 'tr', 'u', 'ul']; function sanitizeRichTextAttributes(tagName, attrText) { const allowedAttributes = { a: ['href', 'title', 'target', 'rel', 'class', 'style'], blockquote: ['class', 'style'], div: ['class', 'style'], figure: ['class', 'style'], figcaption: ['class', 'style'], h1: ['class', 'style'], h2: ['class', 'style'], h3: ['class', 'style'], h4: ['class', 'style'], h5: ['class', 'style'], h6: ['class', 'style'], i: ['class', 'style', 'aria-hidden'], img: ['src', 'alt', 'title', 'width', 'height', 'class', 'style', 'loading', 'decoding'], col: ['class', 'style', 'span', 'width'], colgroup: ['class', 'style', 'span'], li: ['class', 'style'], ol: ['class', 'style', 'start'], p: ['class', 'style'], pre: ['class', 'style'], span: ['class', 'style'], table: ['class', 'style'], td: ['class', 'style', 'colspan', 'rowspan'], th: ['class', 'style', 'colspan', 'rowspan', 'scope'], tr: ['class', 'style'], ul: ['class', 'style'] }; const allowed = allowedAttributes[tagName] || []; if (!allowed.length) { return ''; } if (tagName === 'img') { const srcMatch = String(attrText || '').match(/\bsrc\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>/=`]+))/i); const srcValue = srcMatch ? String(srcMatch[2] !== undefined ? srcMatch[2] : srcMatch[3] !== undefined ? srcMatch[3] : srcMatch[4] !== undefined ? srcMatch[4] : '').trim() : ''; if (!srcValue || !/^(?:https?:\/\/|\/media\/|\/assets\/|\/[^/]|data:image\/)/i.test(srcValue)) { return ''; } } const attrs = []; String(attrText || '').replace(/([a-zA-Z0-9:-]+)(?:\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>/=`]+)))?/g, (_full, key, _valuePart, doubleQuoted, singleQuoted, bareValue) => { const lowerKey = String(key || '').toLowerCase(); if (!allowed.includes(lowerKey)) { return ''; } const value = doubleQuoted !== undefined ? doubleQuoted : singleQuoted !== undefined ? singleQuoted : bareValue !== undefined ? bareValue : ''; if (lowerKey === 'href' && /^(?:\s*javascript:|\s*data:)/i.test(String(value || ''))) { return ''; } if (lowerKey === 'style' && /(?:expression\s*\(|javascript:|url\s*\()/i.test(String(value || ''))) { return ''; } if (lowerKey === 'target') { const targetValue = String(value || '').trim(); if (targetValue === '_blank') { attrs.push(' target="_blank"'); if (!attrs.includes(' rel="noreferrer noopener"')) { attrs.push(' rel="noreferrer noopener"'); } return ''; } } attrs.push(' ' + lowerKey + '="' + escapeHtml(lowerKey === 'style' ? normalizeStyleAttributeValue(value) : value) + '"'); return ''; }); return attrs.join(''); } function safeJsonForScript(value) { return JSON.stringify(value === undefined ? null : value).replace(//gi, ''); output = output.replace(/
' + raw + '