document.addEventListener('DOMContentLoaded', () => { const darkModeToggle = document.getElementById('darkModeToggle'); const body = document.body; const celsiusRadio = document.getElementById('celsius'); const fahrenheitRadio = document.getElementById('fahrenheit'); // Apply the cached theme on page load if (localStorage.getItem('darkMode') === 'enabled') { body.classList.add('dark-mode'); darkModeToggle.checked = true; } darkModeToggle.addEventListener('change', () => { if (darkModeToggle.checked) { body.classList.add('dark-mode'); localStorage.setItem('darkMode', 'enabled'); } else { body.classList.remove('dark-mode'); localStorage.setItem('darkMode', 'disabled'); } }); const weatherDisplay = document.getElementById('weather-display'); const citySearchForm = document.getElementById('city-search-form'); const cityInput = document.getElementById('city-input'); const favoriteLocationsList = document.getElementById('favorite-locations-list'); const searchHistoryContainer = document.getElementById('search-history'); const getUnit = () => localStorage.getItem('unit') || 'metric'; const getSearchHistory = () => JSON.parse(localStorage.getItem('searchHistory')) || []; const saveSearchHistory = (city) => { let history = getSearchHistory(); history = history.filter(item => item.toLowerCase() !== city.toLowerCase()); history.unshift(city); if (history.length > 5) { history.pop(); } localStorage.setItem('searchHistory', JSON.stringify(history)); displaySearchHistory(); }; const displaySearchHistory = () => { const history = getSearchHistory(); if (history.length > 0) { let historyHtml = '
Recent Searches:
'; history.forEach(city => { historyHtml += `${city}`; }); historyHtml += '
'; searchHistoryContainer.innerHTML = historyHtml; } else { searchHistoryContainer.innerHTML = ''; } }; const showLoading = () => { weatherDisplay.innerHTML = '
Loading...

Loading weather...

'; }; const showError = (message) => { weatherDisplay.innerHTML = `
${message}
`; }; const getAqiString = (aqi) => { switch (aqi) { case 1: return 'Good'; case 2: return 'Fair'; case 3: return 'Moderate'; case 4: return 'Poor'; case 5: return 'Very Poor'; default: return 'Unknown'; } }; const showWeather = (data) => { if (!data || !data.name || data.cod === "404") { showError(data.message || 'Could not display weather data.'); return; } const unit = getUnit(); const unitSymbol = unit === 'metric' ? '°C' : '°F'; const iconUrl = `https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`; let favoriteStar = ''; if (isUserLoggedIn) { favoriteStar = ` `; // Empty star } let aqiHtml = ''; if (data.aqi) { aqiHtml = `

Air Quality: ${getAqiString(data.aqi)}

`; } let forecastHtml = ''; if (data.forecast && data.forecast.length > 0) { forecastHtml += '

5-Day Forecast

'; data.forecast.forEach(day => { const dayDate = new Date(day.dt * 1000); const dayName = dayDate.toLocaleDateString('en-US', { weekday: 'short' }); const iconUrl = `https://openweathermap.org/img/wn/${day.icon}.png`; forecastHtml += `

${dayName}

${day.description}

${day.temp.toFixed(1)}${unitSymbol}

`; }); forecastHtml += '
'; } weatherDisplay.innerHTML = `

${data.name}${favoriteStar}

Weather icon

${data.main.temp}${unitSymbol}

${data.weather[0].description}

Humidity: ${data.main.humidity}%

${aqiHtml} ${forecastHtml}
`; initMap(data.coord.lat, data.coord.lon); saveSearchHistory(data.name); }; const initMap = (lat, lon) => { const apiKey = '2350ecafed26b115b1557305d956630d'; const mapContainer = document.getElementById('weather-map'); if (!mapContainer || mapContainer._leaflet_id) { // If map is already initialized, just update its view if (mapContainer._leaflet_id) { mapContainer._leaflet_map.setView([lat, lon], 10); } return; } const map = L.map('weather-map').setView([lat, lon], 10); mapContainer._leaflet_map = map; // Store map instance L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); const tempLayer = L.tileLayer(`https://tile.openweathermap.org/map/temp_new/{z}/{x}/{y}.png?appid=${apiKey}`, { attribution: 'Map data © OpenWeatherMap' }); const cloudsLayer = L.tileLayer(`https://tile.openweathermap.org/map/clouds_new/{z}/{x}/{y}.png?appid=${apiKey}`, { attribution: 'Map data © OpenWeatherMap' }); const precipitationLayer = L.tileLayer(`https://tile.openweathermap.org/map/precipitation_new/{z}/{x}/{y}.png?appid=${apiKey}`, { attribution: 'Map data © OpenWeatherMap' }); const baseMaps = { "OpenStreetMap": L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png') }; const overlayMaps = { "Temperature": tempLayer, "Clouds": cloudsLayer, "Precipitation": precipitationLayer }; L.control.layers(baseMaps, overlayMaps).addTo(map); overlayMaps.Temperature.addTo(map); }; const fetchWeatherByCoords = (lat, lon) => { const unit = getUnit(); showLoading(); fetch(`weather.php?lat=${lat}&lon=${lon}&units=${unit}`) .then(response => response.json()) .then(data => { if (data.error) { showError(data.error); } else { showWeather(data); } }) .catch(() => showError('Failed to fetch weather data.')); }; const fetchWeatherByCity = (city) => { const unit = getUnit(); showLoading(); fetch(`weather.php?city=${encodeURIComponent(city)}&units=${unit}`) .then(response => response.json()) .then(data => { if (data.error || data.cod === "404") { showError(data.error || data.message); } else { showWeather(data); } }) .catch(() => showError('Failed to fetch weather data.')); }; const saveLocation = () => { const weatherCardBody = weatherDisplay.querySelector('.card-body'); if (!weatherCardBody) return; const city = weatherCardBody.dataset.city; const lat = weatherCardBody.dataset.lat; const lon = weatherCardBody.dataset.lon; if (!city || !lat || !lon) { alert('Could not get location data to save.'); return; } fetch('save_location.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ city: city, lat: lat, lon: lon }), }) .then(response => response.json()) .then(data => { if (data.success) { alert('Location saved!'); location.reload(); // Reload to see the updated list } else { alert(data.message || 'Could not save location.'); } }) .catch(() => alert('An error occurred while saving the location.')); }; const deleteLocation = (locationId, listItemElement) => { if (!confirm('Are you sure you want to delete this location?')) return; fetch('delete_location.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ location_id: locationId }), }) .then(response => response.json()) .then(data => { if (data.success) { listItemElement.remove(); alert('Location deleted!'); } else { alert(data.message || 'Could not delete location.'); } }) .catch(() => alert('An error occurred.')); }; const handleSubscription = (locationId, time, subscriptionId, listItem) => { const isSubscribing = !!locationId; const url = isSubscribing ? 'subscribe_weather.php' : 'unsubscribe_weather.php'; const body = isSubscribing ? { location_id: locationId, time: time } : { subscription_id: subscriptionId }; fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) .then(response => response.json()) .then(data => { if (data.success) { alert(data.message); // Dynamically update the UI const controlsDiv = listItem.querySelector('.subscription-controls'); if (isSubscribing) { const newTime = new Date(`1970-01-01T${time}Z`); const formattedTime = newTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', timeZone: 'UTC' }); controlsDiv.innerHTML = ` Subscribed for daily alerts at ${formattedTime}. `; } else { controlsDiv.innerHTML = `
`; } } else { alert(data.message || 'An error occurred.'); } }) .catch(() => alert('A network error occurred.')); }; citySearchForm.addEventListener('submit', (e) => { e.preventDefault(); const city = cityInput.value.trim(); if (city) { fetchWeatherByCity(city); } }); weatherDisplay.addEventListener('click', (e) => { if (e.target.id === 'add-to-favorites') { saveLocation(); } }); if (favoriteLocationsList) { favoriteLocationsList.addEventListener('click', (e) => { e.preventDefault(); const listItem = e.target.closest('li'); if (e.target.classList.contains('subscribe-btn')) { const locationId = listItem.dataset.locationId; const timeInput = listItem.querySelector('.alert-time-input'); handleSubscription(locationId, timeInput.value, null, listItem); } else if (e.target.classList.contains('unsubscribe-btn')) { const subscriptionId = e.target.dataset.id; handleSubscription(null, null, subscriptionId, listItem); } }); } if (searchHistoryContainer) { searchHistoryContainer.addEventListener('click', (e) => { e.preventDefault(); if (e.target.classList.contains('history-link')) { const city = e.target.dataset.city; fetchWeatherByCity(city); } }); } const getHistoryBtn = document.getElementById('get-history-btn'); if (getHistoryBtn) { getHistoryBtn.addEventListener('click', () => { const city = document.querySelector('#weather-display .card-title')?.textContent; const date = document.getElementById('history-date').value; if (!city) { alert('Please search for a city first.'); return; } if (!date) { alert('Please select a date.'); return; } fetch(`get_history.php?city=${encodeURIComponent(city)}&date=${date}`) .then(response => response.json()) .then(data => { const historyDisplay = document.getElementById('history-display'); if (data.error) { historyDisplay.innerHTML = `
${data.error}
`; } else if (data.length === 0) { historyDisplay.innerHTML = `
No historical data found for this date.
`; } else { let table = ''; data.forEach(record => { const recordDate = new Date(record.timestamp + ' UTC'); table += ``; }); table += '
TimeTempHumidityDescription
${recordDate.toLocaleTimeString()} ${record.temperature}°C ${record.humidity}% ${record.description}
'; historyDisplay.innerHTML = table; } }) .catch(() => { const historyDisplay = document.getElementById('history-display'); historyDisplay.innerHTML = `
Failed to fetch historical data.
`; }); }); } // Set initial unit toggle state if (getUnit() === 'imperial') { fahrenheitRadio.checked = true; } else { celsiusRadio.checked = true; } // Add event listeners for unit switching [celsiusRadio, fahrenheitRadio].forEach(radio => { radio.addEventListener('change', () => { const selectedUnit = fahrenheitRadio.checked ? 'imperial' : 'metric'; localStorage.setItem('unit', selectedUnit); const currentCity = document.querySelector('#weather-display .card-title')?.textContent; if (currentCity) { fetchWeatherByCity(currentCity); } }); }); // Initial weather fetch if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( position => { fetchWeatherByCoords(position.coords.latitude, position.coords.longitude); }, () => { displaySearchHistory(); // Don't show an error, just let the user search. } ); } else { displaySearchHistory(); } // Initial display of search history displaySearchHistory(); });