1(function () { 2 'use strict'; 3 4 var SCHEMA = 1; 5 var CACHE_KEY = 'sfist-weather-v1'; 6 var CACHE_TTL = 15 * 60 * 1000; 7 var MAX_AGE = 3 * 60 * 60 * 1000; 8 var REQUEST_TIMEOUT = 5000; 9 var CONDITIONS = [ 10 'Clear', 'Mostly sunny', 'Partly cloudy', 'Cloudy', 'Morning fog', 11 'Afternoon fog', 'Light rain', 'Showers', 'Storms', 'Windy', 'Forecast' 12 ]; 13 14 function validPayload(payload, now) { 15 if (!payload || payload.schema !== SCHEMA || payload.location !== 'San Francisco' || payload.unit !== 'F') return false; 16 if (!Number.isInteger(payload.temperature) || payload.temperature < -20 || payload.temperature > 130) return false; 17 if (!Number.isInteger(payload.high) || payload.high < -20 || payload.high > 130) return false; 18 if (CONDITIONS.indexOf(payload.condition) === -1) return false; 19 var collected = Date.parse(payload.collectedAt); 20 var expires = Date.parse(payload.expiresAt); 21 return Number.isFinite(collected) && Number.isFinite(expires) && 22 collected <= now + 5 * 60 * 1000 && now - collected <= MAX_AGE && expires > now; 23 } 24 25 function readCache(now) { 26 try { 27 var cached = JSON.parse(localStorage.getItem(CACHE_KEY)); 28 if (!cached || !validPayload(cached.payload, now)) return null; 29 return cached; 30 } catch (error) { 31 return null; 32 } 33 } 34 35 function writeCache(payload, now) { 36 try { 37 localStorage.setItem(CACHE_KEY, JSON.stringify({cachedAt: now, payload: payload})); 38 } catch (error) { 39 // Storage can be unavailable in privacy modes; rendering still works. 40 } 41 } 42 43 function renderWeather(element, payload) { 44 var temperature = element.querySelector('[data-weather-temperature]'); 45 var detail = element.querySelector('[data-weather-detail]'); 46 if (!temperature || !detail) return; 47 temperature.textContent = payload.temperature + '°'; 48 detail.textContent = payload.condition + ' · High ' + payload.high + '°'; 49 element.setAttribute('aria-label', 'San Francisco weather: ' + payload.temperature + '°, ' + payload.condition.toLowerCase() + ', high ' + payload.high + '°'); 50 element.setAttribute('aria-hidden', 'false'); 51 element.dataset.weatherState = 'ready'; 52 element.title = 'National Weather Service forecast, updated ' + new Date(payload.collectedAt).toLocaleTimeString([], {hour: 'numeric', minute: '2-digit'}); 53 } 54 55 async function fetchWeather(endpoint) { 56 var controller = typeof AbortController === 'function' ? new AbortController() : null; 57 var timer = controller ? window.setTimeout(function () { controller.abort(); }, REQUEST_TIMEOUT) : null; 58 try { 59 var response = await fetch(endpoint, { 60 method: 'GET', 61 mode: 'cors', 62 credentials: 'omit', 63 headers: {Accept: 'application/json'}, 64 signal: controller ? controller.signal : undefined 65 }); 66 if (!response.ok) throw new Error('Weather endpoint returned ' + response.status); 67 return response.json(); 68 } finally { 69 if (timer) window.clearTimeout(timer); 70 } 71 } 72 73 async function initializeWeather(element, now) { 74 var cached = readCache(now); 75 if (cached) { 76 renderWeather(element, cached.payload); 77 if (now - Number(cached.cachedAt || 0) < CACHE_TTL) return; 78 } 79 try { 80 var payload = await fetchWeather(element.dataset.weatherEndpoint); 81 var responseTime = Date.now(); 82 if (!validPayload(payload, responseTime)) throw new Error('Weather payload failed validation'); 83 writeCache(payload, responseTime); 84 renderWeather(element, payload); 85 } catch (error) { 86 if (!cached) { 87 element.dataset.weatherState = 'unavailable'; 88 element.setAttribute('aria-hidden', 'true'); 89 } 90 } 91 } 92 93 if (typeof module !== 'undefined' && module.exports) { 94 module.exports = {validPayload: validPayload, renderWeather: renderWeather}; 95 return; 96 } 97 98 var weather = document.querySelector('[data-weather-endpoint]'); 99 if (weather) initializeWeather(weather, Date.now()); 100}());
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.