diff --git a/server/scripts/index.js b/server/scripts/index.js index c650a92..4363090 100644 --- a/server/scripts/index.js +++ b/server/scripts/index.js @@ -4,6 +4,13 @@ $(() => { }); const index = (() => { + const overrides = { + // '32899, Orlando, Florida, USA': { x: -80.6774, y: 28.6143 }, + }; + const _AutoRefreshIntervalMs = 500; + const _AutoRefreshTotalIntervalMs = 600000; // 10 min. + const _NoSleep = new NoSleep(); + let divTwc; let divTwcTop; let divTwcMiddle; @@ -13,15 +20,11 @@ const index = (() => { let divTwcNavContainer; let txtScrollText; - const _NoSleep = new NoSleep(); let _AutoSelectQuery = false; - let _IsPlaying = false; let _LastUpdate = null; let _AutoRefreshIntervalId = null; - const _AutoRefreshIntervalMs = 500; - const _AutoRefreshTotalIntervalMs = 600000; // 10 min. let _AutoRefreshCountMs = 0; let _FullScreenOverride = false; @@ -70,9 +73,6 @@ const index = (() => { $('.ToggleFullScreen').on('click', btnFullScreen_click); FullScreenResize(); - // listen for messages (from iframe) and handle accordingly - window.addEventListener('message', messageHandler, false); - const categories = [ 'Land Features', 'Bay', 'Channel', 'Cove', 'Dam', 'Delta', 'Gulf', 'Lagoon', 'Lake', 'Ocean', 'Reef', 'Reservoir', 'Sea', 'Sound', 'Strait', 'Waterfall', 'Wharf', // Water Features @@ -85,54 +85,6 @@ const index = (() => { ]; const cats = categories.join(','); - const overrides = { - // '32899, Orlando, Florida, USA': { x: -80.6774, y: 28.6143 }, - }; - - const roundToPlaces = function (num, decimals) { - var n = Math.pow(10, decimals); - return Math.round((n * num).toFixed(decimals)) / n; - }; - - const doRedirectToGeometry = (geom) => { - const latLon = {lat:roundToPlaces(geom.y, 4), lon:roundToPlaces(geom.x, 4)}; - LoadTwcData(latLon); - // Save the query - localStorage.setItem('TwcQuery', $('#txtAddress').val()); - }; - - let PreviousSuggestionValue = null; - let PreviousSuggestion = null; - const OnSelect = (suggestion) => { - let request; - - // Do not auto get the same city twice. - if (PreviousSuggestionValue === suggestion.value) return; - PreviousSuggestionValue = suggestion.value; - PreviousSuggestion = suggestion; - - if (overrides[suggestion.value]) { - doRedirectToGeometry(overrides[suggestion.value]); - } else { - request = $.ajax({ - url: location.protocol + '//geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find', - data: { - text: suggestion.value, - magicKey: suggestion.data, - f: 'json', - }, - }); - request.done((data) => { - const loc = data.locations[0]; - if (loc) { - doRedirectToGeometry(loc.feature.geometry); - } else { - alert('An unexpected error occurred. Please try a different search string.'); - } - }); - } - }; - $('#frmGetLatLng #txtAddress').devbridgeAutocomplete({ serviceUrl: location.protocol + '//geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest', deferRequestBy: 300, @@ -164,21 +116,13 @@ const index = (() => { minChars: 3, showNoSuggestionNotice: true, noSuggestionNotice: 'No results found. Please try a different search string.', - onSelect: OnSelect, + onSelect: autocompleteOnSelect, width: 490, }); const ac = $('#frmGetLatLng #txtAddress').devbridgeAutocomplete(); - $('#frmGetLatLng').submit(function () { - if (ac.suggestions[0]) { - $(ac.suggestionsContainer.children[0]).click(); - return false; - } - if (PreviousSuggestion) { - PreviousSuggestionValue = null; - OnSelect(PreviousSuggestion); - } - + $('#frmGetLatLng').submit(() => { + if (ac.suggestions[0]) $(ac.suggestionsContainer.children[0]).click(); return false; }); @@ -192,9 +136,7 @@ const index = (() => { } const TwcPlay = localStorage.getItem('TwcPlay'); - if (!TwcPlay || TwcPlay === 'true') { - _IsPlaying = true; - } + if (TwcPlay === null || TwcPlay === 'true') postMessage('navButton', 'play'); const TwcScrollText = localStorage.getItem('TwcScrollText'); if (TwcScrollText) { @@ -226,11 +168,9 @@ const index = (() => { localStorage.removeItem('TwcUnits'); localStorage.removeItem('TwcPlay'); - _IsPlaying = true; + postMessage('navButton', 'play'); localStorage.removeItem('TwcQuery'); - PreviousSuggestionValue = null; - PreviousSuggestion = null; }); const TwcUnits = localStorage.getItem('TwcUnits'); @@ -248,7 +188,6 @@ const index = (() => { postMessage('units', Units); }); - $('#chkAutoRefresh').on('change', (e) => { const Checked = $(e.target).is(':checked'); @@ -272,6 +211,43 @@ const index = (() => { }; + const autocompleteOnSelect = (suggestion) => { + let request; + + // Do not auto get the same city twice. + if (this.previousSuggestionValue === suggestion.value) return; + this.previousSuggestionValue = suggestion.value; + PreviousSuggestion = suggestion; + + if (overrides[suggestion.value]) { + doRedirectToGeometry(overrides[suggestion.value]); + } else { + request = $.ajax({ + url: location.protocol + '//geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find', + data: { + text: suggestion.value, + magicKey: suggestion.data, + f: 'json', + }, + }); + request.done((data) => { + const loc = data.locations[0]; + if (loc) { + doRedirectToGeometry(loc.feature.geometry); + } else { + alert('An unexpected error occurred. Please try a different search string.'); + } + }); + } + }; + + const doRedirectToGeometry = (geom) => { + const latLon = {lat:Math.round2(geom.y, 4), lon:Math.round2(geom.x, 4)}; + LoadTwcData(latLon); + // Save the query + localStorage.setItem('TwcQuery', $('#txtAddress').val()); + }; + const FullScreenResize = () => { const WindowWidth = $(window).width(); const WindowHeight = $(window).height(); @@ -402,7 +378,7 @@ const index = (() => { ExitFullscreen(); } - if (_IsPlaying) { + if (navigation.isPlaying()) { _NoSleep.enable(); } else { _NoSleep.disable(); @@ -482,7 +458,6 @@ const index = (() => { postMessage('units', $('input[type=\'radio\'][name=\'radUnits\']:checked').val()); - if (_IsPlaying) postMessage('navButton', 'playToggle'); const display = $('#display'); display.on('mousemove', document_mousemove); @@ -557,11 +532,7 @@ const index = (() => { _WindowHeight = $window.height(); _WindowWidth = $window.width(); - try { - postMessage('navButton', 'reset'); - } catch (ex) { - console.log(ex); - } + postMessage('navButton', 'reset'); UpdateFullScreenNavigate(); }; @@ -582,7 +553,6 @@ const index = (() => { } _NavigateFadeIntervalId = window.setTimeout(() => { - //console.log("window_mousemove: TimeOut"); if (InFullScreen()) { $('body').addClass('HideCursor'); @@ -695,12 +665,8 @@ const index = (() => { }; // read and dispatch an event from the iframe - const messageHandler = (event) => { + const message = (data) => { // test for trust - if (!event.isTrusted) return; - // get the data - const data = JSON.parse(event.data); - // dispatch event if (!data.type) return; switch (data.type) { @@ -714,12 +680,10 @@ const index = (() => { break; case 'isPlaying': - _IsPlaying = data.message; - localStorage.setItem('TwcPlay', _IsPlaying); + localStorage.setItem('TwcPlay', navigation.isPlaying()); - if (_IsPlaying) { + if (navigation.isPlaying()) { _NoSleep.enable(); - $('img[src=\'images/nav/ic_play_arrow_white_24dp_1x.png\']').attr('title', 'Pause'); $('img[src=\'images/nav/ic_play_arrow_white_24dp_1x.png\']').attr('src', 'images/nav/ic_pause_white_24dp_1x.png'); $('img[src=\'images/nav/ic_play_arrow_white_24dp_2x.png\']').attr('title', 'Pause'); @@ -850,6 +814,7 @@ const index = (() => { return { init, + message, }; })(); \ No newline at end of file diff --git a/server/scripts/modules/currentweather.js b/server/scripts/modules/currentweather.js index 1dcd83c..1f6bc16 100644 --- a/server/scripts/modules/currentweather.js +++ b/server/scripts/modules/currentweather.js @@ -44,7 +44,7 @@ class CurrentWeather extends WeatherDisplay { } // we only get here if there was no error above this.data = Object.assign({}, observations, {station: station}); - this.drawCanvas(); + this.setStatus(STATUS.loaded); } async drawCanvas () { @@ -175,7 +175,6 @@ class CurrentWeather extends WeatherDisplay { })); this.finishDraw(); - this.setStatus(STATUS.loaded); } shortConditions(condition) { diff --git a/server/scripts/modules/extendedforecast.js b/server/scripts/modules/extendedforecast.js index 771d439..e122212 100644 --- a/server/scripts/modules/extendedforecast.js +++ b/server/scripts/modules/extendedforecast.js @@ -41,8 +41,7 @@ class ExtendedForecast extends WeatherDisplay { // we only get here if there was no error above this.data = this.parseExtendedForecast(forecast.properties.periods); this.screenIndex = 0; - this.drawCanvas(); - + this.setStatus(STATUS.loaded); } // the api provides the forecast in 12 hour increments, flatten to day increments with high and low temperatures @@ -166,6 +165,5 @@ class ExtendedForecast extends WeatherDisplay { })); this.finishDraw(); - this.setStatus(STATUS.loaded); } } \ No newline at end of file diff --git a/server/scripts/modules/latestobservations.js b/server/scripts/modules/latestobservations.js index 8937e23..5f182a6 100644 --- a/server/scripts/modules/latestobservations.js +++ b/server/scripts/modules/latestobservations.js @@ -55,8 +55,7 @@ class LatestObservations extends WeatherDisplay { this.setStatus(STATUS.noData); return; } - - this.drawCanvas(); + this.setStatus(STATUS.loaded); } async drawCanvas() { @@ -111,7 +110,6 @@ class LatestObservations extends WeatherDisplay { y += 40; }); this.finishDraw(); - this.setStatus(STATUS.loaded); } shortenCurrentConditions(condition) { diff --git a/server/scripts/modules/navigation.js b/server/scripts/modules/navigation.js index c13b53f..9d41431 100644 --- a/server/scripts/modules/navigation.js +++ b/server/scripts/modules/navigation.js @@ -1,6 +1,6 @@ 'use strict'; // navigation handles progress, next/previous and initial load messages from the parent frame -/* globals utils, _StationInfo, STATUS */ +/* globals index, utils, _StationInfo, STATUS */ /* globals CurrentWeather, LatestObservations, TravelForecast, RegionalForecast, LocalForecast, ExtendedForecast, Almanac, Radar, Progress */ document.addEventListener('DOMContentLoaded', () => { @@ -15,7 +15,6 @@ const UNITS = { const navigation = (() => { let weatherParameters = {}; let displays = []; - let initialLoadDone = false; let currentUnits = UNITS.english; let playing = false; let progress; @@ -25,7 +24,6 @@ const navigation = (() => { }; const message = (data) => { - // dispatch event if (!data.type) return; switch (data.type) { @@ -48,14 +46,10 @@ const navigation = (() => { }; const postMessage = (type, message = {}) => { - const parent = window.parent; - parent.postMessage(JSON.stringify({type, message}, window.location.origin)); + index.message({type, message}); }; const getWeather = async (latLon) => { - // reset statuses - initialLoadDone = false; - // get initial weather data const point = await utils.weather.getPoint(latLon.lat, latLon.lon); @@ -93,6 +87,11 @@ const navigation = (() => { // update the main process for display purposes postMessage('weatherParameters', weatherParameters); + // draw the progress canvas + progress = new Progress(-1,'progress'); + await progress.drawCanvas(); + progress.showCanvas(); + // start loading canvases if necessary if (displays.length === 0) { displays = [ @@ -114,25 +113,21 @@ const navigation = (() => { // ShowDopplerMap(this.weatherParameters); // GetWeatherHazards3(this.weatherParameters); - // draw the progress canvas - progress = new Progress(-1,'progress'); - progress.drawCanvas(); - progress.showCanvas(); }; // receive a status update from a module {id, value} const updateStatus = (value) => { - // skip if initial load - if (initialLoadDone) return; - // test for loaded status - if (value.status !== STATUS.loaded) return; - + if (value.id < 0) return; progress.drawCanvas(displays, countLoadedCanvases()); + // 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); + } + // send loaded messaged to parent if (countLoadedCanvases() < displays.length) return; postMessage('loaded'); - // store the display number }; const countLoadedCanvases = () => displays.reduce((acc, display) => { @@ -183,9 +178,9 @@ const navigation = (() => { const navTo = (direction) => { // test for a current display const current = currentDisplay(); + progress.hideCanvas(); if (!current) { // special case for no active displays (typically on progress screen) - progress.hideCanvas(); displays[0].navNext(msg.command.firstFrame); return; } @@ -223,14 +218,23 @@ const navigation = (() => { const setPlaying = (newValue) => { playing = newValue; postMessage('isPlaying', playing); + // if we're playing and on the progress screen jump to the next screen + if (!progress) return; + if (playing && !currentDisplay()) navTo(msg.command.firstFrame); }; // handle all navigation buttons const handleNavButton = (button) => { switch (button) { + case 'play': + setPlaying(true); + break; case 'playToggle': setPlaying(!playing); break; + case 'stop': + setPlaying(false); + break; case 'next': setPlaying(false); navTo(msg.command.nextFrame); diff --git a/server/scripts/modules/progress.js b/server/scripts/modules/progress.js index 0865a78..bd2261e 100644 --- a/server/scripts/modules/progress.js +++ b/server/scripts/modules/progress.js @@ -95,12 +95,13 @@ class Progress extends WeatherDisplay { canvasClick(e) { const x = e.offsetX; const y = e.offsetY; - console.log(x,y); // eliminate off canvas and outside area clicks if (!this.isActive()) return; if (y < 100 || y > 410) return; if (x < 440 || x > 570) return; + // stop playing + navigation.message('navButton', stop); // use the y value to determine an index const index = Math.floor((y-100)/29); const display = navigation.getDisplay(index); diff --git a/server/scripts/modules/travelforecast.js b/server/scripts/modules/travelforecast.js index df3ec9a..f79bedc 100644 --- a/server/scripts/modules/travelforecast.js +++ b/server/scripts/modules/travelforecast.js @@ -67,14 +67,11 @@ class TravelForecast extends WeatherDisplay { return; } - this.drawCanvas(true); + this.setStatus(STATUS.loaded); + this.drawLongCanvas(); } - async drawCanvas(newData) { - // there are technically 2 canvases: the standard canvas and the extra-long canvas that contains the complete - // list of cities. The second canvas is copied into the standard canvas to create the scroll - super.drawCanvas(); - + async drawLongCanvas () { // create the "long" canvas if necessary if (!this.longCanvas) { this.longCanvas = document.createElement('canvas'); @@ -85,61 +82,67 @@ class TravelForecast extends WeatherDisplay { // set up variables const cities = this.data; - // draw the long canvas only if there is new data - if (newData) { - this.longContext.clearRect(0,0,this.longCanvas.width,this.longCanvas.height); + this.longContext.clearRect(0,0,this.longCanvas.width,this.longCanvas.height); - // draw the "long" canvas with all cities - draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, _TravelCities.length*this.cityHeight); + // draw the "long" canvas with all cities + draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, _TravelCities.length*this.cityHeight); - for (let i = 0; i <= 4; i++) { - const y = i * 346; - draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040'); - } + for (let i = 0; i <= 4; i++) { + const y = i * 346; + draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040'); + } - await Promise.all(cities.map(async (city, index) => { + await Promise.all(cities.map(async (city, index) => { // calculate base y value - const y = 50+this.cityHeight*index; + const y = 50+this.cityHeight*index; - // city name - draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2); + // city name + draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2); - // check for forecast data - if (city.icon) { + // check for forecast data + if (city.icon) { // get temperatures and convert if necessary - let {low, high} = city; + let {low, high} = city; - if (navigation.units() === UNITS.metric) { - low = utils.units.fahrenheitToCelsius(low); - high = utils.units.fahrenheitToCelsius(high); - } - - // convert to strings with no decimal - const lowString = Math.round(low).toString(); - const highString = Math.round(high).toString(); - - const xLow = (500 - (lowString.length * 20)); - draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', xLow, y, lowString, 2); - - const xHigh = (560 - (highString.length * 20)); - draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', xHigh, y, highString, 2); - - this.gifs.push(await utils.image.superGifAsync({ - src: city.icon, - loop_delay: 100, - auto_play: true, - canvas: this.longCanvas, - x: 330, - y: y - 35, - max_width: 47, - })); - } else { - draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y - 18, 'NO TRAVEL', 2); - draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y, 'DATA AVAILABLE', 2); + if (navigation.units() === UNITS.metric) { + low = utils.units.fahrenheitToCelsius(low); + high = utils.units.fahrenheitToCelsius(high); } - })); - } + // convert to strings with no decimal + const lowString = Math.round(low).toString(); + const highString = Math.round(high).toString(); + + const xLow = (500 - (lowString.length * 20)); + draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', xLow, y, lowString, 2); + + const xHigh = (560 - (highString.length * 20)); + draw.text(this.longContext, 'Star4000 Large', '24pt', '#FFFF00', xHigh, y, highString, 2); + + this.gifs.push(await utils.image.superGifAsync({ + src: city.icon, + loop_delay: 100, + auto_play: true, + canvas: this.longCanvas, + x: 330, + y: y - 35, + max_width: 47, + })); + } else { + draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y - 18, 'NO TRAVEL', 2); + draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y, 'DATA AVAILABLE', 2); + } + + })); + } + + async drawCanvas() { + // there are technically 2 canvases: the standard canvas and the extra-long canvas that contains the complete + // list of cities. The second canvas is copied into the standard canvas to create the scroll + super.drawCanvas(); + + // set up variables + const cities = this.data; // draw the standard context this.context.drawImage(await this.backgroundImage, 0, 0); @@ -155,7 +158,12 @@ class TravelForecast extends WeatherDisplay { this.context.drawImage(this.longCanvas, 0, 0, 640, 289, 0, 110, 640, 289); this.finishDraw(); - this.setStatus(STATUS.loaded); + } + + async showCanvas() { + // special to travel forecast to draw the remainder of the canvas + await this.drawCanvas(); + super.showCanvas(); } // screen index change callback just runs the base count callback diff --git a/server/scripts/modules/weatherdisplay.js b/server/scripts/modules/weatherdisplay.js index 75a7fa1..8cf39c5 100644 --- a/server/scripts/modules/weatherdisplay.js +++ b/server/scripts/modules/weatherdisplay.js @@ -225,6 +225,10 @@ class WeatherDisplay { return document.getElementById(this.elemId+'Canvas').offsetParent !== null; } + isEnabled() { + return true; + } + // navigation timings // totalScreens = total number of screens that are available // baseDelay = ms to delay before re-evaluating screenIndex @@ -246,7 +250,7 @@ class WeatherDisplay { this.updateScreenFromBaseCount(); } - updateScreenFromBaseCount() { + async updateScreenFromBaseCount() { // get the next screen index let nextScreenIndex = this.screenIndexFromBaseCount(); @@ -261,11 +265,17 @@ class WeatherDisplay { // test for no change and exit early if (nextScreenIndex === this.screenIndex) return; - this.screenIndex = nextScreenIndex; + // test for -1 (no screen displayed yet) + if (nextScreenIndex === -1) { + this.screenIndex = 0; + } else { + this.screenIndex = nextScreenIndex; + } // call the appropriate screen index change method if (!this.screenIndexChange) { - this.drawCanvas(); + await this.drawCanvas(); + this.showCanvas(); } else { this.screenIndexChange(this.screenIndex); } diff --git a/version.js b/version.js index 490fac0..49daa50 100644 --- a/version.js +++ b/version.js @@ -1 +1 @@ -module.exports = '3.0.0'; \ No newline at end of file +module.exports = '3.0.1'; \ No newline at end of file