additional eslint rules

This commit is contained in:
Matt Walsh 2023-01-06 14:39:39 -06:00
parent b890b4e53d
commit 3743c45de6
26 changed files with 1079 additions and 184 deletions

View file

@ -6,7 +6,10 @@ module.exports = {
node: true, node: true,
jquery: true, jquery: true,
}, },
extends: 'airbnb-base', extends: [
'airbnb-base',
'plugin:sonarjs/recommended',
],
globals: { globals: {
Atomics: 'readonly', Atomics: 'readonly',
SharedArrayBuffer: 'readonly', SharedArrayBuffer: 'readonly',
@ -21,6 +24,10 @@ module.exports = {
parserOptions: { parserOptions: {
ecmaVersion: 2021, ecmaVersion: 2021,
}, },
plugins: [
'unicorn',
'sonarjs',
],
rules: { rules: {
indent: [ indent: [
'error', 'error',
@ -60,6 +67,24 @@ module.exports = {
json: 'always', json: 'always',
}, },
], ],
// unicorn
'unicorn/numeric-separators-style': 'error',
'unicorn/prefer-query-selector': 'error',
'unicorn/catch-error-name': 'error',
'unicorn/no-negated-condition': 'error',
'unicorn/better-regex': 'error',
'unicorn/consistent-function-scoping': 'error',
'unicorn/prefer-array-flat-map': 'error',
'unicorn/prefer-array-find': 'error',
'unicorn/prefer-regexp-test': 'error',
'unicorn/consistent-destructuring': 'error',
'unicorn/prefer-date-now': 'error',
'unicorn/prefer-ternary': 'error',
'unicorn/prefer-dom-node-append': 'error',
'unicorn/explicit-length-check': 'error',
'unicorn/prefer-at': 'error',
// sonarjs
'sonarjs/cognitive-complexity': 0,
}, },
ignorePatterns: [ ignorePatterns: [
'*.min.js', '*.min.js',

14
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "lint",
"problemMatcher": [
"$eslint-stylish"
],
"label": "npm: lint",
"detail": "eslint ./server/scripts/**"
}
]
}

867
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,11 @@
"devDependencies": { "devDependencies": {
"del": "^6.0.0", "del": "^6.0.0",
"ejs": "^3.1.5", "ejs": "^3.1.5",
"eslint": "^8.21.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-sonarjs": "^0.17.0",
"eslint-plugin-unicorn": "^45.0.2",
"express": "^4.17.1", "express": "^4.17.1",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
@ -40,9 +44,6 @@
"suncalc": "^1.8.0", "suncalc": "^1.8.0",
"swiped-events": "^1.1.4", "swiped-events": "^1.1.4",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"webpack-stream": "^7.0.0", "webpack-stream": "^7.0.0"
"eslint": "^8.21.0", }
"eslint-plugin-import": "^2.26.0"
},
"dependencies": {}
} }

View file

@ -0,0 +1,6 @@
module.exports = {
rules: {
// unicorn
'unicorn/numeric-separators-style': 0,
},
};

View file

