Compare commits

...

19 Commits

Author SHA1 Message Date
Matt Walsh
942fa8b817 6.5.4 2026-03-26 14:45:41 -05:00
Matt Walsh
15b68eba2f fix extended forecast day names close #190 2026-03-26 14:44:13 -05:00
Matt Walsh
933a289d03 update dependencies 2026-03-26 14:12:38 -05:00
Matt Walsh
e6121327ce 6.5.3 2026-03-16 12:31:36 -05:00
Matt Walsh
678f04fe42 fix extended forecast preload close #189 2026-03-16 12:31:27 -05:00
Matt Walsh
77592a08a3 update test dependencies 2026-03-10 09:33:04 -05:00
Matt Walsh
dadfcb8a5c readme spelling 2026-03-09 13:50:09 -05:00
Matt Walsh
245e9daf9c update eslint 2026-03-09 13:11:31 -05:00
Matt Walsh
177012317b update dependencies 2026-03-09 13:08:22 -05:00
Matt Walsh
7bd21bcf1d 6.5.2 2026-03-06 15:55:39 -06:00
Matt Walsh
ec65025ae2 search for 4-letter stations on regional maps close #187 2026-03-06 15:55:28 -06:00
Matt Walsh
194e108037 6.5.1 2026-03-01 10:04:02 -06:00
Matt Walsh
d5b7c6630a re-instate custom text scroll close #186 2026-03-01 10:03:52 -06:00
Matt Walsh
39bafae394 Merge pull request #185 from rmitchellscott/music-dist-1
fix: correctly use music mount when DIST=1. Fixes #174
2026-02-17 11:53:04 -06:00
Mitchell Scott
ec8fffbb64 fix: correctly use music mount when DIST=1. Fixes #174 2026-02-17 07:34:12 -07:00
Matt Walsh
0a794eae36 6.5.0 2026-02-16 21:30:23 -06:00
Matt Walsh
8c83736aba remove rss feeds close #184 2026-02-16 21:30:13 -06:00
Matt Walsh
872162080d 6.4.3 2026-02-16 20:40:25 -06:00
Matt Walsh
69d2b0f40b more robust backfilling of current weather properties close #177 2026-02-16 20:40:13 -06:00
15 changed files with 1485 additions and 1426 deletions

View File

