"use strict"; /** * Entry point for the night photography data visualization app. * Coordinates fetching data, initializing chart & gallery, and handling filters. */ (async () => { window.addEventListener('load', initApp); /** * Initializes the application by fetching data and rendering UI components. * @returns {Promise} */ async function initApp() { const mainContainer = document.querySelector('main'); if (!mainContainer) return; try { const response = await fetch('/data'); if (!response.ok) throw new Error(`HTTP Error ${response.status}`); const data = await response.json(); const { temperatures = [], images = [] } = data; renderSummary(temperatures, images); renderChart(temperatures); renderGallery(images); const filterControls = document.querySelector('.filter-controls'); if (filterControls) { filterControls.addEventListener('input', handleFilterChange.bind(null, temperatures, images)); } } catch (err) { console.error('App initialization failed:', err); showError(mainContainer, 'Fehler beim Laden der Daten.'); } } /** * Handles changes in filter controls. * @param {Array} temperatures * @param {Array} images * @param {Event} evt */ function handleFilterChange(temperatures, images, evt) { const filterContainer = evt.currentTarget; if (!filterContainer) return; const timeRange = filterContainer.querySelector('[name="timeRange"]')?.value || ''; const minTemp = parseFloat(filterContainer.querySelector('[name="minTemp"]')?.value) || null; const maxTemp = parseFloat(filterContainer.querySelector('[name="maxTemp"]')?.value) || null; const filteredTemps = temperatures.filter(t => { const valueOk = (!minTemp || t.value >= minTemp) && (!maxTemp || t.value <= maxTemp); return valueOk; }); const filteredImages = images.filter(i => { const relatedTemp = temperatures.find(t => t.timestamp === i.timestamp); if (!relatedTemp) return true; return (!minTemp || relatedTemp.value >= minTemp) && (!maxTemp || relatedTemp.value <= maxTemp); }); renderChart(filteredTemps); renderGallery(filteredImages); renderSummary(filteredTemps, filteredImages); } /** * Renders a simple line chart (placeholder logic – to be replaced by visualization module if available). * @param {Array<{timestamp:string,value:number}>} temps */ function renderChart(temps) { const chart = document.querySelector('#temperature-chart'); if (!chart) return; chart.innerHTML = ''; const avg = temps.reduce((acc, t) => acc + t.value, 0) / (temps.length || 1); const list = document.createElement('ul'); list.className = 'chart__list'; temps.forEach(t => { const li = document.createElement('li'); li.textContent = `${new Date(t.timestamp).toLocaleTimeString()} – ${t.value.toFixed(1)}°C`; list.appendChild(li); }); const avgDisplay = document.createElement('p'); avgDisplay.className = 'chart__avg'; avgDisplay.textContent = `Durchschnitt: ${avg.toFixed(1)}°C`; chart.appendChild(list); chart.appendChild(avgDisplay); } /** * Renders an image gallery. * @param {Array<{filename:string,timestamp:string,metadata:object}>} images */ function renderGallery(images) { const gallery = document.querySelector('#image-gallery'); if (!gallery) return; gallery.innerHTML = ''; images.forEach(img => { const imgEl = document.createElement('div'); imgEl.className = 'gallery__image-placeholder'; imgEl.setAttribute('aria-label', `Bild aufgenommen um ${new Date(img.timestamp).toLocaleString()}`); imgEl.textContent = `[Bild: ${img.filename}]`; gallery.appendChild(imgEl); }); const count = document.createElement('p'); count.className = 'gallery__count'; count.textContent = `Anzahl Bilder: ${images.length}`; gallery.appendChild(count); } /** * Render a quick summary of available data. * @param {Array} temps * @param {Array} images */ function renderSummary(temps, images) { const summary = document.querySelector('#data-summary'); if (!summary) return; const avgTemp = temps.reduce((acc, t) => acc + t.value, 0) / (temps.length || 1); summary.textContent = `Durchschnittstemperatur: ${avgTemp.toFixed(1)}°C | Bilder: ${images.length}`; } /** * Shows an error message in a container. * @param {HTMLElement} container * @param {string} msg */ function showError(container, msg) { container.innerHTML = ''; const p = document.createElement('p'); p.className = 'error'; p.textContent = msg; container.appendChild(p); } })();