change to airbnb eslint plugin

This commit is contained in:
Matt Walsh 2020-10-29 16:44:28 -05:00
parent b7967fca05
commit dd98daf0c2
31 changed files with 1179 additions and 790 deletions

View file

@ -1,35 +1,39 @@
module.exports = { module.exports = {
'env': { env: {
'browser': true, browser: true,
'commonjs': true, commonjs: true,
'es6': true, es6: true,
'node': true, node: true,
'jquery': true, jquery: true,
}, },
'extends': 'eslint:recommended', extends: 'airbnb-base',
'globals': { globals: {
'Atomics': 'readonly', Atomics: 'readonly',
'SharedArrayBuffer': 'readonly' SharedArrayBuffer: 'readonly',
}, },
'parserOptions': { parserOptions: {
'ecmaVersion': 2018 ecmaVersion: 2020,
}, },
'rules': { rules: {
'indent': [ indent: [
'error', 'error',
'tab' 'tab',
], ],
'no-tabs': 0,
'no-use-before-define': 0,
'no-console': 0,
'linebreak-style': [ 'linebreak-style': [
'error', 'error',
'unix' 'unix',
], ],
'quotes': [ 'max-len': 0,
quotes: [
'error', 'error',
'single' 'single',
], ],
'semi': [ semi: [
'error', 'error',
'always' 'always',
], ],
'no-prototype-builtins': 0, 'no-prototype-builtins': 0,
'comma-dangle': ['error', 'always-multiline'], 'comma-dangle': ['error', 'always-multiline'],
@ -37,14 +41,14 @@ module.exports = {
'default-case': ['error'], 'default-case': ['error'],
'default-param-last': ['error'], 'default-param-last': ['error'],
'dot-location': ['error', 'property'], 'dot-location': ['error', 'property'],
'eqeqeq': ['error'], eqeqeq: ['error'],
'no-eval': ['error'], 'no-eval': ['error'],
'no-eq-null': ['error'], 'no-eq-null': ['error'],
'no-floating-decimal': ['error'], 'no-floating-decimal': ['error'],
'no-trailing-spaces': ['error'], 'no-trailing-spaces': ['error'],
'brace-style': [2, '1tbs', { 'allowSingleLine': true }], 'brace-style': [2, '1tbs', { allowSingleLine: true }],
}, },
'ignorePatterns': [ ignorePatterns: [
'*.min.js' '*.min.js',
], ],
}; };

View file

@ -1,4 +1,4 @@
const _StationInfo = { const StationInfo = {
MADC: { MADC: {
id: 'MADC', id: 'MADC',
city: 'Durango Complex', city: 'Durango Complex',

2
dist/index.html vendored
View file

@ -1 +1 @@
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"><head><meta charset="utf-8"><link rel="preload" href="fonts/Star4000.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star 4 Radar.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Extended.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large Compressed.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Small.woff" as="font" crossorigin="anonymous"><title>WeatherStar 4000+</title><meta name="description" content="Web based WeatherStar 4000 simulator that reports current and forecast weather conditions plus a few extras!"><meta name="keywords" content="WeatherStar 4000+"><meta name="author" content="Matt Walsh"><meta name="application-name" content="WeatherStar 4000+"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><link rel="manifest" href="manifest.json"><link rel="icon" href="images/Logo192.png"><link rel="stylesheet" type="text/css" href="resources/ws.min.css?_=3.7.1"><script type="text/javascript" src="resources/data.min.js?_=3.7.1"></script><script type="text/javascript" src="resources/ws.min.js?_=3.7.1"></script></head><body><div id="divQuery"><form id="frmGetLatLng"><input id="txtAddress" type="text" value="" placeholder="Zip or City, State"><button id="btnGetGps" type="button" title="Get GPS Location"><img id="imgGetGps" src="images/nav/ic_gps_fixed_black_18dp_1x.png"></button> <input id="btnGetLatLng" type="submit" value="GO"> <input id="btnClearQuery" type="reset" value="Reset"></form><div id="divLat"></div><div id="divLng"></div></div><br><img id="imgPause1x" src="images/nav/ic_pause_white_24dp_1x.png"> <img id="imgPause2x" src="images/nav/ic_pause_white_24dp_2x.png"><div id="version" style="display:none">3.7.1</div><div id="divTwc"><div id="container"><div id="loading" width="640" height="480"><div><div class="title">WeatherStar 4000+</div><div class="instructions">Enter your location above to continue</div></div></div></div><div id="divTwcBottom"><div id="divTwcBottomLeft"><img id="NavigateMenu" class="navButton" src="images/nav/ic_menu_white_24dp_1x.png" title="Menu"> <img id="NavigatePrevious" class="navButton" src="images/nav/ic_skip_previous_white_24dp_1x.png" title="Previous"> <img id="NavigateNext" class="navButton" src="images/nav/ic_skip_next_white_24dp_1x.png" title="Next"> <img id="NavigatePlay" class="navButton" src="images/nav/ic_play_arrow_white_24dp_1x.png" title="Play"></div><div id="divTwcBottomMiddle"><img id="NavigateRefresh" class="navButton" src="images/nav/ic_refresh_white_24dp_1x.png" title="Refresh"></div><div id="divTwcBottomRight"><img id="ToggleFullScreen" class="navButton" src="images/nav/ic_fullscreen_exit_white_24dp_1x.png" title="Exit Fullscreen"></div></div></div><br><div class="info"><a href="https://github.com/netbymatt/ws4kp#weatherstar-4000">More information</a></div><div class="heading">Selected displays</div><div id="enabledDisplays"></div><div id="divInfo">Location: <span id="spanCity"></span> <span id="spanState"></span><br>Station Id: <span id="spanStationId"></span><br>Radar Id: <span id="spanRadarId"></span><br>Zone Id: <span id="spanZoneId"></span><br></div><div id="divRefresh">Last Update: <span id="spanLastRefresh">(None)</span><br><input id="chkAutoRefresh" name="chkAutoRefresh" type="checkbox"><label id="lblRefreshCountDown" for="chkAutoRefresh">Auto Refresh: <span id="spanRefreshCountDown">--:--</span></label></div><div id="divUnits">Units: <input id="radEnglish" name="radUnits" type="radio" value="ENGLISH"><label for="radEnglish">English</label> <input id="radMetric" name="radUnits" type="radio" value="METRIC"><label for="radMetric">Metric</label></div></body></html> <!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"><head><meta charset="utf-8"><link rel="preload" href="fonts/Star4000.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star 4 Radar.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Extended.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large Compressed.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Large.woff" as="font" crossorigin="anonymous"><link rel="preload" href="fonts/Star4000 Small.woff" as="font" crossorigin="anonymous"><title>WeatherStar 4000+</title><meta name="description" content="Web based WeatherStar 4000 simulator that reports current and forecast weather conditions plus a few extras!"><meta name="keywords" content="WeatherStar 4000+"><meta name="author" content="Matt Walsh"><meta name="application-name" content="WeatherStar 4000+"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><link rel="manifest" href="manifest.json"><link rel="icon" href="images/Logo192.png"><link rel="stylesheet" type="text/css" href="resources/ws.min.css?_=3.8.0"><script type="text/javascript" src="resources/data.min.js?_=3.8.0"></script><script type="text/javascript" src="resources/ws.min.js?_=3.8.0"></script></head><body><div id="divQuery"><form id="frmGetLatLng"><input id="txtAddress" type="text" value="" placeholder="Zip or City, State"><button id="btnGetGps" type="button" title="Get GPS Location"><img id="imgGetGps" src="images/nav/ic_gps_fixed_black_18dp_1x.png"></button> <input id="btnGetLatLng" type="submit" value="GO"> <input id="btnClearQuery" type="reset" value="Reset"></form><div id="divLat"></div><div id="divLng"></div></div><br><img id="imgPause1x" src="images/nav/ic_pause_white_24dp_1x.png"> <img id="imgPause2x" src="images/nav/ic_pause_white_24dp_2x.png"><div id="version" style="display:none">3.8.0</div><div id="divTwc"><div id="container"><div id="loading" width="640" height="480"><div><div class="title">WeatherStar 4000+</div><div class="instructions">Enter your location above to continue</div></div></div></div><div id="divTwcBottom"><div id="divTwcBottomLeft"><img id="NavigateMenu" class="navButton" src="images/nav/ic_menu_white_24dp_1x.png" title="Menu"> <img id="NavigatePrevious" class="navButton" src="images/nav/ic_skip_previous_white_24dp_1x.png" title="Previous"> <img id="NavigateNext" class="navButton" src="images/nav/ic_skip_next_white_24dp_1x.png" title="Next"> <img id="NavigatePlay" class="navButton" src="images/nav/ic_play_arrow_white_24dp_1x.png" title="Play"></div><div id="divTwcBottomMiddle"><img id="NavigateRefresh" class="navButton" src="images/nav/ic_refresh_white_24dp_1x.png" title="Refresh"></div><div id="divTwcBottomRight"><img id="ToggleFullScreen" class="navButton" src="images/nav/ic_fullscreen_exit_white_24dp_1x.png" title="Exit Fullscreen"></div></div></div><br><div class="info"><a href="https://github.com/netbymatt/ws4kp#weatherstar-4000">More information</a></div><div class="heading">Selected displays</div><div id="enabledDisplays"></div><div id="divInfo">Location: <span id="spanCity"></span> <span id="spanState"></span><br>Station Id: <span id="spanStationId"></span><br>Radar Id: <span id="spanRadarId"></span><br>Zone Id: <span id="spanZoneId"></span><br></div><div id="divRefresh">Last Update: <span id="spanLastRefresh">(None)</span><br><input id="chkAutoRefresh" name="chkAutoRefresh" type="checkbox"><label id="lblRefreshCountDown" for="chkAutoRefresh">Auto Refresh: <span id="spanRefreshCountDown">--:--</span></label></div><div id="divUnits">Units: <input id="radEnglish" name="radUnits" type="radio" value="ENGLISH"><label for="radEnglish">English</label> <input id="radMetric" name="radUnits" type="radio" value="METRIC"><label for="radMetric">Metric</label></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

562
package-lock.json generated
View file

@ -44,9 +44,9 @@
} }
}, },
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "0.1.3", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz",
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.12.4", "ajv": "^6.12.4",
@ -104,26 +104,10 @@
"fastq": "^1.6.0" "fastq": "^1.6.0"
} }
}, },
"@types/glob": { "@types/json5": {
"version": "7.1.3", "version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true,
"requires": {
"@types/minimatch": "*",
"@types/node": "*"
}
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
"dev": true
},
"@types/node": {
"version": "14.6.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz",
"integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==",
"dev": true "dev": true
}, },
"accepts": { "accepts": {
@ -308,6 +292,17 @@
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true "dev": true
}, },
"array-includes": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
"integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0",
"is-string": "^1.0.5"
}
},
"array-initial": { "array-initial": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
@ -380,6 +375,16 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true "dev": true
}, },
"array.prototype.flat": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
"integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
}
},
"assign-symbols": { "assign-symbols": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@ -985,6 +990,18 @@
} }
} }
}, },
"confusing-browser-globals": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz",
"integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
"dev": true
},
"contains-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
"integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
"dev": true
},
"content-disposition": { "content-disposition": {
"version": "0.5.3", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
@ -1118,7 +1135,6 @@
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": { "requires": {
"object-keys": "^1.0.12" "object-keys": "^1.0.12"
} }
@ -1165,18 +1181,18 @@
} }
}, },
"del": { "del": {
"version": "5.1.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz",
"integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"globby": "^10.0.1", "globby": "^11.0.1",
"graceful-fs": "^4.2.2", "graceful-fs": "^4.2.4",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
"is-path-cwd": "^2.2.0", "is-path-cwd": "^2.2.0",
"is-path-inside": "^3.0.1", "is-path-inside": "^3.0.2",
"p-map": "^3.0.0", "p-map": "^4.0.0",
"rimraf": "^3.0.0", "rimraf": "^3.0.2",
"slash": "^3.0.0" "slash": "^3.0.0"
}, },
"dependencies": { "dependencies": {
@ -1317,6 +1333,66 @@
"is-arrayish": "^0.2.1" "is-arrayish": "^0.2.1"
} }
}, },
"es-abstract": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
}
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"es5-ext": { "es5-ext": {
"version": "0.10.53", "version": "0.10.53",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
@ -1389,13 +1465,13 @@
"dev": true "dev": true
}, },
"eslint": { "eslint": {
"version": "7.11.0", "version": "7.12.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.11.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.1.tgz",
"integrity": "sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw==", "integrity": "sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.1.3", "@eslint/eslintrc": "^0.2.1",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.2", "cross-spawn": "^7.0.2",
@ -1450,6 +1526,127 @@
} }
} }
}, },
"eslint-config-airbnb-base": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz",
"integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==",
"dev": true,
"requires": {
"confusing-browser-globals": "^1.0.9",
"object.assign": "^4.1.0",
"object.entries": "^1.1.2"
}
},
"eslint-import-resolver-node": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
"integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"resolve": "^1.13.1"
}
},
"eslint-module-utils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
"integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"pkg-dir": "^2.0.0"
}
},
"eslint-plugin-import": {
"version": "2.22.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
"integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
"dev": true,
"requires": {
"array-includes": "^3.1.1",
"array.prototype.flat": "^1.2.3",
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.4",
"eslint-module-utils": "^2.6.0",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.1",
"read-pkg-up": "^2.0.0",
"resolve": "^1.17.0",
"tsconfig-paths": "^3.9.0"
},
"dependencies": {
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"isarray": "^1.0.0"
}
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
},
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"strip-bom": "^3.0.0"
}
},
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
"dev": true,
"requires": {
"pify": "^2.0.0"
}
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
"dev": true,
"requires": {
"load-json-file": "^2.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^2.0.0"
}
},
"read-pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
"dev": true,
"requires": {
"find-up": "^2.0.0",
"read-pkg": "^2.0.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
}
}
},
"eslint-scope": { "eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -1862,9 +2059,9 @@
"dev": true "dev": true
}, },
"fastq": { "fastq": {
"version": "1.8.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz",
"integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==",
"dev": true, "dev": true,
"requires": { "requires": {
"reusify": "^1.0.4" "reusify": "^1.0.4"
@ -2073,8 +2270,7 @@
"function-bind": { "function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"dev": true
}, },
"functional-red-black-tree": { "functional-red-black-tree": {
"version": "1.0.1", "version": "1.0.1",
@ -2216,18 +2412,16 @@
} }
}, },
"globby": { "globby": {
"version": "10.0.2", "version": "11.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
"integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/glob": "^7.1.1",
"array-union": "^2.1.0", "array-union": "^2.1.0",
"dir-glob": "^3.0.1", "dir-glob": "^3.0.1",
"fast-glob": "^3.0.3", "fast-glob": "^3.1.1",
"glob": "^7.1.3", "ignore": "^5.1.4",
"ignore": "^5.1.1", "merge2": "^1.3.0",
"merge2": "^1.2.3",
"slash": "^3.0.0" "slash": "^3.0.0"
}, },
"dependencies": { "dependencies": {
@ -2452,6 +2646,14 @@
"glogg": "^1.0.0" "glogg": "^1.0.0"
} }
}, },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -2461,8 +2663,7 @@
"has-symbols": { "has-symbols": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
"dev": true
}, },
"has-value": { "has-value": {
"version": "1.0.0", "version": "1.0.0",
@ -2715,6 +2916,11 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true "dev": true
}, },
"is-callable": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
},
"is-data-descriptor": { "is-data-descriptor": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@ -2735,6 +2941,11 @@
} }
} }
}, },
"is-date-object": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
},
"is-descriptor": { "is-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@ -2787,6 +2998,11 @@
"integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
"dev": true "dev": true
}, },
"is-negative-zero": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
"integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
@ -2834,6 +3050,14 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"dev": true "dev": true
}, },
"is-regex": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
"integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
"requires": {
"has-symbols": "^1.0.1"
}
},
"is-relative": { "is-relative": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
@ -2849,6 +3073,20 @@
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true "dev": true
}, },
"is-string": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
"dev": true
},
"is-symbol": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"requires": {
"has-symbols": "^1.0.1"
}
},
"is-unc-path": { "is-unc-path": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@ -2965,6 +3203,15 @@
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true "dev": true
}, },
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"just-debounce": { "just-debounce": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
@ -3053,6 +3300,24 @@
"strip-bom": "^2.0.0" "strip-bom": "^2.0.0"
} }
}, },
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
"dev": true,
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
},
"dependencies": {
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
}
}
},
"lodash": { "lodash": {
"version": "4.17.20", "version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
@ -3396,11 +3661,15 @@
} }
} }
}, },
"object-inspect": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object-keys": { "object-keys": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
"dev": true
}, },
"object-visit": { "object-visit": {
"version": "1.0.1", "version": "1.0.1",
@ -3435,6 +3704,17 @@
"isobject": "^3.0.0" "isobject": "^3.0.0"
} }
}, },
"object.entries": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz",
"integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5",
"has": "^1.0.3"
}
},
"object.map": { "object.map": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
@ -3464,6 +3744,18 @@
"make-iterator": "^1.0.0" "make-iterator": "^1.0.0"
} }
}, },
"object.values": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
"integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1",
"function-bind": "^1.1.1",
"has": "^1.0.3"
}
},
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -3514,15 +3806,39 @@
"lcid": "^1.0.0" "lcid": "^1.0.0"
} }
}, },
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"dev": true,
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"dev": true,
"requires": {
"p-limit": "^1.1.0"
}
},
"p-map": { "p-map": {
"version": "3.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"aggregate-error": "^3.0.0" "aggregate-error": "^3.0.0"
} }
}, },
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
},
"param-case": { "param-case": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
@ -3686,6 +4002,26 @@
"pinkie": "^2.0.0" "pinkie": "^2.0.0"
} }
}, },
"pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
"dev": true,
"requires": {
"find-up": "^2.1.0"
},
"dependencies": {
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
}
}
},
"plugin-error": { "plugin-error": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
@ -4014,9 +4350,9 @@
} }
}, },
"run-parallel": { "run-parallel": {
"version": "1.1.9", "version": "1.1.10",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz",
"integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==",
"dev": true "dev": true
}, },
"safe-buffer": { "safe-buffer": {
@ -4463,6 +4799,88 @@
} }
} }
}, },
"string.prototype.trimend": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz",
"integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
}
}
},
"string.prototype.trimstart": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz",
"integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
}
}
},
"string_decoder": { "string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@ -4672,6 +5090,26 @@
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true "dev": true
}, },
"tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
"integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
},
"dependencies": {
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
}
}
},
"type": { "type": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
@ -4915,9 +5353,9 @@
"dev": true "dev": true
}, },
"v8-compile-cache": { "v8-compile-cache": {
"version": "2.1.1", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
"dev": true "dev": true
}, },
"v8flags": { "v8flags": {

View file

@ -17,9 +17,11 @@
}, },
"homepage": "https://github.com/netbymatt/ws4kp#readme", "homepage": "https://github.com/netbymatt/ws4kp#readme",
"devDependencies": { "devDependencies": {
"del": "^5.1.0", "del": "^6.0.0",
"ejs": "^3.1.5", "ejs": "^3.1.5",
"eslint": "^7.11.0", "eslint": "^7.12.1",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-plugin-import": "^2.22.1",
"express": "^4.17.1", "express": "^4.17.1",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-clean-css": "^4.3.0", "gulp-clean-css": "^4.3.0",

View file

@ -2,3 +2,4 @@
[1020/120229.281:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) [1020/120229.281:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)
[1020/192548.246:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) [1020/192548.246:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)
[1026/100236.922:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) [1026/100236.922:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)
[1029/153152.790:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)

View file

@ -1,5 +1,5 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const _RegionalCities = [ const RegionalCities = [
{ {
city: 'Atlanta', city: 'Atlanta',
lat: 33.749, lat: 33.749,

View file

@ -1,60 +1,59 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const states = (() => { const states = (() => {
const stateList = { const stateList = {
'Arizona': 'AZ', Arizona: 'AZ',
'Alabama': 'AL', Alabama: 'AL',
'Alaska': 'AK', Alaska: 'AK',
'Arkansas': 'AR', Arkansas: 'AR',
'California': 'CA', California: 'CA',
'Colorado': 'CO', Colorado: 'CO',
'Connecticut': 'CT', Connecticut: 'CT',
'Delaware': 'DE', Delaware: 'DE',
'Florida': 'FL', Florida: 'FL',
'Georgia': 'GA', Georgia: 'GA',
'Hawaii': 'HI', Hawaii: 'HI',
'Idaho': 'ID', Idaho: 'ID',
'Illinois': 'IL', Illinois: 'IL',
'Indiana': 'IN', Indiana: 'IN',
'Iowa': 'IA', Iowa: 'IA',
'Kansas': 'KS', Kansas: 'KS',
'Kentucky': 'KY', Kentucky: 'KY',
'Louisiana': 'LA', Louisiana: 'LA',
'Maine': 'ME', Maine: 'ME',
'Maryland': 'MD', Maryland: 'MD',
'Massachusetts': 'MA', Massachusetts: 'MA',
'Michigan': 'MI', Michigan: 'MI',
'Minnesota': 'MN', Minnesota: 'MN',
'Mississippi': 'MS', Mississippi: 'MS',
'Missouri': 'MO', Missouri: 'MO',
'Montana': 'MT', Montana: 'MT',
'Nebraska': 'NE', Nebraska: 'NE',
'Nevada': 'NV', Nevada: 'NV',
'New Hampshire': 'NH', 'New Hampshire': 'NH',
'New Jersey': 'NJ', 'New Jersey': 'NJ',
'New Mexico': 'NM', 'New Mexico': 'NM',
'New York': 'NY', 'New York': 'NY',
'North Carolina': 'NC', 'North Carolina': 'NC',
'North Dakota': 'ND', 'North Dakota': 'ND',
'Ohio': 'OH', Ohio: 'OH',
'Oklahoma': 'OK', Oklahoma: 'OK',
'Oregon': 'OR', Oregon: 'OR',
'Pennsylvania': 'PA', Pennsylvania: 'PA',
'Rhode Island': 'RI', 'Rhode Island': 'RI',
'South Carolina': 'SC', 'South Carolina': 'SC',
'South Dakota': 'SD', 'South Dakota': 'SD',
'Tennessee': 'TN', Tennessee: 'TN',
'Texas': 'TX', Texas: 'TX',
'Utah': 'UT', Utah: 'UT',
'Vermont': 'VT', Vermont: 'VT',
'Virginia': 'VA', Virginia: 'VA',
'Washington': 'WA', Washington: 'WA',
'West Virginia': 'WV', 'West Virginia': 'WV',
'Wisconsin': 'WI', Wisconsin: 'WI',
'Wyoming': 'WY', Wyoming: 'WY',
}; };
return { return {
getTwoDigitCode: (stateFullName) => stateList[stateFullName], getTwoDigitCode: (stateFullName) => stateList[stateFullName],
}; };
})(); })();

View file

@ -1,4 +1,6 @@
const _StationInfo = { // cspell: disable
// eslint-disable-next-line no-unused-vars
const StationInfo = {
MADC: { MADC: {
id: 'MADC', id: 'MADC',
city: 'Durango Complex', city: 'Durango Complex',

View file

@ -1,30 +1,5 @@
//Atlanta
//Boston
//Chicago
//Cleveland
//Dallas
//Denver
//Detroit
//Hartford
//Houston
//Indianapolis
//Los Angeles
//Miami
//Minneapolis
//New York
//Norfolk
//Orlando
//Philadelphia
//Pittsburgh
//St. Louis
//San Francisco
//Seattle
//Syracuse
//Tampa
//Washington DC
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const _TravelCities = [ const TravelCities = [
{ {
Name: 'Atlanta', Name: 'Atlanta',
Latitude: 33.749, Latitude: 33.749,
@ -146,4 +121,3 @@ const _TravelCities = [
Longitude: -77.0364, Longitude: -77.0364,
}, },
]; ];

View file

@ -1,4 +1,3 @@
'use strict';
/* globals NoSleep, states, navigation, UNITS, utils */ /* globals NoSleep, states, navigation, UNITS, utils */
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
index.init(); index.init();
@ -8,17 +7,16 @@ const index = (() => {
const overrides = { const overrides = {
// '32899, Orlando, Florida, USA': { x: -80.6774, y: 28.6143 }, // '32899, Orlando, Florida, USA': { x: -80.6774, y: 28.6143 },
}; };
const _AutoRefreshIntervalMs = 500; const AutoRefreshIntervalMs = 500;
const _AutoRefreshTotalIntervalMs = 600000; // 10 min. const AutoRefreshTotalIntervalMs = 600000; // 10 min.
const _NoSleep = new NoSleep();
let _AutoSelectQuery = false; let AutoSelectQuery = false;
let _LastUpdate = null; let LastUpdate = null;
let _AutoRefreshIntervalId = null; let AutoRefreshIntervalId = null;
let _AutoRefreshCountMs = 0; let AutoRefreshCountMs = 0;
let _FullScreenOverride = false; let FullScreenOverride = false;
const categories = [ const categories = [
'Land Features', 'Land Features',
@ -37,20 +35,20 @@ const index = (() => {
e.target.select(); e.target.select();
}); });
document.getElementById('NavigateMenu').addEventListener('click', btnNavigateMenu_click); document.getElementById('NavigateMenu').addEventListener('click', btnNavigateMenuClick);
document.getElementById('NavigateRefresh').addEventListener('click', btnNavigateRefresh_click); document.getElementById('NavigateRefresh').addEventListener('click', btnNavigateRefreshClick);
document.getElementById('NavigateNext').addEventListener('click', btnNavigateNext_click); document.getElementById('NavigateNext').addEventListener('click', btnNavigateNextClick);
document.getElementById('NavigatePrevious').addEventListener('click', btnNavigatePrevious_click); document.getElementById('NavigatePrevious').addEventListener('click', btnNavigatePreviousClick);
document.getElementById('NavigatePlay').addEventListener('click', btnNavigatePlay_click); document.getElementById('NavigatePlay').addEventListener('click', btnNavigatePlayClick);
document.getElementById('ToggleFullScreen').addEventListener('click', btnFullScreen_click); document.getElementById('ToggleFullScreen').addEventListener('click', btnFullScreenClick);
document.getElementById('btnGetGps').addEventListener('click', btnGetGps_click); document.getElementById('btnGetGps').addEventListener('click', btnGetGpsClick);
document.getElementById('divTwc').addEventListener('click', () => { document.getElementById('divTwc').addEventListener('click', () => {
if (document.fullscreenElement) UpdateFullScreenNavigate(); if (document.fullscreenElement) UpdateFullScreenNavigate();
}); });
document.addEventListener('keydown', document_keydown); document.addEventListener('keydown', documentKeydown);
document.addEventListener('touchmove', e => { if (_FullScreenOverride) e.preventDefault(); }); document.addEventListener('touchmove', (e) => { if (FullScreenOverride) e.preventDefault(); });
$('#frmGetLatLng #txtAddress').devbridgeAutocomplete({ $('#frmGetLatLng #txtAddress').devbridgeAutocomplete({
serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest', serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest',
@ -58,26 +56,24 @@ const index = (() => {
paramName: 'text', paramName: 'text',
params: { params: {
f: 'json', f: 'json',
countryCode: 'USA', //'USA,PRI,VIR,GUM,ASM', countryCode: 'USA', // 'USA,PRI,VIR,GUM,ASM',
category: cats, category: cats,
maxSuggestions: 10, maxSuggestions: 10,
}, },
dataType: 'json', dataType: 'json',
transformResult: (response) => { transformResult: (response) => {
if (_AutoSelectQuery) { if (AutoSelectQuery) {
_AutoSelectQuery = false; AutoSelectQuery = false;
window.setTimeout(() => { window.setTimeout(() => {
$(ac.suggestionsContainer.children[0]).click(); $(ac.suggestionsContainer.children[0]).click();
}, 1); }, 1);
} }
return { return {
suggestions: $.map(response.suggestions, function (i) { suggestions: $.map(response.suggestions, (i) => ({
return {
value: i.text, value: i.text,
data: i.magicKey, data: i.magicKey,
}; })),
}),
}; };
}, },
minChars: 3, minChars: 3,
@ -96,7 +92,7 @@ const index = (() => {
// Auto load the previous query // Auto load the previous query
const TwcQuery = localStorage.getItem('TwcQuery'); const TwcQuery = localStorage.getItem('TwcQuery');
if (TwcQuery) { if (TwcQuery) {
_AutoSelectQuery = true; AutoSelectQuery = true;
const txtAddress = document.getElementById('txtAddress'); const txtAddress = document.getElementById('txtAddress');
txtAddress.value = TwcQuery; txtAddress.value = TwcQuery;
txtAddress.blur(); txtAddress.blur();
@ -141,7 +137,7 @@ const index = (() => {
document.getElementById('chkAutoRefresh').addEventListener('change', (e) => { document.getElementById('chkAutoRefresh').addEventListener('change', (e) => {
const Checked = e.target.checked; const Checked = e.target.checked;
if (_LastUpdate) { if (LastUpdate) {
if (Checked) { if (Checked) {
StartAutoRefreshTimer(); StartAutoRefreshTimer();
} else { } else {
@ -162,12 +158,10 @@ const index = (() => {
// swipe functionality // swipe functionality
document.getElementById('container').addEventListener('swiped-left', () => swipeCallBack('left')); document.getElementById('container').addEventListener('swiped-left', () => swipeCallBack('left'));
document.getElementById('container').addEventListener('swiped-right', () => swipeCallBack('right')); document.getElementById('container').addEventListener('swiped-right', () => swipeCallBack('right'));
}; };
const changeUnits = (e) => { const changeUnits = (e) => {
const Units = e.target.value; const Units = e.target.value;
e;
localStorage.setItem('TwcUnits', Units); localStorage.setItem('TwcUnits', Units);
AssignLastUpdate(); AssignLastUpdate();
postMessage('units', Units); postMessage('units', Units);
@ -192,20 +186,19 @@ const index = (() => {
if (loc) { if (loc) {
doRedirectToGeometry(loc.feature.geometry); doRedirectToGeometry(loc.feature.geometry);
} else { } else {
alert('An unexpected error occurred. Please try a different search string.'); console.error('An unexpected error occurred. Please try a different search string.');
} }
} }
}; };
const doRedirectToGeometry = (geom) => { const doRedirectToGeometry = (geom) => {
const latLon = {lat:Math.round2(geom.y, 4), lon:Math.round2(geom.x, 4)}; const latLon = { lat: Math.round2(geom.y, 4), lon: Math.round2(geom.x, 4) };
LoadTwcData(latLon); LoadTwcData(latLon);
// Save the query // Save the query
localStorage.setItem('TwcQuery', document.getElementById('txtAddress').value); localStorage.setItem('TwcQuery', document.getElementById('txtAddress').value);
}; };
const btnFullScreen_click = () => { const btnFullScreenClick = () => {
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
EnterFullScreen(); EnterFullScreen();
} else { } else {
@ -213,9 +206,9 @@ const index = (() => {
} }
if (navigation.isPlaying()) { if (navigation.isPlaying()) {
noSleepEnable(); noSleep(true);
} else { } else {
noSleepDisable(); noSleep(false);
} }
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
@ -227,7 +220,8 @@ const index = (() => {
const element = document.getElementById('divTwc'); const element = document.getElementById('divTwc');
// Supports most browsers and their versions. // Supports most browsers and their versions.
const requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen; const requestMethod = element.requestFullScreen || element.webkitRequestFullScreen
|| element.mozRequestFullScreen || element.msRequestFullscreen;
if (requestMethod) { if (requestMethod) {
// Native full screen. // Native full screen.
@ -235,7 +229,7 @@ const index = (() => {
} else { } else {
// iOS doesn't support FullScreen API. // iOS doesn't support FullScreen API.
window.scrollTo(0, 0); window.scrollTo(0, 0);
_FullScreenOverride = true; FullScreenOverride = true;
} }
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
@ -244,8 +238,8 @@ const index = (() => {
const ExitFullscreen = () => { const ExitFullscreen = () => {
// exit full-screen // exit full-screen
if (_FullScreenOverride) { if (FullScreenOverride) {
_FullScreenOverride = false; FullScreenOverride = false;
} }
if (document.exitFullscreen) { if (document.exitFullscreen) {
@ -260,7 +254,7 @@ const index = (() => {
} }
}; };
const btnNavigateMenu_click = () => { const btnNavigateMenuClick = () => {
postMessage('navButton', 'menu'); postMessage('navButton', 'menu');
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
return false; return false;
@ -270,134 +264,133 @@ const index = (() => {
// if latlon is provided store it locally // if latlon is provided store it locally
if (_latLon) LoadTwcData.latLon = _latLon; if (_latLon) LoadTwcData.latLon = _latLon;
// get the data // get the data
const latLon = LoadTwcData.latLon; const { latLon } = LoadTwcData;
// if there's no data stop // if there's no data stop
if (!latLon) return; if (!latLon) return;
document.getElementById('txtAddress').blur(); document.getElementById('txtAddress').blur();
StopAutoRefreshTimer(); StopAutoRefreshTimer();
_LastUpdate = null; LastUpdate = null;
AssignLastUpdate(); AssignLastUpdate();
postMessage('latLon', latLon); postMessage('latLon', latLon);
}; };
const swipeCallBack = (direction) => { const swipeCallBack = (direction) => {
switch (direction) { switch (direction) {
case 'left': case 'left':
btnNavigateNext_click(); btnNavigateNextClick();
break; break;
case 'right': case 'right':
default: default:
btnNavigatePrevious_click(); btnNavigatePreviousClick();
break; break;
} }
}; };
const AssignLastUpdate = () => { const AssignLastUpdate = () => {
let LastUpdate = '(None)'; if (LastUpdate) {
if (_LastUpdate) {
switch (navigation.units()) { switch (navigation.units()) {
case UNITS.english: case UNITS.english:
LastUpdate = _LastUpdate.toLocaleString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }); LastUpdate = LastUpdate.toLocaleString('en-US', {
weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short',
});
break; break;
default: default:
LastUpdate = _LastUpdate.toLocaleString('en-GB', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }); LastUpdate = LastUpdate.toLocaleString('en-GB', {
weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short',
});
break; break;
} }
} }
document.getElementById('spanLastRefresh').innerHTML = LastUpdate; document.getElementById('spanLastRefresh').innerHTML = LastUpdate;
if (_LastUpdate && document.getElementById('chkAutoRefresh').checked) StartAutoRefreshTimer(); if (LastUpdate && document.getElementById('chkAutoRefresh').checked) StartAutoRefreshTimer();
}; };
const btnNavigateRefresh_click = () => { const btnNavigateRefreshClick = () => {
LoadTwcData(); LoadTwcData();
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
return false; return false;
}; };
const btnNavigateNext_click = () => { const btnNavigateNextClick = () => {
postMessage('navButton', 'next'); postMessage('navButton', 'next');
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
return false; return false;
}; };
const btnNavigatePrevious_click = () => { const btnNavigatePreviousClick = () => {
postMessage('navButton', 'previous'); postMessage('navButton', 'previous');
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
return false; return false;
}; };
let _NavigateFadeIntervalId = null; let NavigateFadeIntervalId = null;
const UpdateFullScreenNavigate = () => { const UpdateFullScreenNavigate = () => {
document.activeElement.blur(); document.activeElement.blur();
document.getElementById('divTwcBottom').classList.remove('hidden'); document.getElementById('divTwcBottom').classList.remove('hidden');
document.getElementById('divTwcBottom').classList.add('visible'); document.getElementById('divTwcBottom').classList.add('visible');
if (_NavigateFadeIntervalId) { if (NavigateFadeIntervalId) {
clearTimeout(_NavigateFadeIntervalId); clearTimeout(NavigateFadeIntervalId);
_NavigateFadeIntervalId = null; NavigateFadeIntervalId = null;
} }
_NavigateFadeIntervalId = setTimeout(() => { NavigateFadeIntervalId = setTimeout(() => {
if (document.fullscreenElement) { if (document.fullscreenElement) {
document.getElementById('divTwcBottom').classList.remove('visible'); document.getElementById('divTwcBottom').classList.remove('visible');
document.getElementById('divTwcBottom').classList.add('hidden'); document.getElementById('divTwcBottom').classList.add('hidden');
} }
}, 2000); }, 2000);
}; };
const document_keydown = (e) => { const documentKeydown = (e) => {
const code = (e.keyCode || e.which); const code = (e.keyCode || e.which);
if (document.fullscreenElement || document.activeElement === document.body) { if (document.fullscreenElement || document.activeElement === document.body) {
switch (code) { switch (code) {
case 32: // Space case 32: // Space
btnNavigatePlay_click(); btnNavigatePlayClick();
return false; return false;
case 39: // Right Arrow case 39: // Right Arrow
case 34: // Page Down case 34: // Page Down
btnNavigateNext_click(); btnNavigateNextClick();
return false; return false;
case 37: // Left Arrow case 37: // Left Arrow
case 33: // Page Up case 33: // Page Up
btnNavigatePrevious_click(); btnNavigatePreviousClick();
return false; return false;
case 36: // Home case 36: // Home
btnNavigateMenu_click(); btnNavigateMenuClick();
return false; return false;
case 48: // Restart case 48: // Restart
btnNavigateRefresh_click(); btnNavigateRefreshClick();
return false; return false;
case 70: // F case 70: // F
btnFullScreen_click(); btnFullScreenClick();
return false; return false;
default: default:
} }
} }
return false;
}; };
Math.round2 = (value, decimals) => Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); Math.round2 = (value, decimals) => Number(`${Math.round(`${value}e${decimals}`)}e-${decimals}`);
const btnNavigatePlay_click = () => { const btnNavigatePlayClick = () => {
postMessage('navButton', 'playToggle'); postMessage('navButton', 'playToggle');
UpdateFullScreenNavigate(); UpdateFullScreenNavigate();
@ -411,7 +404,7 @@ const index = (() => {
if (!data.type) return; if (!data.type) return;
switch (data.type) { switch (data.type) {
case 'loaded': case 'loaded':
_LastUpdate = new Date(); LastUpdate = new Date();
AssignLastUpdate(); AssignLastUpdate();
break; break;
@ -422,79 +415,74 @@ const index = (() => {
case 'isPlaying': case 'isPlaying':
localStorage.setItem('TwcPlay', navigation.isPlaying()); localStorage.setItem('TwcPlay', navigation.isPlaying());
if (navigation.isPlaying()) { if (navigation.isPlaying()) {
noSleepEnable(); noSleep(true);
playButton.title = 'Pause'; playButton.title = 'Pause';
playButton.src = 'images/nav/ic_pause_white_24dp_1x.png'; playButton.src = 'images/nav/ic_pause_white_24dp_1x.png';
} else { } else {
noSleepDisable(); noSleep(false);
playButton.title = 'Play'; playButton.title = 'Play';
playButton.src = 'images/nav/ic_play_arrow_white_24dp_1x.png'; playButton.src = 'images/nav/ic_play_arrow_white_24dp_1x.png';
} }
break; break;
default: default:
console.error(`Unknown event '${data.eventType}`); console.error(`Unknown event '${data.eventType}`);
} }
}; };
// post a message to the iframe // post a message to the iframe
const postMessage = (type, message = {}) => { const postMessage = (type, myMessage = {}) => {
navigation.message({type, message}); navigation.message({ type, message: myMessage });
}; };
const StartAutoRefreshTimer = () => { const StartAutoRefreshTimer = () => {
// Ensure that any previous timer has already stopped. // Ensure that any previous timer has already stopped.
// check if timer is running // check if timer is running
if (_AutoRefreshIntervalId) return; if (AutoRefreshIntervalId) return;
// Reset the time elapsed. // Reset the time elapsed.
_AutoRefreshCountMs = 0; AutoRefreshCountMs = 0;
const AutoRefreshTimer = () => { const AutoRefreshTimer = () => {
// Increment the total time elapsed. // Increment the total time elapsed.
_AutoRefreshCountMs += _AutoRefreshIntervalMs; AutoRefreshCountMs += AutoRefreshIntervalMs;
// Display the count down. // Display the count down.
let RemainingMs = (_AutoRefreshTotalIntervalMs - _AutoRefreshCountMs); let RemainingMs = (AutoRefreshTotalIntervalMs - AutoRefreshCountMs);
if (RemainingMs < 0) { if (RemainingMs < 0) {
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.getElementById('spanRefreshCountDown').innerHTML = `${dt.getMinutes() < 10 ? `0${dt.getMinutes()}` : dt.getMinutes()}:${dt.getSeconds() < 10 ? `0${dt.getSeconds()}` : dt.getSeconds()}`;
// Time has elapsed. // Time has elapsed.
if (_AutoRefreshCountMs >= _AutoRefreshTotalIntervalMs) LoadTwcData(); if (AutoRefreshCountMs >= AutoRefreshTotalIntervalMs) LoadTwcData();
}; };
_AutoRefreshIntervalId = window.setInterval(AutoRefreshTimer, _AutoRefreshIntervalMs); AutoRefreshIntervalId = window.setInterval(AutoRefreshTimer, AutoRefreshIntervalMs);
AutoRefreshTimer(); AutoRefreshTimer();
}; };
const StopAutoRefreshTimer = () => { const StopAutoRefreshTimer = () => {
if (_AutoRefreshIntervalId) { if (AutoRefreshIntervalId) {
window.clearInterval(_AutoRefreshIntervalId); window.clearInterval(AutoRefreshIntervalId);
document.getElementById('spanRefreshCountDown').innerHTML = '--:--'; document.getElementById('spanRefreshCountDown').innerHTML = '--:--';
_AutoRefreshIntervalId = null; AutoRefreshIntervalId = null;
} }
}; };
const btnGetGps_click = async () => { const btnGetGpsClick = async () => {
if (!navigator.geolocation) return; if (!navigator.geolocation) return;
const position = await (() => { const position = await (() => new Promise((resolve) => {
return new Promise(resolve => {
navigator.geolocation.getCurrentPosition(resolve); navigator.geolocation.getCurrentPosition(resolve);
}); }))();
})(); const { latitude, longitude } = position.coords;
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
let data; let data;
try { try {
data = await utils.fetch.json('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode', { data = await utils.fetch.json('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode', {
data: { data: {
location: longitude + ',' + latitude, location: `${longitude},${latitude}`,
distance: 1000, // Find location up to 1 KM. distance: 1000, // Find location up to 1 KM.
f: 'json', f: 'json',
}, },
@ -504,7 +492,7 @@ const index = (() => {
console.error(e.status, e.responseJSONe); console.error(e.status, e.responseJSONe);
} }
const ZipCode = data.address.Postal; const ZipCode = data.address.Postal;
const City = data.address.City; const { City } = data.address;
const State = states.getTwoDigitCode(data.address.Region); const State = states.getTwoDigitCode(data.address.Region);
const Country = data.address.CountryCode; const Country = data.address.CountryCode;
const TwcQuery = `${ZipCode}, ${City}, ${State}, ${Country}`; const TwcQuery = `${ZipCode}, ${City}, ${State}, ${Country}`;
@ -519,29 +507,30 @@ const index = (() => {
}; };
const populateWeatherParameters = (weatherParameters) => { const populateWeatherParameters = (weatherParameters) => {
document.getElementById('spanCity').innerHTML = `${weatherParameters.city}, `;
document.getElementById('spanCity').innerHTML = weatherParameters.city + ', ';
document.getElementById('spanState').innerHTML = weatherParameters.state; document.getElementById('spanState').innerHTML = weatherParameters.state;
document.getElementById('spanStationId').innerHTML = weatherParameters.stationId; document.getElementById('spanStationId').innerHTML = weatherParameters.stationId;
document.getElementById('spanRadarId').innerHTML = weatherParameters.radarId; document.getElementById('spanRadarId').innerHTML = weatherParameters.radarId;
document.getElementById('spanZoneId').innerHTML = weatherParameters.zoneId; document.getElementById('spanZoneId').innerHTML = weatherParameters.zoneId;
}; };
// track state of nosleep locally to avoid a null case error when nosleep.disable is called without first calling .enable // track state of nosleep locally to avoid a null case error
// when nosleep.disable is called without first calling .enable
let wakeLock = false; let wakeLock = false;
const noSleepEnable = () => { const noSleep = (enable = false) => {
_NoSleep.enable(); // get a nosleep controller
wakeLock = true; if (!noSleep.controller) noSleep.controller = new NoSleep();
}; // don't call anything if the states match
const noSleepDisable = () => { if (wakeLock === enable) return false;
if (!wakeLock) return; // store the value
_NoSleep.disable(); wakeLock = enable;
wakeLock = false; // call the function
if (enable) return noSleep.controller.enable();
return noSleep.controller.disable();
}; };
return { return {
init, init,
message, message,
}; };
})(); })();

View file

@ -4,8 +4,8 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class Almanac extends WeatherDisplay { class Almanac extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId,'Almanac'); super(navId, elemId, 'Almanac');
// pre-load background images (returns promises) // pre-load background images (returns promises)
this.backgroundImage0 = utils.image.load('images/BackGround3_1.png'); this.backgroundImage0 = utils.image.load('images/BackGround3_1.png');
@ -20,12 +20,11 @@ class Almanac extends WeatherDisplay {
]; ];
this.timing.totalScreens = 2; this.timing.totalScreens = 2;
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// get images for outlook // get images for outlook
const imagePromises = [ const imagePromises = [
@ -34,12 +33,12 @@ class Almanac extends WeatherDisplay {
]; ];
// get sun/moon data // get sun/moon data
const {sun, moon} = this.calcSunMoonData(weatherParameters); const { sun, moon } = this.calcSunMoonData(weatherParameters);
// process images for outlook // process images for outlook
const [outlookTemp, outlookPrecip] = await Promise.all(imagePromises); const [outlookTemp, outlookPrecip] = await Promise.all(imagePromises);
const outlook = this.parseOutlooks(weatherParameters.latitude, weatherParameters.longitude, outlookTemp, outlookPrecip); const outlook = Almanac.parseOutlooks(weatherParameters.latitude, weatherParameters.longitude, outlookTemp, outlookPrecip);
// store the data // store the data
this.data = { this.data = {
@ -52,28 +51,27 @@ class Almanac extends WeatherDisplay {
// share data // share data
this.getDataCallback(); this.getDataCallback();
} }
calcSunMoonData(weatherParameters) { calcSunMoonData(weatherParameters) {
const {DateTime} = luxon; const { DateTime } = luxon;
const sun = [ const sun = [
SunCalc.getTimes(new Date(), weatherParameters.latitude, weatherParameters.longitude), SunCalc.getTimes(new Date(), weatherParameters.latitude, weatherParameters.longitude),
SunCalc.getTimes(DateTime.local().plus({days:1}).toJSDate(), weatherParameters.latitude, weatherParameters.longitude), SunCalc.getTimes(DateTime.local().plus({ days: 1 }).toJSDate(), weatherParameters.latitude, weatherParameters.longitude),
]; ];
// brute force the moon phases by scanning the next 30 days // brute force the moon phases by scanning the next 30 days
const moon = []; const moon = [];
// start with yesterday // start with yesterday
let moonDate = DateTime.local().minus({days:1}); let moonDate = DateTime.local().minus({ days: 1 });
let phase = SunCalc.getMoonIllumination(moonDate.toJSDate()).phase; let { phase } = SunCalc.getMoonIllumination(moonDate.toJSDate());
let iterations = 0; let iterations = 0;
do { do {
// get yesterday's moon info // get yesterday's moon info
const lastPhase = phase; const lastPhase = phase;
// calculate new values // calculate new values
moonDate = moonDate.plus({days:1}); moonDate = moonDate.plus({ days: 1 });
phase = SunCalc.getMoonIllumination(moonDate.toJSDate()).phase; phase = SunCalc.getMoonIllumination(moonDate.toJSDate()).phase;
// check for 4 cases // check for 4 cases
if (lastPhase < 0.25 && phase >= 0.25) moon.push(this.getMoonTransition(0.25, 'First', moonDate)); if (lastPhase < 0.25 && phase >= 0.25) moon.push(this.getMoonTransition(0.25, 'First', moonDate));
@ -82,7 +80,7 @@ class Almanac extends WeatherDisplay {
if (lastPhase > phase) moon.push(this.getMoonTransition(0.00, 'New', moonDate)); if (lastPhase > phase) moon.push(this.getMoonTransition(0.00, 'New', moonDate));
// stop after 30 days or 4 moon phases // stop after 30 days or 4 moon phases
iterations++; iterations += 1;
} while (iterations <= 30 && moon.length < 4); } while (iterations <= 30 && moon.length < 4);
return { return {
@ -94,19 +92,19 @@ class Almanac extends WeatherDisplay {
// get moon transition from one phase to the next by drilling down by hours, minutes and seconds // get moon transition from one phase to the next by drilling down by hours, minutes and seconds
getMoonTransition(threshold, phaseName, start, iteration = 0) { getMoonTransition(threshold, phaseName, start, iteration = 0) {
let moonDate = start; let moonDate = start;
let phase = SunCalc.getMoonIllumination(moonDate.toJSDate()).phase; let { phase } = SunCalc.getMoonIllumination(moonDate.toJSDate());
let iterations = 0; let iterations = 0;
const step = { const step = {
hours: iteration === 0 ? -1:0, hours: iteration === 0 ? -1 : 0,
minutes: iteration === 1 ? 1:0, minutes: iteration === 1 ? 1 : 0,
seconds: iteration === 2 ? -1:0, seconds: iteration === 2 ? -1 : 0,
milliseconds: iteration === 3 ? 1:0, milliseconds: iteration === 3 ? 1 : 0,
}; };
// increasing test // increasing test
let test = (lastPhase,phase,threshold) => lastPhase < threshold && phase >= threshold; let test = (lastPhase, testPhase) => lastPhase < threshold && testPhase >= threshold;
// decreasing test // decreasing test
if (iteration%2===0) test = (lastPhase,phase,threshold) => lastPhase > threshold && phase <= threshold; if (iteration % 2 === 0) test = (lastPhase, testPhase) => lastPhase > threshold && testPhase <= threshold;
do { do {
// store last phase // store last phase
@ -117,42 +115,42 @@ class Almanac extends WeatherDisplay {
// wrap phases > 0.9 to -0.1 for ease of detection // wrap phases > 0.9 to -0.1 for ease of detection
if (phase > 0.9) phase -= 1.0; if (phase > 0.9) phase -= 1.0;
// compare // compare
if (test(lastPhase, phase, threshold)) { if (test(lastPhase, phase)) {
// last iteration is three, return value // last iteration is three, return value
if (iteration >= 3) break; if (iteration >= 3) break;
// iterate recursively // iterate recursively
return this.getMoonTransition(threshold, phaseName, moonDate, iteration+1); return this.getMoonTransition(threshold, phaseName, moonDate, iteration + 1);
} }
iterations++; iterations += 1;
} while (iterations < 1000); } while (iterations < 1000);
return {phase: phaseName, date: moonDate}; return { phase: phaseName, date: moonDate };
} }
// use the color of the pixel to determine the outlook // use the color of the pixel to determine the outlook
parseOutlooks(lat, lon, temp, precip) { static parseOutlooks(lat, lon, temp, precip) {
const {DateTime} = luxon; const { DateTime } = luxon;
const month = DateTime.local(); const month = DateTime.local();
const thisMonth = month.toLocaleString({month: 'short'}); const thisMonth = month.toLocaleString({ month: 'short' });
const nextMonth = month.plus({months: 1}).toLocaleString({month: 'short'}); const nextMonth = month.plus({ months: 1 }).toLocaleString({ month: 'short' });
// draw the images on the canvases // draw the images on the canvases
const tempContext = utils.image.drawLocalCanvas(temp); const tempContext = utils.image.drawLocalCanvas(temp);
const precipContext = utils.image.drawLocalCanvas(precip); const precipContext = utils.image.drawLocalCanvas(precip);
// get the color from each canvas // get the color from each canvas
const tempColor = this.getOutlookColor(lat, lon, tempContext); const tempColor = Almanac.getOutlookColor(lat, lon, tempContext);
const precipColor = this.getOutlookColor(lat, lon, precipContext); const precipColor = Almanac.getOutlookColor(lat, lon, precipContext);
return { return {
thisMonth, thisMonth,
nextMonth, nextMonth,
temperature: this.getOutlookTemperatureIndicator(tempColor), temperature: Almanac.getOutlookTemperatureIndicator(tempColor),
precipitation: this.getOutlookPrecipitationIndicator(precipColor), precipitation: Almanac.getOutlookPrecipitationIndicator(precipColor),
}; };
} }
getOutlookColor (lat, lon, context) { static getOutlookColor(lat, lon, context) {
let x = 0; let x = 0;
let y = 0; let y = 0;
@ -195,11 +193,11 @@ class Almanac extends WeatherDisplay {
// Determine if there is any "non-white" colors around the area. // Determine if there is any "non-white" colors around the area.
// Search a 16x16 region. // Search a 16x16 region.
for (let colorX = x - 8; colorX <= x + 8; colorX++) { for (let colorX = x - 8; colorX <= x + 8; colorX += 1) {
for (let colorY = y - 8; colorY <= y + 8; colorY++) { for (let colorY = y - 8; colorY <= y + 8; colorY += 1) {
const pixelColor = this.getPixelColor(context, colorX, colorY); const pixelColor = Almanac.getPixelColor(context, colorX, colorY);
if ((pixelColor.r !== 0 && pixelColor.g !== 0 && pixelColor.b !== 0) || if ((pixelColor.r !== 0 && pixelColor.g !== 0 && pixelColor.b !== 0)
(pixelColor.r !== 255 && pixelColor.g !== 255 && pixelColor.b !== 255)) { || (pixelColor.r !== 255 && pixelColor.g !== 255 && pixelColor.b !== 255)) {
return pixelColor; return pixelColor;
} }
} }
@ -209,7 +207,7 @@ class Almanac extends WeatherDisplay {
} }
// get rgb values of a pixel // get rgb values of a pixel
getPixelColor (context, x, y) { static getPixelColor(context, x, y) {
const pixelData = context.getImageData(x, y, 1, 1).data; const pixelData = context.getImageData(x, y, 1, 1).data;
return { return {
r: pixelData[0], r: pixelData[0],
@ -219,33 +217,31 @@ class Almanac extends WeatherDisplay {
} }
// get temperature outlook from color // get temperature outlook from color
getOutlookTemperatureIndicator(pixelColor) { static getOutlookTemperatureIndicator(pixelColor) {
if (pixelColor.b > pixelColor.r) { if (pixelColor.b > pixelColor.r) {
return 'Below Normal'; return 'Below Normal';
} else if (pixelColor.r > pixelColor.b) { } if (pixelColor.r > pixelColor.b) {
return 'Above Normal'; return 'Above Normal';
} else {
return 'Normal';
} }
return 'Normal';
} }
// get precipitation outlook from color // get precipitation outlook from color
getOutlookPrecipitationIndicator (pixelColor) { static getOutlookPrecipitationIndicator(pixelColor) {
if (pixelColor.g > pixelColor.r) { if (pixelColor.g > pixelColor.r) {
return 'Above Normal'; return 'Above Normal';
} else if (pixelColor.r > pixelColor.g) { } if (pixelColor.r > pixelColor.g) {
return 'Below Normal'; return 'Below Normal';
} else {
return 'Normal';
} }
return 'Normal';
} }
async drawCanvas() { async drawCanvas() {
super.drawCanvas(); super.drawCanvas();
const info = this.data; const info = this.data;
const {DateTime} = luxon; const { DateTime } = luxon;
const Today = DateTime.local(); const Today = DateTime.local();
const Tomorrow = Today.plus({days: 1}); const Tomorrow = Today.plus({ days: 1 });
// extract moon images // extract moon images
const [FullMoonImage, LastMoonImage, NewMoonImage, FirstMoonImage] = await Promise.all(this.moonImages); const [FullMoonImage, LastMoonImage, NewMoonImage, FirstMoonImage] = await Promise.all(this.moonImages);
@ -261,8 +257,8 @@ class Almanac extends WeatherDisplay {
draw.titleText(this.context, 'Almanac', 'Astronomical'); draw.titleText(this.context, 'Almanac', 'Astronomical');
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 320, 120, Today.toLocaleString({weekday: 'long'}), 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 320, 120, Today.toLocaleString({ weekday: 'long' }), 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 500, 120, Tomorrow.toLocaleString({weekday: 'long'}), 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 500, 120, Tomorrow.toLocaleString({ weekday: 'long' }), 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 150, 'Sunrise:', 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 150, 'Sunrise:', 2);
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 270, 150, DateTime.fromJSDate(info.sun[0].sunrise).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase(), 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 270, 150, DateTime.fromJSDate(info.sun[0].sunrise).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase(), 2);
@ -274,12 +270,11 @@ class Almanac extends WeatherDisplay {
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 70, 220, 'Moon Data:', 2); draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 70, 220, 'Moon Data:', 2);
info.moon.forEach((MoonPhase, Index) => { info.moon.forEach((MoonPhase, Index) => {
const date = MoonPhase.date.toLocaleString({month: 'short', day: 'numeric'}); const date = MoonPhase.date.toLocaleString({ month: 'short', day: 'numeric' });
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120+Index*130, 260, MoonPhase.phase, 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120 + Index * 130, 260, MoonPhase.phase, 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120+Index*130, 390, date, 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120 + Index * 130, 390, date, 2, 'center');
const image = (() => { const image = (() => {
switch (MoonPhase.phase) { switch (MoonPhase.phase) {
@ -294,11 +289,11 @@ class Almanac extends WeatherDisplay {
return FirstMoonImage; return FirstMoonImage;
} }
})(); })();
this.context.drawImage(image, 75+Index*130, 270); this.context.drawImage(image, 75 + Index * 130, 270);
}); });
break; break;
case 1: case 1: {
this.context.drawImage(await this.backgroundImage1, 0, 0); this.context.drawImage(await this.backgroundImage1, 0, 0);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2); draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90); draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
@ -309,14 +304,15 @@ class Almanac extends WeatherDisplay {
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 180, '30 Day Outlook', 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 180, '30 Day Outlook', 2, 'center');
var DateRange = 'MID-' + info.outlook.thisMonth.toUpperCase() + ' TO MID-' + info.outlook.nextMonth.toUpperCase(); const DateRange = `MID-${info.outlook.thisMonth.toUpperCase()} TO MID-${info.outlook.nextMonth.toUpperCase()}`;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 220, DateRange, 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 320, 220, DateRange, 2, 'center');
var Temperature = info.outlook.temperature; const Temperature = info.outlook.temperature;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 300, 'Temperatures: ' + Temperature, 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 300, `Temperatures: ${Temperature}`, 2);
var Precipitation = info.outlook.precipitation; const Precipitation = info.outlook.precipitation;
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 380, 'Precipitation: ' + Precipitation, 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 70, 380, `Precipitation: ${Precipitation}`, 2);
}
} }
this.finishDraw(); this.finishDraw();

View file

@ -3,27 +3,28 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class CurrentWeather extends WeatherDisplay { class CurrentWeather extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId,'Current Conditions'); super(navId, elemId, 'Current Conditions');
// pre-load background image (returns promise) // pre-load background image (returns promise)
this.backgroundImage = utils.image.load('images/BackGround1_1.png'); this.backgroundImage = utils.image.load('images/BackGround1_1.png');
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// Load the observations // Load the observations
let observations, station; let observations; let
station;
// station number counter // station number counter
let stationNum = 0; let stationNum = 0;
while (!observations && stationNum < weatherParameters.stations.length) { while (!observations && stationNum < weatherParameters.stations.length) {
// get the station // get the station
station = weatherParameters.stations[stationNum]; station = weatherParameters.stations[stationNum];
stationNum++; stationNum += 1;
try { try {
// station observations // station observations
observations = await utils.fetch.json(`${station.id}/observations`,{ observations = await utils.fetch.json(`${station.id}/observations`, {
cors: true, cors: true,
data: { data: {
limit: 2, limit: 2,
@ -31,9 +32,9 @@ class CurrentWeather extends WeatherDisplay {
}); });
// test data quality // test data quality
if (observations.features[0].properties.temperature.value === null || if (observations.features[0].properties.temperature.value === null
observations.features[0].properties.windSpeed.value === null || || observations.features[0].properties.windSpeed.value === null
observations.features[0].properties.textDescription === null) { || observations.features[0].properties.textDescription === null) {
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`);
} }
@ -53,7 +54,7 @@ class CurrentWeather extends WeatherDisplay {
utils.image.preload(icons.getWeatherIconFromIconLink(observations.features[0].properties.icon)); utils.image.preload(icons.getWeatherIconFromIconLink(observations.features[0].properties.icon));
// we only get here if there was no error above // we only get here if there was no error above
this.data = Object.assign({}, observations, {station: station}); this.data = { ...observations, station };
this.setStatus(STATUS.loaded); this.setStatus(STATUS.loaded);
this.getDataCallback(); this.getDataCallback();
@ -71,7 +72,7 @@ class CurrentWeather extends WeatherDisplay {
data.DewPoint = Math.round(observations.dewpoint.value); data.DewPoint = Math.round(observations.dewpoint.value);
data.Ceiling = Math.round(observations.cloudLayers[0].base.value); data.Ceiling = Math.round(observations.cloudLayers[0].base.value);
data.CeilingUnit = 'm.'; data.CeilingUnit = 'm.';
data.Visibility = Math.round(observations.visibility.value/1000); data.Visibility = Math.round(observations.visibility.value / 1000);
data.VisibilityUnit = ' km.'; data.VisibilityUnit = ' km.';
data.WindSpeed = Math.round(observations.windSpeed.value); data.WindSpeed = Math.round(observations.windSpeed.value);
data.WindDirection = utils.calc.directionToNSEW(observations.windDirection.value); data.WindDirection = utils.calc.directionToNSEW(observations.windDirection.value);
@ -95,9 +96,9 @@ class CurrentWeather extends WeatherDisplay {
data.Temperature = utils.units.celsiusToFahrenheit(data.Temperature); data.Temperature = utils.units.celsiusToFahrenheit(data.Temperature);
data.TemperatureUnit = 'F'; data.TemperatureUnit = 'F';
data.DewPoint = utils.units.celsiusToFahrenheit(data.DewPoint); data.DewPoint = utils.units.celsiusToFahrenheit(data.DewPoint);
data.Ceiling = Math.round(utils.units.metersToFeet(data.Ceiling)/100)*100; data.Ceiling = Math.round(utils.units.metersToFeet(data.Ceiling) / 100) * 100;
data.CeilingUnit = 'ft.'; data.CeilingUnit = 'ft.';
data.Visibility = utils.units.kilometersToMiles(observations.visibility.value/1000); data.Visibility = utils.units.kilometersToMiles(observations.visibility.value / 1000);
data.VisibilityUnit = ' mi.'; data.VisibilityUnit = ' mi.';
data.WindSpeed = utils.units.kphToMph(data.WindSpeed); data.WindSpeed = utils.units.kphToMph(data.WindSpeed);
data.WindUnit = 'MPH'; data.WindUnit = 'MPH';
@ -109,7 +110,7 @@ class CurrentWeather extends WeatherDisplay {
return data; return data;
} }
async drawCanvas () { async drawCanvas() {
super.drawCanvas(); super.drawCanvas();
// parse each time to deal with a change in units if necessary // parse each time to deal with a change in units if necessary
const data = this.parseData(); const data = this.parseData();
@ -131,14 +132,14 @@ class CurrentWeather extends WeatherDisplay {
draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 195, 170, Conditions, 2, 'center'); draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 195, 170, Conditions, 2, 'center');
draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 80, 330, 'Wind:', 2); draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 80, 330, 'Wind:', 2);
draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 300, 330, data.WindDirection + ' ' + data.WindSpeed, 2, 'right'); draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 300, 330, `${data.WindDirection} ${data.WindSpeed}`, 2, 'right');
if (data.WindGust) draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 80, 375, 'Gusts to ' + data.WindGust, 2); if (data.WindGust) draw.text(this.context, 'Star4000 Extended', '24pt', '#FFFFFF', 80, 375, `Gusts to ${data.WindGust}`, 2);
draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFF00', 315, 120, this.data.station.properties.name.substr(0, 20), 2); draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFF00', 315, 120, this.data.station.properties.name.substr(0, 20), 2);
draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 340, 165, 'Humidity:', 2); draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 340, 165, 'Humidity:', 2);
draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 560, 165, data.Humidity + '%', 2, 'right'); draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 560, 165, `${data.Humidity}%`, 2, 'right');
draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 340, 205, 'Dewpoint:', 2); draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 340, 205, 'Dewpoint:', 2);
draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 560, 205, data.DewPoint + String.fromCharCode(176), 2, 'right'); draw.text(this.context, 'Star4000 Large', 'bold 16pt', '#FFFFFF', 560, 205, data.DewPoint + String.fromCharCode(176), 2, 'right');
@ -213,7 +214,8 @@ class CurrentWeather extends WeatherDisplay {
}); });
} }
shortConditions(condition) { static shortConditions(_condition) {
let condition = _condition;
condition = condition.replace(/Light/g, 'L'); condition = condition.replace(/Light/g, 'L');
condition = condition.replace(/Heavy/g, 'H'); condition = condition.replace(/Heavy/g, 'H');
condition = condition.replace(/Partly/g, 'P'); condition = condition.replace(/Partly/g, 'P');
@ -230,5 +232,4 @@ class CurrentWeather extends WeatherDisplay {
condition = condition.replace(/ with /g, '/'); condition = condition.replace(/ with /g, '/');
return condition; return condition;
} }
} }

View file

@ -1,5 +1,3 @@
'use strict';
/* globals draw, navigation */ /* globals draw, navigation */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -10,7 +8,6 @@ const currentWeatherScroll = (() => {
// local variables // local variables
let context; // currently active context let context; // currently active context
let blankDrawArea; // original state of context let blankDrawArea; // original state of context
let station;
let interval; let interval;
let screenIndex = 0; let screenIndex = 0;
@ -36,7 +33,6 @@ const currentWeatherScroll = (() => {
// draw the data // draw the data
drawScreen(); drawScreen();
}; };
const stop = (reset) => { const stop = (reset) => {
@ -53,7 +49,7 @@ const currentWeatherScroll = (() => {
// increment interval, roll over // increment interval, roll over
const incrementInterval = () => { const incrementInterval = () => {
screenIndex = (screenIndex+1)%(screens.length); screenIndex = (screenIndex + 1) % (screens.length);
// draw new text // draw new text
drawScreen(); drawScreen();
}; };
@ -74,7 +70,7 @@ const currentWeatherScroll = (() => {
// the "screens" are stored in an array for easy addition and removal // the "screens" are stored in an array for easy addition and removal
const screens = [ const screens = [
// station name // station name
(data) => `Conditions at ${data.station.properties.name.substr(0,20)}`, (data) => `Conditions at ${data.station.properties.name.substr(0, 20)}`,
// temperature // temperature
(data) => { (data) => {
@ -108,7 +104,7 @@ const currentWeatherScroll = (() => {
}, },
// visibility // visibility
(data) => `Visib: ${data.Visibility} ${data.VisibilityUnit} Ceiling: ${data.Ceiling===0?'Unlimited':`${data.Ceiling} ${data.CeilingUnit}`}`, (data) => `Visib: ${data.Visibility} ${data.VisibilityUnit} Ceiling: ${data.Ceiling === 0 ? 'Unlimited' : `${data.Ceiling} ${data.CeilingUnit}`}`,
]; ];
// internal draw function with preset parameters // internal draw function with preset parameters

View file

@ -46,17 +46,17 @@ const draw = (() => {
} }
}; };
const text = (context, font, size, color, x, y, text, shadow = 0, align = 'start') => { const text = (context, font, size, color, x, y, myText, shadow = 0, align = 'start') => {
context.textAlign = align; context.textAlign = align;
context.font = size + ` '${font}'`; context.font = `${size} '${font}'`;
context.shadowColor = '#000000'; context.shadowColor = '#000000';
context.shadowOffsetX = shadow; context.shadowOffsetX = shadow;
context.shadowOffsetY = shadow; context.shadowOffsetY = shadow;
context.strokeStyle = '#000000'; context.strokeStyle = '#000000';
context.lineWidth = 2; context.lineWidth = 2;
context.strokeText(text, x, y); context.strokeText(myText, x, y);
context.fillStyle = color; context.fillStyle = color;
context.fillText(text, x, y); context.fillText(myText, x, y);
context.fillStyle = ''; context.fillStyle = '';
context.strokeStyle = ''; context.strokeStyle = '';
context.shadowOffsetX = 0; context.shadowOffsetX = 0;

View file

@ -5,8 +5,8 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class ExtendedForecast extends WeatherDisplay { class ExtendedForecast extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId,'Extended Forecast'); super(navId, elemId, 'Extended Forecast');
// set timings // set timings
this.timing.totalScreens = 2; this.timing.totalScreens = 2;
@ -15,17 +15,16 @@ class ExtendedForecast extends WeatherDisplay {
this.backgroundImage = utils.image.load('images/BackGround2_1.png'); this.backgroundImage = utils.image.load('images/BackGround2_1.png');
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// request us or si units // request us or si units
let units = 'us'; let units = 'us';
if (navigation.units() === UNITS.metric) units = 'si'; if (navigation.units() === UNITS.metric) units = 'si';
let forecast; let forecast;
try { try {
forecast = await utils.fetch.json(weatherParameters.forecast,{ forecast = await utils.fetch.json(weatherParameters.forecast, {
data: { data: {
units, units,
}, },
@ -37,32 +36,36 @@ class ExtendedForecast extends WeatherDisplay {
return; return;
} }
// we only get here if there was no error above // we only get here if there was no error above
this.data = this.parseExtendedForecast(forecast.properties.periods); this.data = ExtendedForecast.parse(forecast.properties.periods);
this.screenIndex = 0; this.screenIndex = 0;
this.setStatus(STATUS.loaded); this.setStatus(STATUS.loaded);
} }
// the api provides the forecast in 12 hour increments, flatten to day increments with high and low temperatures // the api provides the forecast in 12 hour increments, flatten to day increments with high and low temperatures
parseExtendedForecast(fullForecast) { static parse(fullForecast) {
// create a list of days starting with today // create a list of days starting with today
const _Days = [0, 1, 2, 3, 4, 5, 6]; const Days = [0, 1, 2, 3, 4, 5, 6];
const dates = _Days.map(shift => { const dates = Days.map((shift) => {
const date = luxon.DateTime.local().startOf('day').plus({days:shift}); const date = luxon.DateTime.local().startOf('day').plus({ days: shift });
return date.toLocaleString({weekday: 'short'}); return date.toLocaleString({ weekday: 'short' });
}); });
// track the destination forecast index // track the destination forecast index
let destIndex = 0; let destIndex = 0;
const forecast = []; const forecast = [];
fullForecast.forEach(period => { fullForecast.forEach((period) => {
// create the destination object if necessary // create the destination object if necessary
if (!forecast[destIndex]) forecast.push({dayName:'', low: undefined, high: undefined, text: undefined, icon: undefined}); if (!forecast[destIndex]) {
forecast.push({
dayName: '', low: undefined, high: undefined, text: undefined, icon: undefined,
});
}
// get the object to modify/populate // get the object to modify/populate
const fDay = forecast[destIndex]; const fDay = forecast[destIndex];
// high temperature will always be last in the source array so it will overwrite the low values assigned below // high temperature will always be last in the source array so it will overwrite the low values assigned below
fDay.icon = icons.getWeatherIconFromIconLink(period.icon); fDay.icon = icons.getWeatherIconFromIconLink(period.icon);
fDay.text = this.shortenExtendedForecastText(period.shortForecast); fDay.text = ExtendedForecast.shortenExtendedForecastText(period.shortForecast);
fDay.dayName = dates[destIndex]; fDay.dayName = dates[destIndex];
// preload the icon // preload the icon
@ -71,7 +74,7 @@ class ExtendedForecast extends WeatherDisplay {
if (period.isDaytime) { if (period.isDaytime) {
// day time is the high temperature // day time is the high temperature
fDay.high = period.temperature; fDay.high = period.temperature;
destIndex++; destIndex += 1;
} else { } else {
// low temperature // low temperature
fDay.low = period.temperature; fDay.low = period.temperature;
@ -81,7 +84,7 @@ class ExtendedForecast extends WeatherDisplay {
return forecast; return forecast;
} }
shortenExtendedForecastText(long) { static shortenExtendedForecastText(long) {
let short = long; let short = long;
short = short.replace(/ and /g, ' '); short = short.replace(/ and /g, ' ');
short = short.replace(/Slight /g, ''); short = short.replace(/Slight /g, '');
@ -112,7 +115,7 @@ class ExtendedForecast extends WeatherDisplay {
} }
short = short1; short = short1;
if (short2 !== '') { if (short2 !== '') {
short += ' ' + short2; short += ` ${short2}`;
} }
return [short, short1, short2]; return [short, short1, short2];
@ -123,7 +126,7 @@ class ExtendedForecast extends WeatherDisplay {
// determine bounds // determine bounds
// grab the first three or second set of three array elements // grab the first three or second set of three array elements
const forecast = this.data.slice(0+3*this.screenIndex, 3+this.screenIndex*3); const forecast = this.data.slice(0 + 3 * this.screenIndex, 3 + this.screenIndex * 3);
const backgroundImage = await this.backgroundImage; const backgroundImage = await this.backgroundImage;
@ -138,27 +141,27 @@ class ExtendedForecast extends WeatherDisplay {
draw.titleText(this.context, 'Extended', 'Forecast'); draw.titleText(this.context, 'Extended', 'Forecast');
await Promise.all(forecast.map(async (Day, Index) => { await Promise.all(forecast.map(async (Day, Index) => {
const offset = Index*195; const offset = Index * 195;
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 100+offset, 135, Day.dayName.toUpperCase(), 2); draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 100 + offset, 135, Day.dayName.toUpperCase(), 2);
draw.text(this.context, 'Star4000', '24pt', '#8080FF', 85+offset, 345, 'Lo', 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#8080FF', 85 + offset, 345, 'Lo', 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 165+offset, 345, 'Hi', 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFF00', 165 + offset, 345, 'Hi', 2, 'center');
let low = Day.low; let { low } = Day;
if (low !== undefined) { if (low !== undefined) {
if (navigation.units() === UNITS.metric) low = utils.units.rahrenheitToCelsius(low); if (navigation.units() === UNITS.metric) low = utils.units.fahrenheitToCelsius(low);
draw.text(this.context, 'Star4000 Large', '24pt', '#FFFFFF', 85+offset, 385, low, 2, 'center'); draw.text(this.context, 'Star4000 Large', '24pt', '#FFFFFF', 85 + offset, 385, low, 2, 'center');
} }
let high = Day.high; let { high } = Day;
if (navigation.units() === UNITS.metric) high = utils.units.rahrenheitToCelsius(high); if (navigation.units() === UNITS.metric) high = utils.units.fahrenheitToCelsius(high);
draw.text(this.context, 'Star4000 Large', '24pt', '#FFFFFF', 165+offset, 385, high, 2, 'center'); draw.text(this.context, 'Star4000 Large', '24pt', '#FFFFFF', 165 + offset, 385, high, 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120+offset, 270, Day.text[1], 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120 + offset, 270, Day.text[1], 2, 'center');
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120+offset, 310, Day.text[2], 2, 'center'); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 120 + offset, 310, Day.text[2], 2, 'center');
// draw the icon // draw the icon
this.gifs.push(await utils.image.superGifAsync({ this.gifs.push(await utils.image.superGifAsync({
src: Day.icon, src: Day.icon,
auto_play: true, auto_play: true,
canvas: this.canvas, canvas: this.canvas,
x: 70 + Index*195, x: 70 + Index * 195,
y: 150, y: 150,
max_height: 75, max_height: 75,
})); }));

View file

@ -16,10 +16,10 @@ class Hourly extends WeatherDisplay {
this.timing.baseDelay = 20; this.timing.baseDelay = 20;
// 24 hours = 6 pages // 24 hours = 6 pages
const pages = 4; // first page is already displayed, last page doesn't happen const pages = 4; // first page is already displayed, last page doesn't happen
const timingStep = this.hourHeight*4; const timingStep = this.hourHeight * 4;
this.timing.delay = [150+timingStep]; this.timing.delay = [150 + timingStep];
// add additional pages // add additional pages
for (let i = 0; i < pages; i++) this.timing.delay.push(timingStep); for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
// add the final 3 second delay // add the final 3 second delay
this.timing.delay.push(150); this.timing.delay.push(150);
} }
@ -37,34 +37,36 @@ class Hourly extends WeatherDisplay {
this.setStatus(STATUS.failed); this.setStatus(STATUS.failed);
} }
this.data = await this.parseForecast(forecast.properties); this.data = await Hourly.parseForecast(forecast.properties);
this.setStatus(STATUS.loaded); this.setStatus(STATUS.loaded);
this.drawLongCanvas(); this.drawLongCanvas();
} }
// extract specific values from forecast and format as an array // extract specific values from forecast and format as an array
async parseForecast(data) { static async parseForecast(data) {
const temperature = this.expand(data.temperature.values); const temperature = Hourly.expand(data.temperature.values);
const apparentTemperature = this.expand(data.apparentTemperature.values); const apparentTemperature = Hourly.expand(data.apparentTemperature.values);
const windSpeed = this.expand(data.windSpeed.values); const windSpeed = Hourly.expand(data.windSpeed.values);
const windDirection = this.expand(data.windDirection.values); const windDirection = Hourly.expand(data.windDirection.values);
const skyCover = this.expand(data.skyCover.values); // cloud icon const skyCover = Hourly.expand(data.skyCover.values); // cloud icon
const weather = this.expand(data.weather.values); // fog icon const weather = Hourly.expand(data.weather.values); // fog icon
const iceAccumulation = this.expand(data.iceAccumulation.values); // ice icon const iceAccumulation = Hourly.expand(data.iceAccumulation.values); // ice icon
const probabilityOfPrecipitation = this.expand(data.probabilityOfPrecipitation.values); // rain icon const probabilityOfPrecipitation = Hourly.expand(data.probabilityOfPrecipitation.values); // rain icon
const snowfallAmount = this.expand(data.snowfallAmount.values); // snow icon const snowfallAmount = Hourly.expand(data.snowfallAmount.values); // snow icon
const icons = await this.determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed); const icons = await Hourly.determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed);
return temperature.map((val, idx) => { return temperature.map((val, idx) => {
if (navigation.units === UNITS.metric) return { if (navigation.units === UNITS.metric) {
return {
temperature: temperature[idx], temperature: temperature[idx],
apparentTemperature: apparentTemperature[idx], apparentTemperature: apparentTemperature[idx],
windSpeed: windSpeed[idx], windSpeed: windSpeed[idx],
windDirection: utils.calc.directionToNSEW(windDirection[idx]), windDirection: utils.calc.directionToNSEW(windDirection[idx]),
icon: icons[idx], icon: icons[idx],
}; };
}
return { return {
temperature: utils.units.celsiusToFahrenheit(temperature[idx]), temperature: utils.units.celsiusToFahrenheit(temperature[idx]),
@ -73,30 +75,29 @@ class Hourly extends WeatherDisplay {
windDirection: utils.calc.directionToNSEW(windDirection[idx]), windDirection: utils.calc.directionToNSEW(windDirection[idx]),
icon: icons[idx], icon: icons[idx],
}; };
}); });
} }
// given forecast paramaters determine a suitable icon // given forecast paramaters determine a suitable icon
async determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed) { static async determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed) {
const startOfHour = luxon.DateTime.local().startOf('hour'); const startOfHour = luxon.DateTime.local().startOf('hour');
const sunTimes = (await navigation.getSun()).sun; const sunTimes = (await navigation.getSun()).sun;
const overnight = luxon.Interval.fromDateTimes(luxon.DateTime.fromJSDate(sunTimes[0].sunset), luxon.DateTime.fromJSDate(sunTimes[1].sunrise)); const overnight = luxon.Interval.fromDateTimes(luxon.DateTime.fromJSDate(sunTimes[0].sunset), luxon.DateTime.fromJSDate(sunTimes[1].sunrise));
const tomorrowOvernight = luxon.DateTime.fromJSDate(sunTimes[1].sunset); const tomorrowOvernight = luxon.DateTime.fromJSDate(sunTimes[1].sunset);
return skyCover.map((val, idx) => { return skyCover.map((val, idx) => {
const hour = startOfHour.plus({hours: idx}); const hour = startOfHour.plus({ hours: idx });
const isNight = overnight.contains(hour) || (hour > tomorrowOvernight); const isNight = overnight.contains(hour) || (hour > tomorrowOvernight);
return icons.getHourlyIcon(skyCover[idx], weather[idx], iceAccumulation[idx], probabilityOfPrecipitation[idx], snowfallAmount[idx], windSpeed[idx], isNight); return icons.getHourlyIcon(skyCover[idx], weather[idx], iceAccumulation[idx], probabilityOfPrecipitation[idx], snowfallAmount[idx], windSpeed[idx], isNight);
}); });
} }
// expand a set of values with durations to an hour-by-hour array // expand a set of values with durations to an hour-by-hour array
expand(data) { static expand(data) {
const startOfHour = luxon.DateTime.utc().startOf('hour').toMillis(); const startOfHour = luxon.DateTime.utc().startOf('hour').toMillis();
const result = []; // resulting expanded values const result = []; // resulting expanded values
data.forEach(item => { data.forEach((item) => {
let startTime = Date.parse(item.validTime.substr(0, item.validTime.indexOf('/'))); let startTime = Date.parse(item.validTime.substr(0, item.validTime.indexOf('/')));
const duration = luxon.Duration.fromISO(item.validTime.substr(item.validTime.indexOf('/')+1)).shiftTo('milliseconds').values.milliseconds; const duration = luxon.Duration.fromISO(item.validTime.substr(item.validTime.indexOf('/') + 1)).shiftTo('milliseconds').values.milliseconds;
const endTime = startTime + duration; const endTime = startTime + duration;
// loop through duration at one hour intervals // loop through duration at one hour intervals
do { do {
@ -105,39 +106,39 @@ class Hourly extends WeatherDisplay {
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 = startTime + 3600000; startTime += 3600000;
} while (startTime < endTime && result.length < 24); } while (startTime < endTime && result.length < 24);
}); // for each value }); // for each value
return result; return result;
} }
async drawLongCanvas () { async drawLongCanvas() {
// create the "long" canvas if necessary // create the "long" canvas if necessary
if (!this.longCanvas) { if (!this.longCanvas) {
this.longCanvas = document.createElement('canvas'); this.longCanvas = document.createElement('canvas');
this.longCanvas.width = 640; this.longCanvas.width = 640;
this.longCanvas.height = 24*this.hourHeight; this.longCanvas.height = 24 * this.hourHeight;
this.longContext = this.longCanvas.getContext('2d'); this.longContext = this.longCanvas.getContext('2d');
this.longCanvasGifs = []; this.longCanvasGifs = [];
} }
// stop all gifs // stop all gifs
this.longCanvasGifs.forEach(gif => gif.pause()); this.longCanvasGifs.forEach((gif) => gif.pause());
// delete the gifs // delete the gifs
this.longCanvasGifs.length = 0; this.longCanvasGifs.length = 0;
// clean up existing gifs // clean up existing gifs
this.gifs.forEach(gif => gif.pause()); this.gifs.forEach((gif) => gif.pause());
// delete the gifs // delete the gifs
this.gifs.length = 0; this.gifs.length = 0;
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 the "long" canvas with all cities
draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, 24*this.hourHeight); draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, 24 * this.hourHeight);
for (let i = 0; i <= 4; i++) { for (let i = 0; i <= 4; i += 1) {
const y = i * 346; const y = i * 346;
draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040'); draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040');
} }
@ -146,14 +147,13 @@ class Hourly extends WeatherDisplay {
await Promise.all(this.data.map(async (data, index) => { await Promise.all(this.data.map(async (data, index) => {
// calculate base y value // calculate base y value
const y = 50+this.hourHeight*index; const y = 50 + this.hourHeight * index;
// hour // hour
const hour = startingHour.plus({hours: index}); const hour = startingHour.plus({ hours: index });
const formattedHour = hour.toLocaleString({weekday: 'short', hour: 'numeric'}); const formattedHour = hour.toLocaleString({ weekday: 'short', hour: 'numeric' });
draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, formattedHour, 2); draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, formattedHour, 2);
// temperatures, convert to strings with no decimal // temperatures, convert to strings with no decimal
const temperature = Math.round(data.temperature).toString().padStart(3); const temperature = Math.round(data.temperature).toString().padStart(3);
const feelsLike = Math.round(data.apparentTemperature).toString().padStart(3); const feelsLike = Math.round(data.apparentTemperature).toString().padStart(3);
@ -177,8 +177,6 @@ class Hourly extends WeatherDisplay {
y: y - 35, y: y - 35,
max_width: 47, max_width: 47,
})); }));
})); }));
} }
@ -221,7 +219,7 @@ class Hourly extends WeatherDisplay {
const longCanvas = this.getLongCanvas(); const longCanvas = this.getLongCanvas();
// calculate scroll offset and don't go past end // calculate scroll offset and don't go past end
let offsetY = Math.min(longCanvas.height-289, (count-150)); let offsetY = Math.min(longCanvas.height - 289, (count - 150));
// don't let offset go negative // don't let offset go negative
if (offsetY < 0) offsetY = 0; if (offsetY < 0) offsetY = 0;
@ -230,15 +228,15 @@ class Hourly extends WeatherDisplay {
this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289); this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289);
} }
getTravelCitiesDayName(cities) { static getTravelCitiesDayName(cities) {
const {DateTime} = luxon; const { DateTime } = luxon;
// effectively returns early on the first found date // effectively returns early on the first found date
return cities.reduce((dayName, city) => { return cities.reduce((dayName, city) => {
if (city && dayName === '') { if (city && dayName === '') {
// today or tomorrow // today or tomorrow
const day = DateTime.local().plus({days: (city.today)?0:1}); const day = DateTime.local().plus({ days: (city.today) ? 0 : 1 });
// return the day // return the day
return day.toLocaleString({weekday: 'long'}); return day.toLocaleString({ weekday: 'long' });
} }
return dayName; return dayName;
}, ''); }, '');

View file

@ -1,11 +1,9 @@
'use strict';
/* spell-checker: disable */ /* spell-checker: disable */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const icons = (() => { const icons = (() => {
const getWeatherRegionalIconFromIconLink = (link, _isNightTime) => {
const getWeatherRegionalIconFromIconLink = (link, isNightTime) => {
// extract day or night if not provided // extract day or night if not provided
if (isNightTime === undefined) isNightTime = link.indexOf('/night/') >=0; const isNightTime = _isNightTime ?? link.indexOf('/night/') >= 0;
// internal function to add path to returned icon // internal function to add path to returned icon
const addPath = (icon) => `images/r/${icon}`; const addPath = (icon) => `images/r/${icon}`;
@ -16,12 +14,11 @@ const icons = (() => {
// if a 'DualImage' is captured, adjust to just the j parameter // if a 'DualImage' is captured, adjust to just the j parameter
if (conditionName === 'dualimage') { if (conditionName === 'dualimage') {
const match = link.match(/&j=(.*)&/); const match = link.match(/&j=(.*)&/);
conditionName = match[1]; [, conditionName] = match;
} }
// find the icon // find the icon
switch (conditionName + (isNightTime?'-n':'')) { switch (conditionName + (isNightTime ? '-n' : '')) {
case 'skc': case 'skc':
case 'hot': case 'hot':
case 'haze': case 'haze':
@ -132,11 +129,11 @@ const icons = (() => {
} }
}; };
const getWeatherIconFromIconLink = function (link, isNightTime = false) { const getWeatherIconFromIconLink = (link, _isNightTime) => {
// internal function to add path to returned icon // internal function to add path to returned icon
const addPath = (icon) => `images/${icon}`; const addPath = (icon) => `images/${icon}`;
// extract day or night if not provided // extract day or night if not provided
if (isNightTime === undefined) isNightTime = link.indexOf('/night/') >=0; const isNightTime = _isNightTime ?? link.indexOf('/night/') >= 0;
// 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];
@ -145,12 +142,11 @@ const icons = (() => {
// if a 'DualImage' is captured, adjust to just the j parameter // if a 'DualImage' is captured, adjust to just the j parameter
if (conditionName === 'dualimage') { if (conditionName === 'dualimage') {
const match = link.match(/&j=(.*)&/); const match = link.match(/&j=(.*)&/);
conditionName = match[1]; [, conditionName] = match;
} }
// find the icon // find the icon
switch (conditionName + (isNightTime?'-n':'')) { switch (conditionName + (isNightTime ? '-n' : '')) {
case 'skc': case 'skc':
case 'hot': case 'hot':
case 'haze': case 'haze':
@ -266,8 +262,7 @@ const icons = (() => {
let wind = false; let wind = false;
// test the phenomenon for various value if it is provided. // test the phenomenon for various value if it is provided.
weather.forEach(phenomenon => { weather.forEach((phenomenon) => {
console.log(phenomenon.weather);
if (!phenomenon.weather) return; if (!phenomenon.weather) return;
if (phenomenon.weather.toLowerCase().includes('thunder')) thunder = true; if (phenomenon.weather.toLowerCase().includes('thunder')) thunder = true;
if (phenomenon.weather.toLowerCase().includes('snow')) snow = true; if (phenomenon.weather.toLowerCase().includes('snow')) snow = true;
@ -284,7 +279,7 @@ const icons = (() => {
} }
if ((snowfallAmount > 0 || snow) && thunder) return addPath('ThunderSnow.gif'); if ((snowfallAmount > 0 || snow) && thunder) return addPath('ThunderSnow.gif');
if (snowfallAmount > 0 || snow) return addPath('Light-Snow.gif'); if (snowfallAmount > 0 || snow) return addPath('Light-Snow.gif');
if (thunder) return(addPath('Thunderstorm.gif')); if (thunder) return (addPath('Thunderstorm.gif'));
if (probabilityOfPrecipitation > 70) return addPath('Rain-1992.gif'); if (probabilityOfPrecipitation > 70) return addPath('Rain-1992.gif');
if (probabilityOfPrecipitation > 50) return addPath('Shower.gif'); if (probabilityOfPrecipitation > 50) return addPath('Shower.gif');
if (probabilityOfPrecipitation > 30) { if (probabilityOfPrecipitation > 30) {

View file

@ -1,10 +1,10 @@
// current weather conditions display // current weather conditions display
/* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, _StationInfo */ /* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, StationInfo */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class LatestObservations extends WeatherDisplay { class LatestObservations extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId,'Latest Observations'); super(navId, elemId, 'Latest Observations');
// pre-load background image (returns promise) // pre-load background image (returns promise)
this.backgroundImage = utils.image.load('images/BackGround1_1.png'); this.backgroundImage = utils.image.load('images/BackGround1_1.png');
@ -12,44 +12,45 @@ class LatestObservations extends WeatherDisplay {
this.MaximumRegionalStations = 7; this.MaximumRegionalStations = 7;
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// calculate distance to each station // calculate distance to each station
const stationsByDistance = Object.keys(_StationInfo).map(key => { const stationsByDistance = Object.keys(StationInfo).map((key) => {
const station = _StationInfo[key]; const station = StationInfo[key];
const distance = utils.calc.distance(station.lat, station.lon, weatherParameters.latitude, weatherParameters.longitude); const distance = utils.calc.distance(station.lat, station.lon, weatherParameters.latitude, weatherParameters.longitude);
return Object.assign({}, station, {distance}); return { ...station, distance };
}); });
// sort the stations by distance // sort the stations by distance
const sortedStations = stationsByDistance.sort((a,b) => a.distance - b.distance); const sortedStations = stationsByDistance.sort((a, b) => a.distance - b.distance);
// try up to 30 regional stations // try up to 30 regional stations
const regionalStations = sortedStations.slice(0,30); const regionalStations = sortedStations.slice(0, 30);
// get data for regional stations // get data for regional stations
const allConditions = await Promise.all(regionalStations.map(async station => { const allConditions = await Promise.all(regionalStations.map(async (station) => {
try { try {
const data = await utils.fetch.json(`https://api.weather.gov/stations/${station.id}/observations/latest`); const data = await utils.fetch.json(`https://api.weather.gov/stations/${station.id}/observations/latest`);
// test for temperature, weather and wind values present // test for temperature, weather and wind values present
if (data.properties.temperature.value === null || if (data.properties.temperature.value === null
data.properties.textDescription === '' || || data.properties.textDescription === ''
data.properties.windSpeed.value === null) return; || data.properties.windSpeed.value === null) return false;
// format the return values // format the return values
return Object.assign({}, data.properties, { return {
...data.properties,
StationId: station.id, StationId: station.id,
city: station.city, city: station.city,
}); };
} catch (e) { } catch (e) {
console.log(`Unable to get latest observations for ${station.id}`); console.log(`Unable to get latest observations for ${station.id}`);
return; return false;
} }
})); }));
// remove and stations that did not return data // remove and stations that did not return data
const actualConditions = allConditions.filter(condition => condition); const actualConditions = allConditions.filter((condition) => condition);
// cut down to the maximum of 7 // cut down to the maximum of 7
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 < 1) {
@ -64,7 +65,7 @@ class LatestObservations extends WeatherDisplay {
const conditions = this.data; const conditions = this.data;
// sort array by station name // sort array by station name
const sortedConditions = conditions.sort((a,b) => ((a.Name < b.Name) ? -1 : ((a.Name > b.Name) ? 1 : 0))); const sortedConditions = conditions.sort((a, b) => ((a.Name < b.Name) ? -1 : 1));
this.context.drawImage(await this.backgroundImage, 0, 0); this.context.drawImage(await this.backgroundImage, 0, 0);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2); draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
@ -75,9 +76,9 @@ class LatestObservations extends WeatherDisplay {
draw.titleText(this.context, 'Latest', 'Observations'); draw.titleText(this.context, 'Latest', 'Observations');
if (navigation.units() === UNITS.english) { if (navigation.units() === UNITS.english) {
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 295, 105, String.fromCharCode(176) + 'F', 2); draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 295, 105, `${String.fromCharCode(176)}F`, 2);
} else { } else {
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 295, 105, String.fromCharCode(176) + 'C', 2); draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 295, 105, `${String.fromCharCode(176)}C`, 2);
} }
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 345, 105, 'WEATHER', 2); draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 345, 105, 'WEATHER', 2);
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 495, 105, 'WIND', 2); draw.text(this.context, 'Star4000 Small', '24pt', '#FFFFFF', 495, 105, 'WIND', 2);
@ -95,7 +96,7 @@ class LatestObservations extends WeatherDisplay {
} }
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 65, y, condition.city.substr(0, 14), 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 65, y, condition.city.substr(0, 14), 2);
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 345, y, this.shortenCurrentConditions(condition.textDescription).substr(0, 9), 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 345, y, LatestObservations.shortenCurrentConditions(condition.textDescription).substr(0, 9), 2);
if (WindSpeed > 0) { if (WindSpeed > 0) {
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 495, y, windDirection + (Array(6 - windDirection.length - WindSpeed.toString().length).join(' ')) + WindSpeed.toString(), 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 495, y, windDirection + (Array(6 - windDirection.length - WindSpeed.toString().length).join(' ')) + WindSpeed.toString(), 2);
@ -113,7 +114,8 @@ class LatestObservations extends WeatherDisplay {
this.finishDraw(); this.finishDraw();
} }
shortenCurrentConditions(condition) { static shortenCurrentConditions(_condition) {
let condition = _condition;
condition = condition.replace(/Light/, 'L'); condition = condition.replace(/Light/, 'L');
condition = condition.replace(/Heavy/, 'H'); condition = condition.replace(/Heavy/, 'H');
condition = condition.replace(/Partly/, 'P'); condition = condition.replace(/Partly/, 'P');

View file

@ -1,23 +1,22 @@
// display text based local forecast // display text based local forecast
/* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation*/ /* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class LocalForecast extends WeatherDisplay { class LocalForecast extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId, 'Local Forecast'); super(navId, elemId, 'Local Forecast');
// set timings // set timings
this.timing.baseDelay= 5000; this.timing.baseDelay = 5000;
// pre-load background image (returns promise) // pre-load background image (returns promise)
this.backgroundImage = utils.image.load('images/BackGround1_1.png'); this.backgroundImage = utils.image.load('images/BackGround1_1.png');
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// get raw data // get raw data
const rawData = await this.getRawData(weatherParameters); const rawData = await this.getRawData(weatherParameters);
@ -27,7 +26,7 @@ class LocalForecast extends WeatherDisplay {
return; return;
} }
// parse raw data // parse raw data
const conditions = this.parseLocalForecast(rawData); const conditions = LocalForecast.parse(rawData);
// split this forecast into the correct number of screens // split this forecast into the correct number of screens
const maxRows = 7; const maxRows = 7;
@ -36,9 +35,9 @@ class LocalForecast extends WeatherDisplay {
this.screenTexts = []; this.screenTexts = [];
// read each text // read each text
conditions.forEach(condition => { conditions.forEach((condition) => {
// process the text // process the text
let text = condition.DayName.toUpperCase() + '...'; let text = `${condition.DayName.toUpperCase()}...`;
let conditionText = condition.Text; let conditionText = condition.Text;
if (navigation.units() === UNITS.metric) { if (navigation.units() === UNITS.metric) {
conditionText = condition.TextC; conditionText = condition.TextC;
@ -52,15 +51,13 @@ class LocalForecast extends WeatherDisplay {
const maxRowCount = maxRows; const maxRowCount = maxRows;
let rowCount = 0; let rowCount = 0;
// if (PrependAlert) { // if (PrependAlert) {
// ScreenText = LocalForecastScreenTexts[LocalForecastScreenTexts.length - 1]; // ScreenText = LocalForecastScreenTexts[LocalForecastScreenTexts.length - 1];
// rowCount = ScreenText.split('\n').length - 1; // rowCount = ScreenText.split('\n').length - 1;
// } // }
for (let i = 0; i <= lineCount - 1; i++) { for (let i = 0; i <= lineCount - 1; i += 1) {
if (lines[i] === '') continue; if (lines[i] !== '') {
if (rowCount > maxRowCount - 1) { if (rowCount > maxRowCount - 1) {
// if (PrependAlert) { // if (PrependAlert) {
// LocalForecastScreenTexts[LocalForecastScreenTexts.length - 1] = ScreenText; // LocalForecastScreenTexts[LocalForecastScreenTexts.length - 1] = ScreenText;
@ -72,8 +69,9 @@ class LocalForecast extends WeatherDisplay {
rowCount = 0; rowCount = 0;
} }
ScreenText += lines[i] + '\n'; ScreenText += `${lines[i]}\n`;
rowCount++; rowCount += 1;
}
} }
// if (PrependAlert) { // if (PrependAlert) {
// this.screenTexts[this.screenTexts.length - 1] = ScreenText; // this.screenTexts[this.screenTexts.length - 1] = ScreenText;
@ -99,7 +97,6 @@ class LocalForecast extends WeatherDisplay {
units, units,
}, },
}); });
} catch (e) { } catch (e) {
console.error(`GetWeatherForecast failed: ${weatherParameters.forecast}`); console.error(`GetWeatherForecast failed: ${weatherParameters.forecast}`);
console.error(e.status, e.responseJSON); console.error(e.status, e.responseJSON);
@ -125,15 +122,15 @@ class LocalForecast extends WeatherDisplay {
draw.box(this.context, 'rgb(33, 40, 90)', 65, 105, 505, 280); draw.box(this.context, 'rgb(33, 40, 90)', 65, 105, 505, 280);
// Draw the text. // Draw the text.
this.screenTexts[this.screenIndex].split('\n').forEach((text, index) => { this.screenTexts[this.screenIndex].split('\n').forEach((text, index) => {
draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 75, 140+40*index, text, 2); draw.text(this.context, 'Star4000', '24pt', '#FFFFFF', 75, 140 + 40 * index, text, 2);
}); });
this.finishDraw(); this.finishDraw();
} }
// format the forecast // format the forecast
parseLocalForecast (forecast) { static parse(forecast) {
// only use the first 6 lines // only use the first 6 lines
return forecast.properties.periods.slice(0,6).map(text => ({ return forecast.properties.periods.slice(0, 6).map((text) => ({
// format day and text // format day and text
DayName: text.name.toUpperCase(), DayName: text.name.toUpperCase(),
Text: text.detailedForecast, Text: text.detailedForecast,

View file

@ -1,6 +1,5 @@
'use strict';
// navigation handles progress, next/previous and initial load messages from the parent frame // navigation handles progress, next/previous and initial load messages from the parent frame
/* globals index, utils, _StationInfo, STATUS */ /* globals index, utils, StationInfo, STATUS */
/* globals CurrentWeather, LatestObservations, TravelForecast, RegionalForecast, LocalForecast, ExtendedForecast, Almanac, Radar, Progress, Hourly */ /* globals CurrentWeather, LatestObservations, TravelForecast, RegionalForecast, LocalForecast, ExtendedForecast, Almanac, Radar, Progress, Hourly */
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
@ -13,11 +12,11 @@ const UNITS = {
}; };
const navigation = (() => { const navigation = (() => {
let weatherParameters = {};
let displays = []; let displays = [];
let currentUnits = UNITS.english; let currentUnits = UNITS.english;
let playing = false; let playing = false;
let progress; let progress;
const weatherParameters = {};
// current conditions and sunrise/sunset are made available from the display below // current conditions and sunrise/sunset are made available from the display below
let currentWeather; let currentWeather;
@ -46,11 +45,10 @@ const navigation = (() => {
default: default:
console.error(`Unknown event ${data.type}`); console.error(`Unknown event ${data.type}`);
} }
}; };
const postMessage = (type, message = {}) => { const postMessage = (type, myMessage = {}) => {
index.message({type, message}); index.message({ type, message: myMessage });
}; };
const getWeather = async (latLon) => { const getWeather = async (latLon) => {
@ -62,11 +60,11 @@ const navigation = (() => {
const StationId = stations.features[0].properties.stationIdentifier; const StationId = stations.features[0].properties.stationIdentifier;
let city = point.properties.relativeLocation.properties.city; let { city } = point.properties.relativeLocation.properties;
if (StationId in _StationInfo) { if (StationId in StationInfo) {
city = _StationInfo[StationId].city; city = StationInfo[StationId].city;
city = city.split('/')[0]; [city] = city.split('/');
} }
// populate the weather parameters // populate the weather parameters
@ -89,13 +87,13 @@ const navigation = (() => {
// draw the progress canvas and hide others // draw the progress canvas and hide others
hideAllCanvases(); hideAllCanvases();
document.getElementById('loading').style.display = 'none'; document.getElementById('loading').style.display = 'none';
progress = new Progress(-1,'progress'); progress = new Progress(-1, 'progress');
await progress.drawCanvas(); await progress.drawCanvas();
progress.showCanvas(); progress.showCanvas();
// start loading canvases if necessary // start loading canvases if necessary
if (displays.length === 0) { if (displays.length === 0) {
currentWeather = new CurrentWeather(0,'currentWeather'); currentWeather = new CurrentWeather(0, 'currentWeather');
almanac = new Almanac(7, 'almanac'); almanac = new Almanac(7, 'almanac');
displays = [ displays = [
currentWeather, currentWeather,
@ -110,8 +108,7 @@ const navigation = (() => {
]; ];
} }
// call for new data on each display // call for new data on each display
displays.forEach(display => display.getData(weatherParameters)); displays.forEach((display) => display.getData(weatherParameters));
}; };
// receive a status update from a module {id, value} // receive a status update from a module {id, value}
@ -130,12 +127,12 @@ const navigation = (() => {
}; };
const countLoadedCanvases = () => displays.reduce((acc, display) => { const countLoadedCanvases = () => displays.reduce((acc, display) => {
if (display.status !== STATUS.loading) return acc+1; if (display.status !== STATUS.loading) return acc + 1;
return acc; return acc;
},0); }, 0);
const hideAllCanvases = () => { const hideAllCanvases = () => {
displays.forEach(display => display.hideCanvas()); displays.forEach((display) => display.hideCanvas());
}; };
const units = () => currentUnits; const units = () => currentUnits;
@ -168,9 +165,9 @@ const navigation = (() => {
}; };
// receive navigation messages from displays // receive navigation messages from displays
const displayNavMessage = (message) => { const displayNavMessage = (myMessage) => {
if (message.type === msg.response.previous) loadDisplay(-1); if (myMessage.type === msg.response.previous) loadDisplay(-1);
if (message.type === msg.response.next) loadDisplay(1); if (myMessage.type === msg.response.next) loadDisplay(1);
}; };
// navigate to next or previous // navigate to next or previous
@ -192,9 +189,9 @@ const navigation = (() => {
const totalDisplays = displays.length; const totalDisplays = displays.length;
const curIdx = currentDisplayIndex(); const curIdx = currentDisplayIndex();
let idx; let idx;
for (let i = 0; i < totalDisplays; i++) { for (let i = 0; i < totalDisplays; i += 1) {
// convert form simple 0-10 to start at current display index +/-1 and wrap // convert form simple 0-10 to start at current display index +/-1 and wrap
idx = utils.calc.wrap(curIdx+(i+1)*direction,totalDisplays); idx = utils.calc.wrap(curIdx + (i + 1) * direction, totalDisplays);
if (displays[idx].status === STATUS.loaded) break; if (displays[idx].status === STATUS.loaded) break;
} }
const newDisplay = displays[idx]; const newDisplay = displays[idx];
@ -207,12 +204,10 @@ const navigation = (() => {
// get the current display index or value // get the current display index or value
const currentDisplayIndex = () => { const currentDisplayIndex = () => {
let index = displays.findIndex(display=>display.isActive()); const index = displays.findIndex((display) => display.isActive());
return index; return index;
}; };
const currentDisplay = () => { const currentDisplay = () => displays[currentDisplayIndex()];
return displays[currentDisplayIndex()];
};
const setPlaying = (newValue) => { const setPlaying = (newValue) => {
playing = newValue; playing = newValue;

View file

@ -4,7 +4,7 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class Progress extends WeatherDisplay { class Progress extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId, elemId); super(navId, elemId);
// pre-load background image (returns promise) // pre-load background image (returns promise)
@ -34,18 +34,17 @@ class Progress extends WeatherDisplay {
draw.horizontalGradientSingle(this.context, 584, 90, 640, 399, draw.sideColor1, draw.sideColor2); draw.horizontalGradientSingle(this.context, 584, 90, 640, 399, draw.sideColor1, draw.sideColor2);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2); draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90); draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
draw.titleText(this.context, 'WeatherStar', '4000+ ' + this.version); draw.titleText(this.context, 'WeatherStar', `4000+ ${this.version}`);
} }
this.finishDraw(); this.finishDraw();
// if no displays provided just draw the backgrounds (above) // if no displays provided just draw the backgrounds (above)
if (!displays) return; if (!displays) return;
displays.forEach((display, idx) => { displays.forEach((display, idx) => {
const y = 120 + idx*29; const y = 120 + idx * 29;
const dots = Array(120 - Math.floor(display.name.length * 2.5)).join('.'); const dots = Array(120 - Math.floor(display.name.length * 2.5)).join('.');
draw.text(this.context, 'Star4000 Extended', '19pt', '#ffffff', 70, y, display.name + dots, 2); draw.text(this.context, 'Star4000 Extended', '19pt', '#ffffff', 70, y, display.name + dots, 2);
let statusText; let statusText;
let statusColor; let statusColor;
switch (display.status) { switch (display.status) {
@ -77,24 +76,21 @@ class Progress extends WeatherDisplay {
// Erase any dots that spill into the status text. // Erase any dots that spill into the status text.
this.context.drawImage(backgroundImage, 475, y - 20, 165, 30, 475, y - 20, 165, 30); this.context.drawImage(backgroundImage, 475, y - 20, 165, 30, 475, y - 20, 165, 30);
draw.text(this.context, 'Star4000 Extended', '19pt', statusColor, 565, y, statusText, 2, 'end'); draw.text(this.context, 'Star4000 Extended', '19pt', statusColor, 565, y, statusText, 2, 'end');
}); });
// calculate loaded percent // calculate loaded percent
const loadedPercent = (loadedCount/displays.length); const loadedPercent = (loadedCount / displays.length);
if (loadedPercent < 1.0) { if (loadedPercent < 1.0) {
// Draw a box for the progress. // Draw a box for the progress.
draw.box(this.context, '#000000', 51, 428, 534, 22); draw.box(this.context, '#000000', 51, 428, 534, 22);
draw.box(this.context, '#ffffff', 53, 430, 530, 18); draw.box(this.context, '#ffffff', 53, 430, 530, 18);
// update the progress gif // update the progress gif
draw.box(this.context, '#1d7fff', 55, 432, 526*loadedPercent, 14); draw.box(this.context, '#1d7fff', 55, 432, 526 * loadedPercent, 14);
} else { } else {
// restore the background // restore the background
this.context.drawImage(backgroundImage, 51, 428, 534, 22, 51, 428, 534, 22); this.context.drawImage(backgroundImage, 51, 428, 534, 22, 51, 428, 534, 22);
} }
} }
canvasClick(e) { canvasClick(e) {
@ -106,9 +102,9 @@ class Progress extends WeatherDisplay {
if (x < 440 || x > 570) return; if (x < 440 || x > 570) return;
// stop playing // stop playing
navigation.message('navButton', stop); navigation.message('navButton');
// use the y value to determine an index // use the y value to determine an index
const index = Math.floor((y-100)/29); const index = Math.floor((y - 100) / 29);
const display = navigation.getDisplay(index); const display = navigation.getDisplay(index);
if (display && display.status === STATUS.loaded) { if (display && display.status === STATUS.loaded) {
display.showCanvas(navigation.msg.command.firstFrame); display.showCanvas(navigation.msg.command.firstFrame);

View file

@ -3,42 +3,42 @@
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class Radar extends WeatherDisplay { class Radar extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId,'Local Radar'); super(navId, elemId, 'Local Radar');
// set max images // set max images
this.dopplerRadarImageMax = 6; this.dopplerRadarImageMax = 6;
// update timing // update timing
this.timing.baseDelay = 350; this.timing.baseDelay = 350;
this.timing.delay = [ this.timing.delay = [
{time: 4, si: 5}, { time: 4, si: 5 },
{time: 1, si: 0}, { time: 1, si: 0 },
{time: 1, si: 1}, { time: 1, si: 1 },
{time: 1, si: 2}, { time: 1, si: 2 },
{time: 1, si: 3}, { time: 1, si: 3 },
{time: 1, si: 4}, { time: 1, si: 4 },
{time: 4, si: 5}, { time: 4, si: 5 },
{time: 1, si: 0}, { time: 1, si: 0 },
{time: 1, si: 1}, { time: 1, si: 1 },
{time: 1, si: 2}, { time: 1, si: 2 },
{time: 1, si: 3}, { time: 1, si: 3 },
{time: 1, si: 4}, { time: 1, si: 4 },
{time: 4, si: 5}, { time: 4, si: 5 },
{time: 1, si: 0}, { time: 1, si: 0 },
{time: 1, si: 1}, { time: 1, si: 1 },
{time: 1, si: 2}, { time: 1, si: 2 },
{time: 1, si: 3}, { time: 1, si: 3 },
{time: 1, si: 4}, { time: 1, si: 4 },
{time: 12, si: 5}, { time: 12, si: 5 },
]; ];
// pre-load background image (returns promise) // pre-load background image (returns promise)
this.backgroundImage = utils.image.load('images/BackGround4_1.png'); this.backgroundImage = utils.image.load('images/BackGround4_1.png');
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// ALASKA ISN'T SUPPORTED! // ALASKA ISN'T SUPPORTED!
if (weatherParameters.state === 'AK') { if (weatherParameters.state === 'AK') {
@ -47,7 +47,7 @@ class Radar extends WeatherDisplay {
} }
// date and time parsing // date and time parsing
const {DateTime} = luxon; const { DateTime } = luxon;
// get the base map // get the base map
let src = 'images/4000RadarMap2.jpg'; let src = 'images/4000RadarMap2.jpg';
@ -59,9 +59,9 @@ class Radar extends WeatherDisplay {
let radarHtml; let radarHtml;
try { try {
// get a list of available radars // get a list of available radars
radarHtml = await utils.fetch.text(baseUrl, {cors: true}); radarHtml = await utils.fetch.text(baseUrl, { cors: true });
} catch (e) { } catch (e) {
console.log('Unable to get list of radars'); console.error('Unable to get list of radars');
console.error(e); console.error(e);
this.setStatus(STATUS.failed); this.setStatus(STATUS.failed);
return; return;
@ -72,17 +72,17 @@ class Radar extends WeatherDisplay {
const xmlDoc = parser.parseFromString(radarHtml, 'text/html'); const xmlDoc = parser.parseFromString(radarHtml, 'text/html');
const anchors = xmlDoc.getElementsByTagName('a'); const anchors = xmlDoc.getElementsByTagName('a');
const gifs = []; const gifs = [];
for (let idx in anchors) { Object.values(anchors).forEach((a) => {
gifs.push(anchors[idx].innerHTML); gifs.push(a.innerHTML);
} });
// filter for selected urls // filter for selected urls
let filter = /Conus_\d/; let filter = /Conus_\d/;
if (weatherParameters.state === 'HI') filter = /hawaii_\d/; if (weatherParameters.state === 'HI') filter = /hawaii_\d/;
// get the last few images // get the last few images
const urlsFull = gifs.filter(gif => gif && gif.match(filter)); const urlsFull = gifs.filter((gif) => gif && gif.match(filter));
const urls = urlsFull.slice(-(this.dopplerRadarImageMax-1)); const urls = urlsFull.slice(-(this.dopplerRadarImageMax - 1));
// add additional 'latest.gif' // add additional 'latest.gif'
if (weatherParameters.state !== 'HI') urls.push('latest_radaronly.gif'); if (weatherParameters.state !== 'HI') urls.push('latest_radaronly.gif');
@ -97,13 +97,13 @@ class Radar extends WeatherDisplay {
if (weatherParameters.state === 'HI') { if (weatherParameters.state === 'HI') {
width = 600; width = 600;
height = 571; height = 571;
sourceXY = this.getXYFromLatitudeLongitudeHI(weatherParameters.latitude, weatherParameters.longitude, offsetX, offsetY); sourceXY = Radar.getXYFromLatitudeLongitudeHI(weatherParameters.latitude, weatherParameters.longitude, offsetX, offsetY);
} else { } else {
width = 2550; width = 2550;
height = 1600; height = 1600;
offsetX *= 2; offsetX *= 2;
offsetY *= 2; offsetY *= 2;
sourceXY = this.getXYFromLatitudeLongitudeDoppler(weatherParameters.latitude, weatherParameters.longitude, offsetX, offsetY); sourceXY = Radar.getXYFromLatitudeLongitudeDoppler(weatherParameters.latitude, weatherParameters.longitude, offsetX, offsetY);
} }
// create working context for manipulation // create working context for manipulation
@ -116,7 +116,7 @@ class Radar extends WeatherDisplay {
// calculate radar offsets // calculate radar offsets
let radarOffsetX = 117; let radarOffsetX = 117;
let radarOffsetY = 60; let radarOffsetY = 60;
let radarSourceXY = this.getXYFromLatitudeLongitudeDoppler(weatherParameters.latitude, weatherParameters.longitude, offsetX, offsetY); let radarSourceXY = Radar.getXYFromLatitudeLongitudeDoppler(weatherParameters.latitude, weatherParameters.longitude, offsetX, offsetY);
let radarSourceX = radarSourceXY.x / 2; let radarSourceX = radarSourceXY.x / 2;
let radarSourceY = radarSourceXY.y / 2; let radarSourceY = radarSourceXY.y / 2;
@ -176,7 +176,7 @@ class Radar extends WeatherDisplay {
} }
// get the base map // get the base map
context.drawImage(await this.baseMap, sourceXY.x, sourceXY.y, offsetX*2, offsetY*2, 0, 0, 640, 367); context.drawImage(await this.baseMap, sourceXY.x, sourceXY.y, offsetX * 2, offsetY * 2, 0, 0, 640, 367);
// crop the radar image // crop the radar image
const cropCanvas = document.createElement('canvas'); const cropCanvas = document.createElement('canvas');
@ -186,10 +186,10 @@ class Radar extends WeatherDisplay {
cropContext.imageSmoothingEnabled = false; cropContext.imageSmoothingEnabled = false;
cropContext.drawImage(workingCanvas, radarSourceX, radarSourceY, (radarOffsetX * 2), (radarOffsetY * 2.33), 0, 0, 640, 367); cropContext.drawImage(workingCanvas, radarSourceX, radarSourceY, (radarOffsetX * 2), (radarOffsetY * 2.33), 0, 0, 640, 367);
// clean the image // clean the image
this.removeDopplerRadarImageNoise(cropContext); Radar.removeDopplerRadarImageNoise(cropContext);
// merge the radar and map // merge the radar and map
this.mergeDopplerRadarImage(context, cropContext); Radar.mergeDopplerRadarImage(context, cropContext);
return { return {
canvas, canvas,
@ -199,9 +199,9 @@ class Radar extends WeatherDisplay {
// set max length // set max length
this.timing.totalScreens = radarInfo.length; this.timing.totalScreens = radarInfo.length;
// store the images // store the images
this.data = radarInfo.map(radar=>radar.canvas); this.data = radarInfo.map((radar) => radar.canvas);
this.times = radarInfo.map(radar=>radar.time); this.times = radarInfo.map((radar) => radar.time);
this.setStatus(STATUS.loaded); this.setStatus(STATUS.loaded);
} }
@ -209,7 +209,7 @@ class Radar extends WeatherDisplay {
super.drawCanvas(); super.drawCanvas();
if (this.screenIndex === -1) return; if (this.screenIndex === -1) return;
this.context.drawImage(await this.backgroundImage, 0, 0); this.context.drawImage(await this.backgroundImage, 0, 0);
const {DateTime} = luxon; const { DateTime } = luxon;
// Title // Title
draw.text(this.context, 'Arial', 'bold 28pt', '#ffffff', 155, 60, 'Local', 2); draw.text(this.context, 'Arial', 'bold 28pt', '#ffffff', 155, 60, 'Local', 2);
draw.text(this.context, 'Arial', 'bold 28pt', '#ffffff', 155, 95, 'Radar', 2); draw.text(this.context, 'Arial', 'bold 28pt', '#ffffff', 155, 95, 'Radar', 2);
@ -237,7 +237,7 @@ class Radar extends WeatherDisplay {
} }
// utility latitude/pixel conversions // utility latitude/pixel conversions
getXYFromLatitudeLongitude (Latitude, Longitude, OffsetX, OffsetY, state) { getXYFromLatitudeLongitude(Latitude, Longitude, OffsetX, OffsetY, state) {
if (state === 'HI') return this.getXYFromLatitudeLongitudeHI(...arguments); if (state === 'HI') return this.getXYFromLatitudeLongitudeHI(...arguments);
let y = 0; let y = 0;
let x = 0; let x = 0;
@ -265,7 +265,7 @@ class Radar extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getXYFromLatitudeLongitudeHI(Latitude, Longitude, OffsetX, OffsetY) { static getXYFromLatitudeLongitudeHI(Latitude, Longitude, OffsetX, OffsetY) {
let y = 0; let y = 0;
let x = 0; let x = 0;
const ImgHeight = 571; const ImgHeight = 571;
@ -292,7 +292,7 @@ class Radar extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getXYFromLatitudeLongitudeDoppler (Latitude, Longitude, OffsetX, OffsetY) { static getXYFromLatitudeLongitudeDoppler(Latitude, Longitude, OffsetX, OffsetY) {
let y = 0; let y = 0;
let x = 0; let x = 0;
const ImgHeight = 3200; const ImgHeight = 3200;
@ -319,7 +319,7 @@ class Radar extends WeatherDisplay {
return { x: x * 2, y: y * 2 }; return { x: x * 2, y: y * 2 };
} }
removeDopplerRadarImageNoise (RadarContext) { static removeDopplerRadarImageNoise(RadarContext) {
const RadarImageData = RadarContext.getImageData(0, 0, RadarContext.canvas.width, RadarContext.canvas.height); const RadarImageData = RadarContext.getImageData(0, 0, RadarContext.canvas.width, RadarContext.canvas.height);
// examine every pixel, // examine every pixel,
@ -329,7 +329,7 @@ class Radar extends WeatherDisplay {
// i + 1 = green // i + 1 = green
// i + 2 = blue // i + 2 = blue
// i + 3 = alpha (0 = transparent, 255 = opaque) // i + 3 = alpha (0 = transparent, 255 = opaque)
let [R, G, B, A] = RadarImageData.data.slice(i,i+4); let [R, G, B, A] = RadarImageData.data.slice(i, i + 4);
// is this pixel the old rgb? // is this pixel the old rgb?
if ((R === 1 && G === 159 && B === 244) if ((R === 1 && G === 159 && B === 244)
@ -402,13 +402,13 @@ class Radar extends WeatherDisplay {
RadarContext.putImageData(RadarImageData, 0, 0); RadarContext.putImageData(RadarImageData, 0, 0);
} }
mergeDopplerRadarImage (mapContext, radarContext) { static mergeDopplerRadarImage(mapContext, radarContext) {
var mapImageData = mapContext.getImageData(0, 0, mapContext.canvas.width, mapContext.canvas.height); const mapImageData = mapContext.getImageData(0, 0, mapContext.canvas.width, mapContext.canvas.height);
var radarImageData = radarContext.getImageData(0, 0, radarContext.canvas.width, radarContext.canvas.height); const radarImageData = radarContext.getImageData(0, 0, radarContext.canvas.width, radarContext.canvas.height);
// examine every pixel, // examine every pixel,
// change any old rgb to the new-rgb // change any old rgb to the new-rgb
for (var i = 0; i < radarImageData.data.length; i += 4) { for (let i = 0; i < radarImageData.data.length; i += 4) {
// i + 0 = red // i + 0 = red
// i + 1 = green // i + 1 = green
// i + 2 = blue // i + 2 = blue

View file

@ -1,12 +1,12 @@
// regional forecast and observations // regional forecast and observations
// type 0 = observations, 1 = first forecast, 2 = second forecast // type 0 = observations, 1 = first forecast, 2 = second forecast
/* globals WeatherDisplay, utils, STATUS, icons, UNITS, draw, navigation, luxon, _StationInfo, _RegionalCities */ /* globals WeatherDisplay, utils, STATUS, icons, UNITS, draw, navigation, luxon, StationInfo, RegionalCities */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class RegionalForecast extends WeatherDisplay { class RegionalForecast extends WeatherDisplay {
constructor(navId,elemId) { constructor(navId, elemId) {
super(navId,elemId,'Regional Forecast'); super(navId, elemId, 'Regional Forecast');
// pre-load background image (returns promise) // pre-load background image (returns promise)
this.backgroundImage = utils.image.load('images/BackGround5_1.png'); this.backgroundImage = utils.image.load('images/BackGround5_1.png');
@ -15,9 +15,9 @@ class RegionalForecast extends WeatherDisplay {
this.timing.totalScreens = 3; this.timing.totalScreens = 3;
} }
async getData(weatherParameters) { async getData(_weatherParameters) {
super.getData(weatherParameters); super.getData(_weatherParameters);
if (!weatherParameters) weatherParameters = this.weatherParameters; const weatherParameters = _weatherParameters ?? this.weatherParameters;
// pre-load the base map (returns promise) // pre-load the base map (returns promise)
let src = 'images/Basemap2.png'; let src = 'images/Basemap2.png';
@ -44,35 +44,35 @@ class RegionalForecast extends WeatherDisplay {
if (weatherParameters.state === 'HI') targetDistance = 1; if (weatherParameters.state === 'HI') targetDistance = 1;
// make station info into an array // make station info into an array
const stationInfoArray = Object.keys(_StationInfo).map(key => Object.assign({}, _StationInfo[key], {targetDistance})); const stationInfoArray = Object.values(StationInfo).map((value) => ({ ...value, targetDistance }));
// combine regional cities with station info for additional stations // combine regional cities with station info for additional stations
// stations are intentionally after cities to allow cities priority when drawing the map // stations are intentionally after cities to allow cities priority when drawing the map
const combinedCities = [..._RegionalCities, ...stationInfoArray]; const combinedCities = [...RegionalCities, ...stationInfoArray];
// Determine which cities are within the max/min latitude/longitude. // Determine which cities are within the max/min latitude/longitude.
const regionalCities = []; const regionalCities = [];
combinedCities.forEach(city => { combinedCities.forEach((city) => {
if (city.lat > minMaxLatLon.minLat && city.lat < minMaxLatLon.maxLat && if (city.lat > minMaxLatLon.minLat && city.lat < minMaxLatLon.maxLat
city.lon > minMaxLatLon.minLon && city.lon < minMaxLatLon.maxLon - 1) { && city.lon > minMaxLatLon.minLon && city.lon < minMaxLatLon.maxLon - 1) {
// default to 1 for cities loaded from _RegionalCities, use value calculate above for remaining stations // default to 1 for cities loaded from RegionalCities, use value calculate above for remaining stations
const targetDistance = city.targetDistance || 1; const targetDist = city.targetDistance || 1;
// Only add the city as long as it isn't within set distance degree of any other city already in the array. // Only add the city as long as it isn't within set distance degree of any other city already in the array.
const okToAddCity = regionalCities.reduce((acc, testCity) => { const okToAddCity = regionalCities.reduce((acc, testCity) => {
const distance = utils.calc.distance(city.lon, city.lat, testCity.lon, testCity.lat); const distance = utils.calc.distance(city.lon, city.lat, testCity.lon, testCity.lat);
return acc && distance >= targetDistance; return acc && distance >= targetDist;
}, true); }, true);
if (okToAddCity) regionalCities.push(city); if (okToAddCity) regionalCities.push(city);
} }
}); });
// get regional forecasts and observations (the two are intertwined due to the design of api.weather.gov) // get regional forecasts and observations (the two are intertwined due to the design of api.weather.gov)
const regionalForecastPromises = regionalCities.map(async city => { const regionalForecastPromises = regionalCities.map(async (city) => {
try { try {
// get the point first, then break down into forecast and observations // get the point first, then break down into forecast and observations
const point = await utils.weather.getPoint(city.lat, city.lon); const point = await utils.weather.getPoint(city.lat, city.lon);
// start off the observation task // start off the observation task
const observationPromise = this.getRegionalObservation(point, city); const observationPromise = RegionalForecast.getRegionalObservation(point, city);
const forecast = await utils.fetch.json(point.properties.forecast); const forecast = await utils.fetch.json(point.properties.forecast);
@ -85,7 +85,7 @@ class RegionalForecast extends WeatherDisplay {
const regionalObservation = { const regionalObservation = {
daytime: !!observation.icon.match(/\/day\//), daytime: !!observation.icon.match(/\/day\//),
temperature: utils.units.celsiusToFahrenheit(observation.temperature.value), temperature: utils.units.celsiusToFahrenheit(observation.temperature.value),
name: this.formatCity(city.city), name: RegionalForecast.formatCity(city.city),
icon: observation.icon, icon: observation.icon,
x: cityXY.x, x: cityXY.x,
y: cityXY.y, y: cityXY.y,
@ -101,8 +101,8 @@ class RegionalForecast extends WeatherDisplay {
// always skip the first forecast index because it's what's going on right now // always skip the first forecast index because it's what's going on right now
return [ return [
regionalObservation, regionalObservation,
this.buildForecast(forecast.properties.periods[1], city, cityXY), RegionalForecast.buildForecast(forecast.properties.periods[1], city, cityXY),
this.buildForecast(forecast.properties.periods[2], city, cityXY), RegionalForecast.buildForecast(forecast.properties.periods[2], city, cityXY),
]; ];
} catch (e) { } catch (e) {
console.log(`No regional forecast data for '${city.name}'`); console.log(`No regional forecast data for '${city.name}'`);
@ -114,7 +114,7 @@ class RegionalForecast extends WeatherDisplay {
// wait for the forecasts // wait for the forecasts
const regionalDataAll = await Promise.all(regionalForecastPromises); const regionalDataAll = await Promise.all(regionalForecastPromises);
// filter out any false (unavailable data) // filter out any false (unavailable data)
const regionalData = regionalDataAll.filter(data => data); const regionalData = regionalDataAll.filter((data) => data);
// test for data present // test for data present
if (regionalData.length === 0) { if (regionalData.length === 0) {
@ -132,11 +132,11 @@ class RegionalForecast extends WeatherDisplay {
this.setStatus(STATUS.loaded); this.setStatus(STATUS.loaded);
} }
buildForecast (forecast, city, cityXY) { static buildForecast(forecast, city, cityXY) {
return { return {
daytime: forecast.isDaytime, daytime: forecast.isDaytime,
temperature: forecast.temperature||0, temperature: forecast.temperature || 0,
name: this.formatCity(city.city), name: RegionalForecast.formatCity(city.city),
icon: forecast.icon, icon: forecast.icon,
x: cityXY.x, x: cityXY.x,
y: cityXY.y, y: cityXY.y,
@ -144,7 +144,7 @@ class RegionalForecast extends WeatherDisplay {
}; };
} }
async getRegionalObservation (point, city) { static async getRegionalObservation(point, city) {
try { try {
// get stations // get stations
const stations = await utils.fetch.json(point.properties.observationStations); const stations = await utils.fetch.json(point.properties.observationStations);
@ -165,9 +165,9 @@ class RegionalForecast extends WeatherDisplay {
} }
// utility latitude/pixel conversions // utility latitude/pixel conversions
getXYFromLatitudeLongitude (Latitude, Longitude, OffsetX, OffsetY, state) { getXYFromLatitudeLongitude(Latitude, Longitude, OffsetX, OffsetY, state) {
if (state === 'AK') return this.getXYFromLatitudeLongitudeAK(...arguments); if (state === 'AK') return this.getXYFromLatitudeLongitudeAK(Latitude, Longitude, OffsetX, OffsetY);
if (state === 'HI') return this.getXYFromLatitudeLongitudeHI(...arguments); if (state === 'HI') return this.getXYFromLatitudeLongitudeHI(Latitude, Longitude, OffsetX, OffsetY);
let y = 0; let y = 0;
let x = 0; let x = 0;
const ImgHeight = 1600; const ImgHeight = 1600;
@ -194,7 +194,7 @@ class RegionalForecast extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getXYFromLatitudeLongitudeAK (Latitude, Longitude, OffsetX, OffsetY) { static getXYFromLatitudeLongitudeAK(Latitude, Longitude, OffsetX, OffsetY) {
let y = 0; let y = 0;
let x = 0; let x = 0;
const ImgHeight = 1142; const ImgHeight = 1142;
@ -221,7 +221,7 @@ class RegionalForecast extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getXYFromLatitudeLongitudeHI (Latitude, Longitude, OffsetX, OffsetY) { static getXYFromLatitudeLongitudeHI(Latitude, Longitude, OffsetX, OffsetY) {
let y = 0; let y = 0;
let x = 0; let x = 0;
const ImgHeight = 571; const ImgHeight = 571;
@ -248,38 +248,44 @@ class RegionalForecast extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getMinMaxLatitudeLongitude (X, Y, OffsetX, OffsetY, state) { getMinMaxLatitudeLongitude(X, Y, OffsetX, OffsetY, state) {
if (state === 'AK') return this.getMinMaxLatitudeLongitudeAK(...arguments); if (state === 'AK') return this.getMinMaxLatitudeLongitudeAK(X, Y, OffsetX, OffsetY);
if (state === 'HI') return this.getMinMaxLatitudeLongitudeHI(...arguments); if (state === 'HI') return this.getMinMaxLatitudeLongitudeHI(X, Y, OffsetX, OffsetY);
const maxLat = ((Y / 55.2) - 50.5) * -1; const maxLat = ((Y / 55.2) - 50.5) * -1;
const minLat = (((Y + (OffsetY * 2)) / 55.2) - 50.5) * -1; const minLat = (((Y + (OffsetY * 2)) / 55.2) - 50.5) * -1;
const minLon = (((X * -1) / 41.775) + 127.5) * -1; const minLon = (((X * -1) / 41.775) + 127.5) * -1;
const maxLon = ((((X + (OffsetX * 2)) * -1) / 41.775) + 127.5) * -1; const maxLon = ((((X + (OffsetX * 2)) * -1) / 41.775) + 127.5) * -1;
return { minLat, maxLat, minLon, maxLon }; return {
minLat, maxLat, minLon, maxLon,
};
} }
getMinMaxLatitudeLongitudeAK (X, Y, OffsetX, OffsetY) { static getMinMaxLatitudeLongitudeAK(X, Y, OffsetX, OffsetY) {
const maxLat = ((Y / 56) - 73.0) * -1; const maxLat = ((Y / 56) - 73.0) * -1;
const minLat = (((Y + (OffsetY * 2)) / 56) - 73.0) * -1; const minLat = (((Y + (OffsetY * 2)) / 56) - 73.0) * -1;
const minLon = (((X * -1) / 25) + 175.0) * -1; const minLon = (((X * -1) / 25) + 175.0) * -1;
const maxLon = ((((X + (OffsetX * 2)) * -1) / 25) + 175.0) * -1; const maxLon = ((((X + (OffsetX * 2)) * -1) / 25) + 175.0) * -1;
return { minLat, maxLat, minLon, maxLon }; return {
minLat, maxLat, minLon, maxLon,
};
} }
getMinMaxLatitudeLongitudeHI (X, Y, OffsetX, OffsetY) { static getMinMaxLatitudeLongitudeHI(X, Y, OffsetX, OffsetY) {
const maxLat = ((Y / 55.2) - 25) * -1; const maxLat = ((Y / 55.2) - 25) * -1;
const minLat = (((Y + (OffsetY * 2)) / 55.2) - 25) * -1; const minLat = (((Y + (OffsetY * 2)) / 55.2) - 25) * -1;
const minLon = (((X * -1) / 41.775) + 164.5) * -1; const minLon = (((X * -1) / 41.775) + 164.5) * -1;
const maxLon = ((((X + (OffsetX * 2)) * -1) / 41.775) + 164.5) * -1; const maxLon = ((((X + (OffsetX * 2)) * -1) / 41.775) + 164.5) * -1;
return { minLat, maxLat, minLon, maxLon }; return {
minLat, maxLat, minLon, maxLon,
};
} }
getXYForCity (City, MaxLatitude, MinLongitude, state) { getXYForCity(City, MaxLatitude, MinLongitude, state) {
if (state === 'AK') this.getXYForCityAK(...arguments); if (state === 'AK') this.getXYForCityAK(City, MaxLatitude, MinLongitude);
if (state === 'HI') this.getXYForCityHI(...arguments); if (state === 'HI') this.getXYForCityHI(City, MaxLatitude, MinLongitude);
let x = (City.lon - MinLongitude) * 57; let x = (City.lon - MinLongitude) * 57;
let y = (MaxLatitude - City.lat) * 70; let y = (MaxLatitude - City.lat) * 70;
@ -292,7 +298,7 @@ class RegionalForecast extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getXYForCityAK (City, MaxLatitude, MinLongitude) { static getXYForCityAK(City, MaxLatitude, MinLongitude) {
let x = (City.lon - MinLongitude) * 37; let x = (City.lon - MinLongitude) * 37;
let y = (MaxLatitude - City.lat) * 70; let y = (MaxLatitude - City.lat) * 70;
@ -304,7 +310,7 @@ class RegionalForecast extends WeatherDisplay {
return { x, y }; return { x, y };
} }
getXYForCityHI (City, MaxLatitude, MinLongitude) { static getXYForCityHI(City, MaxLatitude, MinLongitude) {
let x = (City.lon - MinLongitude) * 57; let x = (City.lon - MinLongitude) * 57;
let y = (MaxLatitude - City.lat) * 70; let y = (MaxLatitude - City.lat) * 70;
@ -318,19 +324,19 @@ class RegionalForecast extends WeatherDisplay {
} }
// 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
formatCity(city) { static formatCity(city) {
return city.match(/[^-;/\\,]*/)[0].substr(0,12); return city.match(/[^-;/\\,]*/)[0].substr(0, 12);
} }
async drawCanvas() { async drawCanvas() {
super.drawCanvas(); super.drawCanvas();
// break up data into useful values // break up data into useful values
const {regionalData: data, sourceXY, offsetXY} = this.data; const { regionalData: data, sourceXY, offsetXY } = this.data;
// fixed offset for all y values when drawing to the map // fixed offset for all y values when drawing to the map
const mapYOff = 90; const mapYOff = 90;
const {DateTime} = luxon; const { DateTime } = luxon;
// draw the header graphics // draw the header graphics
this.context.drawImage(await this.backgroundImage, 0, 0); this.context.drawImage(await this.backgroundImage, 0, 0);
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2); draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
@ -340,21 +346,21 @@ class RegionalForecast extends WeatherDisplay {
if (this.screenIndex === 0) { if (this.screenIndex === 0) {
draw.titleText(this.context, 'Regional', 'Observations'); draw.titleText(this.context, 'Regional', 'Observations');
} else { } else {
let forecastDate = DateTime.fromISO(data[0][this.screenIndex].time); const forecastDate = DateTime.fromISO(data[0][this.screenIndex].time);
// get the name of the day // get the name of the day
const dayName = forecastDate.toLocaleString({weekday: 'long'}); const dayName = forecastDate.toLocaleString({ weekday: 'long' });
// draw the title // draw the title
if (data[0][this.screenIndex].daytime) { if (data[0][this.screenIndex].daytime) {
draw.titleText(this.context, 'Forecast for', dayName); draw.titleText(this.context, 'Forecast for', dayName);
} else { } else {
draw.titleText(this.context, 'Forecast for', dayName + ' Night'); draw.titleText(this.context, 'Forecast for', `${dayName} Night`);
} }
} }
// draw the map // draw the map
this.context.drawImage(await this.baseMap, sourceXY.x, sourceXY.y, (offsetXY.x * 2), (offsetXY.y * 2), 0, mapYOff, 640, 312); this.context.drawImage(await this.baseMap, sourceXY.x, sourceXY.y, (offsetXY.x * 2), (offsetXY.y * 2), 0, mapYOff, 640, 312);
await Promise.all(data.map(async city => { await Promise.all(data.map(async (city) => {
const period = city[this.screenIndex]; const period = city[this.screenIndex];
// draw the icon if possible // draw the icon if possible
const icon = icons.getWeatherRegionalIconFromIconLink(period.icon, !period.daytime); const icon = icons.getWeatherRegionalIconFromIconLink(period.icon, !period.daytime);
@ -365,20 +371,19 @@ class RegionalForecast extends WeatherDisplay {
auto_play: true, auto_play: true,
canvas: this.canvas, canvas: this.canvas,
x: period.x, x: period.x,
y: period.y - 15+mapYOff, y: period.y - 15 + mapYOff,
})); }));
} }
// City Name // City Name
draw.text(this.context, 'Star4000', '20px', '#ffffff', period.x - 40, period.y - 15+mapYOff, period.name, 2); draw.text(this.context, 'Star4000', '20px', '#ffffff', period.x - 40, period.y - 15 + mapYOff, period.name, 2);
// Temperature // Temperature
let temperature = period.temperature; let { temperature } = period;
if (navigation.units() === UNITS.metric) temperature = Math.round(utils.units.fahrenheitToCelsius(temperature)); if (navigation.units() === UNITS.metric) temperature = Math.round(utils.units.fahrenheitToCelsius(temperature));
draw.text(this.context, 'Star4000 Large Compressed', '28px', '#ffff00', period.x - (temperature.toString().length * 15), period.y + 20+mapYOff, temperature, 2); draw.text(this.context, 'Star4000 Large Compressed', '28px', '#ffff00', period.x - (temperature.toString().length * 15), period.y + 20 + mapYOff, temperature, 2);
})); }));
this.finishDraw(); this.finishDraw();
} }
} }

View file

@ -1,5 +1,5 @@
// travel forecast display // travel forecast display
/* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, icons, luxon, _TravelCities */ /* globals WeatherDisplay, utils, STATUS, UNITS, draw, navigation, icons, luxon, TravelCities */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
class TravelForecast extends WeatherDisplay { class TravelForecast extends WeatherDisplay {
@ -15,15 +15,15 @@ class TravelForecast extends WeatherDisplay {
// set up the timing // set up the timing
this.timing.baseDelay = 20; this.timing.baseDelay = 20;
// page sizes are 4 cities, calculate the number of pages necessary plus overflow // page sizes are 4 cities, calculate the number of pages necessary plus overflow
const pagesFloat = _TravelCities.length/4; const pagesFloat = TravelCities.length / 4;
const pages = Math.floor(pagesFloat) - 2; // first page is already displayed, last page doesn't happen const pages = Math.floor(pagesFloat) - 2; // first page is already displayed, last page doesn't happen
const extra = pages%1; const extra = pages % 1;
const timingStep = this.cityHeight*4; const timingStep = this.cityHeight * 4;
this.timing.delay = [150+timingStep]; this.timing.delay = [150 + timingStep];
// add additional pages // add additional pages
for (let i = 0; i < pages; i++) this.timing.delay.push(timingStep); for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
// add the extra (not exactly 4 pages portion) // add the extra (not exactly 4 pages portion)
if (extra !== 0) this.timing.delay.push(Math.round(this.extra*this.cityHeight)); if (extra !== 0) this.timing.delay.push(Math.round(this.extra * this.cityHeight));
// add the final 3 second delay // add the final 3 second delay
this.timing.delay.push(150); this.timing.delay.push(150);
} }
@ -31,25 +31,25 @@ class TravelForecast extends WeatherDisplay {
async getData() { async getData() {
// super checks for enabled // super checks for enabled
if (!super.getData()) return; if (!super.getData()) return;
const forecastPromises = _TravelCities.map(async city => { const forecastPromises = TravelCities.map(async (city) => {
try { try {
// get point then forecast // get point then forecast
const point = await utils.weather.getPoint(city.Latitude, city.Longitude); const point = await utils.weather.getPoint(city.Latitude, city.Longitude);
const forecast = await utils.fetch.json(point.properties.forecast); const forecast = await utils.fetch.json(point.properties.forecast);
// determine today or tomorrow (shift periods by 1 if tomorrow) // determine today or tomorrow (shift periods by 1 if tomorrow)
const todayShift = forecast.properties.periods[0].isDaytime? 0:1; const todayShift = forecast.properties.periods[0].isDaytime ? 0 : 1;
// return a pared-down forecast // return a pared-down forecast
return { return {
today: todayShift === 0, today: todayShift === 0,
high: forecast.properties.periods[todayShift].temperature, high: forecast.properties.periods[todayShift].temperature,
low: forecast.properties.periods[todayShift+1].temperature, low: forecast.properties.periods[todayShift + 1].temperature,
name: city.Name, name: city.Name,
icon: icons.getWeatherRegionalIconFromIconLink(forecast.properties.periods[todayShift].icon), icon: icons.getWeatherRegionalIconFromIconLink(forecast.properties.periods[todayShift].icon),
}; };
} catch (e) { } catch (e) {
console.error(`GetTravelWeather for ${city.Name} failed`); console.error(`GetTravelWeather for ${city.Name} failed`);
console.error(e.status, e.responseJSON); console.error(e.status, e.responseJSON);
return {name: city.Name}; return { name: city.Name };
} }
}); });
@ -58,7 +58,7 @@ class TravelForecast extends WeatherDisplay {
this.data = forecasts; this.data = forecasts;
// test for some data available in at least one forecast // test for some data available in at least one forecast
const hasData = this.data.reduce((acc,forecast) => acc || forecast.high, false); const hasData = this.data.reduce((acc, forecast) => acc || forecast.high, false);
if (!hasData) { if (!hasData) {
this.setStatus(STATUS.noData); this.setStatus(STATUS.noData);
return; return;
@ -68,7 +68,7 @@ class TravelForecast extends WeatherDisplay {
this.drawLongCanvas(); this.drawLongCanvas();
} }
async drawLongCanvas () { async drawLongCanvas() {
// create the "long" canvas if necessary // create the "long" canvas if necessary
if (!this.longCanvas) { if (!this.longCanvas) {
this.longCanvas = document.createElement('canvas'); this.longCanvas = document.createElement('canvas');
@ -79,7 +79,7 @@ class TravelForecast extends WeatherDisplay {
} }
// stop all gifs // stop all gifs
this.longCanvasGifs.forEach(gif => gif.pause()); this.longCanvasGifs.forEach((gif) => gif.pause());
// delete the gifs // delete the gifs
this.longCanvasGifs.length = 0; this.longCanvasGifs.length = 0;
@ -87,23 +87,23 @@ class TravelForecast extends WeatherDisplay {
const cities = this.data; const cities = this.data;
// clean up existing gifs // clean up existing gifs
this.gifs.forEach(gif => gif.pause()); this.gifs.forEach((gif) => gif.pause());
// delete the gifs // delete the gifs
this.gifs.length = 0; this.gifs.length = 0;
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 the "long" canvas with all cities
draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, _TravelCities.length*this.cityHeight); draw.box(this.longContext, 'rgb(35, 50, 112)', 0, 0, 640, TravelCities.length * this.cityHeight);
for (let i = 0; i <= 4; i++) { for (let i = 0; i <= 4; i += 1) {
const y = i * 346; const y = i * 346;
draw.horizontalGradient(this.longContext, 0, y, 640, y + 346, '#102080', '#001040'); 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 // calculate base y value
const y = 50+this.cityHeight*index; const y = 50 + this.cityHeight * index;
// city name // city name
draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2); draw.text(this.longContext, 'Star4000 Large Compressed', '24pt', '#FFFF00', 80, y, city.name, 2);
@ -111,7 +111,7 @@ class TravelForecast extends WeatherDisplay {
// check for forecast data // check for forecast data
if (city.icon) { if (city.icon) {
// get temperatures and convert if necessary // get temperatures and convert if necessary
let {low, high} = city; let { low, high } = city;
if (navigation.units() === UNITS.metric) { if (navigation.units() === UNITS.metric) {
low = utils.units.fahrenheitToCelsius(low); low = utils.units.fahrenheitToCelsius(low);
@ -140,7 +140,6 @@ class TravelForecast extends WeatherDisplay {
draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y - 18, 'NO TRAVEL', 2); 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); draw.text(this.longContext, 'Star4000 Small', '24pt', '#FFFFFF', 400, y, 'DATA AVAILABLE', 2);
} }
})); }));
} }
@ -157,7 +156,7 @@ class TravelForecast extends WeatherDisplay {
draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2); draw.horizontalGradientSingle(this.context, 0, 30, 500, 90, draw.topColor1, draw.topColor2);
draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90); draw.triangle(this.context, 'rgb(28, 10, 87)', 500, 30, 450, 90, 500, 90);
draw.titleText(this.context, 'Travel Forecast', 'For ' + this.getTravelCitiesDayName(cities)); draw.titleText(this.context, 'Travel Forecast', `For ${TravelForecast.getTravelCitiesDayName(cities)}`);
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 455, 105, 'LOW', 2); draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 455, 105, 'LOW', 2);
draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 510, 105, 'HIGH', 2); draw.text(this.context, 'Star4000 Small', '24pt', '#FFFF00', 510, 105, 'HIGH', 2);
@ -185,7 +184,7 @@ class TravelForecast extends WeatherDisplay {
const longCanvas = this.getLongCanvas(); const longCanvas = this.getLongCanvas();
// calculate scroll offset and don't go past end // calculate scroll offset and don't go past end
let offsetY = Math.min(longCanvas.height-289, (count-150)); let offsetY = Math.min(longCanvas.height - 289, (count - 150));
// don't let offset go negative // don't let offset go negative
if (offsetY < 0) offsetY = 0; if (offsetY < 0) offsetY = 0;
@ -194,15 +193,15 @@ class TravelForecast extends WeatherDisplay {
this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289); this.context.drawImage(longCanvas, 0, offsetY, 640, 289, 0, 110, 640, 289);
} }
getTravelCitiesDayName(cities) { static getTravelCitiesDayName(cities) {
const {DateTime} = luxon; const { DateTime } = luxon;
// effectively returns early on the first found date // effectively returns early on the first found date
return cities.reduce((dayName, city) => { return cities.reduce((dayName, city) => {
if (city && dayName === '') { if (city && dayName === '') {
// today or tomorrow // today or tomorrow
const day = DateTime.local().plus({days: (city.today)?0:1}); const day = DateTime.local().plus({ days: (city.today) ? 0 : 1 });
// return the day // return the day
return day.toLocaleString({weekday: 'long'}); return day.toLocaleString({ weekday: 'long' });
} }
return dayName; return dayName;
}, ''); }, '');

View file

@ -1,4 +1,3 @@
'use strict';
// radar utilities // radar utilities
/* globals SuperGif */ /* globals SuperGif */
@ -17,8 +16,7 @@ const utils = (() => {
// ****************************** load images ********************************* // ****************************** load images *********************************
// load an image from a blob or url // load an image from a blob or url
const loadImg = (imgData, cors = false) => { const loadImg = (imgData, cors = false) => new Promise((resolve) => {
return new Promise(resolve => {
const img = new Image(); const img = new Image();
img.onload = (e) => { img.onload = (e) => {
resolve(e.target); resolve(e.target);
@ -31,15 +29,12 @@ const utils = (() => {
img.src = url; img.src = url;
} }
}); });
};
// async version of SuperGif // async version of SuperGif
const superGifAsync = (e) => { const superGifAsync = (e) => new Promise((resolve) => {
return new Promise(resolve => {
const gif = new SuperGif(e); const gif = new SuperGif(e);
gif.load(() => resolve(gif)); gif.load(() => resolve(gif));
}); });
};
// preload an image // preload an image
// the goal is to get it in the browser's cache so it is available more quickly when the browser needs it // the goal is to get it in the browser's cache so it is available more quickly when the browser needs it
@ -65,24 +60,24 @@ const utils = (() => {
context.imageSmoothingEnabled = false; context.imageSmoothingEnabled = false;
// draw the image // draw the image
context.drawImage(img, 0,0); context.drawImage(img, 0, 0);
return context; return context;
}; };
// *********************************** unit conversions *********************** // *********************************** unit conversions ***********************
Math.round2 = (value, decimals) => Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); Math.round2 = (value, decimals) => Number(`${Math.round(`${value}e${decimals}`)}e-${decimals}`);
const mphToKph = (Mph) => Math.round(Mph * 1.60934); const mphToKph = (Mph) => Math.round(Mph * 1.60934);
const kphToMph = (Kph) => Math.round(Kph / 1.60934); const kphToMph = (Kph) => Math.round(Kph / 1.60934);
const celsiusToFahrenheit = (Celsius) => Math.round(Celsius * 9 / 5 + 32); const celsiusToFahrenheit = (Celsius) => Math.round((Celsius * 9) / 5 + 32);
const fahrenheitToCelsius = (Fahrenheit) => Math.round2(((Fahrenheit) - 32) * 5 / 9, 1); const fahrenheitToCelsius = (Fahrenheit) => Math.round2((((Fahrenheit) - 32) * 5) / 9, 1);
const milesToKilometers = (Miles) => Math.round(Miles * 1.60934); const milesToKilometers = (Miles) => Math.round(Miles * 1.60934);
const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.60934); const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.60934);
const feetToMeters = (Feet) => Math.round(Feet * 0.3048); const feetToMeters = (Feet) => Math.round(Feet * 0.3048);
const metersToFeet = (Meters) => Math.round(Meters / 0.3048); const metersToFeet = (Meters) => Math.round(Meters / 0.3048);
const inchesToCentimeters = (Inches) => Math.round2(Inches * 2.54, 2); const inchesToCentimeters = (Inches) => Math.round2(Inches * 2.54, 2);
const pascalToInHg = (Pascal) => Math.round2(Pascal*0.0002953,2); const pascalToInHg = (Pascal) => Math.round2(Pascal * 0.0002953, 2);
// ***************************** calculations ********************************** // ***************************** calculations **********************************
@ -125,7 +120,7 @@ const utils = (() => {
const T = Temperature; const T = Temperature;
const V = WindSpeed; const V = WindSpeed;
return Math.round(35.74 + (0.6215 * T) - (35.75 * Math.pow(V, 0.16)) + (0.4275 * T * Math.pow(V, 0.16))); return Math.round(35.74 + (0.6215 * T) - (35.75 * (V ** 0.16)) + (0.4275 * T * (V ** 0.16)));
}; };
// wind direction // wind direction
@ -135,20 +130,22 @@ const utils = (() => {
return arr[(val % 16)]; return arr[(val % 16)];
}; };
const distance = (x1 ,y1, x2, y2) => Math.sqrt((x2-=x1)*x2 + (y2-=y1)*y2); const distance = (x1, y1, x2, y2) => Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
// wrap a number to 0-m // wrap a number to 0-m
const wrap = (x,m) => (x%m + m)%m; const wrap = (x, m) => ((x % m) + m) % m;
// ********************************* strings ********************************************* // ********************************* strings *********************************************
const wordWrap = (str, ...rest) => { const wordWrap = (str = '', ...rest) => {
let m = ((rest.length >= 1) ? rest[0] : 75); const m = ((rest.length >= 1) ? rest[0] : 75);
let b = ((rest.length >= 2) ? rest[1] : '\n'); const b = ((rest.length >= 2) ? rest[1] : '\n');
let c = ((rest.length >= 3) ? rest[2] : false); const c = ((rest.length >= 3) ? rest[2] : false);
let i, j, l, s, r; let i;
let j;
str += ''; let l;
let s;
let r;
if (m < 1) { if (m < 1) {
return str; return str;
@ -160,8 +157,8 @@ const utils = (() => {
r[i] += s.slice(0, j) + ((s = s.slice(j)).length ? b : '')) { r[i] += s.slice(0, j) + ((s = s.slice(j)).length ? b : '')) {
j = c === 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] j = c === 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1]
? m ? m
: j.input.length - j[0].length || c === true && m || : ((j.input.length - j[0].length || c === true) && m)
j.input.length + (j = s.slice(m).match(/^\S*/))[0].length; || j.input.length + (j = s.slice(m).match(/^\S*/))[0].length;
} }
} }
@ -169,7 +166,8 @@ const utils = (() => {
}; };
// ********************************* cors ******************************************** // ********************************* cors ********************************************
// rewrite some urls for local server // rewrite some urls for local server
const rewriteUrl = (url) => { const rewriteUrl = (_url) => {
let url = _url;
url = url.replace('https://api.weather.gov/', window.location.href); url = url.replace('https://api.weather.gov/', window.location.href);
url = url.replace('https://radar.weather.gov/', window.location.href); url = url.replace('https://radar.weather.gov/', window.location.href);
url = url.replace('https://www.cpc.ncep.noaa.gov/', window.location.href); url = url.replace('https://www.cpc.ncep.noaa.gov/', window.location.href);
@ -182,14 +180,14 @@ const utils = (() => {
const raw = (url, params) => fetchAsync(url, '', params); const raw = (url, params) => fetchAsync(url, '', params);
const blob = (url, params) => fetchAsync(url, 'blob', params); const blob = (url, params) => fetchAsync(url, 'blob', params);
const fetchAsync = async (_url, responseType, _params={}) => { const fetchAsync = async (_url, responseType, _params = {}) => {
// combine default and provided parametersutils // combine default and provided parameters
const params = Object.assign({}, { const params = {
method: 'GET', method: 'GET',
mode: 'cors', mode: 'cors',
type: 'GET', type: 'GET',
}, ..._params,
_params); };
// build a url, including the rewrite for cors if necessary // build a url, including the rewrite for cors if necessary
let corsUrl = _url; let corsUrl = _url;
if (params.cors === true) corsUrl = rewriteUrl(_url); if (params.cors === true) corsUrl = rewriteUrl(_url);
@ -212,11 +210,11 @@ const utils = (() => {
// return the requested response // return the requested response
switch (responseType) { switch (responseType) {
case 'json': case 'json':
return await response.json(); return response.json();
case 'text': case 'text':
return await response.text(); return response.text();
case 'blob': case 'blob':
return await response.blob(); return response.blob();
default: default:
return response; return response;
} }

View file

@ -19,7 +19,7 @@ class WeatherDisplay {
this.gifs = []; this.gifs = [];
this.data = undefined; this.data = undefined;
this.loadingStatus = STATUS.loading; this.loadingStatus = STATUS.loading;
this.name = name?name:elemId; this.name = name ?? elemId;
this.getDataCallbacks = []; this.getDataCallbacks = [];
// default navigation timing // default navigation timing
@ -59,7 +59,7 @@ class WeatherDisplay {
// create a checkbox in the selected displays area // create a checkbox in the selected displays area
const checkbox = document.createElement('template'); const checkbox = document.createElement('template');
checkbox.innerHTML = `<label for="${this.elemId}Enabled"> checkbox.innerHTML = `<label for="${this.elemId}Enabled">
<input type="checkbox" value="true" id="${this.elemId}Enabled" name="${this.elemId}Enabled"${this.enabled?' checked':''}/> <input type="checkbox" value="true" id="${this.elemId}Enabled" name="${this.elemId}Enabled"${this.enabled ? ' checked' : ''}/>
${this.name}</label>`; ${this.name}</label>`;
checkbox.content.firstChild.addEventListener('change', (e) => this.checkboxChange(e)); checkbox.content.firstChild.addEventListener('change', (e) => this.checkboxChange(e));
const availableDisplays = document.getElementById('enabledDisplays'); const availableDisplays = document.getElementById('enabledDisplays');
@ -99,7 +99,7 @@ class WeatherDisplay {
// create a canvas // create a canvas
const canvas = document.createElement('template'); const canvas = document.createElement('template');
canvas.innerHTML = `<canvas id='${elemId+'Canvas'}' width='${width}' height='${height}' style='display: none;' />`; canvas.innerHTML = `<canvas id='${`${elemId}Canvas`}' width='${width}' height='${height}' style='display: none;' />`;
// add to the page // add to the page
const container = document.getElementById('container'); const container = document.getElementById('container');
@ -130,23 +130,22 @@ class WeatherDisplay {
// return any data requested before it was available // return any data requested before it was available
getDataCallback() { getDataCallback() {
// call each callback // call each callback
this.getDataCallbacks.forEach(fxn => fxn(this.data)); this.getDataCallbacks.forEach((fxn) => fxn(this.data));
// clear the callbacks // clear the callbacks
this.getDataCallbacks = []; this.getDataCallbacks = [];
} }
drawCanvas() { drawCanvas() {
// stop all gifs // stop all gifs
this.gifs.forEach(gif => gif.pause()); this.gifs.forEach((gif) => gif.pause());
// delete the gifs // delete the gifs
this.gifs.length = 0; this.gifs.length = 0;
// refresh the canvas // refresh the canvas
this.canvas = document.getElementById(this.elemId+'Canvas'); this.canvas = document.getElementById(`${this.elemId}Canvas`);
this.context = this.canvas.getContext('2d'); this.context = this.canvas.getContext('2d');
// clean up the first-run flag in screen index // clean up the first-run flag in screen index
if (this.screenIndex < 0) if (this.screenIndex < 0) this.screenIndex = 0;
this.screenIndex = 0;
} }
finishDraw() { finishDraw() {
@ -155,7 +154,7 @@ class WeatherDisplay {
let OkToDrawCurrentDateTime = true; let OkToDrawCurrentDateTime = true;
let OkToDrawLogoImage = true; let OkToDrawLogoImage = true;
// let OkToDrawCustomScrollText = false; // let OkToDrawCustomScrollText = false;
let bottom = undefined; let bottom;
// visibility tests // visibility tests
// if (_ScrollText !== '') OkToDrawCustomScrollText = true; // if (_ScrollText !== '') OkToDrawCustomScrollText = true;
@ -201,7 +200,7 @@ class WeatherDisplay {
drawCurrentDateTime(bottom) { drawCurrentDateTime(bottom) {
// only draw if canvas is active to conserve battery // only draw if canvas is active to conserve battery
if (!this.isActive()) return; if (!this.isActive()) return;
const {DateTime} = luxon; const { DateTime } = luxon;
const font = 'Star4000 Small'; const font = 'Star4000 Small';
const size = '24pt'; const size = '24pt';
const color = '#ffffff'; const color = '#ffffff';
@ -226,10 +225,10 @@ class WeatherDisplay {
// Get the current date and time. // Get the current date and time.
const now = DateTime.local(); const now = DateTime.local();
//time = "11:35:08 PM"; // time = "11:35:08 PM";
const time = now.toLocaleString(DateTime.TIME_WITH_SECONDS).padStart(11,' '); const time = now.toLocaleString(DateTime.TIME_WITH_SECONDS).padStart(11, ' ');
let x,y; let x; let y;
if (bottom) { if (bottom) {
x = 400; x = 400;
y = 402; y = 402;
@ -241,9 +240,9 @@ class WeatherDisplay {
x += 45; x += 45;
} }
draw.text(this.context, font, size, color, x, y, time.toUpperCase(), shadow); //y += 20; draw.text(this.context, font, size, color, x, y, time.toUpperCase(), shadow); // y += 20;
const date = now.toFormat(' ccc LLL ') + now.day.toString().padStart(2,' '); const date = now.toFormat(' ccc LLL ') + now.day.toString().padStart(2, ' ');
if (bottom) { if (bottom) {
x = 55; x = 55;
@ -255,7 +254,7 @@ class WeatherDisplay {
draw.text(this.context, font, size, color, x, y, date.toUpperCase(), shadow); draw.text(this.context, font, size, color, x, y, date.toUpperCase(), shadow);
} }
async drawNoaaImage () { async drawNoaaImage() {
// load the image and store locally // load the image and store locally
if (!this.drawNoaaImage.image) { if (!this.drawNoaaImage.image) {
this.drawNoaaImage.image = utils.image.load('images/noaa5.gif'); this.drawNoaaImage.image = utils.image.load('images/noaa5.gif');
@ -265,7 +264,7 @@ class WeatherDisplay {
this.context.drawImage(img, 356, 39); this.context.drawImage(img, 356, 39);
} }
async drawLogoImage () { async drawLogoImage() {
// load the image and store locally // load the image and store locally
if (!this.drawLogoImage.image) { if (!this.drawLogoImage.image) {
this.drawLogoImage.image = utils.image.load('images/Logo3.png'); this.drawLogoImage.image = utils.image.load('images/Logo3.png');
@ -287,10 +286,9 @@ class WeatherDisplay {
// show the canvas // show the canvas
this.canvas.style.display = 'block'; this.canvas.style.display = 'block';
return false;
// stop if timing has been disabled
if (!this.timing) return;
} }
hideCanvas() { hideCanvas() {
this.resetNavBaseCount(); this.resetNavBaseCount();
@ -299,7 +297,7 @@ class WeatherDisplay {
} }
isActive() { isActive() {
return document.getElementById(this.elemId+'Canvas').offsetParent !== null; return document.getElementById(`${this.elemId}Canvas`).offsetParent !== null;
} }
isEnabled() { isEnabled() {
@ -318,7 +316,7 @@ class WeatherDisplay {
// see if play is active and screen is active // see if play is active and screen is active
if (!navigation.isPlaying() || !this.isActive()) return; if (!navigation.isPlaying() || !this.isActive()) return;
// increment the base count // increment the base count
this.navBaseCount++; this.navBaseCount += 1;
// call base count change if available for this function // call base count change if available for this function
if (this.baseCountChange) this.baseCountChange(this.navBaseCount); if (this.baseCountChange) this.baseCountChange(this.navBaseCount);
@ -329,7 +327,7 @@ class WeatherDisplay {
async updateScreenFromBaseCount() { async updateScreenFromBaseCount() {
// get the next screen index // get the next screen index
let nextScreenIndex = this.screenIndexFromBaseCount(); const nextScreenIndex = this.screenIndexFromBaseCount();
// special cases for first and last frame // special cases for first and last frame
// must compare with false as nextScreenIndex could be 0 which is valid // must compare with false as nextScreenIndex could be 0 which is valid
@ -368,10 +366,10 @@ class WeatherDisplay {
// if the delay is provided as a single value, expand it to a series of the same value // if the delay is provided as a single value, expand it to a series of the same value
let intermediateDelay = []; let intermediateDelay = [];
if (typeof this.timing.delay === 'number') { if (typeof this.timing.delay === 'number') {
for (let i = 0; i < this.timing.totalScreens; i++) intermediateDelay.push(this.timing.delay); for (let i = 0; i < this.timing.totalScreens; i += 1) intermediateDelay.push(this.timing.delay);
} else { } else {
// map just the delays to the intermediate block // map just the delays to the intermediate block
intermediateDelay = this.timing.delay.map(delay => { intermediateDelay = this.timing.delay.map((delay) => {
if (typeof delay === 'object') return delay.time; if (typeof delay === 'object') return delay.time;
return delay; return delay;
}); });
@ -379,7 +377,7 @@ class WeatherDisplay {
// calculate the cumulative end point of each delay // calculate the cumulative end point of each delay
let sum = 0; let sum = 0;
this.timing.fullDelay = intermediateDelay.map(val => { this.timing.fullDelay = intermediateDelay.map((val) => {
const calc = sum + val; const calc = sum + val;
sum += val; sum += val;
return calc; return calc;
@ -388,11 +386,11 @@ class WeatherDisplay {
// generate a list of screen either sequentially if not provided in an object or from the object // generate a list of screen either sequentially if not provided in an object or from the object
if (Array.isArray(this.timing.delay) && typeof this.timing.delay[0] === 'object') { if (Array.isArray(this.timing.delay) && typeof this.timing.delay[0] === 'object') {
// extract screen indexes from objects // extract screen indexes from objects
this.timing.screenIndexes = this.timing.delay.map(delay => delay.si); this.timing.screenIndexes = this.timing.delay.map((delay) => delay.si);
} else { } else {
// generate sequential screen indexes // generate sequential screen indexes
this.timing.screenIndexes = []; this.timing.screenIndexes = [];
for (let i = 0; i < this.timing.totalScreens; i++) this.timing.screenIndexes.push(i); for (let i = 0; i < this.timing.totalScreens; i += 1) this.timing.screenIndexes.push(i);
} }
} }
@ -403,7 +401,7 @@ class WeatherDisplay {
this.resetNavBaseCount(); this.resetNavBaseCount();
} else { } else {
// set the base count to the next available frame // set the base count to the next available frame
const newBaseCount = this.timing.fullDelay.find(delay => delay > this.navBaseCount); const newBaseCount = this.timing.fullDelay.find((delay) => delay > this.navBaseCount);
this.navBaseCount = newBaseCount; this.navBaseCount = newBaseCount;
} }
this.updateScreenFromBaseCount(); this.updateScreenFromBaseCount();
@ -413,13 +411,13 @@ class WeatherDisplay {
navPrev(command) { navPrev(command) {
// check for special 'last frame' command // check for special 'last frame' command
if (command === navigation.msg.command.lastFrame) { if (command === navigation.msg.command.lastFrame) {
this.navBaseCount = this.timing.fullDelay[this.timing.totalScreens-1]-1; this.navBaseCount = this.timing.fullDelay[this.timing.totalScreens - 1] - 1;
} else { } else {
// find the highest fullDelay that is less than the current base count // find the highest fullDelay that is less than the current base count
const newBaseCount = this.timing.fullDelay.reduce((acc, delay) => { const newBaseCount = this.timing.fullDelay.reduce((acc, delay) => {
if (delay < this.navBaseCount) return delay; if (delay < this.navBaseCount) return delay;
return acc; return acc;
},0); }, 0);
// if the new base count is zero then we're already at the first screen // if the new base count is zero then we're already at the first screen
if (newBaseCount === 0 && this.navBaseCount === 0) { if (newBaseCount === 0 && this.navBaseCount === 0) {
this.sendNavDisplayMessage(navigation.msg.response.previous); this.sendNavDisplayMessage(navigation.msg.response.previous);
@ -436,14 +434,14 @@ class WeatherDisplay {
if (!this.timing) return 0; if (!this.timing) return 0;
// find the first timing in the timing array that is greater than the base count // find the first timing in the timing array that is greater than the base count
if (this.timing && !this.timing.fullDelay) this.calcNavTiming(); if (this.timing && !this.timing.fullDelay) this.calcNavTiming();
const timingIndex = this.timing.fullDelay.findIndex(delay => delay > this.navBaseCount); const timingIndex = this.timing.fullDelay.findIndex((delay) => delay > this.navBaseCount);
if (timingIndex === -1) return false; if (timingIndex === -1) return false;
return this.timing.screenIndexes[timingIndex]; return this.timing.screenIndexes[timingIndex];
} }
// start and stop base counter // start and stop base counter
startNavCount() { startNavCount() {
if (!this.navInterval) this.navInterval = setInterval(()=>this.navBaseTime(), this.timing.baseDelay); if (!this.navInterval) this.navInterval = setInterval(() => this.navBaseTime(), this.timing.baseDelay);
} }
resetNavBaseCount() { resetNavBaseCount() {

View file

@ -1 +1 @@
module.exports = '3.7.1'; module.exports = '3.8.0';

View file

@ -35,5 +35,6 @@
"**/vendor/auto/**", "**/vendor/auto/**",
"**/twc3.js", "**/twc3.js",
], ],
"editor.tabSize": 2,
}, },
} }