@@ -34,7 +34,7 @@ From a learning standpoint, this codebase make use of a lot of different methods
* Hand written CSS made easier to mange with SASS
* A linting library to keep code style consistent
## Quck Start
## Quick Start
Ensure you have Node installed.
```bash
@@ -336,8 +336,10 @@ When using Docker:
* **Static deployment**: Mount your `custom.js` file to `/usr/share/nginx/html/scripts/custom.js`
* **Server deployment**: Mount your `custom.js` file to `/app/server/scripts/custom.js`
### RSS feeds and custom scroll
If you would like your Weatherstar to have custom scrolling text in the bottom blue bar, or show headlines from an rss feed turn on the setting for `Enable RSS Feed/Text` and then enter a URL or text in the resulting text box. Then press set.
### Custom text scroll
If you would like your Weatherstar to have custom scrolling text in the bottom blue bar, turn on the setting for `Enable RSS Feed/Text` and then enter text in the resulting text box. Then press set.
Tip: You can have Weatherstar select randomly between several text strings on each pass through the current conditions. Use a pipe character to separate string. `Welcome to Weatherstar|Thanks for watching`.
## Issue reporting and feature requests

View File

@@ -86,7 +86,7 @@ const mjsSources = [
'server/scripts/modules/travelforecast.mjs',
'server/scripts/modules/progress.mjs',
'server/scripts/modules/media.mjs',
'server/scripts/modules/custom-rss-feed.mjs',
'server/scripts/modules/custom-scroll-text.mjs',
'server/scripts/index.mjs',
];

View File

@@ -158,6 +158,7 @@ if (process.env?.DIST === '1') {
// 'npm run build' and then 'DIST=1 npm start'
app.use('/scripts', express.static('./server/scripts', staticOptions));
app.use('/geoip', geoip);
app.use('/music', express.static('./server/music', staticOptions));
// render the EJS template in production mode (serve compressed files from dist directory)
app.get('/', (req, res) => { renderIndex(req, res, true); });

2412
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ws4kp",
"version": "6.4.2",
"version": "6.5.4",
"description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.mjs",
"type": "module",
@@ -31,7 +31,7 @@
"@eslint/eslintrc": "^3.3.1",
"ajv": "^8.17.1",
"del": "^8.0.0",
"eslint": "^9.0.0",
"eslint": "^10.0.3",
"eslint-config-airbnb-base": "15.0.0",
"eslint-plugin-import": "^2.10.0",
"fancy-log": "^2.0.0",
@@ -40,7 +40,7 @@
"gulp-concat": "^2.6.1",
"gulp-ejs": "^5.1.0",
"gulp-file": "^0.4.0",
"gulp-html-minifier-terser": "^7.1.0",
"gulp-html-minifier-terser": "^8.0.0",
"gulp-rename": "^2.0.0",
"gulp-s3-uploader": "^1.0.6",
"gulp-sass": "^6.0.0",
@@ -58,7 +58,7 @@
},
"dependencies": {
"dotenv": "^17.0.1",
"ejs": "^3.1.5",
"ejs": "^5.0.1",
"express": "^5.1.0"
}
}

View File

@@ -324,7 +324,7 @@ const backfill = (data) => {
// backfill each property
Object.keys(sortedData[0].properties).forEach((key) => {
// qualify the key (must have value)
if (Object.hasOwn(sortedData[0].properties[key], 'value')) {
if (Object.hasOwn(sortedData[0].properties?.[key] ?? {}, 'value')) {
// backfill this property
result[key] = backfillProperty(sortedData, key);
} else {

View File

@@ -261,6 +261,7 @@ const parseMessage = (event) => {
if (event?.data?.type === 'current-weather-scroll') {
if (event.data?.method === 'start') start();
if (event.data?.method === 'reload') stop(true);
if (event.data?.method === 'non-display') nonDisplay();
if (event.data?.method === 'show') show();
if (event.data?.method === 'hide') hide();
}
@@ -274,6 +275,20 @@ const hide = () => {
mainScroll.style.display = 'none';
};
const nonDisplay = () => {
if (interval) {
clearInterval(interval);
interval = null;
stop();
// if greater than default update (typically long scroll) skip to the next weather screen
if (nextUpdate > DEFAULT_UPDATE) {
screenIndex = (screenIndex + 1) % (workingScreens.length);
sinceLastUpdate = 0;
nextUpdate = DEFAULT_UPDATE;
}
}
};
const screenCount = () => workingScreens.length;
const atDefault = () => defaultScreensLoaded;

View File

@@ -1,135 +0,0 @@
import Setting from './utils/setting.mjs';
import { reset as resetScroll, addScreen as addScroll, hazards } from './currentweatherscroll.mjs';
import { json } from './utils/fetch.mjs';
let firstRun = true;
const parser = new DOMParser();
// change of enable handler
const changeEnable = (newValue) => {
let newDisplay;
if (newValue) {
// add the feed to the scroll
parseFeed(customFeed.value);
// show the string box
newDisplay = 'block';
} else {
// set scroll back to original
resetScroll();
// hide the string entry
newDisplay = 'none';
}
const stringEntry = document.getElementById('settings-customFeed-label');
if (stringEntry) {
stringEntry.style.display = newDisplay;
}
};
// parse the feed/text provided
const parseFeed = (textInput) => {
// skip getting the feed on first run
if (firstRun) return;
// test validity
if (textInput === undefined || textInput === '') {
resetScroll();
}
// test for url
if (textInput.match(/https?:\/\//)) {
getFeed(textInput);
return;
}
// add single text scroll after hazards if present
resetScroll();
addScroll(hazards);
addScroll(
() => (
{
type: 'scroll',
text: textInput,
}),
// keep the existing scroll
true,
);
};
// get the rss feed and then swap out the current weather scroll
const getFeed = async (url) => {
// get the text as a string
// it needs to be proxied, use a free service
const rssResponse = await json(`https://api.allorigins.win/get?url=${url}`);
// this returns a data url
// a few sanity checks
if (rssResponse.status.content_type.indexOf('xml') < 0) return;
// determine return type
const isBase64 = rssResponse.status.content_type.substring(0, 8) !== 'text/xml';
// base 64 decode everything after the comma
const rss = isBase64 ? atob(rssResponse.contents.split('base64,')[1]) : rssResponse.contents;
// parse the rss
const doc = parser.parseFromString(rss, 'text/xml');
// get the title
const rssTitle = doc.querySelector('channel title').textContent;
// get each item
const titles = [...doc.querySelectorAll('item title')].map((t) => t.textContent);
// reset the scroll, then add the screens
resetScroll();
// add the hazards scroll first
addScroll(hazards);
titles.forEach((title) => {
// data is provided to the screen handler, so we return a function
addScroll(
() => ({
header: rssTitle,
type: 'scroll',
text: title,
}),
// false parameter does not include the default weather scrolls
false,
);
});
};
// change the feed source and re-load if necessary
const changeFeed = (newValue) => {
// first pass through won't have custom feed enable ready
if (firstRun) return;
if (customFeedEnable.value) {
parseFeed(newValue);
}
};
const customFeed = new Setting('customFeed', {
name: 'Custom RSS Feed',
defaultValue: '',
type: 'string',
changeAction: changeFeed,
placeholder: 'Text or URL',
});
const customFeedEnable = new Setting('customFeedEnable', {
name: 'Enable RSS Feed/Text',
defaultValue: false,
changeAction: changeEnable,
});
// initialize the custom feed inputs on the page
document.addEventListener('DOMContentLoaded', () => {
// add the controls to the page
const settingsSection = document.querySelector('#settings');
settingsSection.append(customFeedEnable.generate(), customFeed.generate());
// clear the first run value
firstRun = false;
// call change enable with the current value to show/hide the url box
// and make the call to get the feed if enabled
changeEnable(customFeedEnable.value);
});

