1/* CIS National Threat Level â homepage badge 2 * Finds .utility-actions on intelamation.com and prepends a small pill showing 3 * the current MS-ISAC alert level (color + label). Click â /threat-level.html. 4 * No deps. Fail-silent: if the API is down, the badge just doesn't appear. 5 * Polls every 6 hours while the page is open (level changes days-to-weeks). 6 */ 7(function () { 8 'use strict'; 9 if (window.__ia_cis_badge_mounted) return; 10 window.__ia_cis_badge_mounted = true; 11 12 var ENDPOINT = '/api/blog/threat-level'; 13 var POLL_MS = 6 * 60 * 60 * 1000; // 6h 14 15 function mount() { 16 var slot = document.querySelector('.utility-actions'); 17 if (!slot) return null; 18 var a = document.createElement('a'); 19 a.id = 'ia-cis-badge'; 20 a.href = '/threat-level.html'; 21 a.setAttribute('title', 'National Threat Level â powered by MS-ISAC / CIS. Click for the explainer.'); 22 a.setAttribute('aria-label', 'National Threat Level'); 23 a.style.cssText = [ 24 'display:inline-flex', 25 'align-items:center', 26 'gap:7px', 27 'padding:4px 11px', 28 'border-radius:999px', 29 'border:1px solid rgba(162,184,220,.18)', 30 'background:rgba(255,255,255,.04)', 31 'font-size:12.5px', 32 'color:#e6edf7', 33 'text-decoration:none', 34 'transition:border-color .15s,background .15s', 35 'white-space:nowrap' 36 ].join(';'); 37 a.innerHTML = 38 '<span class="ia-cis-dot" style="width:9px;height:9px;border-radius:50%;background:#9aa7bd;box-shadow:0 0 0 0 rgba(0,0,0,0);transition:background .2s,box-shadow .2s;flex-shrink:0;"></span>' + 39 '<span class="ia-cis-lbl" style="font-weight:600;letter-spacing:.02em;">Threat Level: <span class="ia-cis-val" style="font-weight:700;">â</span></span>'; 40 a.addEventListener('mouseenter', function () { a.style.borderColor = 'rgba(0,212,255,.45)'; }); 41 a.addEventListener('mouseleave', function () { a.style.borderColor = 'rgba(162,184,220,.18)'; }); 42 // prepend so it sits at the LEFT of the existing utility-action links 43 if (slot.firstChild) slot.insertBefore(a, slot.firstChild); 44 else slot.appendChild(a); 45 return a; 46 } 47 48 function refresh(a) { 49 fetch(ENDPOINT, { credentials: 'omit' }) 50 .then(function (r) { return r.ok ? r.json() : null; }) 51 .then(function (d) { 52 if (!a || !d || !d.ok) return; 53 var dot = a.querySelector('.ia-cis-dot'); 54 var val = a.querySelector('.ia-cis-val'); 55 if (dot) { 56 dot.style.background = d.hex || '#9aa7bd'; 57 dot.style.boxShadow = '0 0 8px ' + (d.hex || '#9aa7bd'); 58 } 59 if (val) val.textContent = d.headline || (d.level ? (d.level.charAt(0).toUpperCase() + d.level.slice(1)) : 'â'); 60 a.setAttribute( 61 'title', 62 (d.headline ? ('National Threat Level: ' + d.headline) : 'National Threat Level') + 63 (d.description ? ('\n\n' + d.description) : '') + 64 '\n\nSource: MS-ISAC / Center for Internet Security. Click for the explainer.' 65 ); 66 }) 67 .catch(function () { /* fail-silent */ }); 68 } 69 70 function start() { 71 var a = mount(); 72 if (!a) return; // no utility-actions on this page 73 refresh(a); 74 setInterval(function () { refresh(a); }, POLL_MS); 75 } 76 77 if (document.readyState === 'loading') { 78 document.addEventListener('DOMContentLoaded', start); 79 } else { 80 start(); 81 } 82})();
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.