const locationInput = document.querySelector('#location-input'); const latitudeInput = document.querySelector('#latitude-input'); const longitudeInput = document.querySelector('#longitude-input'); const options = document.querySelector('#location-options'); let locationTimer; if (locationInput) { locationInput.addEventListener('input', () => { latitudeInput.value = ''; longitudeInput.value = ''; clearTimeout(locationTimer); const query = locationInput.value.trim(); if (query.length < 3) { options.hidden = true; return; } locationTimer = setTimeout(async () => { try { const response = await fetch(`/api/locations?q=${encodeURIComponent(query)}`); const places = await response.json(); options.replaceChildren(); places.forEach(place => { const button = document.createElement('button'); button.type = 'button'; button.textContent = place.label; button.addEventListener('click', () => { locationInput.value = place.label; latitudeInput.value = place.latitude; longitudeInput.value = place.longitude; options.hidden = true; }); options.append(button); }); if (places.length === 0) { const message = document.createElement('div'); message.className = 'location-message'; message.textContent = 'No geographic locations found. Try a suburb, town, or postcode.'; options.append(message); } options.hidden = false; } catch (_) { options.hidden = true; } }, 300); }); document.addEventListener('click', event => { if (!event.target.closest('.location-field')) options.hidden = true; }); } const tagToggle = document.querySelector('#tag-toggle'); const tagOptions = document.querySelector('#tag-options'); if (tagToggle && tagOptions) { tagToggle.addEventListener('click', () => { tagOptions.hidden = !tagOptions.hidden; }); tagOptions.addEventListener('change', () => { const count = tagOptions.querySelectorAll('input:checked').length; tagToggle.textContent = count ? `${count} selected` : 'All types'; }); document.addEventListener('click', event => { if (!event.target.closest('.tag-field')) tagOptions.hidden = true; }); } const tabs = document.querySelectorAll('.tab'); let map; tabs.forEach(tab => tab.addEventListener('click', () => { tabs.forEach(item => item.classList.toggle('active', item === tab)); document.querySelectorAll('.view-panel').forEach(panel => panel.classList.toggle('active', panel.id === tab.dataset.view)); if (tab.dataset.view === 'map-view') initialiseMap(); })); const eventRows = [...document.querySelectorAll('[data-event-start]')]; const visibleCount = document.querySelector('#visible-event-count'); const clearDay = document.querySelector('#clear-day'); const dayEmpty = document.querySelector('#day-empty'); const rangeStatus = document.querySelector('#range-status'); let rangeStart = ''; let rangeEnd = ''; function formatDay(dayKey) { return new Date(`${dayKey}T00:00:00`).toLocaleDateString(undefined, { day: 'numeric', month: 'short' }); } function filterToRange(startDay, endDay = startDay) { let count = 0; eventRows.forEach(row => { row.hidden = Boolean(startDay) && (row.dataset.eventStart > endDay || row.dataset.eventEnd < startDay); if (!row.hidden) count += 1; }); document.querySelectorAll('.calendar-day').forEach(day => { const inRange = Boolean(startDay) && day.dataset.day >= startDay && day.dataset.day <= endDay; day.classList.toggle('selected', inRange); day.classList.toggle('range-start', day.dataset.day === startDay); day.classList.toggle('range-end', day.dataset.day === endDay && endDay !== startDay); }); if (visibleCount) visibleCount.textContent = count; if (clearDay) clearDay.hidden = !startDay; if (dayEmpty) dayEmpty.hidden = count !== 0; } document.querySelectorAll('.calendar-day').forEach(day => day.addEventListener('click', event => { event.preventDefault(); const clicked = day.dataset.day; if (!rangeStart || rangeEnd) { rangeStart = clicked; rangeEnd = ''; filterToRange(rangeStart); if (rangeStatus) rangeStatus.textContent = `${formatDay(rangeStart)} selected · choose an end date`; } else { rangeEnd = clicked; if (rangeEnd < rangeStart) [rangeStart, rangeEnd] = [rangeEnd, rangeStart]; filterToRange(rangeStart, rangeEnd); if (rangeStatus) rangeStatus.textContent = `${formatDay(rangeStart)} – ${formatDay(rangeEnd)}`; document.querySelector('#event-list')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); } })); if (clearDay) clearDay.addEventListener('click', event => { event.preventDefault(); rangeStart = ''; rangeEnd = ''; filterToRange(''); if (rangeStatus) rangeStatus.textContent = 'Select a start date'; }); function initialiseMap() { if (map || !window.maplibregl) return; const events = JSON.parse(document.querySelector('#map-events').textContent); const center = events.length ? [events[0].longitude, events[0].latitude] : [150.8931, -34.4278]; map = new maplibregl.Map({ container: 'events-map', style: { version: 8, sources: { osm: { type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], tileSize: 256, attribution: '© OpenStreetMap contributors' } }, layers: [{ id: 'osm', type: 'raster', source: 'osm' }] }, center, zoom: 12 }); map.addControl(new maplibregl.NavigationControl(), 'top-right'); const bounds = new maplibregl.LngLatBounds(); events.forEach(event => { const popup = new maplibregl.Popup({ offset: 20 }).setHTML(`${escapeHtml(event.title)}
${escapeHtml(event.venue)}
View event`); new maplibregl.Marker({ color: '#dd5b32' }).setLngLat([event.longitude, event.latitude]).setPopup(popup).addTo(map); bounds.extend([event.longitude, event.latitude]); }); if (events.length > 1) map.fitBounds(bounds, { padding: 60, maxZoom: 14 }); } function escapeHtml(value) { const node = document.createElement('div'); node.textContent = value; return node.innerHTML; }