View File

@@ -0,0 +1,89 @@
import Setting from './utils/setting.mjs';
import { reset as resetScroll, addScreen as addScroll, hazards } from './currentweatherscroll.mjs';
let firstRun = true;
// change of enable handler
const changeEnable = (newValue) => {
let newDisplay;
if (newValue) {
// add the text to the scroll
parseText(customText.value);
// show the string box
newDisplay = 'block';
} else {
// set scroll back to original
resetScroll();
// hide the string entry
newDisplay = 'none';
}
const stringEntry = document.getElementById('settings-customText-label');
if (stringEntry) {
stringEntry.style.display = newDisplay;
}
};
// parse the text provided
const parseText = (textInput) => {
// skip updating text on first run
if (firstRun) return;
// test validity
if (textInput === undefined || textInput === '') {
resetScroll();
}
// split the text at pipe characters
const texts = textInput.split('|');
// add single text scroll after hazards if present
resetScroll();
addScroll(hazards);
addScroll(
() => {
// pick a random string from the available list
const randInt = Math.floor(Math.random() * texts.length);
return {
type: 'scroll',
text: texts[randInt],
};
},
// keep the existing scroll
true,
);
};
// change the text
const changeText = (newValue) => {
// first pass through won't have custom text enable ready
if (firstRun) return;
if (customTextEnable.value) {
parseText(newValue);
}
};
const customText = new Setting('customText', {
name: 'Custom Text',
defaultValue: '',
type: 'string',
changeAction: changeText,
placeholder: 'Text to scroll',
});
const customTextEnable = new Setting('customTextEnable', {
name: 'Enable Custom Text',
defaultValue: false,
changeAction: changeEnable,
});
// initialize the custom text inputs on the page
document.addEventListener('DOMContentLoaded', () => {
// add the controls to the page
const settingsSection = document.querySelector('#settings');
settingsSection.append(customTextEnable.generate(), customText.generate());
// clear the first run value
firstRun = false;
// call change enable with the current value to show/hide the url box
changeEnable(customTextEnable.value);
});