@ -22,35 +22,38 @@ const categories = [
'Postal', 'Populated Place', 'Postal', 'Populated Place',
]; ];
const category = categories.join(','); const category = categories.join(',');
const TXT_ADDRESS_SELECTOR = '#txtAddress';
const TOGGLE_FULL_SCREEN_SELECTOR = '#ToggleFullScreen';
const BNT_GET_GPS_SELECTOR = '#btnGetGps';
const init = () => { const init = () => {
document.getElementById('txtAddress').addEventListener('focus', (e) => { document.querySelector(TXT_ADDRESS_SELECTOR).addEventListener('focus', (e) => {
e.target.select(); e.target.select();
}); });
registerRefreshData(loadData); registerRefreshData(loadData);
document.getElementById('NavigateMenu').addEventListener('click', btnNavigateMenuClick); document.querySelector('#NavigateMenu').addEventListener('click', btnNavigateMenuClick);
document.getElementById('NavigateRefresh').addEventListener('click', btnNavigateRefreshClick); document.querySelector('#NavigateRefresh').addEventListener('click', btnNavigateRefreshClick);
document.getElementById('NavigateNext').addEventListener('click', btnNavigateNextClick); document.querySelector('#NavigateNext').addEventListener('click', btnNavigateNextClick);
document.getElementById('NavigatePrevious').addEventListener('click', btnNavigatePreviousClick); document.querySelector('#NavigatePrevious').addEventListener('click', btnNavigatePreviousClick);
document.getElementById('NavigatePlay').addEventListener('click', btnNavigatePlayClick); document.querySelector('#NavigatePlay').addEventListener('click', btnNavigatePlayClick);
document.getElementById('ToggleFullScreen').addEventListener('click', btnFullScreenClick); document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR).addEventListener('click', btnFullScreenClick);
const btnGetGps = document.getElementById('btnGetGps'); const btnGetGps = document.querySelector(BNT_GET_GPS_SELECTOR);
btnGetGps.addEventListener('click', btnGetGpsClick); btnGetGps.addEventListener('click', btnGetGpsClick);
if (!navigator.geolocation) btnGetGps.style.display = 'none'; if (!navigator.geolocation) btnGetGps.style.display = 'none';
document.getElementById('divTwc').addEventListener('click', () => { document.querySelector('#divTwc').addEventListener('click', () => {
if (document.fullscreenElement) updateFullScreenNavigate(); if (document.fullscreenElement) updateFullScreenNavigate();
}); });
document.getElementById('txtAddress').addEventListener('keydown', (key) => { if (key.code === 'Enter') formSubmit(); }); document.querySelector(TXT_ADDRESS_SELECTOR).addEventListener('keydown', (key) => { if (key.code === 'Enter') formSubmit(); });
document.getElementById('btnGetLatLng').addEventListener('click', () => formSubmit()); document.querySelector('#btnGetLatLng').addEventListener('click', () => formSubmit());
document.addEventListener('keydown', documentKeydown); document.addEventListener('keydown', documentKeydown);
document.addEventListener('touchmove', (e) => { if (fullScreenOverride) e.preventDefault(); }); document.addEventListener('touchmove', (e) => { if (fullScreenOverride) e.preventDefault(); });
$('#txtAddress').devbridgeAutocomplete({ $(TXT_ADDRESS_SELECTOR).devbridgeAutocomplete({
serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest', serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest',
deferRequestBy: 300, deferRequestBy: 300,
paramName: 'text', paramName: 'text',
@ -75,7 +78,7 @@ const init = () => {
}); });
const formSubmit = () => { const formSubmit = () => {
const ac = $('#txtAddress').devbridgeAutocomplete(); const ac = $(TXT_ADDRESS_SELECTOR).devbridgeAutocomplete();
if (ac.suggestions[0]) $(ac.suggestionsContainer.children[0]).trigger('click'); if (ac.suggestions[0]) $(ac.suggestionsContainer.children[0]).trigger('click');
return false; return false;
}; };
@ -85,7 +88,7 @@ const init = () => {
const latLon = localStorage.getItem('latLon'); const latLon = localStorage.getItem('latLon');
const fromGPS = localStorage.getItem('latLonFromGPS'); const fromGPS = localStorage.getItem('latLonFromGPS');
if (query && latLon && !fromGPS) { if (query && latLon && !fromGPS) {
const txtAddress = document.getElementById('txtAddress'); const txtAddress = document.querySelector(TXT_ADDRESS_SELECTOR);
txtAddress.value = query; txtAddress.value = query;
loadData(JSON.parse(latLon)); loadData(JSON.parse(latLon));
} }
@ -96,14 +99,14 @@ const init = () => {
const play = localStorage.getItem('play'); const play = localStorage.getItem('play');
if (play === null || play === 'true') postMessage('navButton', 'play'); if (play === null || play === 'true') postMessage('navButton', 'play');
document.getElementById('btnClearQuery').addEventListener('click', () => { document.querySelector('#btnClearQuery').addEventListener('click', () => {
document.getElementById('spanCity').innerHTML = ''; document.querySelector('#spanCity').innerHTML = '';
document.getElementById('spanState').innerHTML = ''; document.querySelector('#spanState').innerHTML = '';
document.getElementById('spanStationId').innerHTML = ''; document.querySelector('#spanStationId').innerHTML = '';
document.getElementById('spanRadarId').innerHTML = ''; document.querySelector('#spanRadarId').innerHTML = '';
document.getElementById('spanZoneId').innerHTML = ''; document.querySelector('#spanZoneId').innerHTML = '';
document.getElementById('chkAutoRefresh').checked = true; document.querySelector('#chkAutoRefresh').checked = true;
localStorage.removeItem('autoRefresh'); localStorage.removeItem('autoRefresh');
localStorage.removeItem('play'); localStorage.removeItem('play');
@ -112,12 +115,12 @@ const init = () => {
localStorage.removeItem('latLonQuery'); localStorage.removeItem('latLonQuery');
localStorage.removeItem('latLon'); localStorage.removeItem('latLon');
localStorage.removeItem('latLonFromGPS'); localStorage.removeItem('latLonFromGPS');
document.getElementById('btnGetGps').classList.remove('active'); document.querySelector(BNT_GET_GPS_SELECTOR).classList.remove('active');
}); });
// swipe functionality // swipe functionality
document.getElementById('container').addEventListener('swiped-left', () => swipeCallBack('left')); document.querySelector('#container').addEventListener('swiped-left', () => swipeCallBack('left'));
document.getElementById('container').addEventListener('swiped-right', () => swipeCallBack('right')); document.querySelector('#container').addEventListener('swiped-right', () => swipeCallBack('right'));
}; };
const autocompleteOnSelect = async (suggestion, elem) => { const autocompleteOnSelect = async (suggestion, elem) => {
@ -135,7 +138,7 @@ const autocompleteOnSelect = async (suggestion, elem) => {
const loc = data.locations[0]; const loc = data.locations[0];
if (loc) { if (loc) {
localStorage.removeItem('latLonFromGPS'); localStorage.removeItem('latLonFromGPS');
document.getElementById('btnGetGps').classList.remove('active'); document.querySelector(BNT_GET_GPS_SELECTOR).classList.remove('active');
doRedirectToGeometry(loc.feature.geometry); doRedirectToGeometry(loc.feature.geometry);
} else { } else {
console.error('An unexpected error occurred. Please try a different search string.'); console.error('An unexpected error occurred. Please try a different search string.');
@ -145,7 +148,7 @@ const autocompleteOnSelect = async (suggestion, elem) => {
const doRedirectToGeometry = (geom, haveDataCallback) => { const doRedirectToGeometry = (geom, haveDataCallback) => {
const latLon = { lat: round2(geom.y, 4), lon: round2(geom.x, 4) }; const latLon = { lat: round2(geom.y, 4), lon: round2(geom.x, 4) };
// Save the query // Save the query
localStorage.setItem('latLonQuery', document.getElementById('txtAddress').value); localStorage.setItem('latLonQuery', document.querySelector(TXT_ADDRESS_SELECTOR).value);
localStorage.setItem('latLon', JSON.stringify(latLon)); localStorage.setItem('latLon', JSON.stringify(latLon));
// get the data // get the data
@ -153,10 +156,10 @@ const doRedirectToGeometry = (geom, haveDataCallback) => {
}; };
const btnFullScreenClick = () => { const btnFullScreenClick = () => {
if (!document.fullscreenElement) { if (document.fullscreenElement) {
enterFullScreen();
} else {
exitFullscreen(); exitFullscreen();
} else {
enterFullScreen();
} }
if (isPlaying()) { if (isPlaying()) {
@ -171,7 +174,7 @@ const btnFullScreenClick = () => {
}; };
const enterFullScreen = () => { const enterFullScreen = () => {
const element = document.getElementById('divTwc'); const element = document.querySelector('#divTwc');
// Supports most browsers and their versions. // Supports most browsers and their versions.
const requestMethod = element.requestFullScreen || element.webkitRequestFullScreen const requestMethod = element.requestFullScreen || element.webkitRequestFullScreen
@ -189,7 +192,7 @@ const enterFullScreen = () => {
updateFullScreenNavigate(); updateFullScreenNavigate();
// change hover text and image // change hover text and image
const img = document.getElementById('ToggleFullScreen'); const img = document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR);
img.src = 'images/nav/ic_fullscreen_exit_white_24dp_2x.png'; img.src = 'images/nav/ic_fullscreen_exit_white_24dp_2x.png';
img.title = 'Exit fullscreen'; img.title = 'Exit fullscreen';
}; };
@ -213,7 +216,7 @@ const exitFullscreen = () => {
} }
resize(); resize();
// change hover text and image // change hover text and image
const img = document.getElementById('ToggleFullScreen'); const img = document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR);
img.src = 'images/nav/ic_fullscreen_white_24dp_2x.png'; img.src = 'images/nav/ic_fullscreen_white_24dp_2x.png';
img.title = 'Enter fullscreen'; img.title = 'Enter fullscreen';
}; };
@ -232,7 +235,7 @@ const loadData = (_latLon, haveDataCallback) => {
// if there's no data stop // if there's no data stop
if (!latLon) return; if (!latLon) return;
document.getElementById('txtAddress').blur(); document.querySelector(TXT_ADDRESS_SELECTOR).blur();
stopAutoRefreshTimer(); stopAutoRefreshTimer();
latLonReceived(latLon, haveDataCallback); latLonReceived(latLon, haveDataCallback);
}; };
@ -276,8 +279,9 @@ let navigateFadeIntervalId = null;
const updateFullScreenNavigate = () => { const updateFullScreenNavigate = () => {
document.activeElement.blur(); document.activeElement.blur();
document.getElementById('divTwcBottom').classList.remove('hidden'); const divTwcBottom = document.querySelector('#divTwcBottom');
document.getElementById('divTwcBottom').classList.add('visible'); divTwcBottom.classList.remove('hidden');
divTwcBottom.classList.add('visible');
if (navigateFadeIntervalId) { if (navigateFadeIntervalId) {
clearTimeout(navigateFadeIntervalId); clearTimeout(navigateFadeIntervalId);
@ -286,8 +290,8 @@ const updateFullScreenNavigate = () => {
navigateFadeIntervalId = setTimeout(() => { navigateFadeIntervalId = setTimeout(() => {
if (document.fullscreenElement) { if (document.fullscreenElement) {
document.getElementById('divTwcBottom').classList.remove('visible'); divTwcBottom.classList.remove('visible');
document.getElementById('divTwcBottom').classList.add('hidden'); divTwcBottom.classList.add('hidden');
} }
}, 2000); }, 2000);
}; };
@ -355,7 +359,7 @@ const getPosition = async () => new Promise((resolve) => {
const btnGetGpsClick = async () => { const btnGetGpsClick = async () => {
if (!navigator.geolocation) return; if (!navigator.geolocation) return;
const btn = document.getElementById('btnGetGps'); const btn = document.querySelector(BNT_GET_GPS_SELECTOR);
// toggle first // toggle first
if (btn.classList.contains('active')) { if (btn.classList.contains('active')) {
@ -371,7 +375,7 @@ const btnGetGpsClick = async () => {
const position = await getPosition(); const position = await getPosition();
const { latitude, longitude } = position.coords; const { latitude, longitude } = position.coords;
const txtAddress = document.getElementById('txtAddress'); const txtAddress = document.querySelector(TXT_ADDRESS_SELECTOR);
txtAddress.value = `${round2(latitude, 4)}, ${round2(longitude, 4)}`; txtAddress.value = `${round2(latitude, 4)}, ${round2(longitude, 4)}`;
doRedirectToGeometry({ y: latitude, x: longitude }, (point) => { doRedirectToGeometry({ y: latitude, x: longitude }, (point) => {

View file

@ -59,8 +59,8 @@ class CurrentWeather extends WeatherDisplay {
observations = undefined; observations = undefined;
throw new Error(`Unable to get observations: ${station.properties.stationIdentifier}, trying next station`); throw new Error(`Unable to get observations: ${station.properties.stationIdentifier}, trying next station`);
} }
} catch (e) { } catch (error) {
console.error(e); console.error(error);
} }
} }
// test for data received // test for data received

View file

@ -75,12 +75,10 @@ const screens = [
// wind // wind
(data) => { (data) => {
let text = ''; let text = data.WindSpeed > 0
if (data.WindSpeed > 0) { ? `Wind: ${data.WindDirection} ${data.WindSpeed} ${data.WindUnit}`
text = `Wind: ${data.WindDirection} ${data.WindSpeed} ${data.WindUnit}`; : 'Wind: Calm';
} else {
text = 'Wind: Calm';
}
if (data.WindGust > 0) { if (data.WindGust > 0) {
text += ` Gusts to ${data.WindGust}`; text += ` Gusts to ${data.WindGust}`;
} }
@ -88,7 +86,10 @@ const screens = [
}, },
// visibility // visibility
(data) => `Visib: ${data.Visibility} ${data.VisibilityUnit} Ceiling: ${data.Ceiling === 0 ? 'Unlimited' : `${data.Ceiling} ${data.CeilingUnit}`}`, (data) => {
const distance = `${data.Ceiling} ${data.CeilingUnit}`;
return `Visib: ${data.Visibility} ${data.VisibilityUnit} Ceiling: ${data.Ceiling === 0 ? 'Unlimited' : distance}`;
},
]; ];
// internal draw function with preset parameters // internal draw function with preset parameters

View file

@ -31,9 +31,9 @@ class ExtendedForecast extends WeatherDisplay {
retryCount: 3, retryCount: 3,
stillWaiting: () => this.stillWaiting(), stillWaiting: () => this.stillWaiting(),
}); });
} catch (e) { } catch (error) {
console.error('Unable to get extended forecast'); console.error('Unable to get extended forecast');
console.error(e.status, e.responseJSON); console.error(error.status, error.responseJSON);
this.setStatus(STATUS.failed); this.setStatus(STATUS.failed);
return; return;
} }
@ -52,7 +52,10 @@ class ExtendedForecast extends WeatherDisplay {
// create each day template // create each day template
const days = forecast.map((Day) => { const days = forecast.map((Day) => {
const fill = {}; const fill = {
icon: { type: 'img', src: Day.icon },
condition: Day.text,
};
fill.date = Day.dayName; fill.date = Day.dayName;
const { low } = Day; const { low } = Day;
@ -61,10 +64,6 @@ class ExtendedForecast extends WeatherDisplay {
} }
const { high } = Day; const { high } = Day;
fill['value-hi'] = Math.round(high); fill['value-hi'] = Math.round(high);
fill.condition = Day.text;
// draw the icon
fill.icon = { type: 'img', src: Day.icon };
// return the filled template // return the filled template
return this.fillTemplate('day', fill); return this.fillTemplate('day', fill);
@ -123,13 +122,13 @@ const parse = (fullForecast) => {
const shortenExtendedForecastText = (long) => { const shortenExtendedForecastText = (long) => {
const regexList = [ const regexList = [
[/ and /ig, ' '], [/ and /gi, ' '],
[/Slight /ig, ''], [/slight /gi, ''],
[/Chance /ig, ''], [/chance /gi, ''],
[/Very /ig, ''], [/very /gi, ''],
[/Patchy /ig, ''], [/patchy /gi, ''],
[/Areas /ig, ''], [/areas /gi, ''],
[/Dense /ig, ''], [/dense /gi, ''],
[/Thunderstorm/g, 'T\'Storm'], [/Thunderstorm/g, 'T\'Storm'],
]; ];
// run all regexes // run all regexes
@ -144,10 +143,10 @@ const shortenExtendedForecastText = (long) => {
let short1 = conditions[0].substr(0, 10); let short1 = conditions[0].substr(0, 10);
let short2 = ''; let short2 = '';
if (conditions[1]) { if (conditions[1]) {
if (!short1.endsWith('.')) { if (short1.endsWith('.')) {
short2 = conditions[1].substr(0, 10);
} else {
short1 = short1.replace(/\./, ''); short1 = short1.replace(/\./, '');
} else {
short2 = conditions[1].substr(0, 10);
} }
if (short2 === 'Blowing') { if (short2 === 'Blowing') {

View file

@ -40,9 +40,9 @@ class Hazards extends WeatherDisplay {
// show alert indicator // show alert indicator
if (this.data.length > 0) alert.classList.add('show'); if (this.data.length > 0) alert.classList.add('show');
} catch (e) { } catch (error) {
console.error('Get hourly forecast failed'); console.error('Get hourly forecast failed');
console.error(e.status, e.responseJSON); console.error(error.status, error.responseJSON);
if (this.isEnabled) this.setStatus(STATUS.failed); if (this.isEnabled) this.setStatus(STATUS.failed);
// return undefined to other subscribers // return undefined to other subscribers
this.getDataCallback(undefined); this.getDataCallback(undefined);

View file

@ -34,9 +34,9 @@ class Hourly extends WeatherDisplay {
try { try {
// get the forecast // get the forecast
forecast = await json(weatherParameters.forecastGridData, { retryCount: 3, stillWaiting: () => this.stillWaiting() }); forecast = await json(weatherParameters.forecastGridData, { retryCount: 3, stillWaiting: () => this.stillWaiting() });
} catch (e) { } catch (error) {
console.error('Get hourly forecast failed'); console.error('Get hourly forecast failed');
console.error(e.status, e.responseJSON); console.error(error.status, error.responseJSON);
if (this.isEnabled) this.setStatus(STATUS.failed); if (this.isEnabled) this.setStatus(STATUS.failed);
// return undefined to other subscribers // return undefined to other subscribers
this.getDataCallback(undefined); this.getDataCallback(undefined);
@ -183,7 +183,7 @@ const expand = (data) => {
result.push(item.value); // push data array result.push(item.value); // push data array
} // timestamp is after now } // timestamp is after now
// increment start time by 1 hour // increment start time by 1 hour
startTime += 3600000; startTime += 3_600_000;
} while (startTime < endTime && result.length < 24); } while (startTime < endTime && result.length < 24);
}); // for each value }); // for each value

View file

@ -1,3 +1,4 @@
/* eslint-disable unicorn/consistent-function-scoping */
/* spell-checker: disable */ /* spell-checker: disable */
const getWeatherRegionalIconFromIconLink = (link, _isNightTime) => { const getWeatherRegionalIconFromIconLink = (link, _isNightTime) => {
@ -8,7 +9,7 @@ const getWeatherRegionalIconFromIconLink = (link, _isNightTime) => {
// grab everything after the last slash ending at any of these: ?&, // grab everything after the last slash ending at any of these: ?&,
const afterLastSlash = link.toLowerCase().match(/[^/]+$/)[0]; const afterLastSlash = link.toLowerCase().match(/[^/]+$/)[0];
let conditionName = afterLastSlash.match(/(.*?)[,?&.]/)[1]; let conditionName = afterLastSlash.match(/(.*?)[&,.?]/)[1];
// using probability as a crude heavy/light indication where possible // using probability as a crude heavy/light indication where possible
const value = +(link.match(/,(\d{2,3})/) ?? [0, 100])[1]; const value = +(link.match(/,(\d{2,3})/) ?? [0, 100])[1];
@ -156,7 +157,7 @@ const getWeatherIconFromIconLink = (link, _isNightTime) => {
// grab everything after the last slash ending at any of these: ?&, // grab everything after the last slash ending at any of these: ?&,
const afterLastSlash = link.toLowerCase().match(/[^/]+$/)[0]; const afterLastSlash = link.toLowerCase().match(/[^/]+$/)[0];
let conditionName = afterLastSlash.match(/(.*?)[,?&.]/)[1]; let conditionName = afterLastSlash.match(/(.*?)[&,.?]/)[1];
// using probability as a crude heavy/light indication where possible // using probability as a crude heavy/light indication where possible
const value = +(link.match(/,(\d{2,3})/) ?? [0, 100])[1]; const value = +(link.match(/,(\d{2,3})/) ?? [0, 100])[1];

View file

@ -50,7 +50,7 @@ class LatestObservations extends WeatherDisplay {
this.data = actualConditions.slice(0, this.MaximumRegionalStations); this.data = actualConditions.slice(0, this.MaximumRegionalStations);
// test for at least one station // test for at least one station
if (this.data.length < 1) { if (this.data.length === 0) {
this.setStatus(STATUS.noData); this.setStatus(STATUS.noData);
return; return;
} }
@ -73,10 +73,12 @@ class LatestObservations extends WeatherDisplay {
const Temperature = Math.round(celsiusToFahrenheit(condition.temperature.value)); const Temperature = Math.round(celsiusToFahrenheit(condition.temperature.value));
const WindSpeed = Math.round(kphToMph(condition.windSpeed.value)); const WindSpeed = Math.round(kphToMph(condition.windSpeed.value));
const fill = {}; const fill = {
fill.location = locationCleanup(condition.city).substr(0, 14); location: locationCleanup(condition.city).substr(0, 14),
fill.temp = Temperature; temp: Temperature,
fill.weather = shortenCurrentConditions(condition.textDescription).substr(0, 9); weather: shortenCurrentConditions(condition.textDescription).substr(0, 9),
};
if (WindSpeed > 0) { if (WindSpeed > 0) {
fill.wind = windDirection + (Array(6 - windDirection.length - WindSpeed.toString().length).join(' ')) + WindSpeed.toString(); fill.wind = windDirection + (Array(6 - windDirection.length - WindSpeed.toString().length).join(' ')) + WindSpeed.toString();
} else if (WindSpeed === 'NA') { } else if (WindSpeed === 'NA') {
@ -128,7 +130,7 @@ const getStations = async (stations) => {
StationId: station.id, StationId: station.id,
city: station.city, city: station.city,
}; };
} catch (e) { } catch (error) {
console.log(`Unable to get latest observations for ${station.id}`); console.log(`Unable to get latest observations for ${station.id}`);
return false; return false;
} }

View file

@ -66,9 +66,9 @@ class LocalForecast extends WeatherDisplay {
retryCount: 3, retryCount: 3,
stillWaiting: () => this.stillWaiting(), stillWaiting: () => this.stillWaiting(),
}); });
} catch (e) { } catch (error) {
console.error(`GetWeatherForecast failed: ${weatherParameters.forecast}`); console.error(`GetWeatherForecast failed: ${weatherParameters.forecast}`);
console.error(e.status, e.responseJSON); console.error(error.status, error.responseJSON);
this.setStatus(STATUS.failed); this.setStatus(STATUS.failed);
return false; return false;
} }

View file

@ -16,7 +16,8 @@ const weatherParameters = {};
// auto refresh // auto refresh
const AUTO_REFRESH_INTERVAL_MS = 500; const AUTO_REFRESH_INTERVAL_MS = 500;
const AUTO_REFRESH_TIME_MS = 600000; // 10 min. const AUTO_REFRESH_TIME_MS = 600_000; // 10 min.
const CHK_AUTO_REFRESH_SELECTOR = '#chkAutoRefresh';
let AutoRefreshIntervalId = null; let AutoRefreshIntervalId = null;
let AutoRefreshCountMs = 0; let AutoRefreshCountMs = 0;
@ -28,25 +29,19 @@ const init = async () => {
// auto refresh // auto refresh
const autoRefresh = localStorage.getItem('autoRefresh'); const autoRefresh = localStorage.getItem('autoRefresh');
if (!autoRefresh || autoRefresh === 'true') { if (!autoRefresh || autoRefresh === 'true') {
document.getElementById('chkAutoRefresh').checked = true; document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked = true;
} else { } else {
document.getElementById('chkAutoRefresh').checked = false; document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked = false;
} }
document.getElementById('chkAutoRefresh').addEventListener('change', autoRefreshChange); document.querySelector(CHK_AUTO_REFRESH_SELECTOR).addEventListener('change', autoRefreshChange);
generateCheckboxes(); generateCheckboxes();
}; };
const message = (data) => { const message = (data) => {
// dispatch event // dispatch event
if (!data.type) return; if (!data.type) return false;
switch (data.type) { if (data.type === 'navButton') return handleNavButton(data.message);
case 'navButton': return console.error(`Unknown event ${data.type}`);
handleNavButton(data.message);
break;
default:
console.error(`Unknown event ${data.type}`);
}
}; };
const getWeather = async (latLon, haveDataCallback) => { const getWeather = async (latLon, haveDataCallback) => {
@ -61,6 +56,7 @@ const getWeather = async (latLon, haveDataCallback) => {
const StationId = stations.features[0].properties.stationIdentifier; const StationId = stations.features[0].properties.stationIdentifier;
let { city } = point.properties.relativeLocation.properties; let { city } = point.properties.relativeLocation.properties;
const { state } = point.properties.relativeLocation.properties;
if (StationId in StationInfo) { if (StationId in StationInfo) {
city = StationInfo[StationId].city; city = StationInfo[StationId].city;
@ -76,7 +72,7 @@ const getWeather = async (latLon, haveDataCallback) => {
weatherParameters.stationId = StationId; weatherParameters.stationId = StationId;
weatherParameters.weatherOffice = point.properties.cwa; weatherParameters.weatherOffice = point.properties.cwa;
weatherParameters.city = city; weatherParameters.city = city;
weatherParameters.state = point.properties.relativeLocation.properties.state; weatherParameters.state = state;
weatherParameters.timeZone = point.properties.timeZone; weatherParameters.timeZone = point.properties.timeZone;
weatherParameters.forecast = point.properties.forecast; weatherParameters.forecast = point.properties.forecast;
weatherParameters.forecastGridData = point.properties.forecastGridData; weatherParameters.forecastGridData = point.properties.forecastGridData;
@ -87,7 +83,7 @@ const getWeather = async (latLon, haveDataCallback) => {
// draw the progress canvas and hide others // draw the progress canvas and hide others
hideAllCanvases(); hideAllCanvases();
document.getElementById('loading').style.display = 'none'; document.querySelector('#loading').style.display = 'none';
if (progress) { if (progress) {
await progress.drawCanvas(); await progress.drawCanvas();
progress.showCanvas(); progress.showCanvas();
@ -200,9 +196,7 @@ const loadDisplay = (direction) => {
if (displays[idx].status === STATUS.loaded && displays[idx].timing.totalScreens > 0) break; if (displays[idx].status === STATUS.loaded && displays[idx].timing.totalScreens > 0) break;
} }
// if new display index is less than current display a wrap occurred, test for reload timeout // if new display index is less than current display a wrap occurred, test for reload timeout
if (idx <= curIdx) { if (idx <= curIdx && refreshCheck()) return;
if (refreshCheck()) return;
}
const newDisplay = displays[idx]; const newDisplay = displays[idx];
// hide all displays // hide all displays
hideAllCanvases(); hideAllCanvases();
@ -212,15 +206,12 @@ const loadDisplay = (direction) => {
}; };
// get the current display index or value // get the current display index or value
const currentDisplayIndex = () => { const currentDisplayIndex = () => displays.findIndex((display) => display.active);
const index = displays.findIndex((display) => display.active);
return index;
};
const currentDisplay = () => displays[currentDisplayIndex()]; const currentDisplay = () => displays[currentDisplayIndex()];
const setPlaying = (newValue) => { const setPlaying = (newValue) => {
playing = newValue; playing = newValue;
const playButton = document.getElementById('NavigatePlay'); const playButton = document.querySelector('#NavigatePlay');
localStorage.setItem('play', playing); localStorage.setItem('play', playing);
if (playing) { if (playing) {
@ -272,14 +263,14 @@ const getDisplay = (index) => displays[index];
// resize the container on a page resize // resize the container on a page resize
const resize = () => { const resize = () => {
const widthZoomPercent = (document.getElementById('divTwcBottom').getBoundingClientRect().width) / 640; const widthZoomPercent = (document.querySelector('#divTwcBottom').getBoundingClientRect().width) / 640;
const heightZoomPercent = (window.innerHeight) / 480; const heightZoomPercent = (window.innerHeight) / 480;
const scale = Math.min(widthZoomPercent, heightZoomPercent); const scale = Math.min(widthZoomPercent, heightZoomPercent);
if (scale < 1.0 || document.fullscreenElement) { if (scale < 1.0 || document.fullscreenElement) {
document.getElementById('container').style.zoom = scale; document.querySelector('#container').style.zoom = scale;
} else { } else {
document.getElementById('container').style.zoom = 1; document.querySelector('#container').style.zoom = 1;
} }
}; };
@ -297,7 +288,7 @@ const registerDisplay = (display) => {
}; };
const generateCheckboxes = () => { const generateCheckboxes = () => {
const availableDisplays = document.getElementById('enabledDisplays'); const availableDisplays = document.querySelector('#enabledDisplays');
if (!availableDisplays) return; if (!availableDisplays) return;
// generate checkboxes // generate checkboxes
@ -314,11 +305,11 @@ const registerProgress = (_progress) => {
}; };
const populateWeatherParameters = (params) => { const populateWeatherParameters = (params) => {
document.getElementById('spanCity').innerHTML = `${params.city}, `; document.querySelector('#spanCity').innerHTML = `${params.city}, `;
document.getElementById('spanState').innerHTML = params.state; document.querySelector('#spanState').innerHTML = params.state;
document.getElementById('spanStationId').innerHTML = params.stationId; document.querySelector('#spanStationId').innerHTML = params.stationId;
document.getElementById('spanRadarId').innerHTML = params.radarId; document.querySelector('#spanRadarId').innerHTML = params.radarId;
document.getElementById('spanZoneId').innerHTML = params.zoneId; document.querySelector('#spanZoneId').innerHTML = params.zoneId;
}; };
const autoRefreshChange = (e) => { const autoRefreshChange = (e) => {
@ -335,12 +326,12 @@ const autoRefreshChange = (e) => {
const AssignLastUpdate = (date) => { const AssignLastUpdate = (date) => {
if (date) { if (date) {
document.getElementById('spanLastRefresh').innerHTML = date.toLocaleString('en-US', { document.querySelector('#spanLastRefresh').innerHTML = date.toLocaleString('en-US', {
weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short', weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short',
}); });
if (document.getElementById('chkAutoRefresh').checked) startAutoRefreshTimer(); if (document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked) startAutoRefreshTimer();
} else { } else {
document.getElementById('spanLastRefresh').innerHTML = '(none)'; document.querySelector('#spanLastRefresh').innerHTML = '(none)';
} }
}; };
@ -367,7 +358,7 @@ const startAutoRefreshTimer = () => {
RemainingMs = 0; RemainingMs = 0;
} }
const dt = new Date(RemainingMs); const dt = new Date(RemainingMs);
document.getElementById('spanRefreshCountDown').innerHTML = `${dt.getMinutes() < 10 ? `0${dt.getMinutes()}` : dt.getMinutes()}:${dt.getSeconds() < 10 ? `0${dt.getSeconds()}` : dt.getSeconds()}`; document.querySelector('#spanRefreshCountDown').innerHTML = `${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}`;
// Time has elapsed. // Time has elapsed.
if (AutoRefreshCountMs >= AUTO_REFRESH_TIME_MS && !isPlaying()) loadTwcData(); if (AutoRefreshCountMs >= AUTO_REFRESH_TIME_MS && !isPlaying()) loadTwcData();
@ -378,7 +369,7 @@ const startAutoRefreshTimer = () => {
const stopAutoRefreshTimer = () => { const stopAutoRefreshTimer = () => {
if (AutoRefreshIntervalId) { if (AutoRefreshIntervalId) {
window.clearInterval(AutoRefreshIntervalId); window.clearInterval(AutoRefreshIntervalId);
document.getElementById('spanRefreshCountDown').innerHTML = '--:--'; document.querySelector('#spanRefreshCountDown').innerHTML = '--:--';
AutoRefreshIntervalId = null; AutoRefreshIntervalId = null;
} }
}; };

View file

@ -18,7 +18,7 @@ class Progress extends WeatherDisplay {
// setup event listener for dom-required initialization // setup event listener for dom-required initialization
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
this.version = document.getElementById('version').innerHTML; this.version = document.querySelector('#version').innerHTML;
this.elem.querySelector('.container').addEventListener('click', this.lineClick.bind(this)); this.elem.querySelector('.container').addEventListener('click', this.lineClick.bind(this));
}); });
@ -36,9 +36,9 @@ class Progress extends WeatherDisplay {
if (!displays) return; if (!displays) return;
const lines = displays.map((display, index) => { const lines = displays.map((display, index) => {
if (display.showOnProgress === false) return false; if (display.showOnProgress === false) return false;
const fill = {}; const fill = {
name: display.name,
fill.name = display.name; };
const statusClass = calcStatusClass(display.status); const statusClass = calcStatusClass(display.status);

View file

@ -71,25 +71,24 @@ class Radar extends WeatherDisplay {
const lists = (await Promise.all(baseUrls.map(async (url) => { const lists = (await Promise.all(baseUrls.map(async (url) => {
try { try {
// get a list of available radars // get a list of available radars
const radarHtml = await text(url, { cors: true }); return text(url, { cors: true });
return radarHtml; } catch (error) {
} catch (e) {
console.log('Unable to get list of radars'); console.log('Unable to get list of radars');
console.error(e); console.error(error);
this.setStatus(STATUS.failed); this.setStatus(STATUS.failed);
return false; return false;
} }
}))).filter((d) => d); }))).filter((d) => d);
// convert to an array of gif urls // convert to an array of gif urls
const pngs = lists.map((html, htmlIdx) => { const pngs = lists.flatMap((html, htmlIdx) => {
const parser = new DOMParser(); const parser = new DOMParser();
const xmlDoc = parser.parseFromString(html, 'text/html'); const xmlDoc = parser.parseFromString(html, 'text/html');
// add the base url // add the base url
const base = xmlDoc.createElement('base'); const base = xmlDoc.createElement('base');
base.href = baseUrls[htmlIdx]; base.href = baseUrls[htmlIdx];
xmlDoc.head.append(base); xmlDoc.head.append(base);
const anchors = xmlDoc.getElementsByTagName('a'); const anchors = xmlDoc.querySelectorAll('a');
const urls = []; const urls = [];
Array.from(anchors).forEach((elem) => { Array.from(anchors).forEach((elem) => {
if (elem.innerHTML?.includes('.png') && elem.innerHTML?.includes('n0r_')) { if (elem.innerHTML?.includes('.png') && elem.innerHTML?.includes('n0r_')) {
@ -97,7 +96,7 @@ class Radar extends WeatherDisplay {
} }
}); });
return urls; return urls;
}).flat(); });
// get the last few images // get the last few images
const sortedPngs = pngs.sort((a, b) => (Date(a) < Date(b) ? -1 : 1)); const sortedPngs = pngs.sort((a, b) => (Date(a) < Date(b) ? -1 : 1));

View file

@ -28,9 +28,9 @@ const getRegionalObservation = async (point, city) => {
preloadImg(icon); preloadImg(icon);
// return the observation // return the observation
return observation.properties; return observation.properties;
} catch (e) { } catch (error) {
console.log(`Unable to get regional observations for ${city.Name ?? city.city}`); console.log(`Unable to get regional observations for ${city.Name ?? city.city}`);
console.error(e.status, e.responseJSON); console.error(error.status, error.responseJSON);
return false; return false;
} }
}; };
@ -195,7 +195,7 @@ const getXYForCityHI = (City, MaxLatitude, MinLongitude) => {
}; };
// to fit on the map, remove anything after punctuation and then limit to 15 characters // to fit on the map, remove anything after punctuation and then limit to 15 characters
const formatCity = (city) => city.match(/[^-;/\\,]*/)[0].substr(0, 12); const formatCity = (city) => city.match(/[^,/;\\-]*/)[0].substr(0, 12);
export { export {
buildForecast, buildForecast,

View file

@ -92,7 +92,7 @@ class RegionalForecast extends WeatherDisplay {
// format the observation the same as the forecast // format the observation the same as the forecast
const regionalObservation = { const regionalObservation = {
daytime: !!observation.icon.match(/\/day\//), daytime: !!/\/day\//.test(observation.icon),
temperature: celsiusToFahrenheit(observation.temperature.value), temperature: celsiusToFahrenheit(observation.temperature.value),
name: utils.formatCity(city.city), name: utils.formatCity(city.city),
icon: observation.icon, icon: observation.icon,
@ -113,9 +113,9 @@ class RegionalForecast extends WeatherDisplay {
utils.buildForecast(forecast.properties.periods[1], city, cityXY), utils.buildForecast(forecast.properties.periods[1], city, cityXY),
utils.buildForecast(forecast.properties.periods[2], city, cityXY), utils.buildForecast(forecast.properties.periods[2], city, cityXY),
]; ];
} catch (e) { } catch (error) {
console.log(`No regional forecast data for '${city.name ?? city.city}'`); console.log(`No regional forecast data for '${city.name ?? city.city}'`);
console.log(e); console.log(error);
return false; return false;
} }
})); }));
@ -159,11 +159,9 @@ class RegionalForecast extends WeatherDisplay {
const dayName = forecastDate.toLocaleString({ weekday: 'long' }); const dayName = forecastDate.toLocaleString({ weekday: 'long' });
titleTop.innerHTML = 'Forecast for'; titleTop.innerHTML = 'Forecast for';
// draw the title // draw the title
if (data[0][this.screenIndex].daytime) { titleBottom.innerHTML = data[0][this.screenIndex].daytime
titleBottom.innerHTML = dayName; ? dayName
} else { : `${dayName} Night`;
titleBottom.innerHTML = `${dayName} Night`;
}
} }
// draw the map // draw the map
@ -182,9 +180,11 @@ class RegionalForecast extends WeatherDisplay {
const { temperature } = period; const { temperature } = period;
fill.temp = temperature; fill.temp = temperature;
const { x, y } = period;
const elem = this.fillTemplate('location', fill); const elem = this.fillTemplate('location', fill);
elem.style.left = `${period.x}px`; elem.style.left = `${x}px`;
elem.style.top = `${period.y}px`; elem.style.top = `${y}px`;
return elem; return elem;
}); });

View file

@ -45,9 +45,9 @@ class TravelForecast extends WeatherDisplay {
name: city.Name, name: city.Name,
icon: getWeatherRegionalIconFromIconLink(forecast.properties.periods[todayShift].icon), icon: getWeatherRegionalIconFromIconLink(forecast.properties.periods[todayShift].icon),
}; };
} catch (e) { } catch (error) {
console.error(`GetTravelWeather for ${city.Name} failed`); console.error(`GetTravelWeather for ${city.Name} failed`);
console.error(e.status, e.responseJSON); console.error(error.status, error.responseJSON);
return { name: city.Name, error: true }; return { name: city.Name, error: true };
} }
}); });
@ -77,10 +77,9 @@ class TravelForecast extends WeatherDisplay {
const lines = cities.map((city) => { const lines = cities.map((city) => {
if (city.error) return false; if (city.error) return false;
const fillValues = {}; const fillValues = {
city,
// city name };
fillValues.city = city;
// check for forecast data // check for forecast data
if (city.icon) { if (city.icon) {
@ -94,8 +93,9 @@ class TravelForecast extends WeatherDisplay {
fillValues.low = lowString; fillValues.low = lowString;
fillValues.high = highString; fillValues.high = highString;
const { icon } = city;
fillValues.icon = { type: 'img', src: city.icon }; fillValues.icon = { type: 'img', src: icon };
} else { } else {
fillValues.error = 'NO TRAVEL DATA AVAILABLE'; fillValues.error = 'NO TRAVEL DATA AVAILABLE';
} }

View file

@ -21,7 +21,7 @@ const fetchAsync = async (_url, responseType, _params = {}) => {
if (params.cors === true) corsUrl = rewriteUrl(_url); if (params.cors === true) corsUrl = rewriteUrl(_url);
const url = new URL(corsUrl, `${window.location.origin}/`); const url = new URL(corsUrl, `${window.location.origin}/`);
// match the security protocol when not on localhost // match the security protocol when not on localhost
url.protocol = window.location.hostname !== 'localhost' ? window.location.protocol : url.protocol; url.protocol = window.location.hostname === 'localhost' ? url.protocol : window.location.protocol;
// add parameters if necessary // add parameters if necessary
if (params.data) { if (params.data) {
Object.keys(params.data).forEach((key) => { Object.keys(params.data).forEach((key) => {
@ -73,7 +73,7 @@ const doFetch = (url, params) => new Promise((resolve, reject) => {
// out of retries // out of retries
return resolve(response); return resolve(response);
}) })
.catch((e) => reject(e)); .catch((error) => reject(error));
}); });
const delay = (time, func, ...args) => new Promise((resolve) => { const delay = (time, func, ...args) => new Promise((resolve) => {
@ -87,8 +87,8 @@ const retryDelay = (retryNumber) => {
case 1: return 1000; case 1: return 1000;
case 2: return 2000; case 2: return 2000;
case 3: return 5000; case 3: return 5000;
case 4: return 10000; case 4: return 10_000;
default: return 30000; default: return 30_000;
} }
}; };

View file

@ -2,11 +2,11 @@
const round2 = (value, decimals) => Math.trunc(value * 10 ** decimals) / 10 ** decimals; const round2 = (value, decimals) => Math.trunc(value * 10 ** decimals) / 10 ** decimals;
const kphToMph = (Kph) => Math.round(Kph / 1.60934); const kphToMph = (Kph) => Math.round(Kph / 1.609_34);
const celsiusToFahrenheit = (Celsius) => Math.round((Celsius * 9) / 5 + 32); const celsiusToFahrenheit = (Celsius) => Math.round((Celsius * 9) / 5 + 32);
const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.60934); const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.609_34);
const metersToFeet = (Meters) => Math.round(Meters / 0.3048); const metersToFeet = (Meters) => Math.round(Meters / 0.3048);
const pascalToInHg = (Pascal) => round2(Pascal * 0.0002953, 2); const pascalToInHg = (Pascal) => round2(Pascal * 0.000_295_3, 2);
export { export {
kphToMph, kphToMph,

View file

@ -3,9 +3,9 @@ import { json } from './fetch.mjs';
const getPoint = async (lat, lon) => { const getPoint = async (lat, lon) => {
try { try {
return await json(`https://api.weather.gov/points/${lat},${lon}`); return await json(`https://api.weather.gov/points/${lat},${lon}`);
} catch (e) { } catch (error) {
console.log(`Unable to get point ${lat}, ${lon}`); console.log(`Unable to get point ${lat}, ${lon}`);
console.error(e); console.error(error);
return false; return false;
} }
}; };

View file

@ -53,11 +53,7 @@ class WeatherDisplay {
// get the saved status of the checkbox // get the saved status of the checkbox
let savedStatus = window.localStorage.getItem(`display-enabled: ${this.elemId}`); let savedStatus = window.localStorage.getItem(`display-enabled: ${this.elemId}`);
if (savedStatus === null) savedStatus = defaultEnabled; if (savedStatus === null) savedStatus = defaultEnabled;
if (savedStatus === 'true' || savedStatus === true) { this.isEnabled = !!((savedStatus === 'true' || savedStatus === true));
this.isEnabled = true;
} else {
this.isEnabled = false;
}
// refresh (or initially store the state of the checkbox) // refresh (or initially store the state of the checkbox)
window.localStorage.setItem(`display-enabled: ${this.elemId}`, this.isEnabled); window.localStorage.setItem(`display-enabled: ${this.elemId}`, this.isEnabled);
@ -253,17 +249,13 @@ class WeatherDisplay {
if (nextScreenIndex === this.screenIndex) return; if (nextScreenIndex === this.screenIndex) return;
// test for -1 (no screen displayed yet) // test for -1 (no screen displayed yet)
if (nextScreenIndex === -1) { this.screenIndex = nextScreenIndex === -1 ? 0 : nextScreenIndex;
this.screenIndex = 0;
} else {
this.screenIndex = nextScreenIndex;
}
// call the appropriate screen index change method // call the appropriate screen index change method
if (!this.screenIndexChange) { if (this.screenIndexChange) {
await this.drawCanvas();
} else {
this.screenIndexChange(this.screenIndex); this.screenIndexChange(this.screenIndex);
} else {
await this.drawCanvas();
} }
this.showCanvas(); this.showCanvas();
} }
@ -377,7 +369,7 @@ class WeatherDisplay {
loadTemplates() { loadTemplates() {
this.templates = {}; this.templates = {};
this.elem = document.getElementById(`${this.elemId}-html`); this.elem = document.querySelector(`#${this.elemId}-html`);
if (!this.elem) return; if (!this.elem) return;
const templates = this.elem.querySelectorAll('.template'); const templates = this.elem.querySelectorAll('.template');
templates.forEach((template) => { templates.forEach((template) => {

View file

@ -27,6 +27,7 @@
"Pngs", "Pngs",
"PRECIP", "PRECIP",
"rtrim", "rtrim",
"sonarjs",
"T", "T",
"T'storm", "T'storm",
"uscomp", "uscomp",