595 lines
23 KiB
JavaScript
595 lines
23 KiB
JavaScript
// 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, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function normalizeStyleAttributeValue(value) {
|
|
return String(value || '')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, "'")
|
|
.replace(/&quot;/g, '"')
|
|
.replace(/&#39;/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(/</g, '\\u003c');
|
|
}
|
|
|
|
function parseMaybeJson(value) {
|
|
if (typeof value !== 'string') {
|
|
return value;
|
|
}
|
|
const raw = value.trim();
|
|
if (!raw) {
|
|
return value;
|
|
}
|
|
if (raw[0] !== '{' && raw[0] !== '[') {
|
|
return value;
|
|
}
|
|
try {
|
|
return JSON.parse(raw);
|
|
} catch (_error) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
function normalizeContentValue(value) {
|
|
if (!value || typeof value !== 'object') {
|
|
return {
|
|
type: 'text',
|
|
value: parseMaybeJson(value)
|
|
};
|
|
}
|
|
const normalized = {};
|
|
Object.keys(value).forEach(function (key) {
|
|
normalized[key] = value[key];
|
|
});
|
|
if (normalized.value !== undefined) {
|
|
normalized.value = parseMaybeJson(normalized.value);
|
|
} else if (normalized.font_family !== undefined && normalized.font_family !== null) {
|
|
normalized.font_family = sanitizeFontFamily(normalized.font_family);
|
|
} else if (normalized.font_size !== undefined && normalized.font_size !== null) {
|
|
normalized.font_size = sanitizeFontSize(normalized.font_size);
|
|
} else if (normalized.font_color !== undefined && normalized.font_color !== null) {
|
|
normalized.font_color = sanitizeTextColor(normalized.font_color);
|
|
} else if (!normalized.type) {
|
|
normalized.type = 'text';
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
function normalizeSlide(slide) {
|
|
const normalized = {};
|
|
Object.keys(slide || {}).forEach(function (key) {
|
|
normalized[key] = slide[key];
|
|
});
|
|
normalized.content = {};
|
|
const content = slide && slide.content && typeof slide.content === 'object' ? slide.content : {};
|
|
Object.keys(content).forEach(function (regionKey) {
|
|
normalized.content[regionKey] = normalizeContentValue(content[regionKey]);
|
|
});
|
|
return normalized;
|
|
}
|
|
|
|
function sanitizeRichText(html) {
|
|
let output = String(html || '');
|
|
output = output.replace(/<script[\s\S]*?<\/script>/gi, '');
|
|
output = output.replace(/<style[\s\S]*?<\/style>/gi, '');
|
|
return output.replace(/<[^>]+>/g, (tag) => {
|
|
const match = tag.match(/^<\s*(\/?)\s*([a-z0-9]+)([\s\S]*?)(\/?)>$/i);
|
|
if (!match) {
|
|
return '';
|
|
}
|
|
const closing = Boolean(match[1]);
|
|
const name = String(match[2] || '').toLowerCase();
|
|
const attrText = String(match[3] || '');
|
|
if (!ALLOWED_RICH_TEXT_TAGS.includes(name)) {
|
|
return '';
|
|
}
|
|
if (closing) {
|
|
return `</${name}>`;
|
|
}
|
|
return `<${name}${sanitizeRichTextAttributes(name, attrText)}>`;
|
|
});
|
|
}
|
|
|
|
function renderEditorJsBlock(block) {
|
|
if (!block || !block.type || !block.data) {
|
|
return '';
|
|
}
|
|
|
|
if (block.type === 'header') {
|
|
const level = Math.max(1, Math.min(6, Number(block.data.level || 2)));
|
|
return '<h' + level + '>' + sanitizeRichText(block.data.text || '') + '</h' + level + '>';
|
|
} else if (block.type === 'list') {
|
|
const tag = block.data.style === 'ordered' ? 'ol' : 'ul';
|
|
const items = Array.isArray(block.data.items) ? block.data.items : [];
|
|
return '<' + tag + ' style="list-style-type:' + (tag === 'ol' ? 'decimal' : 'disc') + ';padding-left:1.4em;">' + items.map((item) => renderEditorJsListItem(item, tag)).join('') + '</' + tag + '>';
|
|
} else if (block.type === 'delimiter') {
|
|
return '<hr />';
|
|
} else if (block.type === 'code') {
|
|
return '<pre><code>' + escapeHtml(block.data.code || '') + '</code></pre>';
|
|
} else if (block.type === 'table') {
|
|
return renderEditorJsTable(block.data);
|
|
} else if (block.type === 'paragraph') {
|
|
return '<p>' + sanitizeRichText(block.data.text || '') + '</p>';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function renderEditorJsListItem(item, tag) {
|
|
if (item && typeof item === 'object') {
|
|
const content = item.content !== undefined ? item.content : (item.text !== undefined ? item.text : item.value !== undefined ? item.value : '');
|
|
const children = Array.isArray(item.items) ? item.items : Array.isArray(item.subItems) ? item.subItems : Array.isArray(item.children) ? item.children : [];
|
|
const nested = children.length ? '<' + tag + ' style="list-style-type:' + (tag === 'ol' ? 'decimal' : 'disc') + ';padding-left:1.4em;">' + children.map((child) => renderEditorJsListItem(child, tag)).join('') + '</' + tag + '>' : '';
|
|
return '<li>' + sanitizeRichText(content || '') + nested + '</li>';
|
|
}
|
|
return '<li>' + sanitizeRichText(item || '') + '</li>';
|
|
}
|
|
|
|
function renderEditorJsTable(data) {
|
|
const rows = Array.isArray(data.content) ? data.content : Array.isArray(data.rows) ? data.rows : [];
|
|
if (!rows.length) {
|
|
return '';
|
|
}
|
|
const hasHeadings = Boolean(data.withHeadings);
|
|
const tableRows = rows.map(function (row, rowIndex) {
|
|
const cells = Array.isArray(row) ? row : [];
|
|
const cellTag = hasHeadings && rowIndex === 0 ? 'th' : 'td';
|
|
const cellAttrs = cellTag === 'th' ? ' scope="col"' : '';
|
|
return '<tr>' + cells.map(function (cell) {
|
|
return '<' + cellTag + cellAttrs + '>' + sanitizeRichText(cell || '') + '</' + cellTag + '>';
|
|
}).join('') + '</tr>';
|
|
}).join('');
|
|
return '<table class="ck-content-table">' + tableRows + '</table>';
|
|
}
|
|
|
|
function wrapRichTextParagraph(html) {
|
|
const raw = String(html || '').trim();
|
|
if (!raw) {
|
|
return '';
|
|
}
|
|
if (/^<\s*(?:p|div|h[1-6]|ul|ol|pre|table|blockquote|figure|hr|li)\b/i.test(raw)) {
|
|
return raw;
|
|
}
|
|
return '<p>' + raw + '</p>';
|
|
}
|
|
|
|
function renderEditorJsContent(value) {
|
|
if (value && typeof value === 'object') {
|
|
if (Array.isArray(value.blocks)) {
|
|
return value.blocks.map(renderEditorJsBlock).join('');
|
|
}
|
|
if (value.value !== undefined) {
|
|
return renderEditorJsContent(value.value);
|
|
}
|
|
}
|
|
const raw = String(value || '');
|
|
try {
|
|
const parsed = JSON.parse(raw);
|
|
if (parsed && Array.isArray(parsed.blocks)) {
|
|
return parsed.blocks.map(renderEditorJsBlock).join('');
|
|
}
|
|
} catch (_error) {
|
|
// fall through to legacy HTML rendering
|
|
}
|
|
return wrapRichTextParagraph(sanitizeRichText(raw));
|
|
}
|
|
|
|
function buildHtmlDocument(html) {
|
|
const raw = String(html || '').trim();
|
|
if (!raw) {
|
|
return '';
|
|
}
|
|
|
|
if (/^<!doctype\b/i.test(raw) || /^<html\b/i.test(raw)) {
|
|
return raw;
|
|
}
|
|
|
|
return '<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + raw + '</body></html>';
|
|
}
|
|
|
|
function renderHtmlRegionContent(value) {
|
|
const html = normalizeRenderableValue(value).trim();
|
|
if (!html) {
|
|
return '<div class="template-region-placeholder">HTML</div>';
|
|
}
|
|
return '<iframe class="template-region-html-frame" sandbox="" srcdoc="' + escapeHtml(buildHtmlDocument(html)) + '" title="HTML region" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>';
|
|
}
|
|
|
|
function normalizeRenderableValue(value) {
|
|
if (value && typeof value === 'object') {
|
|
if (value.value !== undefined) {
|
|
return normalizeRenderableValue(value.value);
|
|
}
|
|
if (value.text !== undefined) {
|
|
return normalizeRenderableValue(value.text);
|
|
}
|
|
if (value.html !== undefined) {
|
|
return normalizeRenderableValue(value.html);
|
|
}
|
|
if (value.content !== undefined) {
|
|
return normalizeRenderableValue(value.content);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
return String(value === undefined || value === null ? '' : value);
|
|
}
|
|
|
|
function fitCanvasSize(canvasWidth, canvasHeight, maxWidth, maxHeight) {
|
|
const width = Math.max(1, Number(canvasWidth || 0) || 1920);
|
|
const height = Math.max(1, Number(canvasHeight || 0) || 1080);
|
|
const viewportWidth = Math.max(1, Number(maxWidth || 0) || width);
|
|
const viewportHeight = Math.max(1, Number(maxHeight || 0) || height);
|
|
const scale = Math.min(viewportWidth / width, viewportHeight / height);
|
|
return {
|
|
width: Math.round(width * scale),
|
|
height: Math.round(height * scale)
|
|
};
|
|
}
|
|
|
|
const playerPageTemplatePath = path.join(__dirname, 'player-page.template.html');
|
|
const playerClientNameScriptPath = path.join(__dirname, 'player-client-name.script.html');
|
|
const playerPageOfflineScriptPath = path.join(__dirname, 'public', 'js', 'player-page-offline.js');
|
|
const playerPagePlaylistScriptPath = path.join(__dirname, 'public', 'js', 'player-page-playlist.js');
|
|
const playerPageAnimationScriptPath = path.join(__dirname, 'public', 'js', 'player-page-animation.js');
|
|
const playerPageMediaScriptPath = path.join(__dirname, 'public', 'js', 'player-page-media.js');
|
|
const playerPageTransitionScriptPath = path.join(__dirname, 'public', 'js', 'player-page-transition.js');
|
|
const playerPageCommandsScriptPath = path.join(__dirname, 'public', 'js', 'player-page-commands.js');
|
|
const playerPageRenderingScriptPath = path.join(__dirname, 'public', 'js', 'player-page-rendering.js');
|
|
const playerPagePlaybackScriptPath = path.join(__dirname, 'public', 'js', 'player-page-playback.js');
|
|
const playerAnnouncementTemplatesScriptPath = path.join(__dirname, 'public', 'js', 'player-announcement-templates.js');
|
|
const playerAnnouncementLowerThirdTemplatePath = path.join(__dirname, 'announcement-templates', 'lower-third.template.html');
|
|
const playerAnnouncementTopBannerTemplatePath = path.join(__dirname, 'announcement-templates', 'top-banner.template.html');
|
|
const playerAnnouncementFullscreenTemplatePath = path.join(__dirname, 'announcement-templates', 'fullscreen.template.html');
|
|
const playerPageAnnouncementsScriptPath = path.join(__dirname, 'player-page-announcements.js');
|
|
const playerPageScriptPath = path.join(__dirname, 'player-page.script.html');
|
|
const playerRegionScriptDir = path.join(__dirname, 'regions');
|
|
const playerOnboardingLandingScriptPath = path.join(__dirname, 'onboarding', 'player-onboarding-landing.script.html');
|
|
const playerOnboardingFormScriptPath = path.join(__dirname, 'onboarding', 'player-onboarding-form.script.html');
|
|
let playerPageTemplateCache = null;
|
|
let playerClientNameScriptCache = null;
|
|
let playerPageOfflineScriptCache = null;
|
|
let playerPagePlaylistScriptCache = null;
|
|
let playerPageCommandsScriptCache = null;
|
|
let playerPageRenderingScriptCache = null;
|
|
let playerPagePlaybackScriptCache = null;
|
|
let playerAnnouncementTemplatesScriptCache = null;
|
|
let playerAnnouncementTemplatesDataScriptCache = null;
|
|
let playerPageAnnouncementsScriptCache = null;
|
|
let playerPageScriptCache = null;
|
|
let playerRegionScriptsCache = null;
|
|
let playerOnboardingLandingScriptCache = null;
|
|
let playerOnboardingFormScriptCache = null;
|
|
|
|
function loadTemplate(filePath, cache) {
|
|
const stat = fs.statSync(filePath);
|
|
if (cache.value && cache.mtimeMs === stat.mtimeMs) {
|
|
return cache.value;
|
|
}
|
|
const compiled = require('handlebars').compile(fs.readFileSync(filePath, 'utf8'));
|
|
cache.value = compiled;
|
|
cache.mtimeMs = stat.mtimeMs;
|
|
return compiled;
|
|
}
|
|
|
|
function getPlayerPageTemplate() {
|
|
return loadTemplate(playerPageTemplatePath, playerPageTemplateCache || (playerPageTemplateCache = {}));
|
|
}
|
|
|
|
function getPlayerClientNameScript() {
|
|
return loadTemplate(playerClientNameScriptPath, playerClientNameScriptCache || (playerClientNameScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerPageOfflineScript() {
|
|
return loadTemplate(playerPageOfflineScriptPath, playerPageOfflineScriptCache || (playerPageOfflineScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerPagePlaylistScript() {
|
|
return loadTemplate(playerPagePlaylistScriptPath, playerPagePlaylistScriptCache || (playerPagePlaylistScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerPageCommandsScript() {
|
|
return loadTemplate(playerPageCommandsScriptPath, playerPageCommandsScriptCache || (playerPageCommandsScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerPageRenderingScript() {
|
|
return loadTemplate(playerPageRenderingScriptPath, playerPageRenderingScriptCache || (playerPageRenderingScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerPagePlaybackScript() {
|
|
return loadTemplate(playerPagePlaybackScriptPath, playerPagePlaybackScriptCache || (playerPagePlaybackScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerAnnouncementTemplatesScript() {
|
|
if (playerAnnouncementTemplatesScriptCache && playerAnnouncementTemplatesScriptCache.path === playerAnnouncementTemplatesScriptPath) {
|
|
return playerAnnouncementTemplatesScriptCache.value;
|
|
}
|
|
|
|
const value = fs.readFileSync(playerAnnouncementTemplatesScriptPath, 'utf8').trim();
|
|
playerAnnouncementTemplatesScriptCache = {
|
|
path: playerAnnouncementTemplatesScriptPath,
|
|
value: value
|
|
};
|
|
return value;
|
|
}
|
|
|
|
function getAnnouncementIconsDataScript() {
|
|
return [
|
|
'(function () {',
|
|
' window.pulseAnnouncementIconKeys = ' + safeJsonForScript(announcementIcons.ANNOUNCEMENT_ICON_CATALOG_KEYS) + ';',
|
|
' window.pulseAnnouncementDefaultIconKey = ' + safeJsonForScript(announcementIcons.DEFAULT_ANNOUNCEMENT_ICON) + ';',
|
|
'}());'
|
|
].join('\n');
|
|
}
|
|
|
|
function loadAnnouncementTemplate(filePath) {
|
|
return fs.readFileSync(filePath, 'utf8').trim();
|
|
}
|
|
|
|
function getPlayerAnnouncementTemplatesDataScript() {
|
|
const templatePaths = [
|
|
playerAnnouncementLowerThirdTemplatePath,
|
|
playerAnnouncementTopBannerTemplatePath,
|
|
playerAnnouncementFullscreenTemplatePath
|
|
];
|
|
const signature = templatePaths.map(function (filePath) {
|
|
return fs.statSync(filePath).mtimeMs;
|
|
}).join('|');
|
|
|
|
if (playerAnnouncementTemplatesDataScriptCache && playerAnnouncementTemplatesDataScriptCache.signature === signature) {
|
|
return playerAnnouncementTemplatesDataScriptCache.value;
|
|
}
|
|
|
|
const value = [
|
|
'(function () {',
|
|
' window.playerAnnouncementTemplates = {',
|
|
' lowerThird: ' + safeJsonForScript(loadAnnouncementTemplate(playerAnnouncementLowerThirdTemplatePath)) + ',',
|
|
' topBanner: ' + safeJsonForScript(loadAnnouncementTemplate(playerAnnouncementTopBannerTemplatePath)) + ',',
|
|
' fullscreen: ' + safeJsonForScript(loadAnnouncementTemplate(playerAnnouncementFullscreenTemplatePath)),
|
|
' };',
|
|
'}());'
|
|
].join('\n');
|
|
|
|
playerAnnouncementTemplatesDataScriptCache = {
|
|
signature: signature,
|
|
value: value
|
|
};
|
|
return value;
|
|
}
|
|
|
|
function getPlayerPageAnnouncementsScript() {
|
|
return loadTemplate(playerPageAnnouncementsScriptPath, playerPageAnnouncementsScriptCache || (playerPageAnnouncementsScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerPageScript() {
|
|
return loadTemplate(playerPageScriptPath, playerPageScriptCache || (playerPageScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerRegionScriptPaths() {
|
|
if (!fs.existsSync(playerRegionScriptDir)) {
|
|
return [];
|
|
}
|
|
|
|
return fs.readdirSync(playerRegionScriptDir, { withFileTypes: true })
|
|
.filter(function (entry) {
|
|
return entry.isFile() && entry.name.toLowerCase().endsWith('.js');
|
|
})
|
|
.map(function (entry) {
|
|
return path.join(playerRegionScriptDir, entry.name);
|
|
})
|
|
.sort(function (left, right) {
|
|
return left.localeCompare(right);
|
|
});
|
|
}
|
|
|
|
function getPlayerRegionScripts() {
|
|
const sharedPlaceholderUtilsPath = path.join(__dirname, '..', 'web', 'public', 'js', 'shared', 'placeholder-utils.js');
|
|
const sharedQrSvgUtilsPath = path.join(__dirname, '..', 'web', 'public', 'js', 'shared', 'qr-code-svg.js');
|
|
const playerRegionScriptPaths = getPlayerRegionScriptPaths();
|
|
const scriptPaths = [sharedPlaceholderUtilsPath, sharedQrSvgUtilsPath].concat(playerRegionScriptPaths);
|
|
const statSignature = scriptPaths.map(function (filePath) {
|
|
return fs.statSync(filePath).mtimeMs;
|
|
}).join('|');
|
|
if (playerRegionScriptsCache && playerRegionScriptsCache.signature === statSignature) {
|
|
return playerRegionScriptsCache.value;
|
|
}
|
|
|
|
const value = scriptPaths.map(function (filePath) {
|
|
return fs.readFileSync(filePath, 'utf8').trim();
|
|
}).join('\n\n');
|
|
playerRegionScriptsCache = {
|
|
signature: statSignature,
|
|
value: value
|
|
};
|
|
return value;
|
|
}
|
|
|
|
function getPlayerOnboardingLandingScript() {
|
|
return loadTemplate(playerOnboardingLandingScriptPath, playerOnboardingLandingScriptCache || (playerOnboardingLandingScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerOnboardingFormScript() {
|
|
return loadTemplate(playerOnboardingFormScriptPath, playerOnboardingFormScriptCache || (playerOnboardingFormScriptCache = {}));
|
|
}
|
|
|
|
function getPlayerRuntimeScripts() {
|
|
function getScriptBody(value) {
|
|
return String(value || '')
|
|
.replace(/^\s*<script(?:\s[^>]*)?>/i, '')
|
|
.replace(/<\/script>\s*$/i, '')
|
|
.trim();
|
|
}
|
|
|
|
return [
|
|
['region-registry', fs.readFileSync(path.join(__dirname, 'public', 'js', 'player-region-registry.js'), 'utf8').trim()],
|
|
['placeholder-utils', fs.readFileSync(path.join(__dirname, '..', 'web', 'public', 'js', 'shared', 'placeholder-utils.js'), 'utf8').trim()],
|
|
['qr-code-svg', fs.readFileSync(path.join(__dirname, '..', 'web', 'public', 'js', 'shared', 'qr-code-svg.js'), 'utf8').trim()],
|
|
['client-name', getScriptBody(getPlayerClientNameScript()())],
|
|
['offline', getScriptBody(getPlayerPageOfflineScript()())],
|
|
['playlist', getScriptBody(getPlayerPagePlaylistScript()())],
|
|
['animation', fs.readFileSync(playerPageAnimationScriptPath, 'utf8').trim()],
|
|
['media', fs.readFileSync(playerPageMediaScriptPath, 'utf8').trim()],
|
|
['transition', fs.readFileSync(playerPageTransitionScriptPath, 'utf8').trim()],
|
|
['commands', getScriptBody(getPlayerPageCommandsScript()())],
|
|
['rendering', getScriptBody(getPlayerPageRenderingScript()())],
|
|
['playback', getScriptBody(getPlayerPagePlaybackScript()())],
|
|
['announcement-icons', getAnnouncementIconsDataScript()],
|
|
['announcement-data', getPlayerAnnouncementTemplatesDataScript()],
|
|
['announcement-templates', getScriptBody(getPlayerAnnouncementTemplatesScript())]
|
|
].concat(getPlayerRegionScriptPaths().map(function (filePath, index) {
|
|
return ['region-' + index, fs.readFileSync(filePath, 'utf8').trim()];
|
|
})).filter(function (entry) { return entry[1]; });
|
|
}
|
|
|
|
module.exports = {
|
|
mediaKind: mediaKind,
|
|
escapeHtml: escapeHtml,
|
|
sanitizeFontFamily: sanitizeFontFamily,
|
|
sanitizeFontSize: sanitizeFontSize,
|
|
sanitizeTextColor: sanitizeTextColor,
|
|
sanitizeRichTextAttributes: sanitizeRichTextAttributes,
|
|
safeJsonForScript: safeJsonForScript,
|
|
parseMaybeJson: parseMaybeJson,
|
|
normalizeContentValue: normalizeContentValue,
|
|
normalizeSlide: normalizeSlide,
|
|
sanitizeRichText: sanitizeRichText,
|
|
renderEditorJsBlock: renderEditorJsBlock,
|
|
renderEditorJsListItem: renderEditorJsListItem,
|
|
renderEditorJsTable: renderEditorJsTable,
|
|
renderEditorJsContent: renderEditorJsContent,
|
|
renderHtmlRegionContent: renderHtmlRegionContent,
|
|
fitCanvasSize: fitCanvasSize,
|
|
loadTemplate: loadTemplate,
|
|
getPlayerPageTemplate: getPlayerPageTemplate,
|
|
getPlayerClientNameScript: getPlayerClientNameScript,
|
|
getPlayerPageOfflineScript: getPlayerPageOfflineScript,
|
|
getPlayerPagePlaylistScript: getPlayerPagePlaylistScript,
|
|
getPlayerPageCommandsScript: getPlayerPageCommandsScript,
|
|
getPlayerPageRenderingScript: getPlayerPageRenderingScript,
|
|
getPlayerPagePlaybackScript: getPlayerPagePlaybackScript,
|
|
getAnnouncementIconsDataScript: getAnnouncementIconsDataScript,
|
|
getPlayerAnnouncementTemplatesDataScript: getPlayerAnnouncementTemplatesDataScript,
|
|
getPlayerAnnouncementTemplatesScript: getPlayerAnnouncementTemplatesScript,
|
|
getPlayerPageAnnouncementsScript: getPlayerPageAnnouncementsScript,
|
|
getPlayerPageScript: getPlayerPageScript,
|
|
getPlayerRegionScripts: getPlayerRegionScripts,
|
|
getPlayerOnboardingLandingScript: getPlayerOnboardingLandingScript,
|
|
getPlayerOnboardingFormScript: getPlayerOnboardingFormScript,
|
|
getPlayerRuntimeScripts: getPlayerRuntimeScripts
|
|
}; |