import { createTemplateSelectorLockController } from '/assets/js/slides/slide-form-template-lock.js'; import { createSlideFormEditorController } from '/assets/js/slides/slide-form-editor.js'; import { createSlideFormRegionHelpers } from '/assets/js/slides/slide-form-regions.js'; import { createSlideFormPreviewHelpers } from '/assets/js/slides/slide-form-preview.js'; (function () { var DEFAULT_FONT_SIZE = 32; function formatUploadLimitLabel(bytes) { var megabytes = Number(bytes || 0) / 1024 / 1024; if (megabytes >= 1024 && megabytes % 1024 === 0) { return String(megabytes / 1024) + ' GB'; } return (Number.isInteger(megabytes) ? String(megabytes) : megabytes.toFixed(2).replace(/0+$/, '').replace(/\.$/, '')) + ' MB'; } var dataElement = document.getElementById('slide-editor-data'); if (!dataElement) { return; } var slideEditorData = {}; try { slideEditorData = JSON.parse(dataElement.getAttribute('data-json') || dataElement.textContent || '{}') || {}; } catch (_error) { slideEditorData = {}; } var templates = Array.isArray(slideEditorData.templates) ? slideEditorData.templates : []; var rssFeeds = Array.isArray(slideEditorData.rssFeeds) ? slideEditorData.rssFeeds : []; var apiSources = Array.isArray(slideEditorData.apiSources) ? slideEditorData.apiSources : []; var timetableGroups = Array.isArray(slideEditorData.timetableGroups) ? slideEditorData.timetableGroups : []; var weatherLocations = Array.isArray(slideEditorData.weatherLocations) ? slideEditorData.weatherLocations : []; var fontStylesheetHref = String(slideEditorData.fontStylesheetHref || '').trim(); var existingTemplateId = slideEditorData.existingTemplateId !== undefined ? slideEditorData.existingTemplateId : null; var existingContent = slideEditorData.existingContent || {}; var templateSelect = document.getElementById('template-select'); var templateFields = document.getElementById('template-fields'); var slideForm = document.getElementById('slide-form'); var slideEditorShell = document.querySelector('.slide-editor-shell'); var slideEditorSidebar = document.querySelector('.slide-editor-sidebar'); var slidePreviewMeta = document.getElementById('slide-preview-meta'); var slidePreviewStage = document.getElementById('slide-preview-stage'); var slidePreviewCanvas = document.getElementById('slide-preview-canvas'); var slidePreviewBackground = document.getElementById('slide-preview-background'); var slidePreviewEmpty = document.getElementById('slide-preview-empty'); var slidePreviewOverlay = document.getElementById('slide-preview-overlay'); var openPreviewPopupButton = document.getElementById('open-preview-popup'); var uploadLimits = slideEditorData.uploadLimits || {}; var uploadMaxBytes = Number(uploadLimits.imageMaxBytes || 100 * 1024 * 1024); var uploadMaxLabel = formatUploadLimitLabel(uploadMaxBytes); var uploadVideoMaxBytes = Number(uploadLimits.videoMaxBytes || 1024 * 1024 * 1024); var uploadVideoMaxLabel = formatUploadLimitLabel(uploadVideoMaxBytes); var wysiwygImageUploadMaxBytes = Number(uploadLimits.wysiwygImageMaxBytes || 2 * 1024 * 1024); var wysiwygImageUploadLimitLabel = formatUploadLimitLabel(wysiwygImageUploadMaxBytes); var uploadMimeTypes = Array.isArray(uploadLimits.allowedMimeTypes) ? uploadLimits.allowedMimeTypes : []; var videoDurationCache = Object.create(null); var previewRenderFrame = 0; var previewPopupWindow = null; var previewPopupRenderFrame = 0; var previewPopupFeatures = ''; var currentPreviewCanvasWidth = 0; var currentPreviewCanvasHeight = 0; var sidebarSyncFrame = 0; var sidebarBaseOffset = 16; var templateSelectorLock = createTemplateSelectorLockController(templateSelect); var regionTypes = window.pulseRegionTypes || {}; function escapeHtml(value) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function getRegionTypeModule(regionType) { if (regionTypes && typeof regionTypes.get === 'function') { return regionTypes.get(regionType); } return regionTypes && regionTypes[regionType] ? regionTypes[regionType] : null; } function getTemplateById(id) { return templates.find(function (item) { return Number(item.id) === Number(id); }) || null; } function applyBackdropStyle(element, backgroundColor, backgroundImagePath, backgroundGradient) { if (!element) { return; } var color = String(backgroundColor || '#111111').trim() || '#111111'; var gradient = ''; try { var gradientData = typeof backgroundGradient === 'string' ? JSON.parse(backgroundGradient) : backgroundGradient; if (gradientData && gradientData.type === 'linear' && ((Array.isArray(gradientData.stops) && gradientData.stops.length >= 2) || (Array.isArray(gradientData.colors) && gradientData.colors.length >= 2))) { var stops = Array.isArray(gradientData.stops) ? gradientData.stops : (gradientData.colors || []).map(function (stopColor, index, colors) { return { color: stopColor, position: colors.length > 1 ? Math.round(index * 100 / (colors.length - 1)) : 0 }; }); stops = stops.filter(function (stop) { return stop && /^#[0-9a-fA-F]{3,8}$/.test(String(stop.color || '').trim()); }).slice(0, 12); if (stops.length >= 2) { var angle = Number(gradientData.angle); gradient = 'linear-gradient(' + (Number.isFinite(angle) ? Math.max(0, Math.min(360, angle)) : 90) + 'deg,' + stops.map(function (stop) { return stop.color + ' ' + Math.max(0, Math.min(100, Number(stop.position) || 0)) + '%'; }).join(',') + ')'; } } } catch (_error) { gradient = ''; } element.style.backgroundColor = color; element.style.backgroundImage = [backgroundImagePath ? 'url("' + encodeURI(String(backgroundImagePath)) + '")' : '', gradient].filter(Boolean).join(',') || 'none'; element.style.backgroundPosition = 'center'; element.style.backgroundSize = 'cover'; element.style.backgroundRepeat = 'no-repeat'; } function applyPopupBackdropStyle(element, backgroundColor) { if (!element) { return; } var color = String(backgroundColor || '#111111').trim() || '#111111'; element.style.backgroundColor = color; var gradient = ''; try { var gradientData = typeof backgroundGradient === 'string' ? JSON.parse(backgroundGradient) : backgroundGradient; if (gradientData && gradientData.type === 'linear' && ((Array.isArray(gradientData.stops) && gradientData.stops.length >= 2) || (Array.isArray(gradientData.colors) && gradientData.colors.length >= 2))) { var stops = Array.isArray(gradientData.stops) ? gradientData.stops : (gradientData.colors || []).map(function (color, index, colors) { return { color: color, position: colors.length > 1 ? Math.round(index * 100 / (colors.length - 1)) : 0 }; }); stops = stops.filter(function (stop) { return stop && /^#[0-9a-fA-F]{3,8}$/.test(String(stop.color || '').trim()); }).slice(0, 12); if (stops.length >= 2) { var angle = Number(gradientData.angle); gradient = 'linear-gradient(' + (Number.isFinite(angle) ? Math.max(0, Math.min(360, angle)) : 90) + 'deg,' + stops.map(function (stop) { return stop.color + ' ' + Math.max(0, Math.min(100, Number(stop.position) || 0)) + '%'; }).join(',') + ')'; } } } catch (_error) { gradient = ''; } element.style.backgroundImage = gradient || 'none'; element.style.backgroundPosition = 'center'; element.style.backgroundSize = '100% 100%'; element.style.backgroundRepeat = 'no-repeat'; } var slideFormEditorController = createSlideFormEditorController({ templateFields: templateFields, templateSelectorLock: templateSelectorLock, defaultFontSize: DEFAULT_FONT_SIZE, fontFamilyFormats: slideEditorData.fontFamilyFormats || '', fontStylesheetHref: fontStylesheetHref, imageUploadMaxBytes: wysiwygImageUploadMaxBytes, imageUploadLimitLabel: wysiwygImageUploadLimitLabel, imageUploadContext: 'wysiwyg', uploadMimeTypes: uploadMimeTypes, getRegionTypeModule: getRegionTypeModule, markFormDirty: function () { slideForm.dataset.dirty = 'true'; }, getEditorBackgroundColor: function () { var template = getTemplateById(templateSelect.value); return template && template.background_color ? String(template.background_color) : '#111111'; }, requestPreviewRender: requestPreviewRender }); var regionUtils = window.pulseRegionUtils || {}; var slideRegionHelperOptions = { templateFields: templateFields, existingContent: existingContent, rssFeeds: rssFeeds, apiSources: apiSources, timetableGroups: timetableGroups, weatherLocations: weatherLocations, mediaAssets: Array.isArray(slideEditorData.mediaAssets) ? slideEditorData.mediaAssets : [], defaultFontSize: DEFAULT_FONT_SIZE, uploadMaxLabel: uploadMaxLabel, uploadVideoMaxLabel: uploadVideoMaxLabel, uploadMaxBytes: uploadMaxBytes, uploadVideoMaxBytes: uploadVideoMaxBytes, uploadMimeTypes: uploadMimeTypes, escapeHtml: escapeHtml, getRegionTypeModule: getRegionTypeModule, requestPreviewRender: requestPreviewRender }; var slideRegionHelpers = createSlideFormRegionHelpers(slideRegionHelperOptions); var slidePreviewHelpers = createSlideFormPreviewHelpers({ getRegionTypeModule: getRegionTypeModule, escapeHtml: escapeHtml }); var sanitizePreviewHtml = slidePreviewHelpers.sanitizePreviewHtml; var sanitizeTagAttributes = slidePreviewHelpers.sanitizeTagAttributes; var renderPreviewListItem = slidePreviewHelpers.renderPreviewListItem; var renderPreviewTable = slidePreviewHelpers.renderPreviewTable; var renderPreviewBlock = slidePreviewHelpers.renderPreviewBlock; var renderPreviewText = slidePreviewHelpers.renderPreviewText; var renderPreviewTextRegion = slidePreviewHelpers.renderPreviewTextRegion; var buildRegionEditorCardContext = slideRegionHelpers.buildRegionEditorCardContext; var getRegionEditorCardHtml = slideRegionHelpers.getRegionEditorCardHtml; var bindRegionCards = slideRegionHelpers.bindRegionCards; var syncRegionCards = slideRegionHelpers.syncRegionCards; var getPreviewRegionContent = slideRegionHelpers.getPreviewRegionContent; var buildPreviewRenderContext = slideRegionHelpers.buildPreviewRenderContext; var startPreviewVideoPlayback = slidePreviewHelpers.startPreviewVideoPlayback; function getSelectedTemplate() { return templates.find(function (item) { return Number(item.id) === Number(templateSelect.value); }) || null; } function hasMeaningfulValue(value) { if (value === null || value === undefined) { return false; } if (Array.isArray(value)) { return value.some(hasMeaningfulValue); } if (typeof value === 'object') { return Object.keys(value).some(function (key) { return hasMeaningfulValue(value[key]); }); } return String(value).trim() !== ''; } function hasExistingSlideContent() { return hasMeaningfulValue(existingContent); } function clearImagePreviewUrls() { templateFields.querySelectorAll('input[type="file"][name^="region_image_"], input[type="file"][name^="region_video_"]').forEach(function (input) { if (input.dataset.previewUrl) { URL.revokeObjectURL(input.dataset.previewUrl); delete input.dataset.previewUrl; } }); } function getRegionVideoDurationHiddenInput(regionId) { var card = templateFields.querySelector('[data-region-id="' + regionId + '"]'); return card ? card.querySelector('input[type="hidden"][name="existing_region_video_duration_' + regionId + '"]') : null; } function getRegionEditorRegionType(card) { if (!card) { return ''; } if (card.dataset && String(card.dataset.regionMediaType || '').trim()) { return String(card.dataset.regionMediaType || '').trim(); } if (card.querySelector && card.querySelector('input[type="file"][name^="region_qr_image_"], input[type="url"][name^="region_qr_code_"]')) { return 'qr-code'; } var regionTypeInput = card.querySelector ? card.querySelector('input[type="hidden"][name="region_type[]"]') : null; return String(regionTypeInput && regionTypeInput.value || '').trim(); } function getRegionEditorModule(card) { return getRegionTypeModule(getRegionEditorRegionType(card)); } var regionMediaController = regionUtils.createRegionMediaUploadController ? regionUtils.createRegionMediaUploadController(templateFields, { getCardByRegionId: function (regionId) { return templateFields.querySelector('[data-region-id="' + regionId + '"]'); }, handleMediaSelection: function (input, file) { return handleRegionMediaFileSelection(input, file); }, renderPreview: function (card, value) { renderRegionMediaPreview(card, value); }, syncDuration: function (card, source) { return syncVideoRegionDuration(card, source); }, markEdited: function () { templateSelectorLock.arm(); templateSelectorLock.markEdited(); requestPreviewRender(); }, requestPreviewRender: function () { requestPreviewRender(); } }) : null; function buildRegionMediaHookContext(input, file) { var regionId = input && input.name ? input.name.replace(/^region_(?:image|video|qr_image)_/, '') : ''; var card = regionId ? templateFields.querySelector('[data-region-id="' + regionId + '"]') : null; return { input: input, file: file || null, card: card, regionId: regionId, regionType: getRegionEditorRegionType(card), setInputFile: function (nextFile) { return regionMediaController ? regionMediaController.setInputFile(input, nextFile) : null; }, uploadFile: function (nextFile) { return regionMediaController ? regionMediaController.uploadFile(input, nextFile || file) : Promise.resolve(null); } }; } function handleRegionMediaFileSelection(input, file) { var card = input && input.name ? templateFields.querySelector('[data-region-id="' + input.name.replace(/^region_(?:image|video|qr_image)_/, '') + '"]') : null; var module = getRegionEditorModule(card); var hookContext = buildRegionMediaHookContext(input, file); if (module && typeof module.handleMediaInputChange === 'function') { return module.handleMediaInputChange(hookContext); } return false; } function renderRegionMediaPreview(card, value) { if (!card) { return; } var previewBox = card.querySelector('.slide-image-region-preview-box'); if (!previewBox) { return; } var mediaType = card && card.dataset && String(card.dataset.regionMediaType || '').trim() === 'video' ? 'video' : card && card.querySelector && card.querySelector('input[type="file"][name^="region_qr_image_"]') ? 'qr-image' : 'image'; var isVideo = mediaType === 'video'; var isQrImage = mediaType === 'qr-image'; previewBox.innerHTML = value ? '
' : ''; } function loadVideoDuration(mediaPath) { var cacheKey = String(mediaPath || '').trim(); var cachedPromise; if (!cacheKey) { return Promise.reject(new Error('No video source is available for this slide.')); } cachedPromise = videoDurationCache[cacheKey]; if (cachedPromise) { return cachedPromise; } cachedPromise = new Promise(function (resolve, reject) { var video = document.createElement('video'); var timeoutId = window.setTimeout(function () { cleanup(); reject(new Error('Timed out loading the video metadata.')); }, 15000); function cleanup() { window.clearTimeout(timeoutId); video.removeAttribute('src'); video.load(); if (video.parentNode) { video.parentNode.removeChild(video); } } video.preload = 'metadata'; video.muted = true; video.playsInline = true; video.setAttribute('playsinline', ''); video.setAttribute('muted', ''); video.style.position = 'absolute'; video.style.left = '-9999px'; video.style.top = '0'; video.style.width = '1px'; video.style.height = '1px'; video.style.opacity = '0'; video.style.pointerEvents = 'none'; video.addEventListener('loadedmetadata', function () { var duration = Number(video.duration); cleanup(); if (Number.isFinite(duration) && duration > 0) { resolve(Math.max(0.001, Math.round(duration * 1000) / 1000)); } else { reject(new Error('The video duration could not be read.')); } }, { once: true }); video.addEventListener('error', function () { cleanup(); reject(new Error('The video duration could not be read.')); }, { once: true }); video.addEventListener('abort', function () { cleanup(); reject(new Error('The video duration could not be read.')); }, { once: true }); document.body.appendChild(video); video.src = cacheKey; video.load(); }).catch(function (error) { delete videoDurationCache[cacheKey]; throw error; }); videoDurationCache[cacheKey] = cachedPromise; return cachedPromise; } async function syncVideoRegionDuration(card, source) { var module = getRegionTypeModule('video'); if (module && typeof module.syncDuration === 'function') { return module.syncDuration(card, source, { getHiddenInput: function (targetCard) { var regionId = targetCard && targetCard.getAttribute('data-region-id'); return regionId ? getRegionVideoDurationHiddenInput(regionId) : null; }, loadDuration: loadVideoDuration }); } if (!card) { return; } var regionId = card.getAttribute('data-region-id'); var hidden = regionId ? getRegionVideoDurationHiddenInput(regionId) : null; if (!hidden) { return; } var mediaPath = String(source || '').trim(); if (!mediaPath) { hidden.value = ''; return; } try { hidden.value = String(await loadVideoDuration(mediaPath)); } catch (_error) { hidden.value = ''; } } function renderImageRegionPreview(card, value) { return renderRegionMediaPreview(card, value); } function getRegionImageInputs() { return Array.prototype.slice.call(templateFields.querySelectorAll('input[type="file"][name^="region_image_"], input[type="file"][name^="region_video_"], input[type="file"][name^="region_qr_image_"]')); } function getRegionMediaInputs() { return Array.prototype.slice.call(templateFields.querySelectorAll('input[type="file"][name^="region_image_"], input[type="file"][name^="region_video_"], input[type="file"][name^="region_qr_image_"]')); } function hasPendingRegionImageUpload() { return getRegionMediaInputs().some(function (input) { return Boolean(input && input.files && input.files.length); }); } function hasPendingRegionMediaUpload() { return hasPendingRegionImageUpload(); } function getRegionBox(region, canvasWidth, canvasHeight) { return { left: Math.max(0, Math.round(Number(region.x || 0))), top: Math.max(0, Math.round(Number(region.y || 0))), width: Math.max(1, Math.round(Number(region.width || 0) || 1)), height: Math.max(1, Math.round(Number(region.height || 0) || 1)) }; } function getPreviewCanvasDimensions(template) { return { width: Math.max(1, Number(template && template.canvas_size_width ? template.canvas_size_width : 1920)), height: Math.max(1, Number(template && template.canvas_size_height ? template.canvas_size_height : 1080)) }; } function buildPreviewPopupPayload() { var template = getSelectedTemplate(); return { html: slidePreviewCanvas.innerHTML, canvasWidth: currentPreviewCanvasWidth || Math.max(1, Number(template && template.canvas_size_width ? template.canvas_size_width : 1920)), canvasHeight: currentPreviewCanvasHeight || Math.max(1, Number(template && template.canvas_size_height ? template.canvas_size_height : 1080)), backgroundColor: template && template.background_color ? String(template.background_color) : '#111111', backgroundImagePath: template && template.background_image_path ? String(template.background_image_path) : '', backgroundGradient: template && template.background_gradient ? String(template.background_gradient) : '', fontStylesheetHref: fontStylesheetHref }; } function buildPreviewPopupHash() { return encodeURIComponent(JSON.stringify(buildPreviewPopupPayload())); } function buildPreviewPopupUrl() { return '/slides/popup-preview#' + buildPreviewPopupHash(); } function syncPopupPreview() { if (!previewPopupWindow || previewPopupWindow.closed) { previewPopupWindow = null; return; } try { if (previewPopupWindow.location && previewPopupWindow.location.pathname !== '/slides/popup-preview') { return; } } catch (_error) { return; } previewPopupWindow.location.hash = buildPreviewPopupHash(); } function openPreviewPopup() { var popupWidth = 1280; var popupHeight = 900; var left = Math.max(0, Math.round((window.screen.width - popupWidth) / 2)); var top = Math.max(0, Math.round((window.screen.height - popupHeight) / 2)); previewPopupFeatures = [ 'popup=yes', 'toolbar=no', 'location=no', 'menubar=no', 'directories=no', 'status=no', 'scrollbars=yes', 'resizable=yes', 'width=' + popupWidth, 'height=' + popupHeight, 'left=' + left, 'top=' + top ].join(','); previewPopupWindow = window.open('about:blank', 'slide-preview-popup', previewPopupFeatures); if (!previewPopupWindow) { return; } try { previewPopupWindow.location.replace(buildPreviewPopupUrl()); } catch (_error) { previewPopupWindow.location.href = buildPreviewPopupUrl(); } previewPopupWindow.focus(); } function syncSlideEditorSidebar() { sidebarSyncFrame = 0; if (!slideEditorShell || !slideEditorSidebar) { return; } if (window.matchMedia && window.matchMedia('(max-width: 1199.98px)').matches) { slideEditorSidebar.style.transform = ''; return; } var stickyHeader = document.querySelector('.app-header'); var stickyHeaderHeight = stickyHeader ? Math.max(0, Math.round(stickyHeader.getBoundingClientRect().height || stickyHeader.offsetHeight || 0)) : 0; var sidebarTopOffset = sidebarBaseOffset + stickyHeaderHeight + 12; var shellRect = slideEditorShell.getBoundingClientRect(); var sidebarHeight = slideEditorSidebar.offsetHeight; var shellTop = window.scrollY + shellRect.top; var shellBottom = shellTop + slideEditorShell.offsetHeight; var maxTranslate = Math.max(0, shellBottom - shellTop - sidebarHeight - sidebarTopOffset); var desiredTranslate = Math.max(0, Math.min(window.scrollY + sidebarTopOffset - shellTop, maxTranslate)); slideEditorSidebar.style.transform = desiredTranslate ? 'translate3d(0, ' + desiredTranslate + 'px, 0)' : ''; } function requestSidebarSync() { if (sidebarSyncFrame) { return; } sidebarSyncFrame = window.requestAnimationFrame(syncSlideEditorSidebar); } function renderPreview() { var template = getSelectedTemplate(); if (!template) { slidePreviewMeta.textContent = '--'; slidePreviewBackground.style.display = 'none'; slidePreviewEmpty.style.display = 'flex'; slidePreviewEmpty.textContent = 'Select a template to preview the slide.'; slidePreviewCanvas.style.width = ''; slidePreviewCanvas.style.height = ''; slidePreviewOverlay.innerHTML = ''; slidePreviewOverlay.style.width = ''; slidePreviewOverlay.style.height = ''; slidePreviewOverlay.style.transform = ''; slidePreviewStage.style.aspectRatio = '16 / 9'; applyBackdropStyle(slidePreviewStage, '#111111'); return; } var canvasSize = getPreviewCanvasDimensions(template); var canvasWidth = canvasSize.width; var canvasHeight = canvasSize.height; currentPreviewCanvasWidth = canvasWidth; currentPreviewCanvasHeight = canvasHeight; slidePreviewStage.style.aspectRatio = canvasWidth + ' / ' + canvasHeight; applyBackdropStyle(slidePreviewStage, template.background_color || '#111111', template.background_image_path, template.background_gradient); slidePreviewMeta.textContent = canvasWidth + 'x' + canvasHeight; if (!(template.regions || []).length) { slidePreviewEmpty.style.display = 'flex'; slidePreviewEmpty.textContent = 'This template has no regions.'; slidePreviewBackground.style.display = template.background_image_path ? 'block' : 'none'; slidePreviewBackground.src = template.background_image_path || ''; slidePreviewCanvas.style.width = canvasWidth + 'px'; slidePreviewCanvas.style.height = canvasHeight + 'px'; slidePreviewCanvas.style.transform = ''; slidePreviewOverlay.innerHTML = ''; slidePreviewOverlay.style.width = ''; slidePreviewOverlay.style.height = ''; slidePreviewOverlay.style.transform = ''; return; } slidePreviewEmpty.style.display = 'none'; slidePreviewBackground.style.display = template.background_image_path ? 'block' : 'none'; slidePreviewBackground.src = template.background_image_path || ''; var stageRect = slidePreviewStage.getBoundingClientRect(); if (!stageRect.width || !stageRect.height) { if (previewRenderFrame) { cancelAnimationFrame(previewRenderFrame); } previewRenderFrame = requestAnimationFrame(function () { previewRenderFrame = 0; renderPreview(); }); return; } var scale = Math.min(stageRect.width / canvasWidth, stageRect.height / canvasHeight) || 1; slidePreviewCanvas.style.width = canvasWidth + 'px'; slidePreviewCanvas.style.height = canvasHeight + 'px'; slidePreviewCanvas.style.transform = 'translate3d(0, 0, 0) scale(' + scale + ')'; slidePreviewCanvas.style.transformOrigin = 'top left'; var regions = template.regions || []; slidePreviewOverlay.innerHTML = regions.map(function (region) { var box = getRegionBox(region, canvasWidth, canvasHeight); var card = templateFields.querySelector('[data-region-id="' + region.id + '"]'); var typeModule = getRegionTypeModule(region.region_type); var previewContext = buildPreviewRenderContext(region, card); var content = typeModule && typeof typeModule.renderPreview === 'function' ? typeModule.renderPreview(region, previewContext, previewContext) : renderPreviewTextRegion(region, previewContext.value, previewContext.style, scale); if (typeModule && typeof typeModule.renderPreview === 'function' && region.region_type !== 'api' && region.region_type !== 'rss') { content = content || renderPreviewTextRegion(region, previewContext.value, previewContext.style, scale); } var selected = region.region_type === 'image' ? ' slide-preview-image-region' : region.region_type === 'video' ? ' slide-preview-video-region' : region.region_type === 'webpage' ? ' slide-preview-webpage-region' : region.region_type === 'qr-code' ? ' slide-preview-qr-code-region' : region.region_type === 'rtmp' ? ' slide-preview-rtmp-region' : region.region_type === 'rss' ? ' slide-preview-rss-region' : region.region_type === 'api' ? ' slide-preview-api-region' : region.region_type === 'html' ? ' slide-preview-html-region' : ' slide-preview-text-region'; return ''; }).join(''); startPreviewVideoPlayback(slidePreviewOverlay); syncPopupPreview(); } function requestPreviewRender() { if (previewRenderFrame) { return; } previewRenderFrame = window.requestAnimationFrame(function () { previewRenderFrame = 0; renderPreview(); }); } var reduceAspectRatio = slideRegionHelpers.reduceAspectRatio; var getCurrentTextStyle = slideRegionHelpers.getCurrentTextStyle; var getRssFieldList = slideRegionHelpers.getRssFieldList; var updateRssPlaceholderChips = slideRegionHelpers.updateRssPlaceholderChips; var getCurrentRssConfig = slideRegionHelpers.getCurrentRssConfig; var getApiFieldList = slideRegionHelpers.getApiFieldList; var updateApiPlaceholderChips = slideRegionHelpers.updateApiPlaceholderChips; var updateApiSampleDataPanel = slideRegionHelpers.updateApiSampleDataPanel; var updateApiItemNumberLimit = slideRegionHelpers.updateApiItemNumberLimit; var getCurrentApiConfig = slideRegionHelpers.getCurrentApiConfig; var updateRssItemNumberLimit = slideRegionHelpers.updateRssItemNumberLimit; function renderTemplate() { templateSelectorLock.reset(); clearImagePreviewUrls(); slideFormEditorController.destroyEditors(); var template = getTemplateById(templateSelect.value); if (!template) { templateFields.innerHTML = ''; requestPreviewRender(); return; } templateFields.innerHTML = template.regions.length ? template.regions.map(function (region) { return getRegionEditorCardHtml(region) || '