dodex-vertx
Version:
A java asynchronous server for Dodex and Dodex-mess
473 lines (421 loc) • 18.1 kB
JavaScript
// Weather App
class WeatherService {
constructor(apiKey) {
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./service-worker.js');
});
}
this.apiKey = apiKey;
this.generalData = 'https://api.openweathermap.org/data/2.5/weather';
this.forecastData = 'https://api.openweathermap.org/data/2.5/forecast';
this.units = 'imperial';
this.geolocationButton = document.querySelector('#geolocation-btn');
this.cities;
}
async byName(location) {
return await axios.get(
`${this.generalData}?q=${location}&appid=${this.apiKey}&units=${this.units}`
);
}
async fetchDailyForecast(coordinates) {
const response = await axios.get(
`${this.forecastData}?lat=${coordinates.lat}&lon=${coordinates.lon}&exclude=minutely,hourly,alerts&appid=${this.apiKey}&units=${this.units}`
);
dailyWeather().displayForecast(response);
}
async byGeolocation(position) {
const { latitude: lat, longitude: lon } = position.coords;
const response = await axios.get(
`${this.generalData}?lat=${lat}&lon=${lon}&appid=${this.apiKey}&units=${this.units}`
);
selectedLocationWeather().displayCurrentTemperature(response);
document.querySelector("#search-input").value = "";
}
displaySelectedLocationWeather(location) {
this.byName(location).then(response =>
selectedLocationWeather().displayCurrentTemperature(response)
);
}
initializeGeolocation() {
this.geolocationButton.addEventListener('click', () => {
const searchInput = document.querySelector("#search-input")
new Promise((resolve, reject) => {
searchInput.value = "Fetching: "
const id = setInterval(function () {
if (searchInput.value === "") {
clearInterval(id);
resolve(true);
} else {
searchInput.value = searchInput.value+".";
}
}, 500);
}).then(result => {
//
});
navigator.geolocation.getCurrentPosition(this.byGeolocation.bind(this));
});
}
getSavedLocation() {
const userLocation = localStorage.getItem('location');
if (userLocation) {
this.displaySelectedLocationWeather(userLocation);
} else {
this.displaySelectedLocationWeather('San Francisco');
}
}
async renderIcons(location, dataId, dataIcon, imgEl) {
const response = await axios.get('./json/icons.json');
const customIcons = response.data;
const iconMatch = customIcons.find(icon => icon.id === dataId && icon.icon === dataIcon);
if (iconMatch) {
const icon = location.querySelector(imgEl);
icon.setAttribute('src', iconMatch.src);
icon.setAttribute('alt', iconMatch.alt);
}
}
}
const themeManager = () => {
return {
body: document.querySelector('body'),
themeToggle: document.querySelector('#flexSwitchCheckChecked'),
initialize: function () {
this.themeToggle.addEventListener('click', this.toggleTheme.bind(this));
this.getSavedTheme();
},
toggleTheme: function () {
this.body.classList.toggle('dark');
localStorage.setItem('theme', this.body.classList.contains('dark') ? 'dark' : 'light');
},
getSavedTheme: function () {
const userTheme = localStorage.getItem('theme');
if (userTheme === 'dark') {
this.themeToggle.click();
}
},
}
};
const searchManager = () => {
return {
suggestionsList: document.querySelector('.search-suggestions'),
searchBtn: document.querySelector('.search-form'),
searchInput: document.querySelector('#search-input'),
initialize: function () {
this.searchBtn.addEventListener('submit', this.submitCity.bind(this));
this.searchInput.addEventListener('keyup', this.typeInput.bind(this));
document.addEventListener('click', this.clickOutsideInput.bind(this));
},
submitCity: function (event) {
event.preventDefault();
const searchInputValue = this.searchInput.value;
if (searchInputValue) {
weatherService.displaySelectedLocationWeather(searchInputValue);
}
},
typeInput: function () {
const inputText = this.searchInput.value.trim();
// this.clearSuggestions();
//
// if (inputText) {
// let suggestions = weatherService.cities
// .filter(city => city.name.toLowerCase().startsWith(inputText.toLowerCase()))
// .slice(0, 4);
// this.showSuggestions(suggestions);
// }
},
clickOutsideInput: function (event) {
if (!this.suggestionsList.contains(event.target)) {
this.clearSuggestions();
}
},
showSuggestions: function (suggestions) {
suggestions.forEach(city => {
const li = document.createElement('li');
li.textContent = city.name;
this.suggestionsList.appendChild(li);
this.suggestionsList.style.opacity = '1';
li.addEventListener('click', () => {
li.textContent;
weatherService.displaySelectedLocationWeather(li.textContent);
this.clearSuggestions();
});
});
},
clearSuggestions: function () {
this.suggestionsList.innerHTML = '';
this.suggestionsList.style.opacity = '0';
},
}
};
const timeManager = () => {
return {
// Get Local Date Object for Searched Cities
convertUnixToTimezone: function (unix, timezone) {
const date = new Date();
const timestamp = unix;
const offset = date.getTimezoneOffset() * 60000;
const utc = timestamp + offset;
const convertedDateObject = new Date(utc + 1000 * timezone);
return convertedDateObject;
},
// Format Local Date Objects to Strings
formatDate: function (object, options, method) {
if (method === 'toLocaleString') {
return object.toLocaleString([], options);
}
if (method === 'toLocaleDateString') {
return object.toLocaleDateString([], options);
}
},
// Format Daily Forecast Unix Timestamps
formatDay: function (unix) {
const date = new Date(unix * 1000);
const day = date.getDay();
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return days[day];
},
// Display Local Date
printLocalDateString: function (data, dateObject) {
const localDateString = this.formatDate(
this.convertUnixToTimezone(dateObject, data.timezone),
{
weekday: 'long',
month: 'long',
day: 'numeric',
},
'toLocaleDateString'
);
const localTimeString = this.convertUnixToTimezone(dateObject, data.timezone).toLocaleString(
[],
{
hour: '2-digit',
minute: '2-digit',
hour12: true,
}
);
const todaysDate = document.querySelector('#today');
todaysDate.innerHTML = `${localDateString} at ${localTimeString}`;
},
// Handle sunset/sunrise
displaySunsetSunriseTime: function (data, localDateObject, sunriseTime, sunsetTime) {
const sunrise = document.querySelector('#sunrise-time');
const sunset = document.querySelector('#sunset-time');
sunrise.innerHTML = this.formatDate(
this.convertUnixToTimezone(sunriseTime, data.timezone),
{
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
'toLocaleString'
);
sunset.innerHTML = this.formatDate(
this.convertUnixToTimezone(sunsetTime, data.timezone),
{
hour: '2-digit',
minute: '2-digit',
hour12: true,
},
'toLocaleString'
);
this.changeSceneryImage(data, localDateObject, sunriseTime, sunsetTime);
},
changeSceneryImage: function (data, localDateObject, sunriseTime, sunsetTime) {
const scenery = document.querySelector('#scenery');
const sunriseHour = this.convertUnixToTimezone(sunriseTime, data.timezone).getHours();
const sunsetHour = this.convertUnixToTimezone(sunsetTime, data.timezone).getHours();
if (
this.convertUnixToTimezone(localDateObject, data.timezone).getHours() < sunriseHour ||
this.convertUnixToTimezone(localDateObject, data.timezone).getHours() >= sunsetHour
) {
scenery.src = './assets/night-landscape.png';
scenery.alt = 'Night landscape';
} else {
scenery.src = './assets/day-landscape.png';
scenery.alt = 'Day landscape';
}
},
}
};
const selectedLocationWeather = () => {
return {
locationHeading: document.querySelector('#location'),
allTemps: document.querySelectorAll('#temp-now, .temps, .faded-temp'),
fahrenheit: document.querySelectorAll('.fahrenheit'),
celsius: document.querySelector('.celsius'),
windUnit: document.querySelector('#wind-unit'),
currentTemp: document.querySelector('#temp-now'),
highTemp: document.querySelector('#high-temp'),
lowTemp: document.querySelector('#low-temp'),
feelsLikeTemp: document.querySelector('#feels-like'),
tempDescription: document.querySelector('#description-temp'),
wind: document.querySelector('#wind'),
humidity: document.querySelector('#humidity'),
visibility: document.querySelector('#visibility'),
clouds: document.querySelector('#clouds'),
conditionMsg: document.querySelector('#condition-msg'),
initialize: function () {
this.celsius.addEventListener('click', this.toggleTemp.bind(this));
},
toggleTemp: function (event) {
event.preventDefault();
if (weatherService.units === 'metric') {
this.celsius.innerHTML = 'C';
this.fahrenheit.forEach(el => (el.innerHTML = 'F'));
this.windUnit.innerHTML = `mph`;
weatherService.units = 'imperial';
} else if (weatherService.units === 'imperial') {
this.celsius.innerHTML = 'F';
this.fahrenheit.forEach(el => (el.innerHTML = 'C'));
this.windUnit.innerHTML = `km/h`;
weatherService.units = 'metric';
}
// Update Data to Reflect Celsius or Fahrenheit Change
weatherService.displaySelectedLocationWeather(this.locationHeading.textContent);
},
displayCurrentTemperature: function (response) {
if (response.status == 200) {
const data = response.data;
// Rendering selected location's weather data
this.displayWeatherDetails(data);
this.displayWeatherCondition(data.weather[0].main);
weatherService.fetchDailyForecast(response.data.coord);
weatherService.renderIcons(
document,
data.weather[0].id,
data.weather[0].icon,
`.default-main-icon`
);
// User's Local Time
const localDateObject = new Date().getTime();
timeManager().printLocalDateString(data, localDateObject);
// Location's Local Time
const apiSunrise = data.sys.sunrise * 1000;
const apiSunset = data.sys.sunset * 1000;
timeManager().displaySunsetSunriseTime(data, localDateObject, apiSunrise, apiSunset);
localStorage.setItem('location', `${data.name}`);
}
},
displayWeatherDetails: function (data) {
this.locationHeading.innerHTML = `${data.name}, ${data.sys.country}`;
this.currentTemp.innerHTML = `${Math.round(data.main.temp)}`;
this.highTemp.innerHTML = `${Math.round(data.main.temp_max)}`;
this.lowTemp.innerHTML = `${Math.round(data.main.temp_min)}`;
this.feelsLikeTemp.innerHTML = `${Math.round(data.main.feels_like)}`;
this.tempDescription.innerHTML = `${data.weather[0].description}`;
this.wind.innerHTML = `${Math.round(data.wind.speed)}`;
this.humidity.innerHTML = `${data.main.humidity}`;
this.visibility.innerHTML = `${Math.round(data.visibility / 1000)}`;
this.clouds.innerHTML = `${data.clouds.all}`;
},
displayWeatherCondition: function (data) {
const weatherType = data;
switch (weatherType) {
case 'Rain':
case 'Drizzle':
case 'Clouds':
this.conditionMsg.innerHTML = `<i class="fa-solid fa-umbrella"></i> Umbrella Required`;
break;
case 'Thunderstorm':
case 'Tornado':
this.conditionMsg.innerHTML = `<i class="fa-solid fa-cloud-bolt"></i> Stay Indoors`;
break;
case 'Snow':
this.conditionMsg.innerHTML = `<i class="fa-solid fa-snowflake"></i> Dress Warm`;
break;
case 'Clear':
this.conditionMsg.innerHTML = `<i class="fa-solid fa-circle-check"></i> Ideal Conditions`;
break;
case 'Mist':
case 'Fog':
case 'Haze':
this.conditionMsg.innerHTML = `<i class="fa-solid fa-triangle-exclamation"></i> Poor Visibility`;
break;
default:
this.conditionMsg.innerHTML = `<i class="fa-solid fa-triangle-exclamation"></i> Poor Air Quality`;
}
},
}
};
const dailyWeather = () => {
return {
dewPoint: document.querySelector('#dew-point'),
forecastContainer: document.querySelector('.full-forecast'),
displayForecast: function (response) {
// Note: dew point is only available w/ one-call API & not with the general data call
// this.dewPoint.innerHTML = `${Math.round(response.data.current.dew_point)}`;
const forecastData = response.data.list; //.daily;
let forecastHTML = '';
forecastData.forEach((day, index) => {
if (index < 40 && index % 8 === 0) {
let max_temp = 0;
let min_temp = 100;
for(let i = index; i < index + 8; i++) {
let max = Number(response.data.list[i].main.temp_max);
let min = Number(response.data.list[i].main.temp_min);
if(max > max_temp) {
max_temp = max;
}
if(min < min_temp) {
min_temp = min;
}
}
forecastHTML += `
<div class="daily m-2 m-md-0">
<p>${timeManager().formatDay(day.dt)}</p>
<img
src="./assets/loading.svg"
class="weather-icon forecast-icon mb-2"
height="45"
width="50"
alt="Loading icon"
id="icon-${index}"
/>
<p>
<span class="temps">${Math.round(max_temp)}</span>°<span class="fahrenheit">${
weatherService.units === 'metric' ? 'C' : 'F'
}
</span><br />
<span class="daily-low">
<span class="forecast-low temps">${Math.round(min_temp)}</span>°<span class="fahrenheit">${
weatherService.units === 'metric' ? 'C' : 'F'
}
</span>
</span>
</p>
</div>`;
this.forecastContainer.innerHTML = forecastHTML;
weatherService.renderIcons(
this.forecastContainer,
day.weather[0].id,
day.weather[0].icon,
`#icon-${index}`
);
}
});
},
}
};
const runFetchHtml = async fetchWeatherHtml => {
return await fetch('./weather.html')
}
/*
Please obtain your own api-key @ https://home.openweathermap.org/users/sign_up
*/
runFetchHtml()
.then(response => {
new Response(response.body).text().then(html => {
document.querySelector("#weather-conditions").innerHTML = html;
const OPENWEATHER_KEY = "3da301f156bb5f1e5e5a425af22dcb15";
const weatherService = new WeatherService(OPENWEATHER_KEY);
window.timeManager = timeManager();
window.dailyWeather = dailyWeather();
weatherService.initializeGeolocation();
weatherService.getSavedLocation();
window.weatherService = weatherService;
themeManager().initialize();
searchManager().initialize();
window.selectedLocationWeather = selectedLocationWeather();
selectedLocationWeather().initialize();
})
});