From 994c9240b856845e73a7040670a6a3560ff6aa29 Mon Sep 17 00:00:00 2001 From: Matt Walsh Date: Mon, 13 Apr 2026 16:19:26 -0500 Subject: [PATCH] shorten permalinks close #206 --- README.md | 18 ++++++++++++------ server/scripts/index.mjs | 2 +- server/scripts/modules/media.mjs | 5 +---- server/scripts/modules/settings.mjs | 4 ++-- server/scripts/modules/share.mjs | 18 ++++++++++++------ server/scripts/modules/utils/setting.mjs | 12 +++++++----- server/scripts/modules/weatherdisplay.mjs | 7 ++++--- views/index.ejs | 4 ++-- 8 files changed, 41 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index fdf2a90..52ec82a 100644 --- a/README.md +++ b/README.md @@ -139,8 +139,8 @@ services: # Following the "Sharing a Permalink" example below, here are a few environment variables defined. Visit that section for a # more complete list of configuration options. - WSQS_latLonQuery=Orlando International Airport Orlando FL USA - - WSQS_hazards_checkbox=false - - WSQS_current_weather_checkbox=true + - WSQS_hazards=false + - WSQS_current_weather=true ports: - 8080:8080 # change the first 8080 to meet your local network needs restart: unless-stopped @@ -192,13 +192,13 @@ Selected displays, the forecast city and widescreen setting are sticky from one Your permalink will be very long. Here is an example for the Orlando International Airport: ``` -https://weatherstar.netbymatt.com/?hazards-checkbox=false¤t-weather-checkbox=true&latest-observations-checkbox=true&hourly-checkbox=false&hourly-graph-checkbox=true&travel-checkbox=false®ional-forecast-checkbox=true&local-forecast-checkbox=true&extended-forecast-checkbox=true&almanac-checkbox=false&spc-outlook-checkbox=true&radar-checkbox=true&settings-wide-checkbox=false&settings-kiosk-checkbox=false&settings-scanLines-checkbox=false&settings-speed-select=1.00&settings-units-select=us&latLonQuery=Orlando+International+Airport%2C+Orlando%2C+FL%2C+USA&latLon=%7B%22lat%22%3A28.431%2C%22lon%22%3A-81.3076%7D +https://weatherstar.netbymatt.com/?hazards=false¤t-weather=true&latest-observations=true&hourly=false&hourly-graph=true&travel=false®ional-forecast=true&local-forecast=true&extended-forecast=true&almanac=false&spc-outlook=true&radar=true&wide=false&kiosk=false&scanLines=false&speed-select=1.00&units-select=us&latLonQuery=Orlando+International+Airport%2C+Orlando%2C+FL%2C+USA&latLon=%7B%22lat%22%3A28.431%2C%22lon%22%3A-81.3076%7D ``` You can also build your own permalink. Any omitted settings will be filled with defaults. Here are a few examples: ``` https://weatherstar.netbymatt.com/?latLonQuery=Orlando+International+Airport https://weatherstar.netbymatt.com/?kiosk=true -https://weatherstar.netbymatt.com/?settings-units-select=metric +https://weatherstar.netbymatt.com/?units-select=metric ``` ### Kiosk mode @@ -213,7 +213,7 @@ When serving this via the built-in Express server, it's possible to define envir Environment variables can be added to the command line as usual, or via a .env file which is parsed with [dotenv](https://github.com/motdotla/dotenv). Both methods have the same effect. -Environment variables that are to be added to the default query string are prefixed with `WSQS_` and then use the same key/value pairs generated by the [Permalink](#sharing-a-permalink-bookmarking) above, with the `- (dash)` character replaced by an `_ (underscore)`. For example, if you wanted to turn the travel forecast on, you would find `travel-checkbox=true` in the permalink, its matching environment variable becomes `WSQS_travel_checkbox=true`. +Environment variables that are to be added to the default query string are prefixed with `WSQS_` and then use the same key/value pairs generated by the [Permalink](#sharing-a-permalink-bookmarking) above, with the `- (dash)` character replaced by an `_ (underscore)`. For example, if you wanted to turn the travel forecast on, you would find `travel-checkbox=true` in the permalink, its matching environment variable becomes `WSQS_travel=true`. When using the Docker container, these environment variables are read on container start-up to generate the static redirect HTML. @@ -221,7 +221,13 @@ When using the Docker container, these environment variables are read on contain **Speed:** Controls the playback speed multiplier of the displays, from "Very Fast" (1.5x) to "Very Slow" (0.5x) with "Normal" being 1x -**Widescreen:** Stretches the background to 16:9 to avoid "pillarboxing" on modern displays +**Display Mode:** +- Standard: Classic 4:3 display with the classic (not enhanced, below) screen layouts. +- Widescreen: Stretches the background to 16:9 to avoid "pillarboxing" on modern displays +- Widescreen Enhanced: Stretches as above, and makes use of the additional space to provide wider maps, more weather data and/or additional days in the forecast +- Portrait Enhanced: (in progress) Rotates the screen to a 16:9 portrait orientation and enhances the original displays by adjusting them to fit the new orientation. + + **Kiosk:** Immediately activates kiosk mode, which hides all settings. Exit by refreshing the page or using `Ctrl-K`. (Kiosk mode is similar to clicking the "Fullscreen" icon, but scales to the current browser viewport instead of activating the browser's actual "Fullscreen" mode.) diff --git a/server/scripts/index.mjs b/server/scripts/index.mjs index a64b1ac..641bf27 100644 --- a/server/scripts/index.mjs +++ b/server/scripts/index.mjs @@ -141,7 +141,7 @@ const init = async () => { } // Handle kiosk mode initialization - const urlKioskCheckbox = parsedParameters['settings-kiosk-checkbox']; + const urlKioskCheckbox = parsedParameters?.kiosk ?? parsedParameters['settings-kiosk-checkbox']; // If kiosk=false is specified, disable kiosk mode and clear any stored value if (urlKioskCheckbox === 'false') { diff --git a/server/scripts/modules/media.mjs b/server/scripts/modules/media.mjs index e06c6b7..8f81d0a 100644 --- a/server/scripts/modules/media.mjs +++ b/server/scripts/modules/media.mjs @@ -1,6 +1,5 @@ import { text } from './utils/fetch.mjs'; import Setting from './utils/setting.mjs'; -import { registerHiddenSetting } from './share.mjs'; let playlist; let currentTrack = 0; @@ -33,9 +32,6 @@ document.addEventListener('DOMContentLoaded', () => { // get the playlist getMedia(); - - // register the volume setting - registerHiddenSetting(mediaVolume.elemId, mediaVolume); }); const scanMusicDirectory = async () => { @@ -246,6 +242,7 @@ const mediaVolume = new Setting('mediaVolume', { [0.25, '25%'], ], changeAction: setVolume, + visible: false, }); const initializePlayer = () => { diff --git a/server/scripts/modules/settings.mjs b/server/scripts/modules/settings.mjs index 89589a7..aafc559 100644 --- a/server/scripts/modules/settings.mjs +++ b/server/scripts/modules/settings.mjs @@ -273,6 +273,7 @@ const init = () => { ['medium', 'Medium (2x)'], ['thick', 'Thick (3x)'], ], + visible: false, }); settings.units = new Setting('units', { name: 'Units', @@ -322,7 +323,7 @@ document.addEventListener('DOMContentLoaded', () => { const settingHtml = Object.values(settings).map((setting) => { if (hiddenSettings.includes(setting.shortName)) { // setting is hidden, register it - registerHiddenSetting(setting.elemId, setting); + registerHiddenSetting(setting.shortName, setting); return false; } // generate HTML for setting @@ -340,7 +341,6 @@ document.addEventListener('DOMContentLoaded', () => { } else if (modeSelect) { modeSelect.style.display = 'none'; } - registerHiddenSetting('settings-scanLineMode-select', settings.scanLineMode); }); export default settings; diff --git a/server/scripts/modules/share.mjs b/server/scripts/modules/share.mjs index 77f5e59..8ebde43 100644 --- a/server/scripts/modules/share.mjs +++ b/server/scripts/modules/share.mjs @@ -25,22 +25,28 @@ const createLink = async (e) => { const queryStringElements = {}; elemForEach('input[type=checkbox]', (elem) => { - if (elem?.id) { - queryStringElements[elem.id] = elem?.checked ?? false; + // use name, and fallback to id (older prefix/suffix permalinks) + const key = elem?.name ?? elem?.id; + if (key) { + queryStringElements[key] = elem?.checked ?? false; } }); // get all select boxes elemForEach('select', (elem) => { - if (elem?.id) { - queryStringElements[elem.id] = encodeURIComponent(elem?.value ?? ''); + // use name, and fallback to id (older prefix/suffix permalinks) + const key = elem?.name ?? elem?.id; + if (key) { + queryStringElements[key] = encodeURIComponent(elem?.value ?? ''); } }); // get all text boxes elemForEach('input[type=text]', ((elem) => { - if (elem?.id) { - queryStringElements[elem.id] = elem?.value ?? 0; + // use name, and fallback to id (older prefix/suffix permalinks) + const key = elem?.name ?? elem?.id; + if (key && key !== '') { + queryStringElements[key] = elem?.value ?? 0; } })); diff --git a/server/scripts/modules/utils/setting.mjs b/server/scripts/modules/utils/setting.mjs index 933612e..cbc5e23 100644 --- a/server/scripts/modules/utils/setting.mjs +++ b/server/scripts/modules/utils/setting.mjs @@ -41,7 +41,9 @@ class Setting { this.elemId = `settings-${shortName}-${this.type}`; // get value from url - const urlValue = parseQueryString()?.[this.elemId]; + // includes a fallback to the older prefix/suffix version + const queryString = parseQueryString(); + const urlValue = queryString?.[shortName] ?? queryString?.[this.elemId]; let urlState; if (this.type === 'checkbox' && urlValue !== undefined) { urlState = urlValue === 'true'; @@ -92,7 +94,7 @@ class Setting { const select = document.createElement('select'); select.id = `settings-${this.shortName}-select`; - select.name = `settings-${this.shortName}-select`; + select.name = this.shortName; select.addEventListener('change', (e) => this.selectChange(e)); this.values.forEach(([value, text]) => { @@ -125,7 +127,7 @@ class Setting { checkbox.type = 'checkbox'; checkbox.value = true; checkbox.id = `settings-${this.shortName}-checkbox`; - checkbox.name = `settings-${this.shortName}-checkbox`; + checkbox.name = this.shortName; checkbox.checked = this.myValue; checkbox.addEventListener('change', (e) => this.checkboxChange(e)); const span = document.createElement('span'); @@ -148,14 +150,14 @@ class Setting { textInput.type = 'text'; textInput.value = this.myValue; textInput.id = `settings-${this.shortName}-string`; - textInput.name = `settings-${this.shortName}-string`; + textInput.name = this.shortName; textInput.placeholder = this.placeholder; // set button const setButton = document.createElement('input'); setButton.type = 'button'; setButton.value = 'Set'; setButton.id = `settings-${this.shortName}-button`; - setButton.name = `settings-${this.shortName}-button`; + setButton.name = this.shortName; setButton.addEventListener('click', () => { this.stringChange({ target: { value: textInput.value } }); }); diff --git a/server/scripts/modules/weatherdisplay.mjs b/server/scripts/modules/weatherdisplay.mjs index 7e53cc5..b2517c5 100644 --- a/server/scripts/modules/weatherdisplay.mjs +++ b/server/scripts/modules/weatherdisplay.mjs @@ -55,8 +55,9 @@ class WeatherDisplay { // no checkbox if progress if (this.elemId === 'progress') return false; - // get url provided state - const urlValue = parseQueryString()?.[`${this.elemId}-checkbox`]; + // get url provided state, and fall back to the older suffix naming convention + const queryString = parseQueryString(); + const urlValue = queryString?.[this.elemId] ?? queryString?.[`${this.elemId}-checkbox`]; let urlState; if (urlValue !== undefined) { urlState = urlValue === 'true'; @@ -78,7 +79,7 @@ class WeatherDisplay { checkbox.type = 'checkbox'; checkbox.value = true; checkbox.id = `${this.elemId}-checkbox`; - checkbox.name = `${this.elemId}-checkbox`; + checkbox.name = this.elemId; checkbox.checked = this.isEnabled; checkbox.addEventListener('change', (e) => this.checkboxChange(e)); const span = document.createElement('span'); diff --git a/views/index.ejs b/views/index.ejs index c4c022d..7e9dcc7 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -68,10 +68,10 @@ -class="kiosk" <% }%>> +class="kiosk" <% }%>>
- +