Compare commits

...

12 Commits

Author SHA1 Message Date
Matt Walsh
b6cd75ab42 5.11.6 2024-07-07 21:49:28 -05:00
Matt Walsh
934a489340 update dependencies 2024-07-07 21:49:21 -05:00
Matt Walsh
c5f5c101f9 clean up full screen enter/exit close #48 2024-07-07 21:14:07 -05:00
Matt Walsh
933367974f capture dist 2024-07-07 20:16:33 -05:00
Matt Walsh
0e67eb22dc 5.11.5 2024-07-07 20:15:46 -05:00
Matt Walsh
9df6f6888f Fix copy permalink when on non-secure source close #47 2024-07-07 20:15:29 -05:00
Matt Walsh
04cc5d4252 fix encoding issue with gulp 5 2024-05-19 23:47:58 -05:00
Matt Walsh
543d3f5196 Document custom.js close #43 close #44 2024-05-19 23:03:57 -05:00
Matt Walsh
78ceba9c19 capture dist 2024-05-19 22:40:25 -05:00
Matt Walsh
763d42061e 5.11.4 2024-05-19 22:39:27 -05:00
Matt Walsh
318c55b92d Fix date/time update rate #45 2024-05-19 22:39:15 -05:00
Matt Walsh
84d39101e5 capture dist 2024-05-08 16:42:00 -05:00
15 changed files with 2777 additions and 359 deletions

View File

