mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-14 07:39:29 -07:00
code cleanup
This commit is contained in:
@@ -61,7 +61,7 @@ const init = () => {
|
||||
paramName: 'text',
|
||||
params: {
|
||||
f: 'json',
|
||||
countryCode: 'USA', // 'USA,PRI,VIR,GUM,ASM',
|
||||
countryCode: 'USA',
|
||||
category,
|
||||
maxSuggestions: 10,
|
||||
},
|
||||
@@ -82,7 +82,6 @@ const init = () => {
|
||||
|
||||
// attempt to parse the url parameters
|
||||
const parsedParameters = parseQueryString();
|
||||
|
||||
const loadFromParsed = parsedParameters.latLonQuery && parsedParameters.latLon;
|
||||
|
||||
// Auto load the parsed parameters and fall back to the previous query
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// display sun and moon data
|
||||
import { loadImg, preloadImg } from './utils/image.mjs';
|
||||
import { preloadImg } from './utils/image.mjs';
|
||||
import { DateTime } from '../vendor/auto/luxon.mjs';
|
||||
import STATUS from './status.mjs';
|
||||
import WeatherDisplay from './weatherdisplay.mjs';
|
||||
@@ -9,9 +9,6 @@ class Almanac extends WeatherDisplay {
|
||||
constructor(navId, elemId) {
|
||||
super(navId, elemId, 'Almanac', true);
|
||||
|
||||
// pre-load background images (returns promises)
|
||||
this.backgroundImage0 = loadImg('images/backgrounds/1.png');
|
||||
|
||||
// preload the moon images
|
||||
preloadImg(imageName('Full'));
|
||||
preloadImg(imageName('Last'));
|
||||
@@ -122,10 +119,10 @@ class Almanac extends WeatherDisplay {
|
||||
// sun and moon data
|
||||
this.elem.querySelector('.day-1').innerHTML = Today.toLocaleString({ weekday: 'long' });
|
||||
this.elem.querySelector('.day-2').innerHTML = Tomorrow.toLocaleString({ weekday: 'long' });
|
||||
this.elem.querySelector('.rise-1').innerHTML = DateTime.fromJSDate(info.sun[0].sunrise).setZone(timeZone()).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase();
|
||||
this.elem.querySelector('.rise-2').innerHTML = DateTime.fromJSDate(info.sun[1].sunrise).setZone(timeZone()).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase();
|
||||
this.elem.querySelector('.set-1').innerHTML = DateTime.fromJSDate(info.sun[0].sunset).setZone(timeZone()).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase();
|
||||
this.elem.querySelector('.set-2').innerHTML = DateTime.fromJSDate(info.sun[1].sunset).setZone(timeZone()).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase();
|
||||
this.elem.querySelector('.rise-1').innerHTML = timeFormat(DateTime.fromJSDate(info.sun[0].sunrise));
|
||||
this.elem.querySelector('.rise-2').innerHTML = timeFormat(DateTime.fromJSDate(info.sun[1].sunrise));
|
||||
this.elem.querySelector('.set-1').innerHTML = timeFormat(DateTime.fromJSDate(info.sun[0].sunset));
|
||||
this.elem.querySelector('.set-2').innerHTML = timeFormat(DateTime.fromJSDate(info.sun[1].sunset));
|
||||
|
||||
const days = info.moon.map((MoonPhase) => {
|
||||
const fill = {};
|
||||
@@ -171,6 +168,8 @@ const imageName = (type) => {
|
||||
}
|
||||
};
|
||||
|
||||
const timeFormat = (dt) => dt.setZone(timeZone()).toLocaleString(DateTime.TIME_SIMPLE).toLowerCase();
|
||||
|
||||
// register display
|
||||
const display = new Almanac(9, 'almanac');
|
||||
registerDisplay(display);
|
||||
|
||||
@@ -3,43 +3,24 @@ import { json } from './utils/fetch.mjs';
|
||||
|
||||
const KEYS = {
|
||||
ESC: 27,
|
||||
TAB: 9,
|
||||
RETURN: 13,
|
||||
LEFT: 37,
|
||||
UP: 38,
|
||||
RIGHT: 39,
|
||||
DOWN: 40,
|
||||
ENTER: 13,
|
||||
};
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
autoSelectFirst: false,
|
||||
serviceUrl: null,
|
||||
lookup: null,
|
||||
onSelect: () => { },
|
||||
onHint: null,
|
||||
width: 'auto',
|
||||
minChars: 3,
|
||||
maxHeight: 300,
|
||||
deferRequestBy: 0,
|
||||
params: {},
|
||||
delimiter: null,
|
||||
zIndex: 9999,
|
||||
type: 'GET',
|
||||
noCache: false,
|
||||
preserveInput: false,
|
||||
containerClass: 'autocomplete-suggestions',
|
||||
tabDisabled: false,
|
||||
dataType: 'text',
|
||||
currentRequest: null,
|
||||
triggerSelectOnValidInput: true,
|
||||
preventBadQueries: true,
|
||||
paramName: 'query',
|
||||
transformResult: (a) => a,
|
||||
showNoSuggestionNotice: false,
|
||||
noSuggestionNotice: 'No results',
|
||||
orientation: 'bottom',
|
||||
forceFixPosition: false,
|
||||
};
|
||||
|
||||
const escapeRegExChars = (string) => string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// current weather conditions display
|
||||
import STATUS from './status.mjs';
|
||||
import { loadImg, preloadImg } from './utils/image.mjs';
|
||||
import { preloadImg } from './utils/image.mjs';
|
||||
import { json } from './utils/fetch.mjs';
|
||||
import { directionToNSEW } from './utils/calc.mjs';
|
||||
import { locationCleanup } from './utils/string.mjs';
|
||||
@@ -17,8 +17,6 @@ const skipStations = ['U', 'C', 'H', 'W', 'Y', 'T', 'S', 'M', 'O', 'L', 'A', 'F'
|
||||
class CurrentWeather extends WeatherDisplay {
|
||||
constructor(navId, elemId) {
|
||||
super(navId, elemId, 'Current Conditions', true);
|
||||
// pre-load background image (returns promise)
|
||||
this.backgroundImage = loadImg('images/backgrounds/1.png');
|
||||
}
|
||||
|
||||
async getData(weatherParameters, refresh) {
|
||||
|
||||
@@ -59,11 +59,10 @@ class ExtendedForecast extends WeatherDisplay {
|
||||
date: Day.dayName,
|
||||
};
|
||||
|
||||
const { low } = Day;
|
||||
const { low, high } = Day;
|
||||
if (low !== undefined) {
|
||||
fill['value-lo'] = Math.round(low);
|
||||
}
|
||||
const { high } = Day;
|
||||
fill['value-hi'] = Math.round(high);
|
||||
|
||||
// return the filled template
|
||||
@@ -121,17 +120,17 @@ const parse = (fullForecast) => {
|
||||
return forecast;
|
||||
};
|
||||
|
||||
const regexList = [
|
||||
[/ and /gi, ' '],
|
||||
[/slight /gi, ''],
|
||||
[/chance /gi, ''],
|
||||
[/very /gi, ''],
|
||||
[/patchy /gi, ''],
|
||||
[/areas /gi, ''],
|
||||
[/dense /gi, ''],
|
||||
[/Thunderstorm/g, 'T\'Storm'],
|
||||
];
|
||||
const shortenExtendedForecastText = (long) => {
|
||||
const regexList = [
|
||||
[/ and /gi, ' '],
|
||||
[/slight /gi, ''],
|
||||
[/chance /gi, ''],
|
||||
[/very /gi, ''],
|
||||
[/patchy /gi, ''],
|
||||
[/areas /gi, ''],
|
||||
[/dense /gi, ''],
|
||||
[/Thunderstorm/g, 'T\'Storm'],
|
||||
];
|
||||
// run all regexes
|
||||
const short = regexList.reduce((working, [regex, replace]) => working.replace(regex, replace), long);
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class Hazards extends WeatherDisplay {
|
||||
// show alert indicator
|
||||
if (this.data.length > 0) alert.classList.add('show');
|
||||
} catch (error) {
|
||||
console.error('Get hourly forecast failed');
|
||||
console.error('Get hazards failed');
|
||||
console.error(error.status, error.responseJSON);
|
||||
if (this.isEnabled) this.setStatus(STATUS.failed);
|
||||
// return undefined to other subscribers
|
||||
@@ -129,7 +129,7 @@ class Hazards extends WeatherDisplay {
|
||||
// don't let offset go negative
|
||||
if (offsetY < 0) offsetY = 0;
|
||||
|
||||
// copy the scrolled portion of the canvas
|
||||
// move the element
|
||||
this.elem.querySelector('.main').scrollTo(0, offsetY);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import WeatherDisplay from './weatherdisplay.mjs';
|
||||
import { registerDisplay, timeZone } from './navigation.mjs';
|
||||
import { DateTime } from '../vendor/auto/luxon.mjs';
|
||||
|
||||
// get available space
|
||||
const availableWidth = 532;
|
||||
const availableHeight = 285;
|
||||
|
||||
class HourlyGraph extends WeatherDisplay {
|
||||
constructor(navId, elemId, defaultActive) {
|
||||
super(navId, elemId, 'Hourly Graph', defaultActive);
|
||||
@@ -47,10 +51,6 @@ class HourlyGraph extends WeatherDisplay {
|
||||
drawCanvas() {
|
||||
if (!this.image) this.image = this.elem.querySelector('.chart img');
|
||||
|
||||
// get available space
|
||||
const availableWidth = 532;
|
||||
const availableHeight = 285;
|
||||
|
||||
this.image.width = availableWidth;
|
||||
this.image.height = availableHeight;
|
||||
|
||||
|
||||
@@ -69,8 +69,7 @@ class Hourly extends WeatherDisplay {
|
||||
const fillValues = {};
|
||||
// hour
|
||||
const hour = startingHour.plus({ hours: index });
|
||||
const formattedHour = hour.toLocaleString({ weekday: 'short', hour: 'numeric' });
|
||||
fillValues.hour = formattedHour;
|
||||
fillValues.hour = hour.toLocaleString({ weekday: 'short', hour: 'numeric' });
|
||||
|
||||
// temperatures, convert to strings with no decimal
|
||||
const temperature = data.temperature.toString().padStart(3);
|
||||
@@ -81,12 +80,11 @@ class Hourly extends WeatherDisplay {
|
||||
fillValues.like = feelsLike;
|
||||
|
||||
// wind
|
||||
let wind = 'Calm';
|
||||
fillValues.wind = 'Calm';
|
||||
if (data.windSpeed > 0) {
|
||||
const windSpeed = Math.round(data.windSpeed).toString();
|
||||
wind = data.windDirection + (Array(6 - data.windDirection.length - windSpeed.length).join(' ')) + windSpeed;
|
||||
fillValues.wind = data.windDirection + (Array(6 - data.windDirection.length - windSpeed.length).join(' ')) + windSpeed;
|
||||
}
|
||||
fillValues.wind = wind;
|
||||
|
||||
// image
|
||||
fillValues.icon = { type: 'img', src: data.icon };
|
||||
@@ -96,8 +94,7 @@ class Hourly extends WeatherDisplay {
|
||||
// alter the color of the feels like column to reflect wind chill or heat index
|
||||
if (feelsLike < temperature) {
|
||||
filledRow.querySelector('.like').classList.add('wind-chill');
|
||||
}
|
||||
if (feelsLike > temperature) {
|
||||
} else if (feelsLike > temperature) {
|
||||
filledRow.querySelector('.like').classList.add('heat-index');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const hourlyIcon = (skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed, isNight = false) => {
|
||||
// internal function to add path to returned icon
|
||||
const addPath = (icon) => `images/icons/regional-maps/${icon}`;
|
||||
// internal function to add path to returned icon
|
||||
const addPath = (icon) => `images/icons/regional-maps/${icon}`;
|
||||
|
||||
const hourlyIcon = (skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed, isNight = false) => {
|
||||
// possible phenomenon
|
||||
let thunder = false;
|
||||
let snow = false;
|
||||
|
||||
@@ -22,8 +22,7 @@ class LatestObservations extends WeatherDisplay {
|
||||
// this is intentional because up to 30 stations are available to pull data from
|
||||
|
||||
// calculate distance to each station
|
||||
const stationsByDistance = Object.keys(StationInfo).map((key) => {
|
||||
const station = StationInfo[key];
|
||||
const stationsByDistance = Object.values(StationInfo).map((station) => {
|
||||
const distance = calcDistance(station.lat, station.lon, this.weatherParameters.latitude, this.weatherParameters.longitude);
|
||||
return { ...station, distance };
|
||||
});
|
||||
@@ -104,8 +103,6 @@ class LatestObservations extends WeatherDisplay {
|
||||
linesContainer.innerHTML = '';
|
||||
linesContainer.append(...lines);
|
||||
|
||||
// update temperature unit header
|
||||
|
||||
this.finishDraw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// regional forecast and observations
|
||||
import { loadImg } from './utils/image.mjs';
|
||||
import STATUS, { calcStatusClass, statusClasses } from './status.mjs';
|
||||
import WeatherDisplay from './weatherdisplay.mjs';
|
||||
import {
|
||||
@@ -10,9 +9,6 @@ class Progress extends WeatherDisplay {
|
||||
constructor(navId, elemId) {
|
||||
super(navId, elemId, '', false);
|
||||
|
||||
// pre-load background image (returns promise)
|
||||
this.backgroundImage = loadImg('images/backgrounds/1.png');
|
||||
|
||||
// disable any navigation timing
|
||||
this.timing = false;
|
||||
|
||||
|
||||
@@ -100,10 +100,8 @@ class Radar extends WeatherDisplay {
|
||||
const urls = sortedPngs.slice(-(this.dopplerRadarImageMax));
|
||||
|
||||
// calculate offsets and sizes
|
||||
let offsetX = 120;
|
||||
let offsetY = 69;
|
||||
offsetX *= 2;
|
||||
offsetY *= 2;
|
||||
const offsetX = 120 * 2;
|
||||
const offsetY = 69 * 2;
|
||||
const sourceXY = utils.getXYFromLatitudeLongitudeMap(this.weatherParameters, offsetX, offsetY);
|
||||
const radarSourceXY = utils.getXYFromLatitudeLongitudeDoppler(this.weatherParameters, offsetX, offsetY);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const buildForecast = (forecast, city, cityXY) => {
|
||||
const getRegionalObservation = async (point, city) => {
|
||||
try {
|
||||
// get stations
|
||||
const stations = await json(`https://api.weather.gov/gridpoints/${point.wfo}/${point.x},${point.y}/stations`);
|
||||
const stations = await json(`https://api.weather.gov/gridpoints/${point.wfo}/${point.x},${point.y}/stations?limit=1`);
|
||||
|
||||
// get the first station
|
||||
const station = stations.features[0].id;
|
||||
|
||||
@@ -13,6 +13,12 @@ import { registerDisplay } from './navigation.mjs';
|
||||
import * as utils from './regionalforecast-utils.mjs';
|
||||
import { getPoint } from './utils/weather.mjs';
|
||||
|
||||
// map offset
|
||||
const mapOffsetXY = {
|
||||
x: 240,
|
||||
y: 117,
|
||||
};
|
||||
|
||||
class RegionalForecast extends WeatherDisplay {
|
||||
constructor(navId, elemId) {
|
||||
super(navId, elemId, 'Regional Forecast', true);
|
||||
@@ -36,23 +42,18 @@ class RegionalForecast extends WeatherDisplay {
|
||||
}
|
||||
this.elem.querySelector('.map img').src = baseMap;
|
||||
|
||||
// map offset
|
||||
const offsetXY = {
|
||||
x: 240,
|
||||
y: 117,
|
||||
};
|
||||
// get user's location in x/y
|
||||
const sourceXY = utils.getXYFromLatitudeLongitude(this.weatherParameters.latitude, this.weatherParameters.longitude, offsetXY.x, offsetXY.y, weatherParameters.state);
|
||||
const sourceXY = utils.getXYFromLatitudeLongitude(this.weatherParameters.latitude, this.weatherParameters.longitude, mapOffsetXY.x, mapOffsetXY.y, weatherParameters.state);
|
||||
|
||||
// get latitude and longitude limits
|
||||
const minMaxLatLon = utils.getMinMaxLatitudeLongitude(sourceXY.x, sourceXY.y, offsetXY.x, offsetXY.y, this.weatherParameters.state);
|
||||
const minMaxLatLon = utils.getMinMaxLatitudeLongitude(sourceXY.x, sourceXY.y, mapOffsetXY.x, mapOffsetXY.y, this.weatherParameters.state);
|
||||
|
||||
// get a target distance
|
||||
let targetDistance = 2.5;
|
||||
if (this.weatherParameters.state === 'HI') targetDistance = 1;
|
||||
|
||||
// make station info into an array
|
||||
const stationInfoArray = Object.values(StationInfo).map((value) => ({ ...value, targetDistance }));
|
||||
const stationInfoArray = Object.values(StationInfo).map((station) => ({ ...station, targetDistance }));
|
||||
// combine regional cities with station info for additional stations
|
||||
// stations are intentionally after cities to allow cities priority when drawing the map
|
||||
const combinedCities = [...RegionalCities, ...stationInfoArray];
|
||||
@@ -137,7 +138,7 @@ class RegionalForecast extends WeatherDisplay {
|
||||
// return the weather data and offsets
|
||||
this.data = {
|
||||
regionalData,
|
||||
offsetXY,
|
||||
mapOffsetXY,
|
||||
sourceXY,
|
||||
};
|
||||
|
||||
@@ -147,7 +148,7 @@ class RegionalForecast extends WeatherDisplay {
|
||||
drawCanvas() {
|
||||
super.drawCanvas();
|
||||
// break up data into useful values
|
||||
const { regionalData: data, sourceXY, offsetXY } = this.data;
|
||||
const { regionalData: data, sourceXY } = this.data;
|
||||
|
||||
// draw the header graphics
|
||||
|
||||
@@ -170,7 +171,7 @@ class RegionalForecast extends WeatherDisplay {
|
||||
}
|
||||
|
||||
// draw the map
|
||||
const scale = 640 / (offsetXY.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)`;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { elemForEach } from './utils/elem.mjs';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => init());
|
||||
|
||||
// shorthand mappings for frequently used values
|
||||
@@ -19,21 +21,18 @@ const init = () => {
|
||||
const createLink = async (e) => {
|
||||
// cancel default event (click on hyperlink)
|
||||
e.preventDefault();
|
||||
// get all checkboxes on page
|
||||
const checkboxes = document.querySelectorAll('input[type=checkbox]');
|
||||
|
||||
// list to receive checkbox statuses
|
||||
const queryStringElements = {};
|
||||
|
||||
[...checkboxes].forEach((elem) => {
|
||||
elemForEach('input[type=checkbox]', (elem) => {
|
||||
if (elem?.id) {
|
||||
queryStringElements[elem.id] = elem?.checked ?? false;
|
||||
}
|
||||
});
|
||||
|
||||
// get all select boxes
|
||||
const selects = document.querySelectorAll('select');
|
||||
[...selects].forEach((elem) => {
|
||||
elemForEach('select', (elem) => {
|
||||
if (elem?.id) {
|
||||
queryStringElements[elem.id] = elem?.value ?? 0;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,4 @@
|
||||
import { blob } from './fetch.mjs';
|
||||
import { rewriteUrl } from './cors.mjs';
|
||||
|
||||
// ****************************** load images *********************************
|
||||
// load an image from a blob or url
|
||||
const loadImg = (imgData, cors = false) => new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.onload = (e) => {
|
||||
resolve(e.target);
|
||||
};
|
||||
if (imgData instanceof Blob) {
|
||||
img.src = window.URL.createObjectURL(imgData);
|
||||
} else {
|
||||
let url = imgData;
|
||||
if (cors) url = rewriteUrl(imgData);
|
||||
img.src = url;
|
||||
}
|
||||
});
|
||||
|
||||
// preload an image
|
||||
// the goal is to get it in the browser's cache so it is available more quickly when the browser needs it
|
||||
@@ -28,15 +11,7 @@ const preloadImg = (src) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const loadImgElement = (url) => new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.onload = () => resolve(image);
|
||||
image.onerror = reject;
|
||||
image.src = url;
|
||||
});
|
||||
|
||||
export {
|
||||
loadImg,
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
preloadImg,
|
||||
loadImgElement,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { json } from './fetch.mjs';
|
||||
|
||||
const getPoint = async (lat, lon) => {
|
||||
try {
|
||||
return await json(`https://api.weather.gov/points/${lat},${lon}`);
|
||||
return await json(`https://api.weather.gov/points/${lat.toFixed(4)},${lon.toFixed(4)}`);
|
||||
} catch (error) {
|
||||
console.log(`Unable to get point ${lat}, ${lon}`);
|
||||
console.error(error);
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from './navigation.mjs';
|
||||
import { parseQueryString } from './share.mjs';
|
||||
import settings from './settings.mjs';
|
||||
import { elemForEach } from './utils/elem.mjs';
|
||||
|
||||
class WeatherDisplay {
|
||||
constructor(navId, elemId, name, defaultEnabled) {
|
||||
@@ -391,8 +392,7 @@ class WeatherDisplay {
|
||||
this.templates = {};
|
||||
this.elem = document.querySelector(`#${this.elemId}-html`);
|
||||
if (!this.elem) return;
|
||||
const templates = this.elem.querySelectorAll('.template');
|
||||
templates.forEach((template) => {
|
||||
elemForEach(`#${this.elemId}-html .template`, (template) => {
|
||||
const className = template.classList[0];
|
||||
const node = template.cloneNode(true);
|
||||
node.classList.remove('template');
|
||||
|
||||
Reference in New Issue
Block a user