Files
pulse-signage/src/player/regions/timetable.js
T
lzstealth e7ec276317
Publish Docker Image / build-and-push (./build/Dockerfile, git.lzstealth.com/lzstealth/pulse-signage-web, web) (push) Successful in 1m18s
Publish Docker Image / build-and-push (./build/Dockerfile.player, git.lzstealth.com/lzstealth/pulse-signage-player, player) (push) Successful in 33s
Release v2.7.0
2026-08-14 13:36:47 +01:00

160 lines
6.3 KiB
JavaScript

// Timetable region rendering for live playback.
var registry = window.pulsePlayerRegionTypes;
function substituteTimetableVariables(html, entry) {
var source = String(html || '');
return source.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, function (_match, expression) {
if (!entry || typeof entry !== 'object') {
return '';
}
if (!window.placeholderUtils || typeof window.placeholderUtils.resolvePlaceholderExpression !== 'function' || typeof window.placeholderUtils.formatPlaceholderValue !== 'function') {
return '';
}
return escapeHtml(window.placeholderUtils.formatPlaceholderValue(window.placeholderUtils.resolvePlaceholderExpression(entry, expression)));
});
}
function renderTemplate(template, context) {
var source = String(template || '');
if (!source) {
return '';
}
return substituteTimetableVariables(source, context);
}
function getTimetableGroups() {
return Array.isArray(initialData && initialData.timetableGroups) ? initialData.timetableGroups : [];
}
function getGroupById(groupId, groups) {
var normalizedId = Number(groupId || 0);
return (Array.isArray(groups) ? groups : getTimetableGroups()).find(function (group) {
return Number(group.id) === normalizedId;
}) || null;
}
function getEntries(groupId, groups) {
var group = getGroupById(groupId, groups);
return group && Array.isArray(group.entries) ? group.entries : [];
}
function toDate(value) {
if (!value) {
return null;
}
var date = value instanceof Date ? value : new Date(value);
return Number.isNaN(date.getTime()) ? null : date;
}
function isUpcoming(entry, now) {
var start = toDate(entry && entry.start_datetime);
return Boolean(start && now < start);
}
function isLive(entry, now) {
var start = toDate(entry && entry.start_datetime);
var end = toDate(entry && entry.end_datetime);
return Boolean(start && end && now >= start && now < end);
}
function getVisibleEntries(groupId, displayMode, maxItems, groups) {
var now = new Date();
var entries = getEntries(groupId, groups).slice().sort(function (left, right) {
var leftStart = toDate(left && left.start_datetime);
var rightStart = toDate(right && right.start_datetime);
return (leftStart ? leftStart.getTime() : 0) - (rightStart ? rightStart.getTime() : 0) || Number(left.id || 0) - Number(right.id || 0);
});
var mode = String(displayMode || 'upcoming').trim().toLowerCase();
entries = entries.filter(function (entry) {
if (mode === 'current') {
return isLive(entry, now);
}
if (mode === 'both') {
return isUpcoming(entry, now) || isLive(entry, now);
}
return isUpcoming(entry, now);
});
if (!entries.length && mode === 'upcoming') {
entries = getEntries(groupId, groups).filter(function (entry) {
return isLive(entry, now);
});
}
return entries.slice(0, Math.max(1, Number(maxItems || 5)));
}
function formatDateTime(value) {
var date = toDate(value);
if (!date) {
return '';
}
try {
return new Intl.DateTimeFormat(undefined, {
month: 'short',
day: '2-digit',
hour: 'numeric',
minute: '2-digit'
}).format(date);
} catch (_error) {
return date.toLocaleString();
}
}
function getDefaultStyle() {
return {
font_family: 'Arial',
font_size: 28,
font_color: '#000000'
};
}
function getTextStyle(region, regionContent) {
var current = regionContent && typeof regionContent === 'object' ? regionContent : {};
var defaultStyle = getDefaultStyle();
return {
font_family: String(current.font_family || region.font_family || defaultStyle.font_family || 'Arial').trim() || 'Arial',
font_size: Math.max(8, Number(current.font_size || region.font_size || defaultStyle.font_size || 28)),
font_color: String(current.font_color || region.font_color || defaultStyle.font_color || '#000000').trim() || '#000000'
};
}
function renderRegion(region, regionContent) {
var value = String(regionContent && (regionContent.value !== undefined ? regionContent.value : regionContent.text !== undefined ? regionContent.text : '') || '').trim();
var style = getTextStyle(region, regionContent || {});
var groups = getTimetableGroups();
var groupId = regionContent && regionContent.timetable_group_id !== undefined ? regionContent.timetable_group_id : '';
var displayMode = regionContent && regionContent.display_mode ? regionContent.display_mode : 'upcoming';
var maxItems = regionContent && regionContent.max_items !== undefined ? regionContent.max_items : 5;
var group = getGroupById(groupId, groups);
var entries = getVisibleEntries(groupId, displayMode, maxItems, groups);
var timeZone = group && (group.timezone || group.time_zone) ? String(group.timezone || group.time_zone) : '';
var width = Math.max(1, Math.round(Number(region && region.pixelWidth ? region.pixelWidth : 0) || 1));
var height = Math.max(1, Math.round(Number(region && region.pixelHeight ? region.pixelHeight : 0) || 1));
var canvasScale = Number(region && region.canvasScale ? region.canvasScale : 1) || 1;
var contentStyle = 'width:' + width + 'px;height:' + height + 'px;transform:scale(' + canvasScale + ');transform-origin:top left;' + (style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : '') + (style.font_size ? 'font-size:' + Math.max(1, Math.round(Number(style.font_size))) + 'px;' : '') + (style.font_color ? 'color:' + escapeHtml(style.font_color) + ';' : '');
if (!value) {
return '<div class="template-region timetable" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + ' overflow:hidden;"></div></div>';
}
return '<div class="template-region timetable" style="' + region.baseStyle + '"><div class="template-region-text-scale" style="' + contentStyle + ' overflow:hidden;">' + entries.map(function (entry, index) {
return '<div class="timetable-region-entry" data-timetable-entry-index="' + index + '">' + sanitizeRichText(substituteTimetableVariables(value, Object.assign({}, entry || {}, {
start: entry && entry.start_datetime !== undefined ? entry.start_datetime : '',
end: entry && entry.end_datetime !== undefined ? entry.end_datetime : '',
timeZone: timeZone,
group: group || {},
entries: entries,
index: index + 1
}))) + '</div>';
}).join('') + '</div></div>';
}
registry.register('timetable', {
renderRegion: renderRegion
});