280 lines
12 KiB
JavaScript
280 lines
12 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');
|
|
const renderingShared = require('#src/player/rendering-shared');
|
|
const {
|
|
escapeHtml,
|
|
sanitizeRichTextAttributes,
|
|
sanitizeRichText,
|
|
renderEditorJsBlock,
|
|
renderEditorJsListItem,
|
|
renderEditorJsTable,
|
|
renderEditorJsContent,
|
|
normalizeRenderableString,
|
|
buildHtmlDocument,
|
|
sanitizeFontFamily,
|
|
sanitizeFontSize,
|
|
sanitizeTextColor,
|
|
parseMaybeJson,
|
|
normalizeContentValue,
|
|
normalizeSlide,
|
|
fitCanvasSize
|
|
} = renderingShared;
|
|
|
|
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 safeJsonForScript(value) {
|
|
return JSON.stringify(value === undefined ? null : value).replace(/</g, '\\u003c');
|
|
}
|
|
|
|
function renderHtmlRegionContent(value) {
|
|
const html = normalizeRenderableString(value, ['value', 'text', 'html', 'content']).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>';
|
|
}
|
|
|
|
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 playerRenderingSharedScriptPath = path.join(__dirname, 'rendering-shared.js');
|
|
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 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 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()],
|
|
['chart-data', fs.readFileSync(path.join(__dirname, '..', 'web', 'public', 'js', 'regions', 'chart-data.js'), 'utf8').trim()],
|
|
['qr-code-svg', fs.readFileSync(path.join(__dirname, '..', 'web', 'public', 'js', 'shared', 'qr-code-svg.js'), 'utf8').trim()],
|
|
['rendering-shared', fs.readFileSync(playerRenderingSharedScriptPath, '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,
|
|
getPlayerOnboardingLandingScript: getPlayerOnboardingLandingScript,
|
|
getPlayerOnboardingFormScript: getPlayerOnboardingFormScript,
|
|
getPlayerRuntimeScripts: getPlayerRuntimeScripts
|
|
}; |