mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-22 19:49:31 -07:00
Compare commits
4 Commits
778b7f4456
...
8158afd039
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8158afd039 | ||
|
|
5fffc495ae | ||
|
|
b2a424a64f | ||
|
|
9f6b90919c |
BIN
server/images/backgrounds/1-chart-wide.png
Normal file
BIN
server/images/backgrounds/1-chart-wide.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
BIN
server/images/gimp/1-chart-wide.xcf
Normal file
BIN
server/images/gimp/1-chart-wide.xcf
Normal file
Binary file not shown.
@@ -5,10 +5,30 @@ import getHourlyData from './hourly.mjs';
|
|||||||
import WeatherDisplay from './weatherdisplay.mjs';
|
import WeatherDisplay from './weatherdisplay.mjs';
|
||||||
import { registerDisplay, timeZone } from './navigation.mjs';
|
import { registerDisplay, timeZone } from './navigation.mjs';
|
||||||
import { DateTime } from '../vendor/auto/luxon.mjs';
|
import { DateTime } from '../vendor/auto/luxon.mjs';
|
||||||
|
import settings from './settings.mjs';
|
||||||
|
|
||||||
// get available space
|
// set up spacing and scales
|
||||||
const availableWidth = 532;
|
const scaling = () => {
|
||||||
const availableHeight = 285;
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
class HourlyGraph extends WeatherDisplay {
|
class HourlyGraph extends WeatherDisplay {
|
||||||
constructor(navId, elemId, defaultActive) {
|
constructor(navId, elemId, defaultActive) {
|
||||||
@@ -46,28 +66,43 @@ class HourlyGraph extends WeatherDisplay {
|
|||||||
skyCover, temperature, probabilityOfPrecipitation, temperatureUnit: data[0].temperatureUnit, dewpoint,
|
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);
|
this.setStatus(STATUS.loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawCanvas() {
|
drawCanvas() {
|
||||||
|
// get scaling parameters
|
||||||
|
const { dataLength, available } = scaling();
|
||||||
|
|
||||||
|
// get the image
|
||||||
if (!this.image) this.image = this.elem.querySelector('.chart img');
|
if (!this.image) this.image = this.elem.querySelector('.chart img');
|
||||||
|
|
||||||
this.image.width = availableWidth;
|
// set up image
|
||||||
this.image.height = availableHeight;
|
this.image.width = available.width;
|
||||||
|
this.image.height = available.height;
|
||||||
|
|
||||||
// get context
|
// get context
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = availableWidth;
|
canvas.width = available.width;
|
||||||
canvas.height = availableHeight;
|
canvas.height = available.height;
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
|
||||||
// calculate time scale
|
// calculate time scale
|
||||||
const timeScale = calcScale(0, 5, this.data.temperature.length - 1, availableWidth);
|
const timeScale = calcScale(0, 5, this.data.temperature.length - 1, available.width);
|
||||||
const timeStep = this.data.temperature.length / 4;
|
const timeStep = this.data.temperature.length / (dataLength.xTicks);
|
||||||
const startTime = DateTime.now().startOf('hour');
|
const startTime = DateTime.now().startOf('hour');
|
||||||
let prevTime = startTime;
|
let prevTime = startTime;
|
||||||
Array(5).fill().forEach((val, idx) => {
|
Array(dataLength.xTicks + 1).fill().forEach((val, idx) => {
|
||||||
// track the previous label so a day of week can be added when it changes
|
// track the previous label so a day of week can be added when it changes
|
||||||
const label = formatTime(startTime.plus({ hour: idx * timeStep }), prevTime);
|
const label = formatTime(startTime.plus({ hour: idx * timeStep }), prevTime);
|
||||||
prevTime = label.ts;
|
prevTime = label.ts;
|
||||||
@@ -77,7 +112,7 @@ class HourlyGraph extends WeatherDisplay {
|
|||||||
|
|
||||||
// order is important last line drawn is on top
|
// order is important last line drawn is on top
|
||||||
// clouds
|
// clouds
|
||||||
const percentScale = calcScale(0, availableHeight - 10, 100, 10);
|
const percentScale = calcScale(0, available.height - 10, 100, 10);
|
||||||
const cloud = createPath(this.data.skyCover, timeScale, percentScale);
|
const cloud = createPath(this.data.skyCover, timeScale, percentScale);
|
||||||
drawPath(cloud, ctx, {
|
drawPath(cloud, ctx, {
|
||||||
strokeStyle: 'lightgrey',
|
strokeStyle: 'lightgrey',
|
||||||
@@ -97,7 +132,7 @@ class HourlyGraph extends WeatherDisplay {
|
|||||||
const thirdScale = (maxScale - minScale) / 3;
|
const thirdScale = (maxScale - minScale) / 3;
|
||||||
const midScale1 = Math.round(minScale + thirdScale);
|
const midScale1 = Math.round(minScale + thirdScale);
|
||||||
const midScale2 = Math.round(minScale + (thirdScale * 2));
|
const midScale2 = Math.round(minScale + (thirdScale * 2));
|
||||||
const tempScale = calcScale(minScale, availableHeight - 10, maxScale, 10);
|
const tempScale = calcScale(minScale, available.height - 10, maxScale, 10);
|
||||||
|
|
||||||
// dewpoint
|
// dewpoint
|
||||||
const dewpointPath = createPath(this.data.dewpoint, timeScale, tempScale);
|
const dewpointPath = createPath(this.data.dewpoint, timeScale, tempScale);
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ const determineIcon = async (skyCover, weather, iceAccumulation, probabilityOfPr
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
||||||
const expand = (data, maxHours = 36) => {
|
const expand = (data, maxHours = 48) => {
|
||||||
const startOfHour = DateTime.utc().startOf('hour').toMillis();
|
const startOfHour = DateTime.utc().startOf('hour').toMillis();
|
||||||
const result = []; // resulting expanded values
|
const result = []; // resulting expanded values
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ const getMinMaxLatitudeLongitudeHI = (X, Y, OffsetX, OffsetY) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getXYForCity = (City, MaxLatitude, MinLongitude, state) => {
|
const getXYForCity = (City, MaxLatitude, MinLongitude, state, maxX = 580) => {
|
||||||
if (state === 'AK') getXYForCityAK(City, MaxLatitude, MinLongitude);
|
if (state === 'AK') getXYForCityAK(City, MaxLatitude, MinLongitude);
|
||||||
if (state === 'HI') getXYForCityHI(City, MaxLatitude, MinLongitude);
|
if (state === 'HI') getXYForCityHI(City, MaxLatitude, MinLongitude);
|
||||||
let x = (City.lon - MinLongitude) * 57;
|
let x = (City.lon - MinLongitude) * 57;
|
||||||
@@ -219,7 +219,7 @@ const getXYForCity = (City, MaxLatitude, MinLongitude, state) => {
|
|||||||
if (y > 282) y = 282;
|
if (y > 282) y = 282;
|
||||||
|
|
||||||
if (x < 40) x = 40;
|
if (x < 40) x = 40;
|
||||||
if (x > 580) x = 580;
|
if (x > maxX) x = maxX;
|
||||||
|
|
||||||
return { x, y };
|
return { x, y };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,11 +14,29 @@ import * as utils from './regionalforecast-utils.mjs';
|
|||||||
import { getPoint } from './utils/weather.mjs';
|
import { getPoint } from './utils/weather.mjs';
|
||||||
import { debugFlag } from './utils/debug.mjs';
|
import { debugFlag } from './utils/debug.mjs';
|
||||||
import filterExpiredPeriods from './utils/forecast-utils.mjs';
|
import filterExpiredPeriods from './utils/forecast-utils.mjs';
|
||||||
|
import settings from './settings.mjs';
|
||||||
|
|
||||||
// map offset
|
// set up spacing and scales
|
||||||
const mapOffsetXY = {
|
const scaling = () => {
|
||||||
x: 240,
|
// available space
|
||||||
y: 117,
|
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,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegionalForecast extends WeatherDisplay {
|
class RegionalForecast extends WeatherDisplay {
|
||||||
@@ -45,6 +63,7 @@ class RegionalForecast extends WeatherDisplay {
|
|||||||
this.elem.querySelector('.map img').src = baseMap;
|
this.elem.querySelector('.map img').src = baseMap;
|
||||||
|
|
||||||
// get user's location in x/y
|
// 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);
|
const sourceXY = utils.getXYFromLatitudeLongitude(this.weatherParameters.latitude, this.weatherParameters.longitude, mapOffsetXY.x, mapOffsetXY.y, weatherParameters.state);
|
||||||
|
|
||||||
// get latitude and longitude limits
|
// get latitude and longitude limits
|
||||||
@@ -102,7 +121,7 @@ class RegionalForecast extends WeatherDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get XY on map for city
|
// get XY on map for city
|
||||||
const cityXY = utils.getXYForCity(city, minMaxLatLon.maxLat, minMaxLatLon.minLon, this.weatherParameters.state);
|
const cityXY = utils.getXYForCity(city, minMaxLatLon.maxLat, minMaxLatLon.minLon, this.weatherParameters.state, available - 60);
|
||||||
|
|
||||||
// wait for the regional observation if it's not done yet
|
// wait for the regional observation if it's not done yet
|
||||||
const observation = await observationPromise;
|
const observation = await observationPromise;
|
||||||
@@ -188,7 +207,8 @@ class RegionalForecast extends WeatherDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// draw the map
|
// draw the map
|
||||||
const scale = 640 / (mapOffsetXY.x * 2);
|
const { available, mapOffsetXY } = scaling();
|
||||||
|
const scale = available.x / (mapOffsetXY.x * 2);
|
||||||
const map = this.elem.querySelector('.map');
|
const map = this.elem.querySelector('.map');
|
||||||
map.style.transform = `scale(${scale}) translate(-${sourceXY.x}px, -${sourceXY.y}px)`;
|
map.style.transform = `scale(${scale}) translate(-${sourceXY.x}px, -${sourceXY.y}px)`;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,25 @@ const wideScreenChange = (value) => {
|
|||||||
window.dispatchEvent(new Event('resize'));
|
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 kioskChange = (value) => {
|
||||||
const body = document.querySelector('body');
|
const body = document.querySelector('body');
|
||||||
if (!body) {
|
if (!body) {
|
||||||
@@ -138,6 +157,7 @@ const init = () => {
|
|||||||
settings.enhancedScreens = new Setting('enhancedScreens', {
|
settings.enhancedScreens = new Setting('enhancedScreens', {
|
||||||
name: 'Enhanced Screens',
|
name: 'Enhanced Screens',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
|
changeAction: enhancedScreenChange,
|
||||||
sticky: true,
|
sticky: true,
|
||||||
});
|
});
|
||||||
settings.kiosk = new Setting('kiosk', {
|
settings.kiosk = new Setting('kiosk', {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
@use 'shared/_colors' as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils' as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
.weather-display .main.current-weather {
|
.weather-display .main.current-weather {
|
||||||
&.main {
|
&.main {
|
||||||
|
width: calc(p.$standard-width - (2 * p.$blue-box-margin));
|
||||||
|
|
||||||
.col {
|
.col {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@@ -17,7 +19,6 @@
|
|||||||
&.left {
|
&.left {
|
||||||
font-family: 'Star4000 Extended';
|
font-family: 'Star4000 Extended';
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
@@ -92,4 +93,4 @@
|
|||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@use 'shared/_colors'as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils'as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
#hazards-html.weather-display {
|
#hazards-html.weather-display {
|
||||||
background-image: url('../images/backgrounds/7.png');
|
background-image: url('../images/backgrounds/7.png');
|
||||||
@@ -8,7 +9,7 @@
|
|||||||
.weather-display .main.hazards {
|
.weather-display .main.hazards {
|
||||||
&.main {
|
&.main {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
height: 480px;
|
height: p.$standard-height;
|
||||||
background-color: rgb(112, 35, 35);
|
background-color: rgb(112, 35, 35);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
#hourly-graph-html {
|
#hourly-graph-html {
|
||||||
background-image: url(../images/backgrounds/1-chart.png);
|
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 {
|
.header {
|
||||||
.right {
|
.right {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -84,10 +90,51 @@
|
|||||||
&.l-5 {
|
&.l-5 {
|
||||||
left: calc(532px / 4 * 4);
|
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 {
|
.chart {
|
||||||
@@ -97,6 +144,11 @@
|
|||||||
img {
|
img {
|
||||||
width: 532px;
|
width: 532px;
|
||||||
height: 285px;
|
height: 285px;
|
||||||
|
|
||||||
|
// wide and enhanced
|
||||||
|
.wide.enhanced & {
|
||||||
|
width: 746px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,32 +180,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
@use 'shared/_colors'as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils'as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
.weather-display .local-forecast {
|
.weather-display .local-forecast {
|
||||||
|
|
||||||
|
// clamp width to standard
|
||||||
|
&.main {
|
||||||
|
width: calc(p.$standard-width - (2 * p.$blue-box-margin));
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@use 'shared/_utils'as u;
|
@use 'shared/_utils'as u;
|
||||||
@use 'shared/_colors'as c;
|
@use 'shared/_colors'as c;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Star4000";
|
font-family: "Star4000";
|
||||||
@@ -33,7 +34,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#divQuery {
|
#divQuery {
|
||||||
max-width: 640px;
|
max-width: p.$standard-width;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
@@ -146,11 +147,11 @@ body {
|
|||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 640px;
|
max-width: p.$standard-width;
|
||||||
margin: 0; // Ensure edge-to-edge display
|
margin: 0; // Ensure edge-to-edge display
|
||||||
|
|
||||||
&.wide {
|
&.wide {
|
||||||
max-width: 854px;
|
max-width: p.$wide-width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,12 +160,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#divTwcMain {
|
#divTwcMain {
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
height: 480px;
|
height: p.$standard-height;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.wide & {
|
.wide & {
|
||||||
width: 854px;
|
width: p.$wide-width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,10 +210,10 @@ body {
|
|||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
|
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
|
|
||||||
.wide & {
|
.wide & {
|
||||||
width: 854px;
|
width: p.$wide-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
@@ -274,7 +275,7 @@ body {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
max-width: 640px;
|
max-width: p.$standard-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
#divTwcNav>div {
|
#divTwcNav>div {
|
||||||
@@ -336,8 +337,8 @@ body {
|
|||||||
|
|
||||||
#container {
|
#container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
height: 480px;
|
height: p.$standard-height;
|
||||||
// overflow: hidden;
|
// overflow: hidden;
|
||||||
background-image: url(../images/backgrounds/1.png);
|
background-image: url(../images/backgrounds/1.png);
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
@@ -345,8 +346,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wide #container {
|
.wide #container {
|
||||||
padding-left: 107px;
|
width: p.$wide-width;
|
||||||
padding-right: 107px;
|
|
||||||
background: url(../images/backgrounds/1-wide.png);
|
background: url(../images/backgrounds/1-wide.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
@@ -359,8 +359,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#loading {
|
#loading {
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
height: 480px;
|
height: p.$standard-height;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
text-shadow: 4px 4px black;
|
text-shadow: 4px 4px black;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -368,6 +368,10 @@ body {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
.wide & {
|
||||||
|
margin-left: p.$wide-margin;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-family: Star4000 Large;
|
font-family: Star4000 Large;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
@use 'shared/_colors' as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils' as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
.weather-display .progress {
|
.weather-display .progress {
|
||||||
@include u.text-shadow();
|
@include u.text-shadow();
|
||||||
font-family: 'Star4000 Extended';
|
font-family: 'Star4000 Extended';
|
||||||
font-size: 19pt;
|
font-size: 19pt;
|
||||||
|
|
||||||
|
// clamp width to standard
|
||||||
|
&.main {
|
||||||
|
width: calc(p.$standard-width - (2 * p.$blue-box-margin));
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
margin: 0px 10px;
|
margin: 0px 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 310px;
|
height: p.$standard-scroll-height;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
|
|
||||||
@@ -118,4 +124,4 @@
|
|||||||
transition: width 1s steps(6);
|
transition: width 1s steps(6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@use 'shared/_colors'as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils'as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
#radar-html.weather-display {
|
#radar-html.weather-display {
|
||||||
background-image: url('../images/backgrounds/4.png');
|
background-image: url('../images/backgrounds/4.png');
|
||||||
@@ -104,6 +105,7 @@
|
|||||||
.weather-display .main.radar {
|
.weather-display .main.radar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 367px;
|
height: 367px;
|
||||||
|
width: p.$standard-width;
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@use 'shared/_colors'as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils'as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
#spc-outlook-html.weather-display {
|
#spc-outlook-html.weather-display {
|
||||||
background-image: url('../images/backgrounds/6.png');
|
background-image: url('../images/backgrounds/6.png');
|
||||||
|
|||||||
@@ -1,29 +1,43 @@
|
|||||||
@use 'shared/_colors'as c;
|
@use 'shared/_colors'as c;
|
||||||
@use 'shared/_utils'as u;
|
@use 'shared/_utils'as u;
|
||||||
|
@use 'shared/positions'as p;
|
||||||
|
|
||||||
.weather-display {
|
.weather-display {
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
height: 480px;
|
height: p.$standard-height;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-image: url(../images/backgrounds/1.png);
|
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 */
|
/* this method is required to hide blocks so they can be measured while off screen */
|
||||||
height: 0px;
|
height: 0px;
|
||||||
|
|
||||||
&.show {
|
&.show {
|
||||||
height: 480px;
|
height: p.$standard-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
.template {
|
.template {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
>.header {
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
position: relative;
|
||||||
padding-top: 30px;
|
padding-top: 30px;
|
||||||
|
|
||||||
|
// adjust for wide
|
||||||
|
.wide & {
|
||||||
|
left: p.$wide-margin;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: c.$title-color;
|
color: c.$title-color;
|
||||||
@include u.text-shadow(3px, 1.5px);
|
@include u.text-shadow(3px, 1.5px);
|
||||||
@@ -92,10 +106,23 @@
|
|||||||
.main {
|
.main {
|
||||||
position: relative;
|
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 {
|
&.has-scroll {
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
height: 310px;
|
height: p.$standard-scroll-height;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&.no-header {
|
&.no-header {
|
||||||
@@ -105,8 +132,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.has-box {
|
&.has-box {
|
||||||
margin-left: 64px;
|
margin-left: p.$blue-box-margin;
|
||||||
margin-right: 64px;
|
margin-right: p.$blue-box-margin;
|
||||||
width: calc(100% - 128px);
|
width: calc(100% - 128px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +144,7 @@
|
|||||||
#container>.scroll {
|
#container>.scroll {
|
||||||
display: none;
|
display: none;
|
||||||
@include u.text-shadow(3px, 1.5px);
|
@include u.text-shadow(3px, 1.5px);
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
height: 77px;
|
height: 77px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
@@ -125,12 +152,17 @@
|
|||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
|
// adjust for wide
|
||||||
|
.wide & {
|
||||||
|
left: p.$wide-margin;
|
||||||
|
}
|
||||||
|
|
||||||
&.hazard {
|
&.hazard {
|
||||||
background-color: rgb(112, 35, 35);
|
background-color: rgb(112, 35, 35);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-container {
|
.scroll-container {
|
||||||
width: 640px;
|
width: p.$standard-width;
|
||||||
|
|
||||||
.fixed,
|
.fixed,
|
||||||
.scroll-header {
|
.scroll-header {
|
||||||
@@ -156,7 +188,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
// the following added by js code as it is dependent on the content of the element
|
// the following added by js code as it is dependent on the content of the element
|
||||||
// transition: left (x)s;
|
// transition: left (x)s;
|
||||||
// left: calc((elem width) - 640px);
|
// left: calc((elem width) - p.$standard-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,10 +198,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wide #container>.scroll {
|
.wide #container>.scroll {
|
||||||
width: 854px;
|
width: p.$wide-width;
|
||||||
margin-left: -107px;
|
margin-left: -1*p.$wide-margin;
|
||||||
|
|
||||||
.scroll-container {
|
.scroll-container {
|
||||||
margin-left: 107px;
|
margin-left: p.$wide-margin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14
server/styles/scss/shared/_positions.scss
Normal file
14
server/styles/scss/shared/_positions.scss
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// 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;
|
||||||
2
server/styles/ws.min.css
vendored
2
server/styles/ws.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,8 +1,8 @@
|
|||||||
<%- include('header.ejs', {title: 'Hourly Graph' , hasTime: false }) %>
|
<%- include('header.ejs', {title: 'Hourly Graph' , hasTime: false }) %>
|
||||||
<div class="main has-scroll hourly-graph">
|
<div class="main has-scroll hourly-graph can-enhance">
|
||||||
<div class="top-right template ">
|
<div class="top-right template ">
|
||||||
<div class="temperature">Temperature</div>
|
<div class="temperature">Temperature</div>
|
||||||
<div class="dewpoint">Dewpoint</div>
|
<div class="dewpoint">Dewpoint</div>
|
||||||
<div class="cloud">Cloud %</div>
|
<div class="cloud">Cloud %</div>
|
||||||
<div class="rain">Precip %</div>
|
<div class="rain">Precip %</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<div class="label l-1">75</div>
|
<div class="label l-1">75</div>
|
||||||
<div class="label l-2">65</div>
|
<div class="label l-2">65</div>
|
||||||
<div class="label l-3">55</div>
|
<div class="label l-3">55</div>
|
||||||
<div class="label l-4">45</div>
|
<div class="label l-4">45</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart">
|
<div class="chart">
|
||||||
<img id="chart-area"></img>
|
<img id="chart-area"></img>
|
||||||
@@ -21,5 +21,7 @@
|
|||||||
<div class="label l-3">12p</div>
|
<div class="label l-3">12p</div>
|
||||||
<div class="label l-4">6p</div>
|
<div class="label l-4">6p</div>
|
||||||
<div class="label l-5">12a</div>
|
<div class="label l-5">12a</div>
|
||||||
|
<div class="label l-6">6a</div>
|
||||||
|
<div class="label l-7">12p</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<%- include('header.ejs', {titleDual:{ top: 'Regional' , bottom: 'Observations' }, hasTime: true }) %>
|
<%- include('header.ejs', {titleDual:{ top: 'Regional' , bottom: 'Observations' }, hasTime: true }) %>
|
||||||
<div class="main has-scroll regional-forecast">
|
<div class="main has-scroll regional-forecast can-enhance">
|
||||||
<div class="map"><img src="images/maps/basemap.webp" /></div>
|
<div class="map"><img src="images/maps/basemap.webp" /></div>
|
||||||
<div class="location-container">
|
<div class="location-container">
|
||||||
<div class="location template">
|
<div class="location template">
|
||||||
|
|||||||
Reference in New Issue
Block a user