(function () { var DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; function formatDays(daysValue) { var days = []; if (Array.isArray(daysValue)) { days = daysValue; } else if (daysValue) { try { var parsedDays = JSON.parse(daysValue); days = Array.isArray(parsedDays) ? parsedDays : []; } catch (_error) { days = []; } } return days .map(function (day) { return DAY_NAMES[Number(day)] || ''; }) .filter(Boolean) .join(', '); } function initPlaylistScheduleModal() { var dialog = document.getElementById('slide-schedule-dialog'); var frame = document.getElementById('slide-schedule-frame'); var closeButton = document.getElementById('slide-schedule-close'); var triggers = document.querySelectorAll('[data-schedule-config]'); if (!dialog || !frame || !closeButton || !triggers.length) { return; } function openScheduleModal(url) { frame.src = url; if (typeof dialog.showModal === 'function') { dialog.showModal(); } else { dialog.setAttribute('open', 'open'); } } function resizeScheduleFrame() { try { if (!frame.contentWindow || !frame.contentWindow.document) { return; } var doc = frame.contentWindow.document; var height = Math.max( doc.body.scrollHeight, doc.documentElement.scrollHeight, doc.body.offsetHeight, doc.documentElement.offsetHeight ); frame.style.height = height + 'px'; } catch (_error) { frame.style.height = '70vh'; } } function closeScheduleModal() { frame.src = 'about:blank'; if (typeof dialog.close === 'function') { dialog.close(); } else { dialog.removeAttribute('open'); } } window.openScheduleModal = openScheduleModal; window.resizeScheduleFrame = resizeScheduleFrame; window.closeScheduleModal = closeScheduleModal; Array.prototype.forEach.call(triggers, function (trigger) { trigger.addEventListener('click', function () { var url = trigger.getAttribute('data-schedule-config'); var rowKey = trigger.getAttribute('data-schedule-config-row') || ''; if (rowKey && url.indexOf('row_key=') === -1) { url += (url.indexOf('?') === -1 ? '?' : '&') + 'row_key=' + encodeURIComponent(rowKey); } openScheduleModal(url); }); }); frame.addEventListener('load', resizeScheduleFrame); closeButton.addEventListener('click', closeScheduleModal); } function scheduleSummary(values) { var mode = String(values.schedule_mode || 'always'); if (mode === 'dates') { if (values.schedule_start_datetime && values.schedule_end_datetime) { return 'Dates: ' + values.schedule_start_datetime.replace('T', ' ') + ' to ' + values.schedule_end_datetime.replace('T', ' '); } return 'Dates: not set'; } if (mode === 'times') { var days = formatDays(values.schedule_days_json); if (days && values.schedule_start_time && values.schedule_end_time) { return 'Times: ' + days + ' ' + values.schedule_start_time.slice(0, 5) + '-' + values.schedule_end_time.slice(0, 5); } return 'Times: not set'; } return 'Always visible'; } function initPlaylistScheduleForm() { var form = document.querySelector('form[action*="/config"]'); var select = document.getElementById('schedule-mode-select'); if (!form || !select) { return; } var datesPanel = document.getElementById('schedule-dates-panel'); var timesPanel = document.getElementById('schedule-times-panel'); var startDateInput = form.querySelector('[name="schedule_start_datetime"]'); var endDateInput = form.querySelector('[name="schedule_end_datetime"]'); var startTimeInput = form.querySelector('[name="schedule_start_time"]'); var endTimeInput = form.querySelector('[name="schedule_end_time"]'); var rowKeyInput = form.querySelector('[name="row_key"]'); var dayCheckboxes = form.querySelectorAll('[name="schedule_days"]'); function notifyParentResize() { if (window.parent && typeof window.parent.resizeScheduleFrame === 'function') { window.parent.resizeScheduleFrame(); } } function updateVisibility() { var mode = select.value; datesPanel.style.display = mode === 'dates' ? '' : 'none'; timesPanel.style.display = mode === 'times' ? '' : 'none'; window.requestAnimationFrame(function () { notifyParentResize(); }); } select.addEventListener('change', updateVisibility); updateVisibility(); form.addEventListener('submit', function (event) { event.preventDefault(); var mode = select.value; var datesIncomplete = mode === 'dates' && (!startDateInput.value || !endDateInput.value); var timesIncomplete = mode === 'times' && (!startTimeInput.value || !endTimeInput.value || !Array.prototype.some.call(dayCheckboxes, function (checkbox) { return checkbox.checked; })); if (mode === 'dates' && !datesIncomplete) { if (new Date(endDateInput.value) < new Date(startDateInput.value)) { alert('End datetime must be after start datetime.'); return; } } if (mode === 'times' && !timesIncomplete) { if (endTimeInput.value < startTimeInput.value) { alert('End time must be after start time.'); return; } } if (datesIncomplete || timesIncomplete) { mode = 'always'; select.value = 'always'; startDateInput.value = ''; endDateInput.value = ''; startTimeInput.value = ''; endTimeInput.value = ''; Array.prototype.forEach.call(dayCheckboxes, function (checkbox) { checkbox.checked = false; }); updateVisibility(); } var selectedDays = []; Array.prototype.forEach.call(dayCheckboxes, function (checkbox) { if (checkbox.checked) { selectedDays.push(Number(checkbox.value)); } }); var values = { row_key: String(rowKeyInput && rowKeyInput.value ? rowKeyInput.value : ''), schedule_mode: mode, schedule_start_datetime: startDateInput.value || '', schedule_end_datetime: endDateInput.value || '', schedule_start_time: startTimeInput.value || '', schedule_end_time: endTimeInput.value || '', schedule_days_json: JSON.stringify(selectedDays.sort()), summary: '' }; values.summary = scheduleSummary(values); if (window.parent && typeof window.parent.applyPlaylistScheduleConfig === 'function') { window.parent.applyPlaylistScheduleConfig(values); if (typeof window.parent.closeScheduleModal === 'function') { window.parent.closeScheduleModal(); } return; } var payload = new URLSearchParams(new FormData(form)); fetch(form.action, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: payload.toString(), credentials: 'same-origin' }).then(function (response) { if (!response.ok) { return response.text().then(function (text) { throw new Error(text || 'Unable to save schedule.'); }); } if (window.parent && typeof window.parent.closeScheduleModal === 'function') { window.parent.closeScheduleModal(); } }).catch(function (error) { alert(error.message || 'Unable to save schedule.'); }); }); } function initPlaylistEditStaging() { var tbody = document.getElementById('playlist-items-body'); var form = document.getElementById('playlist-edit-form'); var addSection = document.getElementById('playlist-add-section'); var addSlideForm = document.getElementById('playlist-add-slide-form'); var addSlideButton = document.getElementById('playlist-add-slide-button'); var addSlideSelect = document.getElementById('playlist-add-slide-select'); var addDurationInput = document.getElementById('playlist-add-duration'); var addSlideEmpty = document.getElementById('playlist-add-slide-empty'); if (!tbody || !form) { return; } function getRows() { return Array.prototype.slice.call(tbody.querySelectorAll('tr[data-playlist-slide-row]')); } function findRowByKey(rowKey) { var rows = getRows(); for (var i = 0; i < rows.length; i += 1) { if (rows[i].getAttribute('data-row-key') === rowKey) { return rows[i]; } } return null; } function updateEmptyState() { var rows = getRows(); var placeholder = tbody.querySelector('.playlist-empty-row'); if (rows.length) { if (placeholder) { placeholder.remove(); } return; } if (!placeholder) { tbody.innerHTML = 'No slides assigned yet.'; } } function scheduleSummaryForRow(row) { var modeInput = row.querySelector('[name="schedule_mode[]"]'); var startDatetime = row.querySelector('[name="schedule_start_datetime[]"]'); var endDatetime = row.querySelector('[name="schedule_end_datetime[]"]'); var startTime = row.querySelector('[name="schedule_start_time[]"]'); var endTime = row.querySelector('[name="schedule_end_time[]"]'); var daysJson = row.querySelector('[name="schedule_days_json[]"]'); var mode = String(modeInput ? modeInput.value : 'always'); var days = formatDays(daysJson && daysJson.value ? daysJson.value : '[]'); if (mode === 'dates') { if (startDatetime && endDatetime && startDatetime.value && endDatetime.value) { return 'Dates: ' + startDatetime.value.replace('T', ' ') + ' to ' + endDatetime.value.replace('T', ' '); } return 'Dates: not set'; } if (mode === 'times') { if (days && startTime && endTime && startTime.value && endTime.value) { return 'Times: ' + days + ' ' + startTime.value.slice(0, 5) + '-' + endTime.value.slice(0, 5); } return 'Times: not set'; } return 'Always visible'; } function syncAddSlideOptions() { if (!addSlideSelect) { return; } var activeSlideIds = {}; var activeCanvasSignatures = {}; getRows().forEach(function (row) { var slideId = String(row.getAttribute('data-slide-id') || ''); var canvasSignature = String(row.getAttribute('data-canvas-signature') || ''); if (slideId) { activeSlideIds[slideId] = true; } if (canvasSignature) { activeCanvasSignatures[canvasSignature] = true; } }); var allowedCanvasSignature = ''; var canvasKeys = Object.keys(activeCanvasSignatures); if (canvasKeys.length === 1) { allowedCanvasSignature = canvasKeys[0]; } Array.prototype.forEach.call(addSlideSelect.options, function (option) { if (!option.value) { return; } var optionCanvasSignature = String(option.getAttribute('data-canvas-signature') || ''); var isActive = Boolean(activeSlideIds[String(option.value)]); var isCanvasMismatch = Boolean(allowedCanvasSignature && optionCanvasSignature && optionCanvasSignature !== allowedCanvasSignature); option.disabled = isActive || isCanvasMismatch; option.hidden = isActive || isCanvasMismatch; }); if ( addSlideSelect.options[addSlideSelect.selectedIndex] && (addSlideSelect.options[addSlideSelect.selectedIndex].disabled || addSlideSelect.options[addSlideSelect.selectedIndex].hidden) ) { for (var i = 0; i < addSlideSelect.options.length; i += 1) { if (!addSlideSelect.options[i].disabled && !addSlideSelect.options[i].hidden) { addSlideSelect.selectedIndex = i; break; } } } var availableCount = 0; Array.prototype.forEach.call(addSlideSelect.options, function (option) { if (!option.value) { return; } if (!option.disabled && !option.hidden) { availableCount += 1; } }); if (addSlideButton) { addSlideButton.disabled = availableCount === 0; } if (addSlideSelect) { addSlideSelect.disabled = availableCount === 0; } if (addDurationInput) { addDurationInput.disabled = availableCount === 0; } if (addSlideEmpty) { addSlideEmpty.style.display = availableCount === 0 ? '' : 'none'; } if (addSection && addSlideForm && addSlideSelect && addSlideButton) { var controlsVisible = availableCount > 0; addSlideForm.style.display = controlsVisible ? '' : 'none'; } } function updateRowOrder() { var rows = getRows(); rows.forEach(function (row, index) { var orderCell = row.querySelector('.playlist-order-cell'); var moveUp = row.querySelector('[data-playlist-move="up"]'); var moveDown = row.querySelector('[data-playlist-move="down"]'); if (orderCell) { orderCell.textContent = String(index + 1); } if (moveUp) { moveUp.disabled = index === 0; } if (moveDown) { moveDown.disabled = index === rows.length - 1; } }); syncAddSlideOptions(); updateEmptyState(); } function setRowSchedule(row, values) { var modeInput = row.querySelector('[name="schedule_mode[]"]'); var startDatetime = row.querySelector('[name="schedule_start_datetime[]"]'); var endDatetime = row.querySelector('[name="schedule_end_datetime[]"]'); var startTime = row.querySelector('[name="schedule_start_time[]"]'); var endTime = row.querySelector('[name="schedule_end_time[]"]'); var daysJson = row.querySelector('[name="schedule_days_json[]"]'); var summary = row.querySelector('.playlist-schedule-summary'); if (modeInput) { modeInput.value = values.schedule_mode || 'always'; } if (startDatetime) { startDatetime.value = values.schedule_start_datetime || ''; } if (endDatetime) { endDatetime.value = values.schedule_end_datetime || ''; } if (startTime) { startTime.value = values.schedule_start_time || ''; } if (endTime) { endTime.value = values.schedule_end_time || ''; } if (daysJson) { daysJson.value = values.schedule_days_json || '[]'; } if (summary) { summary.textContent = values.summary || scheduleSummaryForRow(row); } } function createRow(values) { var row = document.createElement('tr'); var rowKey = values.row_key || ('new-' + Date.now() + '-' + Math.random().toString(36).slice(2)); row.setAttribute('data-playlist-slide-row', ''); row.setAttribute('data-row-key', rowKey); row.setAttribute('data-slide-id', String(values.slide_id)); row.setAttribute('data-canvas-signature', String(values.canvas_signature || '')); row.innerHTML = '' + '' + '' + values.title + '' + '' + '' + '
' + values.summary + '
' + '' + '' + '' + '' + '' + '' + '' + '
' + '' + '' + '' + '' + '
'; return row; } tbody.addEventListener('click', function (event) { var moveButton = event.target.closest('[data-playlist-move]'); var removeButton = event.target.closest('[data-playlist-remove-row]'); var scheduleButton = event.target.closest('[data-schedule-config]'); var row = event.target.closest('tr[data-playlist-slide-row]'); if (!row) { return; } if (moveButton) { event.preventDefault(); var direction = moveButton.getAttribute('data-playlist-move'); if (direction === 'up' && row.previousElementSibling && row.previousElementSibling.matches('[data-playlist-slide-row]')) { tbody.insertBefore(row, row.previousElementSibling); } else if (direction === 'down' && row.nextElementSibling && row.nextElementSibling.matches('[data-playlist-slide-row]')) { tbody.insertBefore(row.nextElementSibling, row); } updateRowOrder(); return; } if (removeButton) { event.preventDefault(); row.remove(); updateRowOrder(); return; } if (scheduleButton) { event.preventDefault(); if (typeof window.openScheduleModal === 'function') { window.openScheduleModal(scheduleButton.getAttribute('data-schedule-config') + '?row_key=' + encodeURIComponent(row.getAttribute('data-row-key') || '')); } } }); if (addSlideButton && addSlideSelect && addDurationInput) { addSlideButton.addEventListener('click', function () { var selectedOption = addSlideSelect.options[addSlideSelect.selectedIndex]; if (!selectedOption || selectedOption.disabled) { return; } var placeholder = tbody.querySelector('.playlist-empty-row'); if (placeholder) { placeholder.remove(); } var row = createRow({ row_key: 'new-' + Date.now(), slide_id: String(selectedOption.value), canvas_signature: String(selectedOption.getAttribute('data-canvas-signature') || ''), title: selectedOption.textContent || 'Slide', duration_seconds: Math.max(1, Number(addDurationInput.value || 10)), schedule_mode: 'always', schedule_start_datetime: '', schedule_end_datetime: '', schedule_start_time: '', schedule_end_time: '', schedule_days_json: '[]', summary: 'Always visible' }); tbody.appendChild(row); updateRowOrder(); }); } window.applyPlaylistScheduleConfig = function (values) { var row = findRowByKey(String(values.row_key || '')); if (!row) { return; } setRowSchedule(row, values); }; updateRowOrder(); } initPlaylistScheduleModal(); initPlaylistScheduleForm(); initPlaylistEditStaging(); }());