View File

@@ -97,11 +97,9 @@ const parse = (fullForecast, forecastUrl) => {
// Skip the first period if it's nighttime (like "Tonight") since extended forecast
// should focus on upcoming full days, not the end of the current day
let startIndex = 0;
let dateOffset = 0; // offset for date labels when we skip periods
if (activePeriods.length > 0 && !activePeriods[0].isDaytime) {
startIndex = 1;
dateOffset = 1; // start date labels from tomorrow since we're skipping tonight
if (debugFlag('extendedforecast')) {
console.log(`ExtendedForecast: Skipping first period "${activePeriods[0].name}" because it's nighttime`);
}
@@ -111,25 +109,14 @@ const parse = (fullForecast, forecastUrl) => {
}
}
// create a list of days starting with the appropriate day
const Days = [0, 1, 2, 3, 4, 5, 6];
const dates = Days.map((shift) => {
const date = DateTime.local().startOf('day').plus({ days: shift + dateOffset });
return date.toLocaleString({ weekday: 'short' });
});
if (debugFlag('extendedforecast')) {
console.log(`ExtendedForecast: Generated date labels: [${dates.join(', ')}]`);
}
// track the destination forecast index
let destIndex = 0;
const forecast = [];
// if the first period is nighttime it is skipped above via startIndex
for (let i = startIndex; i < activePeriods.length; i += 1) {
const period = activePeriods[i];
// create the destination object if necessary
if (!forecast[destIndex]) {
forecast.push({
dayName: '', low: undefined, high: undefined, text: undefined, icon: undefined,
@@ -138,15 +125,14 @@ const parse = (fullForecast, forecastUrl) => {
// get the object to modify/populate
const fDay = forecast[destIndex];
// preload the icon
preloadImg(fDay.icon);
if (period.isDaytime) {
// day time is the high temperature
fDay.high = period.temperature;
fDay.icon = getLargeIcon(period.icon);
fDay.text = shortenExtendedForecastText(period.shortForecast);
fDay.dayName = dates[destIndex];
fDay.dayName = DateTime.fromISO(period.startTime).startOf('day').toLocaleString({ weekday: 'short' });
// preload the icon
preloadImg(fDay.icon);
// Wait for the corresponding night period to increment
} else {
// low temperature

View File

@@ -23,7 +23,7 @@ const buildForecast = (forecast, city, cityXY) => {
const getRegionalObservation = async (point, city) => {
try {
// get stations using centralized safe handling
const stations = await safeJson(`https://api.weather.gov/gridpoints/${point.wfo}/${point.x},${point.y}/stations?limit=1`);
const stations = await safeJson(`https://api.weather.gov/gridpoints/${point.wfo}/${point.x},${point.y}/stations?limit=10`);
if (!stations || !stations.features || stations.features.length === 0) {
if (debugFlag('verbose-failures')) {
@@ -32,9 +32,13 @@ const getRegionalObservation = async (point, city) => {
return false;
}
// get the first station
const station = stations.features[0].id;
const stationId = stations.features[0].properties.stationIdentifier;
// get the first station with a 4-letter id (generally has appropriate data)
const station4Letter = stations.features.find((station) => {
if (station.properties.stationIdentifier.length === 4) return station.properties;
return false;
});
const station = station4Letter.id;
const stationId = station4Letter.properties.stationIdentifier;
// get the observation data using centralized safe handling
const observation = await safeJson(`${station}/observations/latest`);

View File

@@ -172,6 +172,7 @@ class WeatherDisplay {
if (this.screenIndex < 0) this.screenIndex = 0;
if (this.okToDrawCurrentDateTime) this.drawCurrentDateTime();
if (this.okToDrawCurrentConditions) postMessage({ type: 'current-weather-scroll', method: 'start' });
if (!this.okToDrawCurrentConditions) postMessage({ type: 'current-weather-scroll', method: 'non-display' });
if (this.okToDrawCurrentConditions === false) postMessage({ type: 'current-weather-scroll', method: 'hide' });
}

View File

@@ -346,7 +346,7 @@ var TimeIndicator;
TimeIndicator["TL"] = "TL";
})(TimeIndicator || (TimeIndicator = {}));
/**
* https://www.aviationweather.gov/taf/decoder
* https://web.archive.org/web/20230318235549/https://aviationweather.gov/taf/decoder
*/
var WeatherChangeType;
(function (WeatherChangeType) {
@@ -2535,7 +2535,8 @@ class MetarParser extends AbstractParser {
while (i < trendParts.length &&
trendParts[i] !== this.TEMPO &&
trendParts[i] !== this.INTER &&
trendParts[i] !== this.BECMG) {
trendParts[i] !== this.BECMG &&
trendParts[i] !== this.RMK) {
if (trendParts[i].startsWith(this.FM) ||
trendParts[i].startsWith(this.TL) ||
trendParts[i].startsWith(this.AT)) {

197
tests/package-lock.json generated
View File

@@ -14,12 +14,12 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -37,16 +37,16 @@
}
},
"node_modules/@puppeteer/browsers": {
"version": "2.10.13",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz",
"integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==",
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz",
"integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==",
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.4.3",
"extract-zip": "^2.0.1",
"progress": "^2.0.3",
"proxy-agent": "^6.5.0",
"semver": "^7.7.3",
"semver": "^7.7.4",
"tar-fs": "^3.1.1",
"yargs": "^17.7.2"
},
@@ -64,13 +64,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": "~7.16.0"
"undici-types": "~7.18.0"
}
},
"node_modules/@types/yauzl": {
@@ -135,9 +135,9 @@
}
},
"node_modules/b4a": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
"integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz",
"integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==",
"license": "Apache-2.0",
"peerDependencies": {
"react-native-b4a": "*"
@@ -163,11 +163,10 @@
}
},
"node_modules/bare-fs": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.1.tgz",
"integrity": "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg==",
"version": "4.5.6",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.6.tgz",
"integrity": "sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"bare-events": "^2.5.4",
"bare-path": "^3.0.0",
@@ -188,11 +187,10 @@
}
},
"node_modules/bare-os": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
"integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.0.tgz",
"integrity": "sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==",
"license": "Apache-2.0",
"optional": true,
"engines": {
"bare": ">=1.14.0"
}
@@ -202,25 +200,28 @@
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
"integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"bare-os": "^3.0.1"
}
},
"node_modules/bare-stream": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
"integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.11.0.tgz",
"integrity": "sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"streamx": "^2.21.0"
"streamx": "^2.25.0",
"teex": "^1.0.1"
},
"peerDependencies": {
"bare-abort-controller": "*",
"bare-buffer": "*",
"bare-events": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
},
"bare-buffer": {
"optional": true
},
@@ -230,19 +231,18 @@
}
},
"node_modules/bare-url": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
"integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz",
"integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"bare-path": "^3.0.0"
}
},
"node_modules/basic-ftp": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
"integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz",
"integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -279,9 +279,9 @@
}
},
"node_modules/chromium-bidi": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-11.0.0.tgz",
"integrity": "sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==",
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
"integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
"license": "Apache-2.0",
"dependencies": {
"mitt": "^3.0.1",
@@ -324,9 +324,9 @@
"license": "MIT"
},
"node_modules/cosmiconfig": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz",
"integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==",
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.1",
@@ -390,11 +390,10 @@
}
},
"node_modules/devtools-protocol": {
"version": "0.0.1521046",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz",
"integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==",
"license": "BSD-3-Clause",
"peer": true
"version": "0.0.1581282",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
"integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
"license": "BSD-3-Clause"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -816,9 +815,9 @@
"license": "MIT"
},
"node_modules/pump": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
@@ -826,18 +825,18 @@
}
},
"node_modules/puppeteer": {
"version": "24.31.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.31.0.tgz",
"integrity": "sha512-q8y5yLxLD8xdZdzNWqdOL43NbfvUOp60SYhaLZQwHC9CdKldxQKXOyJAciOr7oUJfyAH/KgB2wKvqT2sFKoVXA==",
"version": "24.40.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.40.0.tgz",
"integrity": "sha512-IxQbDq93XHVVLWHrAkFP7F7iHvb9o0mgfsSIMlhHb+JM+JjM1V4v4MNSQfcRWJopx9dsNOr9adYv0U5fm9BJBQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.10.13",
"chromium-bidi": "11.0.0",
"@puppeteer/browsers": "2.13.0",
"chromium-bidi": "14.0.0",
"cosmiconfig": "^9.0.0",
"devtools-protocol": "0.0.1521046",
"puppeteer-core": "24.31.0",
"typed-query-selector": "^2.12.0"
"devtools-protocol": "0.0.1581282",
"puppeteer-core": "24.40.0",
"typed-query-selector": "^2.12.1"
},
"bin": {
"puppeteer": "lib/cjs/puppeteer/node/cli.js"
@@ -847,18 +846,18 @@
}
},
"node_modules/puppeteer-core": {
"version": "24.31.0",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.31.0.tgz",
"integrity": "sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==",
"version": "24.40.0",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.40.0.tgz",
"integrity": "sha512-MWL3XbUCfVgGR0gRsidzT6oKJT2QydPLhMITU6HoVWiiv4gkb6gJi3pcdAa8q4HwjBTbqISOWVP4aJiiyUJvag==",
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.10.13",
"chromium-bidi": "11.0.0",
"@puppeteer/browsers": "2.13.0",
"chromium-bidi": "14.0.0",
"debug": "^4.4.3",
"devtools-protocol": "0.0.1521046",
"typed-query-selector": "^2.12.0",
"webdriver-bidi-protocol": "0.3.9",
"ws": "^8.18.3"
"devtools-protocol": "0.0.1581282",
"typed-query-selector": "^2.12.1",
"webdriver-bidi-protocol": "0.4.1",
"ws": "^8.19.0"
},
"engines": {
"node": ">=18"
@@ -883,9 +882,9 @@
}
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -943,9 +942,9 @@
}
},
"node_modules/streamx": {
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
"integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
"version": "2.25.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz",
"integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==",
"license": "MIT",
"dependencies": {
"events-universal": "^1.0.0",
@@ -980,9 +979,9 @@
}
},
"node_modules/tar-fs": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
"integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz",
"integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==",
"license": "MIT",
"dependencies": {
"pump": "^3.0.0",
@@ -994,20 +993,30 @@
}
},
"node_modules/tar-stream": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
"integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz",
"integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==",
"license": "MIT",
"dependencies": {
"b4a": "^1.6.4",
"bare-fs": "^4.5.5",
"fast-fifo": "^1.2.0",
"streamx": "^2.15.0"
}
},
"node_modules/teex": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
"integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
"license": "MIT",
"dependencies": {
"streamx": "^2.12.5"
}
},
"node_modules/text-decoder": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
"integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
"integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
"license": "Apache-2.0",
"dependencies": {
"b4a": "^1.6.4"
@@ -1020,22 +1029,22 @@
"license": "0BSD"
},
"node_modules/typed-query-selector": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
"integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz",
"integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"license": "MIT",
"optional": true
},
"node_modules/webdriver-bidi-protocol": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.9.tgz",
"integrity": "sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
"integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
"license": "Apache-2.0"
},
"node_modules/wrap-ansi": {
@@ -1062,9 +1071,9 @@
"license": "ISC"
},
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
"integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"

View File

@@ -62,7 +62,7 @@
<script type="module" src="scripts/modules/radar.mjs"></script>
<script type="module" src="scripts/modules/settings.mjs"></script>
<script type="module" src="scripts/modules/media.mjs"></script>
<script type="module" src="scripts/modules/custom-rss-feed.mjs"></script>
<script type="module" src="scripts/modules/custom-scroll-text.mjs"></script>
<script type="module" src="scripts/index.mjs"></script>
<% } %>