Publish Container / publish (push) Successful in 2m44s
Hurricane / tropical storm fixes - NHC intensity is in knots: show real mph (80 kt = 92 mph) instead of mislabelled 'mph' - Real Saffir-Simpson category from wind (was always cat 1); subtropical, post-tropical and potential-cyclone types no longer shown as 'depression'; each storm shows movement (direction + mph, previously a non-existent field), distance from you, update time, advisory link - Forecast track points coloured by their own forecast strength (not the current class) - Map click handlers registered once (popups stacked on every refresh); overlays wait for the map to settle instead of silently not drawing while tiles load; stale renders superseded - Subtropical/tropical advice added to alert guidance; escaped storm names Alert cards (active alerts only) - What it's for, area, window, time remaining, plain-language meaning, what to do, and a Full details expander (headline, text, instructions, severity/urgency/certainty, issuer) - No longer drops alerts beyond 6 per severity group; expired alerts are filtered out - Shared plain-language alert glossary (alert-info.js, 64 products incl. tropical and surge) now used by both the app and the dashboard history Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
102 lines
8.7 KiB
JavaScript
102 lines
8.7 KiB
JavaScript
/* Shared plain-language alert helpers (used by the weather app and the station dashboard) */
|
||
(() => {
|
||
'use strict';
|
||
const GLOSSARY = {
|
||
'heat advisory': 'Dangerously hot conditions expected. Limit time outdoors, drink plenty of water, and check on children, older adults and pets.',
|
||
'excessive heat watch': 'Conditions are favorable for life-threatening heat in the next day or two. Plan ahead.',
|
||
'excessive heat warning': 'Life-threatening heat is occurring or imminent. Avoid outdoor activity and stay cool.',
|
||
'extreme heat watch': 'Conditions are favorable for life-threatening heat in the next day or two. Plan ahead.',
|
||
'extreme heat warning': 'Life-threatening heat is occurring or imminent. Avoid outdoor activity and stay cool.',
|
||
'severe thunderstorm watch': 'Storms capable of damaging wind or large hail are possible in the area. Stay alert and be ready to take shelter.',
|
||
'severe thunderstorm warning': 'A severe storm with damaging wind and/or large hail is happening or imminent. Move indoors away from windows.',
|
||
'tornado watch': 'Conditions are favorable for tornadoes. Stay alert and know where you would take shelter.',
|
||
'tornado warning': 'A tornado is occurring or imminent. Take shelter immediately in the lowest, most interior room.',
|
||
'special weather statement': 'A short notice about a significant but non-warning weather event, often a strong storm, gusty winds, dense fog or sudden changes.',
|
||
'flood watch': 'Flooding is possible. Be prepared to move to higher ground and avoid low-lying areas.',
|
||
'flood advisory': 'Minor flooding is occurring or expected. Avoid flooded roads and low spots.',
|
||
'flood warning': 'Flooding is happening or imminent. Move to higher ground and never drive through flood water.',
|
||
'flash flood watch': 'Flash flooding is possible. Be ready to move to higher ground quickly.',
|
||
'flash flood warning': 'Flash flooding is occurring or imminent. Move to higher ground now.',
|
||
'wind advisory': 'Sustained or gusty winds that can blow around unsecured objects and make driving difficult for high-profile vehicles.',
|
||
'high wind watch': 'Damaging winds are possible. Secure outdoor objects.',
|
||
'high wind warning': 'Damaging winds are occurring or imminent. Secure objects and avoid travel if possible.',
|
||
'freeze watch': 'A freeze is possible. Protect sensitive plants, pets and exposed pipes.',
|
||
'freeze warning': 'Temperatures at or below freezing are expected. Protect plants, pets and exposed pipes.',
|
||
'frost advisory': 'Frost is expected and can damage sensitive plants.',
|
||
'hard freeze warning': 'Severe freezing temperatures are expected. Protect plants, pets and pipes.',
|
||
'winter storm watch': 'Heavy snow and/or ice is possible. Prepare for hazardous travel.',
|
||
'winter storm warning': 'Heavy snow and/or ice is occurring or imminent. Travel may be dangerous or impossible.',
|
||
'winter weather advisory': 'Snow, sleet or freezing rain will cause slippery roads and travel difficulty.',
|
||
'ice storm warning': 'Significant ice accumulation is expected. Power outages and dangerous travel are likely.',
|
||
'blizzard warning': 'Heavy snow with strong winds and very low visibility. Avoid travel.',
|
||
'wind chill advisory': 'Dangerously cold wind chills are expected. Dress in layers and limit time outside.',
|
||
'wind chill warning': 'Life-threatening wind chills are expected. Frostbite can occur quickly.',
|
||
'dense fog advisory': 'Fog is reducing visibility to a quarter mile or less. Slow down and use low-beam headlights.',
|
||
'dense smoke advisory': 'Smoke is reducing visibility and air quality.',
|
||
'air quality alert': 'Air quality is unhealthy for some or all people. Limit prolonged outdoor exertion.',
|
||
'red flag warning': 'Dry, windy and warm conditions create a high wildfire risk. Avoid open flames and outdoor burning.',
|
||
'fire weather watch': 'Conditions may soon create a high wildfire risk.',
|
||
'rip current statement': 'Dangerous rip currents at the beach. Stay out of the water or swim near a lifeguard.',
|
||
'coastal flood advisory': 'Minor coastal flooding is expected.',
|
||
'coastal flood warning': 'Significant coastal flooding is occurring or imminent.',
|
||
'small craft advisory': 'Winds or seas hazardous to small boats.',
|
||
'tropical storm watch': 'Tropical storm conditions are possible within about 48 hours.',
|
||
'tropical storm warning': 'Tropical storm conditions are expected within about 36 hours.',
|
||
'hurricane watch': 'Hurricane conditions are possible within about 48 hours.',
|
||
'hurricane warning': 'Hurricane conditions are expected within about 36 hours.',
|
||
'hazardous weather outlook': 'An outlook of possible hazardous weather in the next several days.',
|
||
'severe weather statement': 'An update on an ongoing severe storm warning.',
|
||
'flood statement': 'An update on an ongoing flood warning.',
|
||
'storm surge watch': 'Life-threatening storm surge flooding is possible within about 48 hours. Know your evacuation zone and be ready to leave.',
|
||
'storm surge warning': 'Life-threatening storm surge flooding is expected within about 36 hours. Follow evacuation orders from local officials immediately.',
|
||
'hurricane local statement': 'Local details on a hurricane threat: expected winds, rain, surge and tornado risk for your area, and what officials are advising.',
|
||
'tropical cyclone statement': 'Local details on a tropical storm or hurricane threat for your area.',
|
||
'tropical depression watch': 'A tropical depression may affect the area; heavy rain and gusty wind are possible.',
|
||
'extreme wind warning': 'Extreme, hurricane-force winds (115+ mph) are about to hit. Take shelter now in the strongest part of a sturdy building.',
|
||
'hurricane force wind watch': 'Hurricane-force winds are possible over marine areas. Stay in port.',
|
||
'hurricane force wind warning': 'Hurricane-force winds are expected over marine areas. Dangerous for all vessels.',
|
||
'storm warning': 'Strong marine winds (48–63 knots) expected. Dangerous for all vessels.',
|
||
'gale warning': 'Strong marine winds (34–47 knots) expected. Hazardous for small and large vessels.',
|
||
'gale watch': 'Gale-force marine winds are possible.',
|
||
'special marine warning': 'Short-lived, hazardous marine weather (strong winds, waterspouts, hail) is happening. Seek safe harbor.',
|
||
'high surf advisory': 'Large breaking waves make swimming and surfing dangerous.',
|
||
'high surf warning': 'Very large breaking waves are expected: dangerous and life-threatening at the shore.',
|
||
'beach hazards statement': 'Hazards at the beach such as rip currents or high surf. Swim near a lifeguard.',
|
||
'lake wind advisory': 'Gusty lake winds can create hazardous waves for small boats.',
|
||
'dust storm warning': 'Blowing dust is reducing visibility to near zero. Pull off the road and turn off your lights.',
|
||
'tornado emergency': 'A confirmed large and destructive tornado is moving toward a populated area. Take cover immediately.',
|
||
'flash flood emergency': 'Catastrophic, life-threatening flash flooding is happening. Move to higher ground now.',
|
||
};
|
||
|
||
function meaning(event) {
|
||
const e = String(event || '').toLowerCase().trim();
|
||
if (GLOSSARY[e]) return GLOSSARY[e];
|
||
if (e.endsWith('watch')) return 'A Watch: conditions are favorable for this hazard. Stay alert and be ready to act.';
|
||
if (e.endsWith('warning')) return 'A Warning: this hazard is happening or imminent. Take action now.';
|
||
if (e.endsWith('advisory')) return 'An Advisory: conditions may cause inconvenience or danger if you are not careful.';
|
||
if (e.endsWith('statement')) return 'An informational statement about developing or ongoing weather.';
|
||
return '';
|
||
}
|
||
// NWS text is often structured "* WHAT...", "* WHERE...", "* WHEN..."; lead with the WHAT part
|
||
function firstSentence(t, max = 220) {
|
||
const what = String(t || '').match(/\*\s*WHAT\.\.\.([\s\S]*?)(?:\n\s*\n|\*\s*[A-Z ]+\.\.\.|$)/);
|
||
const c = (what ? what[1] : String(t || '')).replace(/\s+/g, ' ').trim();
|
||
const m = c.match(/^.*?[.!?](\s|$)/);
|
||
const r = (m ? m[0] : c).trim();
|
||
return r.length > max ? r.slice(0, max - 3) + '…' : r;
|
||
}
|
||
const area = (a) => String(a || '').replace(/;\s*/g, ', ');
|
||
const when = (iso) => (iso ? new Date(iso).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : '');
|
||
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
||
// "ends in 3h 10m" / "ended" for active alerts
|
||
function remaining(iso) {
|
||
if (!iso) return '';
|
||
const ms = Date.parse(iso) - Date.now();
|
||
if (!Number.isFinite(ms)) return '';
|
||
if (ms <= 0) return 'expired';
|
||
const m = Math.round(ms / 60000);
|
||
return m < 60 ? `ends in ${m} min` : m < 1440 ? `ends in ${Math.floor(m / 60)}h ${m % 60}m` : `ends in ${Math.round(m / 144) / 10}d`.replace('.0d', 'd');
|
||
}
|
||
window.AlertInfo = { meaning, firstSentence, area, when, esc, remaining, GLOSSARY };
|
||
})();
|