1/** 2 * OPS-ADA-BANNER 3 * Config: window.__OPS_ADA_BANNER__ from data/ada-banner-data.js 4 * Dismiss sets a 7-day cookie. Admin clear bumps version. 5 * Test: index.html?ada=show 6 */ 7(function () { 8 var banner = document.getElementById('ops-ada-banner'); 9 if (!banner) return; 10 11 var cfg = window.__OPS_ADA_BANNER__; 12 if (!cfg || typeof cfg !== 'object') return; 13 14 var DISMISS_SECONDS = 7 * 24 * 60 * 60; 15 16 function cookieKey() { 17 return 'opsAdaBannerDismissed_v' + String(cfg.version || 1); 18 } 19 20 function getParam(name) { 21 try { 22 return new URLSearchParams(window.location.search).get(name); 23 } catch (e) { 24 return null; 25 } 26 } 27 28 function cookieString() { 29 try { 30 return String(window.document.cookie || ''); 31 } catch (e) { 32 return ''; 33 } 34 } 35 36 function readCookie(name) { 37 try { 38 var parts = cookieString().split(';'); 39 for (var i = 0; i < parts.length; i++) { 40 var p = parts[i].trim(); 41 if (p.indexOf(name + '=') === 0) { 42 return decodeURIComponent(p.slice(name.length + 1)); 43 } 44 } 45 } catch (e) { /* ignore */ } 46 return null; 47 } 48 49 function writeCookie(name, value, maxAgeSeconds) { 50 try { 51 var secure = location.protocol === 'https:' ? '; Secure' : ''; 52 window.document.cookie = 53 name + 54 '=' + 55 encodeURIComponent(value) + 56 '; path=/' + 57 '; max-age=' + 58 Math.max(0, maxAgeSeconds) + 59 '; SameSite=Lax' + 60 secure; 61 } catch (e) { /* ignore */ } 62 } 63 64 function clearAllDismissCookies() { 65 try { 66 var parts = cookieString().split(';'); 67 for (var i = 0; i < parts.length; i++) { 68 var p = parts[i].trim(); 69 var eq = p.indexOf('='); 70 var name = eq >= 0 ? p.slice(0, eq) : p; 71 if (name.indexOf('opsAdaBannerDismissed_v') === 0) { 72 writeCookie(name, '', 0); 73 } 74 } 75 } catch (e) { /* ignore */ } 76 try { 77 for (var v = 1; v < 50; v++) { 78 localStorage.removeItem('opsAdaBannerDismissed_v' + v); 79 } 80 } catch (e2) { /* ignore */ } 81 } 82 83 function dismissed() { 84 return readCookie(cookieKey()) === '1'; 85 } 86 87 function setDismissed() { 88 writeCookie(cookieKey(), '1', DISMISS_SECONDS); 89 } 90 91 function hide() { 92 banner.setAttribute('hidden', ''); 93 banner.classList.remove('is-visible', 'is-revealed', 'is-expanded'); 94 document.body.classList.remove('has-ada-banner'); 95 var moreBtn = document.getElementById('ops-ada-more'); 96 if (moreBtn) { 97 moreBtn.setAttribute('aria-expanded', 'false'); 98 moreBtn.textContent = 'Read full notice'; 99 } 100 } 101 102 function show() { 103 banner.removeAttribute('hidden'); 104 banner.classList.add('is-visible'); 105 document.body.classList.add('has-ada-banner'); 106 var delay = window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 0 : 2000; 107 setTimeout(function () { 108 if (!banner.classList.contains('is-visible')) return; 109 banner.classList.add('is-revealed'); 110 }, delay); 111 } 112 113 function setText(id, value) { 114 var el = document.getElementById(id); 115 if (el && value != null && value !== '') el.textContent = value; 116 } 117 118 function setHtml(id, value) { 119 var el = document.getElementById(id); 120 if (el && value != null && value !== '') el.innerHTML = value; 121 } 122 123 function applyConfig() { 124 setText('ops-ada-badge', cfg.badge); 125 setText('ops-ada-head-label', cfg.headLabel); 126 setText('ops-ada-deadline-label', cfg.deadlineLabel); 127 setText('ops-ada-issuer', cfg.issuer); 128 setText('ops-ada-title', cfg.title); 129 setText('ops-ada-body', cfg.body); 130 setHtml('ops-ada-fine', cfg.fineHtml); 131 setText('ops-ada-dismiss-label', cfg.dismissLabel); 132 var cta = document.getElementById('ops-ada-cta'); 133 if (cta) { 134 if (cfg.ctaLabel) cta.textContent = cfg.ctaLabel; 135 if (cfg.ctaHref) cta.setAttribute('href', cfg.ctaHref); 136 } 137 } 138 139 function pad(n, len) { 140 var s = String(Math.max(0, n)); 141 while (s.length < (len || 2)) s = '0' + s; 142 return s; 143 } 144 145 function tick() { 146 var el = document.getElementById('ops-ada-countdown'); 147 if (!el) return; 148 var deadline = new Date(cfg.deadline || '2027-04-26T23:59:59-04:00').getTime(); 149 if (isNaN(deadline)) return; 150 var left = Math.max(0, deadline - Date.now()); 151 var days = Math.floor(left / 86400000); 152 left %= 86400000;
153 var hours = Math.floor(left / 3600000); 154 left %= 3600000; 155 var mins = Math.floor(left / 60000); 156 left %= 60000; 157 var secs = Math.floor(left / 1000); 158 el.textContent = 159 pad(days, days >= 100 ? 3 : 2) + 160 'd ' + 161 pad(hours) + 162 ':' + 163 pad(mins) + 164 ':' + 165 pad(secs); 166 } 167 168 function isEnabled() { 169 if (cfg.enabled === false || cfg.enabled === 0 || cfg.enabled === '0' || cfg.enabled === 'false') { 170 return false; 171 } 172 return true; 173 } 174 175 applyConfig(); 176 177 var adaParam = getParam('ada'); 178 var forcePreview = adaParam === 'show' || adaParam === '1'; 179 180 if (forcePreview) clearAllDismissCookies(); 181 if (adaParam === 'hide' || adaParam === '0') { 182 setDismissed(); 183 hide(); 184 return; 185 } 186 if (!isEnabled() && !forcePreview) { 187 hide(); 188 return; 189 } 190 if (dismissed() && !forcePreview) { 191 hide(); 192 return; 193 } 194 195 show(); 196 tick(); 197 setInterval(tick, 1000); 198 199 banner.querySelectorAll('[data-ada-dismiss]').forEach(function (btn) { 200 btn.addEventListener('click', function () { 201 setDismissed(); 202 hide(); 203 }); 204 }); 205 206 var more = document.getElementById('ops-ada-more'); 207 if (more) { 208 more.addEventListener('click', function () { 209 var expanded = banner.classList.toggle('is-expanded'); 210 more.setAttribute('aria-expanded', expanded ? 'true' : 'false'); 211 more.textContent = expanded ? 'Show less' : 'Read full notice'; 212 }); 213 } 214 215 try { 216 fetch('data/ada-banner.json', { cache: 'no-store', credentials: 'same-origin' }) 217 .then(function (res) { 218 return res.ok ? res.json() : null; 219 }) 220 .then(function (data) { 221 if (!data || typeof data !== 'object' || Array.isArray(data)) return; 222 if (data.version != null) cfg.version = data.version; 223 if ('enabled' in data) { 224 cfg.enabled = data.enabled; 225 if (!isEnabled() && !forcePreview) hide(); 226 } 227 }) 228 .catch(function () {}); 229 } catch (e) {} 230})();
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.