(() => { 'use strict'; const CONTENT_REFRESH_MS = 30_000; const INDOOR_REFRESH_MS = 10_000; const OUTDOOR_REFRESH_MS = 30_000; const VERTICAL_SPEED_PX_PER_SECOND = 7; const HORIZONTAL_SPEED_PX_PER_SECOND = 10; const WEATHER_ICONS = { cloudy: ``, sun: ``, moon: ``, cloudy_day: ``, cloudy_night: ``, rain_shower: ``, rain: ``, thunder: ``, snowy: ``, mist: ``, }; function setText(selector, value) { const element = document.querySelector(selector); if (element) { element.textContent = value ?? ''; } } function updateClock() { const now = new Date(); const clock = document.getElementById('uhrzeit'); if (clock) { clock.textContent = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`; } } function renderAnnouncements(announcements) { const container = document.querySelector('.news-scroll'); if (!container || !Array.isArray(announcements)) { return; } container.replaceChildren(); announcements.forEach((announcement) => { const box = document.createElement('div'); box.className = 'news-box'; const date = document.createElement('div'); date.className = 'news-date'; date.append(document.createTextNode(announcement.date_label || '')); if (announcement.end_label) { date.append(document.createElement('br'), document.createTextNode(announcement.end_label)); } const body = document.createElement('div'); body.append(document.createTextNode(announcement.title || '')); if (announcement.subtitle) { const subtitle = document.createElement('div'); subtitle.className = 'news-subtitle'; subtitle.textContent = announcement.subtitle; body.append(subtitle); } if (announcement.meta) { const meta = document.createElement('div'); meta.className = 'news-subtitle'; const italic = document.createElement('i'); italic.textContent = announcement.meta; meta.append(italic); body.append(meta); } box.append(date, body); container.append(box); }); } function renderPosters(posters) { const container = document.querySelector('.aushang-flex'); if (!container || !Array.isArray(posters)) { return; } container.replaceChildren(); if (posters.length === 0) { const empty = document.createElement('p'); empty.style.textAlign = 'left'; empty.textContent = 'Keine Aushänge vorgeplant.'; container.append(empty); return; } posters.forEach((poster) => { const image = document.createElement('img'); image.src = poster.url; image.alt = poster.alt || 'Aushang'; image.style.height = '95%'; image.addEventListener('load', () => scheduleScrollingSetup(0), { once: true }); container.append(image); }); } function renderMonthImages(images) { if (!Array.isArray(images)) { return; } images.forEach((image) => { const element = document.querySelector(`[data-month-page="${image.page}"]`); if (element && image.url && element.getAttribute('src') !== image.url) { element.src = image.url; } }); } function renderIndoor(indoor) { if (indoor) { setText('#temperature_txt', `${indoor.value}${indoor.unit || '°C'}`); } } function renderOutdoor(outdoor) { const weather = document.getElementById('wetter'); if (!weather || !outdoor) { return; } const icon = WEATHER_ICONS[outdoor.icon] || WEATHER_ICONS.cloudy; weather.replaceChildren(); const iconContainer = document.createElement('div'); iconContainer.innerHTML = icon; const temperature = document.createElement('div'); temperature.className = 'text-outdoor'; temperature.textContent = `${outdoor.temperature}°C`; weather.append(iconContainer, temperature); } function renderContent(data) { setText('[data-dashboard-date]', data.date_label); if (data.service) { setText('.bible', data.service.bibelwort); setText('.verse', data.service.bibelvers); setText('.GB', data.service.eingang_nummer); setText('.GB-title', data.service.eingang_titel); } renderAnnouncements(data.announcements); renderPosters(data.posters); renderMonthImages(data.month_images); scheduleScrollingSetup(); } function startPolling(resource, interval, render) { const refresh = async () => { try { const response = await fetch(`data.php?resource=${encodeURIComponent(resource)}`, { cache: 'no-store' }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); if (data.error) { throw new Error(data.error); } render(data); } catch (error) { console.error(`Dashboard-Aktualisierung (${resource}) fehlgeschlagen:`, error); } finally { window.setTimeout(refresh, interval); } }; refresh(); } function applyAnimation(element, distance, speed, animationName) { element.style.animation = 'none'; if (distance <= 0) { return; } const variable = animationName === 'scroll-h' ? '--scroll-distance-h' : '--scroll-distance-v'; element.style.setProperty(variable, `-${distance}px`); element.style.animationName = animationName; element.style.animationDuration = `${Math.max(1, distance / speed)}s`; element.style.animationTimingFunction = 'linear'; element.style.animationIterationCount = 'infinite'; } function setupScrolling() { const posterContainer = document.querySelector('.aushang-flex'); if (posterContainer) { const images = Array.from(posterContainer.querySelectorAll('img')); const totalWidth = images.reduce((sum, image) => { const styles = window.getComputedStyle(image); return sum + image.offsetWidth + (parseFloat(styles.marginRight) || 0); }, 0); applyAnimation( posterContainer, Math.max(0, totalWidth - posterContainer.offsetWidth), HORIZONTAL_SPEED_PX_PER_SECOND, 'scroll-h' ); } const newsContainer = document.querySelector('.news-container'); const news = document.querySelector('.news-scroll'); if (newsContainer && news) { applyAnimation( news, Math.max(0, news.scrollHeight - newsContainer.offsetHeight), VERTICAL_SPEED_PX_PER_SECOND, 'scroll-v' ); } } let scrollingTimer = null; function scheduleScrollingSetup(delay = 80) { if (scrollingTimer !== null) { window.clearTimeout(scrollingTimer); } scrollingTimer = window.setTimeout(setupScrolling, delay); } updateClock(); window.setInterval(updateClock, 1000); window.addEventListener('load', () => scheduleScrollingSetup(50)); window.addEventListener('resize', () => scheduleScrollingSetup(100)); startPolling('content', CONTENT_REFRESH_MS, renderContent); startPolling('indoor', INDOOR_REFRESH_MS, (data) => renderIndoor(data.indoor)); startPolling('outdoor', OUTDOOR_REFRESH_MS, (data) => renderOutdoor(data.outdoor)); })();