ws4kp/server/scripts/modules/navigation.js

294 lines
8.4 KiB
JavaScript
Raw Normal View History

2020-09-04 18:02:20 +00:00
// navigation handles progress, next/previous and initial load messages from the parent frame
2020-10-29 21:44:28 +00:00
/* globals index, utils, StationInfo, STATUS */
2020-10-20 21:37:11 +00:00
/* globals CurrentWeather, LatestObservations, TravelForecast, RegionalForecast, LocalForecast, ExtendedForecast, Almanac, Radar, Progress, Hourly */
2020-09-04 18:02:20 +00:00
document.addEventListener('DOMContentLoaded', () => {
navigation.init();
});
const UNITS = {
english: Symbol('english'),
metric: Symbol('metric'),
};
const navigation = (() => {
let displays = [];
let currentUnits = UNITS.english;
let playing = false;
2020-09-08 19:39:17 +00:00
let progress;
2020-10-29 21:44:28 +00:00
const weatherParameters = {};
2020-09-04 18:02:20 +00:00
2020-10-20 21:37:11 +00:00
// current conditions and sunrise/sunset are made available from the display below
2020-09-25 03:44:51 +00:00
let currentWeather;
2020-10-20 21:37:11 +00:00
let almanac;
2020-09-25 03:44:51 +00:00
2020-09-08 03:48:10 +00:00
const init = async () => {
2022-09-23 20:55:37 +00:00
// set up resize handler
window.addEventListener('resize', resize);
2020-09-17 19:37:54 +00:00
};
const message = (data) => {
// dispatch event
if (!data.type) return;
switch (data.type) {
case 'latLon':
getWeather(data.message);
break;
case 'units':
setUnits(data.message);
break;
case 'navButton':
handleNavButton(data.message);
break;
default:
console.error(`Unknown event ${data.type}`);
}
2020-09-04 18:02:20 +00:00
};
2020-10-29 21:44:28 +00:00
const postMessage = (type, myMessage = {}) => {
index.message({ type, message: myMessage });
2020-09-04 18:02:20 +00:00
};
const getWeather = async (latLon) => {
// get initial weather data
const point = await utils.weather.getPoint(latLon.lat, latLon.lon);
// get stations
2020-10-02 02:35:49 +00:00
const stations = await utils.fetch.json(point.properties.observationStations);
2020-09-04 18:02:20 +00:00
const StationId = stations.features[0].properties.stationIdentifier;
2020-10-29 21:44:28 +00:00
let { city } = point.properties.relativeLocation.properties;
2020-09-04 18:02:20 +00:00
2020-10-29 21:44:28 +00:00
if (StationId in StationInfo) {
city = StationInfo[StationId].city;
[city] = city.split('/');
2020-09-04 18:02:20 +00:00
}
// populate the weather parameters
weatherParameters.latitude = latLon.lat;
weatherParameters.longitude = latLon.lon;
weatherParameters.zoneId = point.properties.forecastZone.substr(-6);
weatherParameters.radarId = point.properties.radarStation.substr(-3);
weatherParameters.stationId = StationId;
weatherParameters.weatherOffice = point.properties.cwa;
weatherParameters.city = city;
weatherParameters.state = point.properties.relativeLocation.properties.state;
weatherParameters.timeZone = point.properties.relativeLocation.properties.timeZone;
weatherParameters.forecast = point.properties.forecast;
2020-10-20 21:37:11 +00:00
weatherParameters.forecastGridData = point.properties.forecastGridData;
2020-09-04 18:02:20 +00:00
weatherParameters.stations = stations.features;
// update the main process for display purposes
postMessage('weatherParameters', weatherParameters);
2020-09-26 04:07:13 +00:00
// draw the progress canvas and hide others
hideAllCanvases();
2020-10-02 03:09:47 +00:00
document.getElementById('loading').style.display = 'none';
2020-10-29 21:44:28 +00:00
progress = new Progress(-1, 'progress');
2020-09-17 21:34:38 +00:00
await progress.drawCanvas();
progress.showCanvas();
2020-09-04 18:02:20 +00:00
// start loading canvases if necessary
if (displays.length === 0) {
2022-08-03 02:39:27 +00:00
currentWeather = new CurrentWeather(0, 'current-weather');
2020-10-20 21:37:11 +00:00
almanac = new Almanac(7, 'almanac');
2020-09-04 18:02:20 +00:00
displays = [
2020-09-25 03:44:51 +00:00
currentWeather,
2022-08-04 17:49:04 +00:00
new LatestObservations(1, 'latest-observations'),
2020-10-20 21:37:11 +00:00
new Hourly(2, 'hourly'),
2022-09-23 20:12:10 +00:00
new TravelForecast(3, 'travel', false), // not active by default
2022-08-04 21:30:13 +00:00
new RegionalForecast(4, 'regional-forecast'),
2022-08-04 16:07:35 +00:00
new LocalForecast(5, 'local-forecast'),
2022-08-05 03:03:59 +00:00
new ExtendedForecast(6, 'extended-forecast'),
2020-10-20 21:37:11 +00:00
almanac,
new Radar(8, 'radar'),
2020-09-04 18:02:20 +00:00
];
}
2020-09-08 19:39:17 +00:00
// call for new data on each display
2020-10-29 21:44:28 +00:00
displays.forEach((display) => display.getData(weatherParameters));
2020-09-04 18:02:20 +00:00
};
// receive a status update from a module {id, value}
const updateStatus = (value) => {
2020-09-17 21:34:38 +00:00
if (value.id < 0) return;
2020-09-08 19:39:17 +00:00
progress.drawCanvas(displays, countLoadedCanvases());
2020-09-08 03:48:10 +00:00
2020-09-17 21:34:38 +00:00
// if this is the first display and we're playing, load it up so it starts playing
if (isPlaying() && value.id === 0 && value.status === STATUS.loaded) {
navTo(msg.command.firstFrame);
}
2020-09-04 18:02:20 +00:00
// send loaded messaged to parent
2020-09-08 19:39:17 +00:00
if (countLoadedCanvases() < displays.length) return;
2020-09-04 18:02:20 +00:00
postMessage('loaded');
};
2022-08-05 20:54:52 +00:00
const countLoadedCanvases = () => displays.reduce((acc, display) => {
if (display.status !== STATUS.loading) return acc + 1;
return acc;
}, 0);
2020-09-08 03:48:10 +00:00
2020-09-04 18:02:20 +00:00
const hideAllCanvases = () => {
2020-10-29 21:44:28 +00:00
displays.forEach((display) => display.hideCanvas());
2020-09-04 18:02:20 +00:00
};
const units = () => currentUnits;
const setUnits = (_unit) => {
const unit = _unit.toLowerCase();
if (unit === 'english') {
currentUnits = UNITS.english;
} else {
currentUnits = UNITS.metric;
}
// TODO: refresh current screen
};
// is playing interface
const isPlaying = () => playing;
// navigation message constants
const msg = {
response: { // display to navigation
previous: Symbol('previous'), // already at first frame, calling function should switch to previous canvas
inProgress: Symbol('inProgress'), // have data to display, calling function should do nothing
next: Symbol('next'), // end of frames reached, calling function should switch to next canvas
},
command: { // navigation to display
firstFrame: Symbol('firstFrame'),
previousFrame: Symbol('previousFrame'),
nextFrame: Symbol('nextFrame'),
lastFrame: Symbol('lastFrame'), // used when navigating backwards from the begining of the next canvas
},
};
2020-09-08 15:05:46 +00:00
// receive navigation messages from displays
2020-10-29 21:44:28 +00:00
const displayNavMessage = (myMessage) => {
if (myMessage.type === msg.response.previous) loadDisplay(-1);
if (myMessage.type === msg.response.next) loadDisplay(1);
2020-09-04 18:02:20 +00:00
};
// navigate to next or previous
const navTo = (direction) => {
// test for a current display
const current = currentDisplay();
2020-09-17 21:34:38 +00:00
progress.hideCanvas();
if (!current) {
// special case for no active displays (typically on progress screen)
displays[0].navNext(msg.command.firstFrame);
return;
}
2020-09-04 18:02:20 +00:00
if (direction === msg.command.nextFrame) currentDisplay().navNext();
if (direction === msg.command.previousFrame) currentDisplay().navPrev();
};
// find the next or previous available display
const loadDisplay = (direction) => {
const totalDisplays = displays.length;
const curIdx = currentDisplayIndex();
let idx;
2020-10-29 21:44:28 +00:00
for (let i = 0; i < totalDisplays; i += 1) {
2020-09-04 18:02:20 +00:00
// convert form simple 0-10 to start at current display index +/-1 and wrap
2020-10-29 21:44:28 +00:00
idx = utils.calc.wrap(curIdx + (i + 1) * direction, totalDisplays);
2020-09-04 18:02:20 +00:00
if (displays[idx].status === STATUS.loaded) break;
}
const newDisplay = displays[idx];
// hide all displays
hideAllCanvases();
// show the new display and navigate to an appropriate display
if (direction < 0) newDisplay.showCanvas(msg.command.lastFrame);
if (direction > 0) newDisplay.showCanvas(msg.command.firstFrame);
};
// get the current display index or value
const currentDisplayIndex = () => {
2020-10-29 21:44:28 +00:00
const index = displays.findIndex((display) => display.isActive());
2020-09-04 18:02:20 +00:00
return index;
};
2020-10-29 21:44:28 +00:00
const currentDisplay = () => displays[currentDisplayIndex()];
2020-09-04 18:02:20 +00:00
const setPlaying = (newValue) => {
playing = newValue;
postMessage('isPlaying', playing);
2020-09-17 21:34:38 +00:00
// if we're playing and on the progress screen jump to the next screen
if (!progress) return;
if (playing && !currentDisplay()) navTo(msg.command.firstFrame);
2020-09-04 18:02:20 +00:00
};
// handle all navigation buttons
const handleNavButton = (button) => {
switch (button) {
2020-09-17 21:34:38 +00:00
case 'play':
setPlaying(true);
break;
2020-09-04 18:02:20 +00:00
case 'playToggle':
setPlaying(!playing);
break;
2020-09-17 21:34:38 +00:00
case 'stop':
setPlaying(false);
break;
2020-09-04 18:02:20 +00:00
case 'next':
setPlaying(false);
navTo(msg.command.nextFrame);
break;
case 'previous':
setPlaying(false);
navTo(msg.command.previousFrame);
break;
case 'menu':
setPlaying(false);
progress.showCanvas();
hideAllCanvases();
break;
2020-09-04 18:02:20 +00:00
default:
console.error(`Unknown navButton ${button}`);
}
};
2020-09-08 21:27:03 +00:00
// return the specificed display
const getDisplay = (index) => displays[index];
2020-09-25 03:44:51 +00:00
// get current conditions
const getCurrentWeather = () => {
if (!currentWeather) return false;
return currentWeather.getCurrentWeather();
};
2020-10-20 21:37:11 +00:00
// get sunrise/sunset
const getSun = () => {
if (!almanac) return false;
return almanac.getSun();
};
2022-09-23 20:55:37 +00:00
// resize the container on a page resize
const resize = () => {
const widthZoomPercent = window.innerWidth / 640;
const heightZoomPercent = window.innerHeight / 480;
const scale = Math.min(widthZoomPercent, heightZoomPercent);
if (scale < 1.0 || document.fullscreenElement) {
document.getElementById('container').style.zoom = scale;
} else {
document.getElementById('container').style.zoom = 1;
}
};
2020-09-04 18:02:20 +00:00
return {
init,
2020-09-17 19:37:54 +00:00
message,
2020-09-04 18:02:20 +00:00
updateStatus,
units,
isPlaying,
displayNavMessage,
msg,
2020-09-08 21:27:03 +00:00
getDisplay,
2020-09-25 03:44:51 +00:00
getCurrentWeather,
2020-10-20 21:37:11 +00:00
getSun,
2022-09-23 20:55:37 +00:00
resize,
2020-09-04 18:02:20 +00:00
};
2020-10-29 21:44:28 +00:00
})();