@@ -78,7 +78,7 @@ I've made several changes to this Weather Star 4000 simulation compared to the o
* "Flavors" are not present in this simulation. Flavors refer to the order of the weather information that was shown on the original units. Instead, the order of the displays has been fixed and a checkboxes can be used to turn on and off individual displays. The travel forecast has been defaulted to off so only local information shows for new users. * "Flavors" are not present in this simulation. Flavors refer to the order of the weather information that was shown on the original units. Instead, the order of the displays has been fixed and a checkboxes can be used to turn on and off individual displays. The travel forecast has been defaulted to off so only local information shows for new users.
## Sharing a permalink (bookmarking) ## Sharing a permalink (bookmarking)
Selected displays, the forecast city and widescreen setting are sticky from one session to the next. However if you would like to share your exact configuration or bookmark it click the "Copy Permalink" near the bottom of the page. A URL will be copied to your clipboard with all of you selected displays and location. You can then share this link or add it to your bookmarks. Selected displays, the forecast city and widescreen setting are sticky from one session to the next. However if you would like to share your exact configuration or bookmark it click the "Copy Permalink" (or get "Get Parmalink") near the bottom of the page. A URL will be copied to your clipboard with all of you selected displays and location (or copy it from the page if your browser doesn't support clipboard transfers directly). You can then share this link or add it to your bookmarks.
## Kiosk mode ## Kiosk mode
Kiosk mode can be activated by a checkbox on the page. Note that there is no way out of kiosk mode (except refresh or closing the browser), and the play/pause and other controls will not be available. This is deliberate as a browser's kiosk mode it intended not to be exited or significantly modified. Kiosk mode can be activated by a checkbox on the page. Note that there is no way out of kiosk mode (except refresh or closing the browser), and the play/pause and other controls will not be available. This is deliberate as a browser's kiosk mode it intended not to be exited or significantly modified.
@@ -91,16 +91,15 @@ As time allows I will be working on the following enhancements.
* Better error reporting when api.weather.gov is down (happens more often than you would think) * Better error reporting when api.weather.gov is down (happens more often than you would think)
And the following technical fixes.
* Caching of the animated gifs, specifically after they are decompressed
## Community Notes ## Community Notes
Thanks to the WeatherStar community for providing these discussions to further extend your retro forecasts! Thanks to the WeatherStar community for providing these discussions to further extend your retro forecasts!
* [Stream as FFMPEG](https://github.com/netbymatt/ws4kp/issues/37#issuecomment-2008491948) * [Stream as FFMPEG](https://github.com/netbymatt/ws4kp/issues/37#issuecomment-2008491948)
## Customization
A hook is provided as `/server/scripts/custom.js` to allow customizations to your own fork of this project, without accidentally pushing your customizations back upstream to the git repository. An sample file is provided at `/server/scripts/custom.sample.js` and should be renamed to `custom.js` activate it.
## Issue reporting and feature requests ## Issue reporting and feature requests
Please do not report issues with api.weather.gov being down. It's a new service and not considered fully operational yet. Before reporting an issue or requesting a feature please consider that this is not intended to be a perfect recreation of the WeatherStar 4000, it's a best effort that fits within what's available from the API and within a web browser. Please do not report issues with api.weather.gov being down. It's a new service and not considered fully operational yet. Before reporting an issue or requesting a feature please consider that this is not intended to be a perfect recreation of the WeatherStar 4000, it's a best effort that fits within what's available from the API and within a web browser.

2
dist/index.html vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -14,10 +14,11 @@ const path = require('path');
const clean = () => del(['./dist**']); const clean = () => del(['./dist**']);
// get cloudfront // get cloudfront
const AWS = require('aws-sdk'); const { CloudFrontClient, CreateInvalidationCommand } = require('@aws-sdk/client-cloudfront');
AWS.config.update({ region: 'us-east-1' }); const cloudfront = new CloudFrontClient({ region: 'us-east-1' });
const cloudfront = new AWS.CloudFront({ apiVersion: '2020-01-01' });
const RESOURCES_PATH = './dist/resources';
const jsSourcesData = [ const jsSourcesData = [
'server/scripts/data/travelcities.js', 'server/scripts/data/travelcities.js',
@@ -54,7 +55,7 @@ const webpackOptions = {
gulp.task('compress_js_data', () => gulp.src(jsSourcesData) gulp.task('compress_js_data', () => gulp.src(jsSourcesData)
.pipe(concat('data.min.js')) .pipe(concat('data.min.js'))
.pipe(terser()) .pipe(terser())
.pipe(gulp.dest('./dist/resources'))); .pipe(gulp.dest(RESOURCES_PATH)));
const jsVendorSources = [ const jsVendorSources = [
'server/scripts/vendor/auto/jquery.js', 'server/scripts/vendor/auto/jquery.js',
@@ -67,7 +68,7 @@ const jsVendorSources = [
gulp.task('compress_js_vendor', () => gulp.src(jsVendorSources) gulp.task('compress_js_vendor', () => gulp.src(jsVendorSources)
.pipe(concat('vendor.min.js')) .pipe(concat('vendor.min.js'))
.pipe(terser()) .pipe(terser())
.pipe(gulp.dest('./dist/resources'))); .pipe(gulp.dest(RESOURCES_PATH)));
const mjsSources = [ const mjsSources = [
'server/scripts/modules/currentweatherscroll.mjs', 'server/scripts/modules/currentweatherscroll.mjs',
@@ -89,14 +90,14 @@ const mjsSources = [
gulp.task('build_js', () => gulp.src(mjsSources) gulp.task('build_js', () => gulp.src(mjsSources)
.pipe(webpack(webpackOptions)) .pipe(webpack(webpackOptions))
.pipe(gulp.dest('dist/resources'))); .pipe(gulp.dest(RESOURCES_PATH)));
const cssSources = [ const cssSources = [
'server/styles/main.css', 'server/styles/main.css',
]; ];
gulp.task('copy_css', () => gulp.src(cssSources) gulp.task('copy_css', () => gulp.src(cssSources)
.pipe(concat('ws.min.css')) .pipe(concat('ws.min.css'))
.pipe(gulp.dest('./dist/resources'))); .pipe(gulp.dest(RESOURCES_PATH)));
const htmlSources = [ const htmlSources = [
'views/*.ejs', 'views/*.ejs',
@@ -146,7 +147,7 @@ const imageSources = [
'server/fonts/**', 'server/fonts/**',
'server/images/**', 'server/images/**',
]; ];
gulp.task('upload_images', () => gulp.src(imageSources, { base: './server' }) gulp.task('upload_images', () => gulp.src(imageSources, { base: './server', encoding: false })
.pipe( .pipe(
s3({ s3({
Bucket: 'weatherstar', Bucket: 'weatherstar',
@@ -154,7 +155,7 @@ gulp.task('upload_images', () => gulp.src(imageSources, { base: './server' })
}), }),
)); ));
gulp.task('invalidate', async () => cloudfront.createInvalidation({ gulp.task('invalidate', async () => cloudfront.send(new CreateInvalidationCommand({
DistributionId: 'E9171A4KV8KCW', DistributionId: 'E9171A4KV8KCW',
InvalidationBatch: { InvalidationBatch: {
CallerReference: (new Date()).toLocaleString(), CallerReference: (new Date()).toLocaleString(),
@@ -163,7 +164,7 @@ gulp.task('invalidate', async () => cloudfront.createInvalidation({
Items: ['/*'], Items: ['/*'],
}, },
}, },
}).promise()); }).promise()));
gulp.task('build-dist', gulp.series(clean, gulp.parallel('build_js', 'compress_js_data', 'compress_js_vendor', 'copy_css', 'compress_html', 'copy_other_files'))); gulp.task('build-dist', gulp.series(clean, gulp.parallel('build_js', 'compress_js_data', 'compress_js_vendor', 'copy_css', 'compress_html', 'copy_other_files')));

2987
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "ws4kp", "name": "ws4kp",
"version": "5.11.3", "version": "5.11.6",
"description": "Welcome to the WeatherStar 4000+ project page!", "description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -33,7 +33,6 @@
"gulp-ejs": "^5.1.0", "gulp-ejs": "^5.1.0",
"gulp-htmlmin": "^5.0.1", "gulp-htmlmin": "^5.0.1",
"gulp-rename": "^2.0.0", "gulp-rename": "^2.0.0",
"gulp-s3-upload": "^1.7.3",
"gulp-sass": "^5.1.0", "gulp-sass": "^5.1.0",
"gulp-terser": "^2.0.0", "gulp-terser": "^2.0.0",
"jquery": "^3.6.0", "jquery": "^3.6.0",
@@ -45,5 +44,10 @@
"swiped-events": "^1.1.4", "swiped-events": "^1.1.4",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"webpack-stream": "^7.0.0" "webpack-stream": "^7.0.0"
},
"dependencies": {
"@aws-sdk/client-cloudfront": "^3.609.0",
"gulp-awspublish": "^8.0.0",
"gulp-s3-upload": "^1.7.3"
} }
} }

View File

@@ -0,0 +1,14 @@
// this file is loaded by the main html page (when renamed to custom.js)
// it is intended to allow for customizations that do not get published back to the git repo
// for example, changing the logo
// start running after all content is loaded
document.addEventListener('DOMContentLoaded', () => {
// get all of the logo images
const logos = document.querySelectorAll('.logo img');
// loop through each logo
logos.forEach((elem) => {
// change the source
elem.src = 'my-custom-logo.gif';
});
});

View File

@@ -43,9 +43,12 @@ const init = () => {
btnGetGps.addEventListener('click', btnGetGpsClick); btnGetGps.addEventListener('click', btnGetGpsClick);
if (!navigator.geolocation) btnGetGps.style.display = 'none'; if (!navigator.geolocation) btnGetGps.style.display = 'none';
document.querySelector('#divTwc').addEventListener('click', () => { document.querySelector('#divTwc').addEventListener('mousemove', () => {
if (document.fullscreenElement) updateFullScreenNavigate(); if (document.fullscreenElement) updateFullScreenNavigate();
}); });
// local change detection when exiting full screen via ESC key (or other non button click methods)
window.addEventListener('resize', fullScreenResizeCheck);
fullScreenResizeCheck.wasFull = false;
document.querySelector(TXT_ADDRESS_SELECTOR).addEventListener('keydown', (key) => { if (key.code === 'Enter') formSubmit(); }); document.querySelector(TXT_ADDRESS_SELECTOR).addEventListener('keydown', (key) => { if (key.code === 'Enter') formSubmit(); });
document.querySelector('#btnGetLatLng').addEventListener('click', () => formSubmit()); document.querySelector('#btnGetLatLng').addEventListener('click', () => formSubmit());
@@ -189,7 +192,7 @@ const enterFullScreen = () => {
if (requestMethod) { if (requestMethod) {
// Native full screen. // Native full screen.
requestMethod.call(element, { navigationUI: 'hide' }); // https://bugs.chromium.org/p/chromium/issues/detail?id=933436#c7 requestMethod.call(element, { navigationUI: 'hide' });
} else { } else {
// iOS doesn't support FullScreen API. // iOS doesn't support FullScreen API.
window.scrollTo(0, 0); window.scrollTo(0, 0);
@@ -217,6 +220,10 @@ const exitFullscreen = () => {
document.msExitFullscreen(); document.msExitFullscreen();
} }
resize(); resize();
exitFullScreenVisibilityChanges();
};
const exitFullScreenVisibilityChanges = () => {
// change hover text and image // change hover text and image
const img = document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR); const img = document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR);
img.src = 'images/nav/ic_fullscreen_white_24dp_2x.png'; img.src = 'images/nav/ic_fullscreen_white_24dp_2x.png';
@@ -228,7 +235,6 @@ const exitFullscreen = () => {
const btnNavigateMenuClick = () => { const btnNavigateMenuClick = () => {
postMessage('navButton', 'menu'); postMessage('navButton', 'menu');
updateFullScreenNavigate();
return false; return false;
}; };
@@ -261,21 +267,18 @@ const swipeCallBack = (direction) => {
const btnNavigateRefreshClick = () => { const btnNavigateRefreshClick = () => {
resetStatuses(); resetStatuses();
loadData(); loadData();
updateFullScreenNavigate();
return false; return false;
}; };
const btnNavigateNextClick = () => { const btnNavigateNextClick = () => {
postMessage('navButton', 'next'); postMessage('navButton', 'next');
updateFullScreenNavigate();
return false; return false;
}; };
const btnNavigatePreviousClick = () => { const btnNavigatePreviousClick = () => {
postMessage('navButton', 'previous'); postMessage('navButton', 'previous');
updateFullScreenNavigate();
return false; return false;
}; };
@@ -348,7 +351,6 @@ const documentKeydown = (e) => {
const btnNavigatePlayClick = () => { const btnNavigatePlayClick = () => {
postMessage('navButton', 'playToggle'); postMessage('navButton', 'playToggle');
updateFullScreenNavigate();
return false; return false;
}; };
@@ -393,3 +395,18 @@ const btnGetGpsClick = async () => {
txtAddress.value = `${location.city}, ${location.state}`; txtAddress.value = `${location.city}, ${location.state}`;
}); });
}; };
// check for change in full screen triggered by browser and run local functions
const fullScreenResizeCheck = () => {
if (fullScreenResizeCheck.wasFull && !document.fullscreenElement) {
// leaving full screen
exitFullScreenVisibilityChanges();
}
if (!fullScreenResizeCheck.wasFull && document.fullscreenElement) {
// entering full screen
// can't do much here because a UI interaction is required to change the full screen div element
}
// store state of fullscreen element for next change detection
fullScreenResizeCheck.wasFull = !!document.fullscreenElement;
};

View File

@@ -7,7 +7,13 @@ const specialMappings = {
const init = () => { const init = () => {
// add action to existing link // add action to existing link
document.querySelector('#share-link').addEventListener('click', createLink); const shareLink = document.querySelector('#share-link');
shareLink.addEventListener('click', createLink);
// if navigator.clipboard does not exist, change text
if (!navigator?.clipboard) {
shareLink.textContent = 'Get Permalink';
}
}; };
const createLink = async (e) => { const createLink = async (e) => {
@@ -33,10 +39,23 @@ const createLink = async (e) => {
const url = new URL(`?${queryString}`, document.location.href); const url = new URL(`?${queryString}`, document.location.href);
// send to proper function based on availability of clipboard
if (navigator?.clipboard) {
copyToClipboard(url);
} else {
writeLinkToPage(url);
}
};
const copyToClipboard = async (url) => {
try { try {
// write to clipboard
await navigator.clipboard.writeText(url.toString()); await navigator.clipboard.writeText(url.toString());
// alert user
const confirmSpan = document.querySelector('#share-link-copied'); const confirmSpan = document.querySelector('#share-link-copied');
confirmSpan.style.display = 'inline'; confirmSpan.style.display = 'inline';
// hide confirm text after 5 seconds
setTimeout(() => { setTimeout(() => {
confirmSpan.style.display = 'none'; confirmSpan.style.display = 'none';
}, 5000); }, 5000);
@@ -45,6 +64,18 @@ const createLink = async (e) => {
} }
}; };
const writeLinkToPage = (url) => {
// get elements
const shareLinkInstructions = document.querySelector('#share-link-instructions');
const shareLinkUrl = shareLinkInstructions.querySelector('#share-link-url');
// populate url and display
shareLinkUrl.value = url;
shareLinkInstructions.style.display = 'inline';
// highlight for convenience
shareLinkUrl.focus();
shareLinkUrl.select();
};
const parseQueryString = () => { const parseQueryString = () => {
// return memoized result // return memoized result
if (parseQueryString.params) return parseQueryString.params; if (parseQueryString.params) return parseQueryString.params;

View File

@@ -169,7 +169,7 @@ class WeatherDisplay {
// auto clock refresh // auto clock refresh
if (!this.dateTimeInterval) { if (!this.dateTimeInterval) {
// only draw if canvas is active to conserve battery // only draw if canvas is active to conserve battery
setInterval(() => this.active && this.drawCurrentDateTime(), 100); this.dateTimeInterval = setInterval(() => this.active && this.drawCurrentDateTime(), 100);
} }
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -353,7 +353,8 @@ body {
margin-bottom: 15px; margin-bottom: 15px;
} }
#enabledDisplays, #settings { #enabledDisplays,
#settings {
margin-bottom: 15px; margin-bottom: 15px;
@include u.status-colors(); @include u.status-colors();
@@ -412,6 +413,7 @@ body {
justify-content: center; justify-content: center;
align-content: center; align-content: center;
} }
.kiosk #divTwc { .kiosk #divTwc {
justify-content: unset; justify-content: unset;
} }
@@ -447,10 +449,10 @@ body {
.visible { .visible {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transition: opacity 1s linear; transition: opacity 0.1s linear;
} }
.hidden { #divTwc:fullscreen .hidden {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
transition: visibility 0s 1s, opacity 1s linear transition: visibility 0s 1s, opacity 1s linear
@@ -728,7 +730,12 @@ body {
display: none; display: none;
} }
#share-link-instructions {
display: none;
}
.kiosk { .kiosk {
#divQuery, #divQuery,
>.info, >.info,
>.heading, >.heading,

View File

@@ -45,7 +45,7 @@
<script type="module" src="scripts/modules/radar.mjs"></script> <script type="module" src="scripts/modules/radar.mjs"></script>
<script type="module" src="scripts/modules/settings.mjs"></script> <script type="module" src="scripts/modules/settings.mjs"></script>
<script type="module" src="scripts/index.mjs"></script> <script type="module" src="scripts/index.mjs"></script>
<script type="text/javascript" src="scripts/custom.js"></script>
<!-- data --> <!-- data -->
<script type="text/javascript" src="scripts/data/travelcities.js"></script> <script type="text/javascript" src="scripts/data/travelcities.js"></script>
<script type="text/javascript" src="scripts/data/regionalcities.js"></script> <script type="text/javascript" src="scripts/data/regionalcities.js"></script>
@@ -151,10 +151,16 @@
<div id='settings'> <div id='settings'>
</div> </div>
<div class='heading'>Sharing</div>
<div class='info'> <div class='info'>
<a href='' id='share-link'>Copy Permalink</a> <span id="share-link-copied">Link copied to clipboard!</span> <a href='' id='share-link'>Copy Permalink</a> <span id="share-link-copied">Link copied to clipboard!</span>
<div id="share-link-instructions">
Copy this long URL:
<input type='text' id="share-link-url"></div>
</div>
</div> </div>
<div class='heading'>Forecast Information</div>
<div id="divInfo"> <div id="divInfo">
Location: <span id="spanCity"></span> <span id="spanState"></span><br /> Location: <span id="spanCity"></span> <span id="spanState"></span><br />
Station Id: <span id="spanStationId"></span><br /> Station Id: <span id="spanStationId"></span><br />