add hazards
This commit is contained in:
parent
806ef91000
commit
111f077e20
|
@ -90,8 +90,8 @@ const init = () => {
|
||||||
btnGetGpsClick();
|
btnGetGpsClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
const twcPlay = localStorage.getItem('play');
|
const play = localStorage.getItem('play');
|
||||||
if (twcPlay === null || twcPlay === 'true') postMessage('navButton', 'play');
|
if (play === null || play === 'true') postMessage('navButton', 'play');
|
||||||
|
|
||||||
document.getElementById('btnClearQuery').addEventListener('click', () => {
|
document.getElementById('btnClearQuery').addEventListener('click', () => {
|
||||||
document.getElementById('spanCity').innerHTML = '';
|
document.getElementById('spanCity').innerHTML = '';
|
||||||
|
|
|
@ -171,7 +171,7 @@ const imageName = (type) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// register display
|
// register display
|
||||||
const display = new Almanac(8, 'almanac');
|
const display = new Almanac(9, 'almanac');
|
||||||
registerDisplay(display);
|
registerDisplay(display);
|
||||||
|
|
||||||
export default display.getSun.bind(display);
|
export default display.getSun.bind(display);
|
||||||
|
|
|
@ -204,7 +204,7 @@ const shortConditions = (_condition) => {
|
||||||
return condition;
|
return condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
const display = new CurrentWeather(0, 'current-weather');
|
const display = new CurrentWeather(1, 'current-weather');
|
||||||
registerDisplay(display);
|
registerDisplay(display);
|
||||||
|
|
||||||
export default display.getCurrentWeather.bind(display);
|
export default display.getCurrentWeather.bind(display);
|
||||||
|
|
|
@ -163,4 +163,4 @@ const shortenExtendedForecastText = (long) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// register display
|
// register display
|
||||||
registerDisplay(new ExtendedForecast(7, 'extended-forecast'));
|
registerDisplay(new ExtendedForecast(8, 'extended-forecast'));
|
||||||
|
|
134
server/scripts/modules/hazards.mjs
Normal file
134
server/scripts/modules/hazards.mjs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// hourly forecast list
|
||||||
|
|
||||||
|
import STATUS from './status.mjs';
|
||||||
|
import { json } from './utils/fetch.mjs';
|
||||||
|
import WeatherDisplay from './weatherdisplay.mjs';
|
||||||
|
import { registerDisplay } from './navigation.mjs';
|
||||||
|
|
||||||
|
const hazardLevels = {
|
||||||
|
Extreme: 10,
|
||||||
|
Severe: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Hazards extends WeatherDisplay {
|
||||||
|
constructor(navId, elemId, defaultActive) {
|
||||||
|
// special height and width for scrolling
|
||||||
|
super(navId, elemId, 'Hazards', defaultActive);
|
||||||
|
this.showOnProgress = false;
|
||||||
|
|
||||||
|
// 0 screens skips this during "play"
|
||||||
|
this.timing.totalScreens = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData(weatherParameters) {
|
||||||
|
// super checks for enabled
|
||||||
|
const superResult = super.getData(weatherParameters);
|
||||||
|
|
||||||
|
const alert = this.checkbox.querySelector('.alert');
|
||||||
|
alert.classList.remove('show');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// get the forecast
|
||||||
|
const url = new URL('https://api.weather.gov/alerts/active');
|
||||||
|
url.searchParams.append('point', `${this.weatherParameters.latitude},${this.weatherParameters.longitude}`);
|
||||||
|
url.searchParams.append('limit', 5);
|
||||||
|
const alerts = await json(url, { retryCount: 3, stillWaiting: () => this.stillWaiting() });
|
||||||
|
const unsortedAlerts = alerts.features ?? [];
|
||||||
|
const sortedAlerts = unsortedAlerts.sort((a, b) => (hazardLevels[b.properties.severity] ?? 0) - (hazardLevels[a.properties.severity] ?? 0));
|
||||||
|
this.data = sortedAlerts;
|
||||||
|
|
||||||
|
// show alert indicator
|
||||||
|
if (this.data.length > 0) alert.classList.add('show');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Get hourly forecast failed');
|
||||||
|
console.error(e.status, e.responseJSON);
|
||||||
|
if (this.isEnabled) this.setStatus(STATUS.failed);
|
||||||
|
// return undefined to other subscribers
|
||||||
|
this.getDataCallback(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getDataCallback();
|
||||||
|
|
||||||
|
if (!superResult) return;
|
||||||
|
this.drawLongCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
async drawLongCanvas() {
|
||||||
|
// get the list element and populate
|
||||||
|
const list = this.elem.querySelector('.hazard-lines');
|
||||||
|
list.innerHTML = '';
|
||||||
|
|
||||||
|
const lines = this.data.map((data) => {
|
||||||
|
const fillValues = {};
|
||||||
|
// text
|
||||||
|
fillValues['hazard-text'] = `${data.properties.event}<br/><br/>${data.properties.description.replace('\n', '<br/><br/>')}`;
|
||||||
|
|
||||||
|
return this.fillTemplate('hazard', fillValues);
|
||||||
|
});
|
||||||
|
|
||||||
|
list.append(...lines);
|
||||||
|
|
||||||
|
// no alerts, skip this display by setting timing to zero
|
||||||
|
if (lines.length === 0) {
|
||||||
|
this.timing.totalScreens = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update timing
|
||||||
|
// set up the timing
|
||||||
|
this.timing.baseDelay = 20;
|
||||||
|
// 24 hours = 6 pages
|
||||||
|
const pages = Math.ceil(list.scrollHeight / 390); // first page is already displayed, last page doesn't happen
|
||||||
|
const timingStep = 75 * 4;
|
||||||
|
this.timing.delay = [150 + timingStep];
|
||||||
|
// add additional pages
|
||||||
|
for (let i = 0; i < pages; i += 1) this.timing.delay.push(timingStep);
|
||||||
|
// add the final 3 second delay
|
||||||
|
this.timing.delay.push(150);
|
||||||
|
this.calcNavTiming();
|
||||||
|
this.setStatus(STATUS.loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawCanvas() {
|
||||||
|
super.drawCanvas();
|
||||||
|
this.finishDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
showCanvas() {
|
||||||
|
// special to hourly to draw the remainder of the canvas
|
||||||
|
this.drawCanvas();
|
||||||
|
super.showCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen index change callback just runs the base count callback
|
||||||
|
screenIndexChange() {
|
||||||
|
this.baseCountChange(this.navBaseCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// base count change callback
|
||||||
|
baseCountChange(count) {
|
||||||
|
// calculate scroll offset and don't go past end
|
||||||
|
let offsetY = Math.min(this.elem.querySelector('.hazard-lines').getBoundingClientRect().height - 390, (count - 150));
|
||||||
|
|
||||||
|
// don't let offset go negative
|
||||||
|
if (offsetY < 0) offsetY = 0;
|
||||||
|
|
||||||
|
// copy the scrolled portion of the canvas
|
||||||
|
this.elem.querySelector('.main').scrollTo(0, offsetY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make data available outside this class
|
||||||
|
// promise allows for data to be requested before it is available
|
||||||
|
async getCurrentData(stillWaiting) {
|
||||||
|
if (stillWaiting) this.stillWaitingCallbacks.push(stillWaiting);
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (this.data) resolve(this.data);
|
||||||
|
// data not available, put it into the data callback queue
|
||||||
|
this.getDataCallbacks.push(() => resolve(this.data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register display
|
||||||
|
registerDisplay(new Hazards(0, 'hazards', true));
|
|
@ -146,4 +146,4 @@ const drawPath = (path, ctx, options) => {
|
||||||
const formatTime = (time) => time.toFormat('ha').slice(0, -1);
|
const formatTime = (time) => time.toFormat('ha').slice(0, -1);
|
||||||
|
|
||||||
// register display
|
// register display
|
||||||
registerDisplay(new HourlyGraph(3, 'hourly-graph'));
|
registerDisplay(new HourlyGraph(4, 'hourly-graph'));
|
||||||
|
|
|
@ -191,7 +191,7 @@ const expand = (data) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// register display
|
// register display
|
||||||
const display = new Hourly(2, 'hourly', false);
|
const display = new Hourly(3, 'hourly', false);
|
||||||
registerDisplay(display);
|
registerDisplay(display);
|
||||||
|
|
||||||
export default display.getCurrentData.bind(display);
|
export default display.getCurrentData.bind(display);
|
||||||
|
|
|
@ -120,4 +120,4 @@ const shortenCurrentConditions = (_condition) => {
|
||||||
return condition;
|
return condition;
|
||||||
};
|
};
|
||||||
// register display
|
// register display
|
||||||
registerDisplay(new LatestObservations(1, 'latest-observations'));
|
registerDisplay(new LatestObservations(2, 'latest-observations'));
|
||||||
|
|
|
@ -92,4 +92,4 @@ const parse = (forecast) => forecast.properties.periods.slice(0, 6).map((text) =
|
||||||
Text: text.detailedForecast,
|
Text: text.detailedForecast,
|
||||||
}));
|
}));
|
||||||
// register display
|
// register display
|
||||||
registerDisplay(new LocalForecast(6, 'local-forecast'));
|
registerDisplay(new LocalForecast(7, 'local-forecast'));
|
||||||
|
|
|
@ -26,8 +26,8 @@ const init = async () => {
|
||||||
resize();
|
resize();
|
||||||
|
|
||||||
// auto refresh
|
// auto refresh
|
||||||
const TwcAutoRefresh = localStorage.getItem('TwcAutoRefresh');
|
const autoRefresh = localStorage.getItem('autoRefresh');
|
||||||
if (!TwcAutoRefresh || TwcAutoRefresh === 'true') {
|
if (!autoRefresh || autoRefresh === 'true') {
|
||||||
document.getElementById('chkAutoRefresh').checked = true;
|
document.getElementById('chkAutoRefresh').checked = true;
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('chkAutoRefresh').checked = false;
|
document.getElementById('chkAutoRefresh').checked = false;
|
||||||
|
@ -103,8 +103,11 @@ const updateStatus = (value) => {
|
||||||
if (!progress) return;
|
if (!progress) return;
|
||||||
progress.drawCanvas(displays, countLoadedDisplays());
|
progress.drawCanvas(displays, countLoadedDisplays());
|
||||||
|
|
||||||
|
// first display is hazards and it must load before evaluating the first display
|
||||||
|
if (displays[0].status === STATUS.loading) return;
|
||||||
|
|
||||||
// calculate first enabled display
|
// calculate first enabled display
|
||||||
const firstDisplayIndex = displays.findIndex((display) => display.enabled);
|
const firstDisplayIndex = displays.findIndex((display) => display.enabled && display.timing.totalScreens > 0);
|
||||||
|
|
||||||
// if this is the first display and we're playing, load it up so it starts playing
|
// if this is the first display and we're playing, load it up so it starts playing
|
||||||
if (isPlaying() && value.id === firstDisplayIndex && value.status === STATUS.loaded) {
|
if (isPlaying() && value.id === firstDisplayIndex && value.status === STATUS.loaded) {
|
||||||
|
@ -165,7 +168,7 @@ const navTo = (direction) => {
|
||||||
let firstDisplay;
|
let firstDisplay;
|
||||||
let displayCount = 0;
|
let displayCount = 0;
|
||||||
do {
|
do {
|
||||||
if (displays[displayCount].status === STATUS.loaded) firstDisplay = displays[displayCount];
|
if (displays[displayCount].status === STATUS.loaded && displays[displayCount].timing.totalScreens > 0) firstDisplay = displays[displayCount];
|
||||||
displayCount += 1;
|
displayCount += 1;
|
||||||
} while (!firstDisplay && displayCount < displays.length);
|
} while (!firstDisplay && displayCount < displays.length);
|
||||||
|
|
||||||
|
@ -186,7 +189,7 @@ const loadDisplay = (direction) => {
|
||||||
for (let i = 0; i < totalDisplays; i += 1) {
|
for (let i = 0; i < totalDisplays; i += 1) {
|
||||||
// convert form simple 0-10 to start at current display index +/-1 and wrap
|
// convert form simple 0-10 to start at current display index +/-1 and wrap
|
||||||
idx = wrap(curIdx + (i + 1) * direction, totalDisplays);
|
idx = wrap(curIdx + (i + 1) * direction, totalDisplays);
|
||||||
if (displays[idx].status === STATUS.loaded) break;
|
if (displays[idx].status === STATUS.loaded && displays[idx].timing.totalScreens > 0) break;
|
||||||
}
|
}
|
||||||
// if new display index is less than current display a wrap occurred, test for reload timeout
|
// if new display index is less than current display a wrap occurred, test for reload timeout
|
||||||
if (idx <= curIdx) {
|
if (idx <= curIdx) {
|
||||||
|
@ -210,7 +213,7 @@ const currentDisplay = () => displays[currentDisplayIndex()];
|
||||||
const setPlaying = (newValue) => {
|
const setPlaying = (newValue) => {
|
||||||
playing = newValue;
|
playing = newValue;
|
||||||
const playButton = document.getElementById('NavigatePlay');
|
const playButton = document.getElementById('NavigatePlay');
|
||||||
localStorage.setItem('TwcPlay', playing);
|
localStorage.setItem('play', playing);
|
||||||
|
|
||||||
if (playing) {
|
if (playing) {
|
||||||
noSleep(true);
|
noSleep(true);
|
||||||
|
@ -319,7 +322,7 @@ const autoRefreshChange = (e) => {
|
||||||
stopAutoRefreshTimer();
|
stopAutoRefreshTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem('TwcAutoRefresh', checked);
|
localStorage.setItem('autoRefresh', checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AssignLastUpdate = (date) => {
|
const AssignLastUpdate = (date) => {
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Progress extends WeatherDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
async drawCanvas(displays, loadedCount) {
|
async drawCanvas(displays, loadedCount) {
|
||||||
|
if (!this.elem) return;
|
||||||
super.drawCanvas();
|
super.drawCanvas();
|
||||||
|
|
||||||
// get the progress bar cover (makes percentage)
|
// get the progress bar cover (makes percentage)
|
||||||
|
@ -34,6 +35,7 @@ class Progress extends WeatherDisplay {
|
||||||
// if no displays provided just draw the backgrounds (above)
|
// if no displays provided just draw the backgrounds (above)
|
||||||
if (!displays) return;
|
if (!displays) return;
|
||||||
const lines = displays.map((display, index) => {
|
const lines = displays.map((display, index) => {
|
||||||
|
if (display.showOnProgress === false) return false;
|
||||||
const fill = {};
|
const fill = {};
|
||||||
|
|
||||||
fill.name = display.name;
|
fill.name = display.name;
|
||||||
|
|
|
@ -222,4 +222,4 @@ class Radar extends WeatherDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
// register display
|
// register display
|
||||||
registerDisplay(new Radar(9, 'radar'));
|
registerDisplay(new Radar(10, 'radar'));
|
||||||
|
|
|
@ -204,4 +204,4 @@ const getAndFormatPoint = async (lat, lon) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// register display
|
// register display
|
||||||
registerDisplay(new RegionalForecast(5, 'regional-forecast'));
|
registerDisplay(new RegionalForecast(6, 'regional-forecast'));
|
||||||
|
|
|
@ -158,4 +158,4 @@ const getTravelCitiesDayName = (cities) => cities.reduce((dayName, city) => {
|
||||||
}, '');
|
}, '');
|
||||||
|
|
||||||
// register display, not active by default
|
// register display, not active by default
|
||||||
registerDisplay(new TravelForecast(4, 'travel', false));
|
registerDisplay(new TravelForecast(5, 'travel', false));
|
||||||
|
|
|
@ -20,6 +20,7 @@ class WeatherDisplay {
|
||||||
this.defaultEnabled = defaultEnabled;
|
this.defaultEnabled = defaultEnabled;
|
||||||
this.okToDrawCurrentConditions = true;
|
this.okToDrawCurrentConditions = true;
|
||||||
this.okToDrawCurrentDateTime = true;
|
this.okToDrawCurrentDateTime = true;
|
||||||
|
this.showOnProgress = true;
|
||||||
|
|
||||||
// default navigation timing
|
// default navigation timing
|
||||||
this.timing = {
|
this.timing = {
|
||||||
|
@ -75,8 +76,11 @@ class WeatherDisplay {
|
||||||
checkbox.addEventListener('change', (e) => this.checkboxChange(e));
|
checkbox.addEventListener('change', (e) => this.checkboxChange(e));
|
||||||
const span = document.createElement('span');
|
const span = document.createElement('span');
|
||||||
span.innerHTML = this.name;
|
span.innerHTML = this.name;
|
||||||
|
const alert = document.createElement('span');
|
||||||
|
alert.innerHTML = '!!!';
|
||||||
|
alert.classList.add('alert');
|
||||||
|
|
||||||
label.append(checkbox, span);
|
label.append(checkbox, span, alert);
|
||||||
|
|
||||||
this.checkbox = label;
|
this.checkbox = label;
|
||||||
|
|
||||||
|
@ -257,10 +261,10 @@ class WeatherDisplay {
|
||||||
// call the appropriate screen index change method
|
// call the appropriate screen index change method
|
||||||
if (!this.screenIndexChange) {
|
if (!this.screenIndexChange) {
|
||||||
await this.drawCanvas();
|
await this.drawCanvas();
|
||||||
this.showCanvas();
|
|
||||||
} else {
|
} else {
|
||||||
this.screenIndexChange(this.screenIndex);
|
this.screenIndexChange(this.screenIndex);
|
||||||
}
|
}
|
||||||
|
this.showCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
// take the three timing formats shown above and break them into arrays for consistent usage in navigation functions
|
// take the three timing formats shown above and break them into arrays for consistent usage in navigation functions
|
||||||
|
@ -340,6 +344,7 @@ class WeatherDisplay {
|
||||||
screenIndexFromBaseCount() {
|
screenIndexFromBaseCount() {
|
||||||
// test for timing enabled
|
// test for timing enabled
|
||||||
if (!this.timing) return 0;
|
if (!this.timing) return 0;
|
||||||
|
if (this.timing.totalScreens === 0) return false;
|
||||||
// find the first timing in the timing array that is greater than the base count
|
// find the first timing in the timing array that is greater than the base count
|
||||||
if (this.timing && !this.timing.fullDelay) this.calcNavTiming();
|
if (this.timing && !this.timing.fullDelay) this.calcNavTiming();
|
||||||
const timingIndex = this.timing.fullDelay.findIndex((delay) => delay > this.navBaseCount);
|
const timingIndex = this.timing.fullDelay.findIndex((delay) => delay > this.navBaseCount);
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
27
server/styles/scss/_hazards.scss
Normal file
27
server/styles/scss/_hazards.scss
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
@use 'shared/_colors'as c;
|
||||||
|
@use 'shared/_utils'as u;
|
||||||
|
|
||||||
|
.weather-display .main.hazards {
|
||||||
|
&.main {
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
.hazard-lines {
|
||||||
|
min-height: 400px;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
background-color: rgb(112, 35, 35);
|
||||||
|
|
||||||
|
.hazard {
|
||||||
|
font-family: 'Star4000';
|
||||||
|
font-size: 24pt;
|
||||||
|
color: white;
|
||||||
|
@include u.text-shadow(0px);
|
||||||
|
position: relative;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-top: 110px;
|
||||||
|
margin-left: 80px;
|
||||||
|
margin-right: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -326,6 +326,14 @@ button {
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
display: inline;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,10 @@
|
||||||
width: 640px;
|
width: 640px;
|
||||||
height: 310px;
|
height: 310px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.no-header {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-box {
|
&.has-box {
|
||||||
|
|
|
@ -11,3 +11,4 @@
|
||||||
@import 'radar';
|
@import 'radar';
|
||||||
@import 'regional-forecast';
|
@import 'regional-forecast';
|
||||||
@import 'almanac';
|
@import 'almanac';
|
||||||
|
@import 'hazards';
|
|
@ -28,6 +28,7 @@
|
||||||
<script type="text/javascript" src="scripts/vendor/auto/nosleep.js"></script>
|
<script type="text/javascript" src="scripts/vendor/auto/nosleep.js"></script>
|
||||||
<script type="text/javascript" src="scripts/vendor/auto/swiped-events.js"></script>
|
<script type="text/javascript" src="scripts/vendor/auto/swiped-events.js"></script>
|
||||||
<script type="text/javascript" src="scripts/vendor/auto/suncalc.js"></script>
|
<script type="text/javascript" src="scripts/vendor/auto/suncalc.js"></script>
|
||||||
|
<script type="module" src="scripts/modules/hazards.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/currentweatherscroll.mjs"></script>
|
<script type="module" src="scripts/modules/currentweatherscroll.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/currentweather.mjs"></script>
|
<script type="module" src="scripts/modules/currentweather.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/almanac.mjs"></script>
|
<script type="module" src="scripts/modules/almanac.mjs"></script>
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
<script type="module" src="scripts/modules/regionalforecast.mjs"></script>
|
<script type="module" src="scripts/modules/regionalforecast.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/travelforecast.mjs"></script>
|
<script type="module" src="scripts/modules/travelforecast.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/progress.mjs"></script>
|
<script type="module" src="scripts/modules/progress.mjs"></script>
|
||||||
|
<script type="module" src="scripts/modules/radar.mjs"></script>
|
||||||
<script type="module" src="scripts/index.mjs"></script>
|
<script type="module" src="scripts/index.mjs"></script>
|
||||||
|
|
||||||
<!-- data -->
|
<!-- data -->
|
||||||
|
@ -117,6 +119,9 @@
|
||||||
<div id="radar-html" class="weather-display">
|
<div id="radar-html" class="weather-display">
|
||||||
<%- include('partials/radar.ejs') %>
|
<%- include('partials/radar.ejs') %>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="hazards-html" class="weather-display show">
|
||||||
|
<%- include('partials/hazards.ejs') %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="divTwcBottom">
|
<div id="divTwcBottom">
|
||||||
<div id="divTwcBottomLeft">
|
<div id="divTwcBottomLeft">
|
||||||
|
|
8
views/partials/hazards.ejs
Normal file
8
views/partials/hazards.ejs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<div class="main has-scroll hazards no-header">
|
||||||
|
<div class="hazard-lines">
|
||||||
|
<div class="hazard template">
|
||||||
|
<div class="hazard-text"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%- include('scroll.ejs') %>
|
Loading…
Reference in a new issue