Compare commits

..

11 Commits

Author SHA1 Message Date
Matt Walsh
c07ebe8bdd 6.5.9 2026-04-09 12:19:03 -05:00
Matt Walsh
a41b0da196 more generalized fix for mapclick enhanced timestamps close #203
moves changes made in 0b47cf79c1 to the mapclick processing for benefit of other mapclick calls
2026-04-09 12:18:56 -05:00
Matt Walsh
30887202c8 6.5.8 2026-04-09 11:30:35 -05:00
Matt Walsh
38d1455a4b fix custom text scroll 2026-04-09 11:29:49 -05:00
Matt Walsh
30ec847ed5 Hide cursor in kiosk
via @iapetusz
2026-04-09 11:22:36 -05:00
Matt Walsh
11c54391b2 6.5.7 2026-04-08 22:42:07 -05:00
Matt Walsh
0b47cf79c1 don't overwrite timestamps when enhancing with mapclick 2026-04-08 22:41:42 -05:00
Matt Walsh
ba36904477 6.5.6 2026-04-08 11:39:36 -05:00
Matt Walsh
dae5b20bc6 fix radar round/floor mismatch in calculations close #200 2026-04-08 11:39:25 -05:00
Matt Walsh
ccc936d81a 6.5.5 2026-04-08 09:57:24 -05:00
Matt Walsh
5dc214c6a5 filter station list upon receipt 2026-04-08 09:57:18 -05:00
34 changed files with 144 additions and 313 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020-2025 Matt Walsh
Copyright (c) 2020-2026 Matt Walsh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -8,13 +8,11 @@ import states from './stations-states.mjs';
import chunk from './chunk.mjs';
import overrides from './stations-overrides.mjs';
import postProcessor from './stations-postprocessor.mjs';
import { stationFilter } from '../server/scripts/modules/utils/string.mjs';
// check for cached flag
const USE_CACHE = process.argv.includes('--use-cache');
// skip stations starting with these letters
const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J'];
// chunk the list of states
const chunkStates = chunk(states, 3);
@@ -41,10 +39,8 @@ if (!USE_CACHE) {
// eslint-disable-next-line no-await-in-loop
const stationsRaw = await https(next);
stations = JSON.parse(stationsRaw);
// filter stations for 4 letter identifiers
const stationsFiltered4 = stations.features.filter((station) => station.properties.stationIdentifier.match(/^[A-Z]{4}$/));
// filter against starting letter
const stationsFiltered = stationsFiltered4.filter((station) => !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)));
const stationsFiltered = stations.filter(stationFilter);
// add each resulting station to the output
stationsFiltered.forEach((station) => {
const id = station.properties.stationIdentifier;

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ws4kp",
"version": "6.5.4",
"version": "6.5.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ws4kp",
"version": "6.5.4",
"version": "6.5.9",
"license": "MIT",
"dependencies": {
"dotenv": "^17.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "ws4kp",
"version": "6.5.4",
"version": "6.5.9",
"description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.mjs",
"type": "module",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

View File

@@ -72,7 +72,7 @@ const init = async () => {
if (!navigator.geolocation) btnGetGps.style.display = 'none';
document.querySelector('#divTwc').addEventListener('mousemove', () => {
if (document.fullscreenElement) updateFullScreenNavigate();
if (document.fullscreenElement || settings.kiosk?.value) updateFullScreenNavigate();
});
document.querySelector('#btnGetLatLng').addEventListener('click', () => autoComplete.directFormSubmit());
@@ -384,7 +384,7 @@ const updateFullScreenNavigate = () => {
}
navigateFadeIntervalId = setTimeout(() => {
if (document.fullscreenElement) {
if (document.fullscreenElement || settings.kiosk?.value) {
divTwcBottom.classList.remove('visible');
divTwcBottom.classList.add('hidden');
document.querySelector('#divTwc').classList.add('no-cursor');

View File

@@ -47,7 +47,10 @@ class Almanac extends WeatherDisplay {
}
calcSunMoonData(weatherParameters) {
const sun = [0, 1, 2, 3, 4, 5, 6].map((days) => SunCalc.getTimes(DateTime.local().plus({ days }).toJSDate(), weatherParameters.latitude, weatherParameters.longitude));
const sun = [
SunCalc.getTimes(new Date(), 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
const moon = [];

View File

@@ -15,9 +15,6 @@ import { debugFlag } from './utils/debug.mjs';
import { isDataStale, enhanceObservationWithMapClick } from './utils/mapclick.mjs';
import { DateTime } from '../vendor/auto/luxon.mjs';
// some stations prefixed do not provide all the necessary data
const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J'];
class CurrentWeather extends WeatherDisplay {
constructor(navId, elemId) {
super(navId, elemId, 'Current Conditions', true);
@@ -29,8 +26,8 @@ class CurrentWeather extends WeatherDisplay {
// note: current weather does not use old data on a silent refresh
// this is deliberate because it can pull data from more than one station in sequence
// filter for 4-letter observation stations, only those contain sky conditions and thus an icon
const filteredStations = this.weatherParameters.stations.filter((station) => station?.properties?.stationIdentifier?.length === 4 && !skipStations.includes(station.properties.stationIdentifier.slice(0, 1)));
// get the available stations
const { stations } = this.weatherParameters;
// Load the observations
let observations;
@@ -38,9 +35,9 @@ class CurrentWeather extends WeatherDisplay {
// station number counter
let stationNum = 0;
while (!observations && stationNum < filteredStations.length) {
while (!observations && stationNum < stations.length) {
// get the station
station = filteredStations[stationNum];
station = stations[stationNum];
const stationId = station.properties.stationIdentifier;
stationNum += 1;
@@ -104,7 +101,10 @@ class CurrentWeather extends WeatherDisplay {
debugContext: 'currentweather',
});
// copy enhanced data and restore the timestamp if it was overwritten by older data from mapclick
candidateObservation.features[0].properties = enhancedResult.data;
const { missingFields } = enhancedResult;
const missingRequired = missingFields.filter((fieldName) => {
const field = requiredFields.find((f) => f.name === fieldName && f.required);

View File

@@ -17,7 +17,7 @@ const changeEnable = (newValue) => {
// hide the string entry
newDisplay = 'none';
}
const stringEntry = document.getElementById('settings-customText-label');
const stringEntry = document.getElementById('settings-customText-string');
if (stringEntry) {
stringEntry.style.display = newDisplay;
}

View File

@@ -17,13 +17,7 @@ class ExtendedForecast extends WeatherDisplay {
super(navId, elemId, 'Extended Forecast', true);
// set timings
if (settings.enhancedScreens?.value) {
this.timing.totalScreens = 1;
this.perPage = 4;
} else {
this.timing.totalScreens = 2;
this.perPage = 3;
}
this.timing.totalScreens = 2;
}
async getData(weatherParameters, refresh) {
@@ -60,7 +54,7 @@ class ExtendedForecast extends WeatherDisplay {
// determine bounds
// grab the first three or second set of three array elements
const forecast = parse(this.data.properties.periods, this.weatherParameters.forecast).slice(0 + this.perPage * this.screenIndex, this.perPage + this.screenIndex * this.perPage);
const forecast = parse(this.data.properties.periods, this.weatherParameters.forecast).slice(0 + 3 * this.screenIndex, 3 + this.screenIndex * 3);
// create each day template
const days = forecast.map((Day) => {

View File

@@ -5,30 +5,10 @@ import getHourlyData from './hourly.mjs';
import WeatherDisplay from './weatherdisplay.mjs';
import { registerDisplay, timeZone } from './navigation.mjs';
import { DateTime } from '../vendor/auto/luxon.mjs';
import settings from './settings.mjs';
// set up spacing and scales
const scaling = () => {
const available = {
width: 532,
height: 285,
};
const dataLength = {
hours: 36,
xTicks: 4,
};
if (settings.wide?.value && settings.enhancedScreens?.value) {
available.width = available.width + 107 + 107;
available.height = 285;
dataLength.hours = 48;
dataLength.xTicks = 6;
}
return {
available,
dataLength,
};
};
// get available space
const availableWidth = 532;
const availableHeight = 285;
class HourlyGraph extends WeatherDisplay {
constructor(navId, elemId, defaultActive) {
@@ -66,43 +46,28 @@ class HourlyGraph extends WeatherDisplay {
skyCover, temperature, probabilityOfPrecipitation, temperatureUnit: data[0].temperatureUnit, dewpoint,
};
// get the data length for current settings
const { dataLength } = scaling();
// clamp down the data to the allowed size
Object.entries(this.data).forEach(([key, value]) => {
if (Array.isArray(value)) {
this.data[key] = value.slice(0, dataLength.hours);
}
});
this.setStatus(STATUS.loaded);
}
drawCanvas() {
// get scaling parameters
const { dataLength, available } = scaling();
// get the image
if (!this.image) this.image = this.elem.querySelector('.chart img');
// set up image
this.image.width = available.width;
this.image.height = available.height;
this.image.width = availableWidth;
this.image.height = availableHeight;
// get context
const canvas = document.createElement('canvas');
canvas.width = available.width;
canvas.height = available.height;
canvas.width = availableWidth;
canvas.height = availableHeight;
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
// calculate time scale
const timeScale = calcScale(0, 5, this.data.temperature.length - 1, available.width);
const timeStep = this.data.temperature.length / (dataLength.xTicks);
const timeScale = calcScale(0, 5, this.data.temperature.length - 1, availableWidth);
const timeStep = this.data.temperature.length / 4;
const startTime = DateTime.now().startOf('hour');
let prevTime = startTime;
Array(dataLength.xTicks + 1).fill().forEach((val, idx) => {
Array(5).fill().forEach((val, idx) => {
// track the previous label so a day of week can be added when it changes
const label = formatTime(startTime.plus({ hour: idx * timeStep }), prevTime);
prevTime = label.ts;
@@ -112,7 +77,7 @@ class HourlyGraph extends WeatherDisplay {
// order is important last line drawn is on top
// clouds
const percentScale = calcScale(0, available.height - 10, 100, 10);
const percentScale = calcScale(0, availableHeight - 10, 100, 10);
const cloud = createPath(this.data.skyCover, timeScale, percentScale);
drawPath(cloud, ctx, {
strokeStyle: 'lightgrey',
@@ -132,7 +97,7 @@ class HourlyGraph extends WeatherDisplay {
const thirdScale = (maxScale - minScale) / 3;
const midScale1 = Math.round(minScale + thirdScale);
const midScale2 = Math.round(minScale + (thirdScale * 2));
const tempScale = calcScale(minScale, available.height - 10, maxScale, 10);
const tempScale = calcScale(minScale, availableHeight - 10, maxScale, 10);
// dewpoint
const dewpointPath = createPath(this.data.dewpoint, timeScale, tempScale);

View File

@@ -238,7 +238,7 @@ const determineIcon = async (skyCover, weather, iceAccumulation, probabilityOfPr
};
// expand a set of values with durations to an hour-by-hour array
const expand = (data, maxHours = 48) => {
const expand = (data, maxHours = 36) => {
const startOfHour = DateTime.utc().startOf('hour').toMillis();
const result = []; // resulting expanded values
data.forEach((item) => {

View File

@@ -6,6 +6,7 @@ import { safeJson } from './utils/fetch.mjs';
import { getPoint } from './utils/weather.mjs';
import { debugFlag } from './utils/debug.mjs';
import settings from './settings.mjs';
import { stationFilter } from './utils/string.mjs';
document.addEventListener('DOMContentLoaded', () => {
init();
@@ -85,7 +86,15 @@ const getWeather = async (latLon, haveDataCallback) => {
return;
}
const StationId = stations.features[0].properties.stationIdentifier;
// filter stations for proper format
const stationsFiltered = stations.features.filter(stationFilter);
// check for stations available after filtering
if (stationsFiltered.length === 0) {
console.warn('No observation stations left for location after filtering');
return;
}
const StationId = stationsFiltered[0].properties.stationIdentifier;
let { city } = point.properties.relativeLocation.properties;
const { state } = point.properties.relativeLocation.properties;
@@ -108,7 +117,7 @@ const getWeather = async (latLon, haveDataCallback) => {
weatherParameters.timeZone = point.properties.timeZone;
weatherParameters.forecast = point.properties.forecast;
weatherParameters.forecastGridData = point.properties.forecastGridData;
weatherParameters.stations = stations.features;
weatherParameters.stations = stationsFiltered;
weatherParameters.relativeLocation = point.properties.relativeLocation.properties;
// update the main process for display purposes

View File

@@ -9,10 +9,12 @@ const pixelToFile = (xPixel, yPixel) => {
return `${yTile}-${xTile}`;
};
// convert a pixel location in the overall map to a pixel location on the tile
// convert a pixel location in the overall map to a pixel location on the tile set
const modTile = (xPixel, yPixel) => {
const x = Math.round(xPixel) % TILE_SIZE.x;
const y = Math.round(yPixel) % TILE_SIZE.y;
// adjust for additional 1 tile when odd
const x = (Math.floor(xPixel) % (TILE_SIZE.x));
const y = (Math.floor(yPixel) % (TILE_SIZE.y));
return { x, y };
};
@@ -46,8 +48,8 @@ const setTiles = (data) => {
// determine which tiles are used
const usedTiles = [
true,
TILE_SIZE.x - tileShift.x < RADAR_FINAL_SIZE.width,
TILE_SIZE.y - tileShift.y < RADAR_FINAL_SIZE.width,
tileShift.x + TILE_SIZE.x > RADAR_FINAL_SIZE.width,
tileShift.y + TILE_SIZE.y > RADAR_FINAL_SIZE.height,
];
// if we need t[1] and t[2] then we also need t[3]
usedTiles.push(usedTiles[1] && usedTiles[2]);

View File

@@ -209,7 +209,7 @@ const getMinMaxLatitudeLongitudeHI = (X, Y, OffsetX, OffsetY) => {
};
};
const getXYForCity = (City, MaxLatitude, MinLongitude, state, maxX = 580) => {
const getXYForCity = (City, MaxLatitude, MinLongitude, state) => {
if (state === 'AK') getXYForCityAK(City, MaxLatitude, MinLongitude);
if (state === 'HI') getXYForCityHI(City, MaxLatitude, MinLongitude);
let x = (City.lon - MinLongitude) * 57;
@@ -219,7 +219,7 @@ const getXYForCity = (City, MaxLatitude, MinLongitude, state, maxX = 580) => {
if (y > 282) y = 282;
if (x < 40) x = 40;
if (x > maxX) x = maxX;
if (x > 580) x = 580;
return { x, y };
};

View File

@@ -14,29 +14,11 @@ import * as utils from './regionalforecast-utils.mjs';
import { getPoint } from './utils/weather.mjs';
import { debugFlag } from './utils/debug.mjs';
import filterExpiredPeriods from './utils/forecast-utils.mjs';
import settings from './settings.mjs';
// set up spacing and scales
const scaling = () => {
// available space
const available = {
x: 640,
};
// map offset
const mapOffsetXY = {
x: 240,
y: 117,
};
if (settings.wide?.value && settings.enhancedScreens?.value) {
mapOffsetXY.x = 320;
available.x = 854;
}
return {
mapOffsetXY,
available,
};
// map offset
const mapOffsetXY = {
x: 240,
y: 117,
};
class RegionalForecast extends WeatherDisplay {
@@ -63,7 +45,6 @@ class RegionalForecast extends WeatherDisplay {
this.elem.querySelector('.map img').src = baseMap;
// get user's location in x/y
const { available, mapOffsetXY } = scaling();
const sourceXY = utils.getXYFromLatitudeLongitude(this.weatherParameters.latitude, this.weatherParameters.longitude, mapOffsetXY.x, mapOffsetXY.y, weatherParameters.state);
// get latitude and longitude limits
@@ -121,7 +102,7 @@ class RegionalForecast extends WeatherDisplay {
}
// get XY on map for city
const cityXY = utils.getXYForCity(city, minMaxLatLon.maxLat, minMaxLatLon.minLon, this.weatherParameters.state, available - 60);
const cityXY = utils.getXYForCity(city, minMaxLatLon.maxLat, minMaxLatLon.minLon, this.weatherParameters.state);
// wait for the regional observation if it's not done yet
const observation = await observationPromise;
@@ -207,8 +188,7 @@ class RegionalForecast extends WeatherDisplay {
}
// draw the map
const { available, mapOffsetXY } = scaling();
const scale = available.x / (mapOffsetXY.x * 2);
const scale = 640 / (mapOffsetXY.x * 2);
const map = this.elem.querySelector('.map');
map.style.transform = `scale(${scale}) translate(-${sourceXY.x}px, -${sourceXY.y}px)`;

View File

@@ -32,25 +32,6 @@ const wideScreenChange = (value) => {
window.dispatchEvent(new Event('resize'));
};
const enhancedScreenChange = (value) => {
const container = document.querySelector('#divTwc');
if (!container) {
// DOM not ready; defer enabling if set
if (value) {
deferredDomSettings.add('enhanced');
}
return;
}
if (value) {
container.classList.add('enhanced');
} else {
container.classList.remove('enhanced');
}
// Trigger resize to recalculate scaling for new width
window.dispatchEvent(new Event('resize'));
};
const kioskChange = (value) => {
const body = document.querySelector('body');
if (!body) {
@@ -63,9 +44,11 @@ const kioskChange = (value) => {
if (value) {
body.classList.add('kiosk');
document.querySelector('#divTwc')?.classList.add('no-cursor');
window.dispatchEvent(new Event('resize'));
} else {
body.classList.remove('kiosk');
document.querySelector('#divTwc')?.classList.remove('no-cursor');
window.dispatchEvent(new Event('resize'));
}
@@ -149,17 +132,6 @@ const init = () => {
changeAction: wideScreenChange,
sticky: true,
});
settings.portrait = new Setting('portrait', {
name: 'Allow Portrait',
defaultValue: false,
sticky: true,
});
settings.enhancedScreens = new Setting('enhancedScreens', {
name: 'Enhanced Screens',
defaultValue: false,
changeAction: enhancedScreenChange,
sticky: true,
});
settings.kiosk = new Setting('kiosk', {
name: 'Kiosk',
defaultValue: false,

View File

@@ -650,7 +650,7 @@ export const enhanceObservationWithMapClick = async (observationData, options =
}
return {
data: mapClickProps,
data: { ...mapClickProps, timestamp: observationData.timestamp },
wasImproved: true,
improvements,
missingFields: [...mapClickMissingRequired, ...mapClickMissingOptional],

View File

@@ -13,7 +13,12 @@ const locationCleanup = (input) => {
return regexes.reduce((value, regex) => value.replace(regex, ''), input);
};
// stations must be 4 alpha characters and not start with the provided list
const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F', 'B', 'N', 'V', 'R', 'D', 'E', 'I', 'G', 'J'];
const stationFilter = (station) => station.properties.stationIdentifier.match(/^[A-Z]{4}$/) && !skipStations.includes(station.properties.stationIdentifier.slice(0, 1));
export {
// eslint-disable-next-line import/prefer-default-export
locationCleanup,
stationFilter,
};

View File

@@ -1,10 +1,8 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
@use 'shared/_colors' as c;
@use 'shared/_utils' as u;
.weather-display .main.current-weather {
&.main {
width: calc(p.$standard-width - (2 * p.$blue-box-margin));
.col {
height: 50px;
@@ -19,6 +17,7 @@
&.left {
font-family: 'Star4000 Extended';
font-size: 24pt;
}
&.right {
@@ -93,4 +92,4 @@
text-wrap: nowrap;
}
}
}
}

View File

@@ -1,6 +1,5 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
#hazards-html.weather-display {
background-image: url('../images/backgrounds/7.png');
@@ -9,7 +8,7 @@
.weather-display .main.hazards {
&.main {
overflow-y: hidden;
height: p.$standard-height;
height: 480px;
background-color: rgb(112, 35, 35);

View File

@@ -4,12 +4,6 @@
#hourly-graph-html {
background-image: url(../images/backgrounds/1-chart.png);
// change background for wide-enhanced
.wide.enhanced & {
background-image: url(../images/backgrounds/1-chart-wide.png);
background-position-x: 0px;
}
.header {
.right {
position: absolute;
@@ -90,51 +84,10 @@
&.l-5 {
left: calc(532px / 4 * 4);
}
// adjust when enhanced
.wide.enhanced & {
&.l-1 {
left: 0px;
}
&.l-2 {
left: calc(726px / 6 * 1);
}
&.l-3 {
left: calc(726px / 6 * 2);
}
&.l-4 {
left: calc(726px / 6 * 3);
}
&.l-5 {
left: calc(726px / 6 * 4);
}
&.l-6 {
left: calc(726px / 6 * 5);
}
&.l-7 {
left: calc(726px / 6 * 6);
}
}
// only in wide + enhanced
&.l-6,
&.l-7 {
display: none;
.wide.enhanced & {
display: block;
}
}
}
}
.chart {
@@ -144,11 +97,6 @@
img {
width: 532px;
height: 285px;
// wide and enhanced
.wide.enhanced & {
width: 746px;
}
}
}
@@ -180,5 +128,32 @@
}
}
.column-headers {
background-color: c.$column-header;
height: 20px;
position: absolute;
width: 100%;
}
.column-headers {
position: sticky;
top: 0px;
z-index: 5;
.temp {
left: 355px;
}
.like {
left: 435px;
}
.wind {
left: 535px;
}
}
}
}

View File

@@ -1,14 +1,7 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
.weather-display .local-forecast {
// clamp width to standard
&.main {
width: calc(p.$standard-width - (2 * p.$blue-box-margin));
}
.container {
position: relative;
top: 15px;

View File

@@ -1,6 +1,5 @@
@use 'shared/_utils'as u;
@use 'shared/_colors'as c;
@use 'shared/positions'as p;
@font-face {
font-family: "Star4000";
@@ -34,7 +33,7 @@ body {
}
#divQuery {
max-width: p.$standard-width;
max-width: 640px;
padding: 8px;
.buttons {
@@ -147,11 +146,11 @@ body {
background-color: #000000;
color: #ffffff;
width: 100%;
max-width: p.$standard-width;
max-width: 640px;
margin: 0; // Ensure edge-to-edge display
&.wide {
max-width: p.$wide-width;
max-width: 854px;
}
}
@@ -160,12 +159,12 @@ body {
}
#divTwcMain {
width: p.$standard-width;
height: p.$standard-height;
width: 640px;
height: 480px;
position: relative;
.wide & {
width: p.$wide-width;
width: 854px;
}
}
@@ -210,10 +209,10 @@ body {
background-color: #000000;
color: #ffffff;
width: p.$standard-width;
width: 640px;
.wide & {
width: p.$wide-width;
width: 854px;
}
@media (prefers-color-scheme: dark) {
@@ -275,7 +274,7 @@ body {
flex-direction: row;
background-color: #000000;
color: #ffffff;
max-width: p.$standard-width;
max-width: 640px;
}
#divTwcNav>div {
@@ -337,8 +336,8 @@ body {
#container {
position: relative;
width: p.$standard-width;
height: p.$standard-height;
width: 640px;
height: 480px;
// overflow: hidden;
background-image: url(../images/backgrounds/1.png);
transform-origin: 0 0;
@@ -346,7 +345,8 @@ body {
}
.wide #container {
width: p.$wide-width;
padding-left: 107px;
padding-right: 107px;
background: url(../images/backgrounds/1-wide.png);
background-repeat: no-repeat;
}
@@ -359,8 +359,8 @@ body {
}
#loading {
width: p.$standard-width;
height: p.$standard-height;
width: 640px;
height: 480px;
max-width: 100%;
text-shadow: 4px 4px black;
display: flex;
@@ -368,10 +368,6 @@ body {
text-align: center;
justify-content: center;
.wide & {
margin-left: p.$wide-margin;
}
.title {
font-family: Star4000 Large;
font-size: 36px;

View File

@@ -1,23 +1,17 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
@use 'shared/_colors' as c;
@use 'shared/_utils' as u;
.weather-display .progress {
@include u.text-shadow();
font-family: 'Star4000 Extended';
font-size: 19pt;
// clamp width to standard
&.main {
width: calc(p.$standard-width - (2 * p.$blue-box-margin));
}
.container {
position: relative;
top: 15px;
margin: 0px 10px;
box-sizing: border-box;
height: p.$standard-scroll-height;
height: 310px;
overflow: hidden;
line-height: 28px;
@@ -124,4 +118,4 @@
transition: width 1s steps(6);
}
}
}
}

View File

@@ -1,6 +1,5 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
#radar-html.weather-display {
background-image: url('../images/backgrounds/4.png');
@@ -105,7 +104,6 @@
.weather-display .main.radar {
overflow: hidden;
height: 367px;
width: p.$standard-width;
.container {

View File

@@ -1,6 +1,5 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
#spc-outlook-html.weather-display {
background-image: url('../images/backgrounds/6.png');

View File

@@ -1,43 +1,29 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
@use 'shared/positions'as p;
.weather-display {
width: p.$standard-width;
height: p.$standard-height;
width: 640px;
height: 480px;
overflow: hidden;
position: relative;
background-image: url(../images/backgrounds/1.png);
// adjust for wide
.wide & {
width: p.$wide-width;
background-position-x: p.$wide-margin;
background-repeat: no-repeat;
}
/* this method is required to hide blocks so they can be measured while off screen */
height: 0px;
&.show {
height: p.$standard-height;
height: 480px;
}
.template {
display: none;
}
>.header {
width: p.$standard-width;
.header {
width: 640px;
height: 60px;
position: relative;
padding-top: 30px;
// adjust for wide
.wide & {
left: p.$wide-margin;
}
.title {
color: c.$title-color;
@include u.text-shadow(3px, 1.5px);
@@ -106,23 +92,10 @@
.main {
position: relative;
// adjust for wide
.wide & {
left: p.$wide-margin;
}
// adjust for enhanced when possible
.wide.enhanced & {
&.can-enhance {
left: 0px;
width: p.$wide-width;
}
}
&.has-scroll {
width: p.$standard-width;
width: 640px;
margin-top: 0;
height: p.$standard-scroll-height;
height: 310px;
overflow: hidden;
&.no-header {
@@ -132,8 +105,8 @@
}
&.has-box {
margin-left: p.$blue-box-margin;
margin-right: p.$blue-box-margin;
margin-left: 64px;
margin-right: 64px;
width: calc(100% - 128px);
}
@@ -144,7 +117,7 @@
#container>.scroll {
display: none;
@include u.text-shadow(3px, 1.5px);
width: p.$standard-width;
width: 640px;
height: 77px;
overflow: hidden;
margin-top: 3px;
@@ -152,17 +125,12 @@
bottom: 0px;
z-index: 1;
// adjust for wide
.wide & {
left: p.$wide-margin;
}
&.hazard {
background-color: rgb(112, 35, 35);
}
.scroll-container {
width: p.$standard-width;
width: 640px;
.fixed,
.scroll-header {
@@ -188,7 +156,7 @@
position: relative;
// the following added by js code as it is dependent on the content of the element
// transition: left (x)s;
// left: calc((elem width) - p.$standard-width);
// left: calc((elem width) - 640px);
}
}
}
@@ -198,10 +166,10 @@
}
.wide #container>.scroll {
width: p.$wide-width;
margin-left: -1*p.$wide-margin;
width: 854px;
margin-left: -107px;
.scroll-container {
margin-left: p.$wide-margin;
margin-left: 107px;
}
}

View File

@@ -1,14 +0,0 @@
// standard positioning
$standard-width: 640px;
$standard-height: 480px;
// height with scroll
$standard-scroll-height: 310px;
// blue box size
$blue-box-margin: 64px;
// wide screen positioning
$wide-padding: 107px;
$wide-margin: 107px;
$wide-width: 854px;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
<%- include('header.ejs', {title: 'Hourly Graph' , hasTime: false }) %>
<div class="main has-scroll hourly-graph can-enhance">
<div class="main has-scroll hourly-graph">
<div class="top-right template ">
<div class="temperature">Temperature</div>
<div class="dewpoint">Dewpoint</div>
<div class="dewpoint">Dewpoint</div>
<div class="cloud">Cloud %</div>
<div class="rain">Precip %</div>
</div>
@@ -10,7 +10,7 @@
<div class="label l-1">75</div>
<div class="label l-2">65</div>
<div class="label l-3">55</div>
<div class="label l-4">45</div>
<div class="label l-4">45</div>
</div>
<div class="chart">
<img id="chart-area"></img>
@@ -21,7 +21,5 @@
<div class="label l-3">12p</div>
<div class="label l-4">6p</div>
<div class="label l-5">12a</div>
<div class="label l-6">6a</div>
<div class="label l-7">12p</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<%- include('header.ejs', {titleDual:{ top: 'Regional' , bottom: 'Observations' }, hasTime: true }) %>
<div class="main has-scroll regional-forecast can-enhance">
<div class="main has-scroll regional-forecast">
<div class="map"><img src="images/maps/basemap.webp" /></div>
<div class="location-container">
<div class="location template">