1(function(global) { 2 'use strict'; 3 4 var DEFAULT = { 5 date: '2026-09-24', 6 pollsOpen: '08:00', 7 pollsClose: '20:00', 8 electionNightLive: '19:00', 9 electionNightWatchAgain: '02:00', 10 timezone: 'Europe/London' 11 }; 12 13 var cached = null; 14 15 function parseHm(time) { 16 var parts = String(time || '').trim().split(':'); 17 return { 18 hour: parseInt(parts[0], 10) || 0, 19 minute: parseInt(parts[1], 10) || 0 20 }; 21 } 22 23 function parseDateParts(dateStr) { 24 var parts = String(dateStr || '').split('-'); 25 return { 26 year: parseInt(parts[0], 10), 27 month: parseInt(parts[1], 10), 28 day: parseInt(parts[2], 10) 29 }; 30 } 31 32 function readFromDocument() { 33 var el = global.document && global.document.documentElement; 34 if (!el) return null; 35 36 var date = el.getAttribute('data-election-date'); 37 if (!date) return null; 38 39 return { 40 date: date, 41 pollsOpen: el.getAttribute('data-polls-open') || DEFAULT.pollsOpen, 42 pollsClose: el.getAttribute('data-polls-close') || DEFAULT.pollsClose, 43 electionNightLive: el.getAttribute('data-election-night-live') || DEFAULT.electionNightLive, 44 electionNightWatchAgain: el.getAttribute('data-election-night-watch-again') || DEFAULT.electionNightWatchAgain, 45 timezone: el.getAttribute('data-election-timezone') || DEFAULT.timezone 46 }; 47 } 48 49 function getConfig() { 50 if (!cached) { 51 cached = readFromDocument() || DEFAULT; 52 } 53 return cached; 54 } 55 56 function zonedLocalToUtcMs(year, month, day, hour, minute, timeZone) { 57 var utc = Date.UTC(year, month - 1, day, hour, minute, 0); 58 var dtf = new Intl.DateTimeFormat('en-US', { 59 timeZone: timeZone, 60 year: 'numeric', 61 month: '2-digit', 62 day: '2-digit', 63 hour: '2-digit', 64 minute: '2-digit', 65 second: '2-digit', 66 hourCycle: 'h23' 67 }); 68 69 function asUtcParts(ms) { 70 var map = {}; 71 dtf.formatToParts(new Date(ms)).forEach(function(part) { 72 if (part.type !== 'literal') map[part.type] = parseInt(part.value, 10); 73 }); 74 return Date.UTC(map.year, map.month - 1, map.day, map.hour, map.minute, map.second || 0); 75 } 76 77 return utc - (asUtcParts(utc) - utc); 78 } 79 80 function atScheduleTime(timeKey) { 81 var cfg = getConfig(); 82 var dateParts = parseDateParts(cfg.date); 83 var timeParts = parseHm(cfg[timeKey]); 84 return zonedLocalToUtcMs( 85 dateParts.year, 86 dateParts.month, 87 dateParts.day, 88 timeParts.hour, 89 timeParts.minute, 90 cfg.timezone 91 ); 92 } 93 94 function getPollsOpenMs() { 95 return atScheduleTime('pollsOpen'); 96 } 97 98 function getPollsCloseMs() { 99 return atScheduleTime('pollsClose'); 100 } 101 102 function nextCalendarDay(dateParts) { 103 var utc = Date.UTC(dateParts.year, dateParts.month - 1, dateParts.day + 1); 104 var next = new Date(utc); 105 return { 106 year: next.getUTCFullYear(), 107 month: next.getUTCMonth() + 1, 108 day: next.getUTCDate() 109 }; 110 } 111 112 function getElectionNightLiveMs() { 113 return atScheduleTime('electionNightLive'); 114 } 115 116 function getElectionNightWatchAgainMs() { 117 var cfg = getConfig(); 118 var dateParts = nextCalendarDay(parseDateParts(cfg.date)); 119 var timeParts = parseHm(cfg.electionNightWatchAgain || DEFAULT.electionNightWatchAgain); 120 return zonedLocalToUtcMs( 121 dateParts.year, 122 dateParts.month, 123 dateParts.day, 124 timeParts.hour, 125 timeParts.minute, 126 cfg.timezone 127 ); 128 } 129 130 function isElectionDayPromoWindow(now) { 131 return (now || Date.now()) >= getPollsOpenMs(); 132 } 133 134 function isPollsOpen(now) { 135 now = now || Date.now(); 136 return now >= getPollsOpenMs() && now < getPollsCloseMs(); 137 } 138 139 function isElectionNightLiveWindow(now) { 140 now = now || Date.now(); 141 return now >= getElectionNightLiveMs() && now < getElectionNightWatchAgainMs(); 142 } 143 144 function isElectionNightWatchAgainWindow(now) { 145 return (now || Date.now()) >= getElectionNightWatchAgainMs(); 146 } 147 148 function isResultsWindow(now) { 149 return (now || Date.now()) >= getPollsCloseMs(); 150 } 151 152 function applySettings(settings) {
153 if (!settings || !settings.electionSchedule) return; 154 var s = settings.electionSchedule; 155 if (!s.date) return; 156 cached = { 157 date: s.date, 158 pollsOpen: s.pollsOpen || DEFAULT.pollsOpen, 159 pollsClose: s.pollsClose || DEFAULT.pollsClose, 160 electionNightLive: s.electionNightLive || DEFAULT.electionNightLive, 161 electionNightWatchAgain: s.electionNightWatchAgain || DEFAULT.electionNightWatchAgain, 162 timezone: s.timezone || DEFAULT.timezone 163 }; 164 } 165 166 global.Vote2026Schedule = { 167 getConfig: getConfig, 168 applySettings: applySettings, 169 clearCache: function() { cached = null; }, 170 zonedLocalToUtcMs: zonedLocalToUtcMs, 171 getPollsOpenMs: getPollsOpenMs, 172 getPollsCloseMs: getPollsCloseMs, 173 getElectionNightLiveMs: getElectionNightLiveMs, 174 getElectionNightWatchAgainMs: getElectionNightWatchAgainMs, 175 isElectionDayPromoWindow: isElectionDayPromoWindow, 176 isPollsOpen: isPollsOpen, 177 isElectionNightLiveWindow: isElectionNightLiveWindow, 178 isElectionNightWatchAgainWindow: isElectionNightWatchAgainWindow, 179 isResultsWindow: isResultsWindow 180 }; 181})(window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.