1/** 2 * Shared glossary / acronym popover component for the Kuhn Capital 3 * CyberPulse Suite (DB1âDB6). ONE file, referenced by every dashboard via: 4 * 5 * <script src="glossary.js" defer></script> 6 * 7 * What it does, fully automatically, on every page that includes it: 8 * 1. Scans the page's rendered text and wraps the FIRST occurrence of each 9 * known acronym/term in a semantic <abbr class="glossary-term"> element 10 * with a native `title` tooltip (free hover tooltip on desktop). 11 * 2. Adds a small click/tap popover with the full definition; tapping 12 * anywhere else dismisses it. 13 * 3. Injects the small amount of CSS needed for both, and â since every 14 * dashboard shares the same <main> content region â appends the 15 * "© 2026 Kuhn Capital. All Rights Reserved." notice once, at the end 16 * of that region, so every page gets a consistent footer without any 17 * page-specific markup. 18 * 19 * This is intentionally framework-agnostic (no build step, no dependency on 20 * React) so the exact same file works for both the React SPA (DB1) and the 21 * static HTML dashboards (DB2-DB6). It re-scans on DOM mutations so it also 22 * picks up React's client-rendered / live-refreshed content. 23 */ 24(function () { 25 "use strict"; 26 27 if (window.__cyberpulseGlossaryInit) return; 28 window.__cyberpulseGlossaryInit = true; 29 30 var GLOSSARY = { 31 "MFA": "Multi-Factor Authentication â requiring a second verification step beyond just a password.", 32 "FIDO2": "Fast IDentity Online 2 â a phishing-resistant passkey / hardware security-key authentication standard.", 33 "ZTA": "Zero Trust Architecture â a security model that verifies every access request instead of trusting anything inside the network perimeter by default.", 34 "ZTNA": "Zero Trust Network Access â grants per-session, per-application access instead of broad, always-on VPN-style network access.", 35 "MDR": "Managed Detection & Response â an outsourced service providing 24/7 threat monitoring and active incident response.", 36 "IR": "Incident Response â the organized process of detecting, containing, and recovering from a security breach.", 37 "AiTM": "Adversary-in-the-Middle â a phishing technique that intercepts a live login session to steal auth tokens, bypassing standard MFA.", 38 "BEC": "Business Email Compromise â a scam where attackers impersonate executives or vendors via email to trick employees into wiring funds or data.", 39 "CVE": "Common Vulnerabilities and Exposures â a public catalog of known software security flaws, each with a unique ID.", 40 "KEV": "Known Exploited Vulnerability â CISA's list of vulnerabilities confirmed to be actively exploited in the wild.", 41 "CVSS": "Common Vulnerability Scoring System â an industry-standard 0â10 score rating how severe a vulnerability is.", 42 "DLP": "Data Loss Prevention â technology that detects and blocks sensitive data from leaving an organization.", 43 "WAF": "Web Application Firewall â filters and blocks malicious traffic aimed at web applications.", 44 "DDoS": "Distributed Denial-of-Service â flooding a target with traffic from many sources at once to knock it offline.", 45 "SIEM": "Security Information & Event Management â software that aggregates and analyzes security log data across an organization.", 46 "EDR": "Endpoint Detection & Response â monitors and responds to threats on individual devices like laptops and servers.", 47 "XDR": "Extended Detection & Response â correlates threat data across endpoints, network, cloud, and email into one platform.", 48 "TTE": "Time-to-Exploit â how quickly attackers weaponize a vulnerability after it becomes publicly known.", 49 "RTO": "Recovery Time Objective â the target maximum time allowed to restore systems after a disruption.", 50 "TCO": "Total Cost of Ownership â the full cost of a solution, including purchase, deployment, and ongoing maintenance.", 51 "SBOM": "Software Bill of Materials â an inventory of all components and dependencies inside a piece of software.", 52 "OT": "Operational Technology â the hardware and software that monitors or controls physical industrial equipment and processes.", 53 "ICS": "Industrial Control Systems â the OT systems (e.g. SCADA, PLCs) that run factories, utilities, and critical infrastru
53cture.", 54 "MTTR": "Mean Time to Remediate â the average time it takes to fix a security issue once it has been identified.", 55 "APT": "Advanced Persistent Threat â a well-resourced attacker that maintains long-term, stealthy access inside a target network.", 56 "SOC": "Security Operations Center â the team and systems that continuously monitor for and respond to security threats.", 57 "DBIR": "Data Breach Investigations Report â Verizon's widely cited annual report analyzing confirmed data breaches.", 58 "IC3": "Internet Crime Complaint Center â the FBI's unit for tracking reported cybercrime losses.", 59 "LLM": "Large Language Model â the type of AI model (e.g. behind chatbots) that generates and understands human language.", 60 "VPN": "Virtual Private Network â encrypts and routes a connection through a private network; traditionally used for remote access.", 61 "CISA": "Cybersecurity and Infrastructure Security Agency â the U.S. federal agency that coordinates national cyber defense, including the KEV list." 62 }; 63 window.CYBERPULSE_GLOSSARY = GLOSSARY; 64 65 var TERMS = Object.keys(GLOSSARY).sort(function (a, b) { return b.length - a.length; }); 66 var termPattern = {}; 67 TERMS.forEach(function (t) { 68 termPattern[t] = new RegExp("\\b" + t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b"); 69 }); 70 71 // ââ Styles (abbr underline, popover card, footer copyright) âââââââââââ 72 var style = document.createElement("style"); 73 style.setAttribute("data-glossary-styles", ""); 74 style.textContent = 75 "abbr.glossary-term{text-decoration:none;border-bottom:1px dotted rgba(0,212,255,0.65);cursor:help;}" + 76 "abbr.glossary-term:hover,abbr.glossary-term:focus-visible{border-bottom-color:#00d4ff;outline:none;}" + 77 ".glossary-popover{position:fixed;z-index:9999;max-width:280px;background:#0b1524;border:1px solid rgba(0,212,255,0.35);" + 78 "border-radius:8px;padding:10px 26px 10px 12px;box-shadow:0 8px 24px rgba(0,0,0,0.45);" + 79 "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:12.5px;line-height:1.45;color:#e8eef6;" + 80 "display:none;}" + 81 ".glossary-popover.open{display:block;}" + 82 ".glossary-popover-term{display:block;font-weight:800;color:#00d4ff;font-size:11px;letter-spacing:0.04em;" + 83 "text-transform:uppercase;margin-bottom:4px;font-family:'JetBrains Mono',monospace;}" + 84 ".glossary-popover-close{position:absolute;top:2px;right:4px;cursor:pointer;color:rgba(232,238,246,0.5);" + 85 "font-size:15px;line-height:1;background:none;border:none;padding:4px 6px;}" + 86 ".glossary-popover-close:hover{color:#e8eef6;}" + 87 ".site-copyright{text-align:center;font-size:11px;color:rgba(200,215,230,0.55);padding:18px 12px 10px;" + 88 "letter-spacing:0.02em;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;}"; 89 document.head.appendChild(style); 90 91 // ââ Popover âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 92 var popover = document.createElement("div"); 93 popover.className = "glossary-popover"; 94 popover.setAttribute("role", "tooltip"); 95 var popoverTermEl = document.createElement("span"); 96 popoverTermEl.className = "glossary-popover-term"; 97 var popoverTextEl = document.createElement("span"); 98 var closeBtn = document.createElement("button"); 99 closeBtn.className = "glossary-popover-close"; 100 closeBtn.type = "button"; 101 closeBtn.setAttribute("aria-label", "Close"); 102 closeBtn.textContent = "\u00D7"; 103 popover.appendChild(closeBtn); 104 popover.appendChild(popoverTermEl); 105 popover.appendChild(popoverTextEl); 106 document.body.appendChild(popover); 107 108 var activeAbbr = null; 109 110 function closePopover() { 111 popover.classList.remove("open"); 112 if (activeAbbr) activeAbbr.setAttribute("aria-expanded", "false"); 113 activeAbbr = null; 114 } 115 116 function openPopover(abbr) { 117 var term = abbr.getAttribute("data-term"); 118 var def = GLOSSARY[term]; 119 if (!def) return; 120 if (activeAbbr === abbr && popover.classList.contains("open")) { 121 closePopover(); 122 return; 123 } 124 popoverTermEl.textContent = term; 125 popoverTextEl.textContent = def; 126 popover.classList.add("open"); 127 activeAbbr = abbr; 128 abbr.setAttribute("aria-expanded", "true"); 129 130 var rect = abbr.getBoundingClientRect(); 131 var popRect = popover.getBoundingClientRect(); 132 var vw = window.innerWidth; 133 var vh = window.innerHeight; 134 var left = rect.left; 135 if (left + popRect.width > vw - 8) left = vw - popRect.width - 8; 136 if (left < 8) left = 8; 137 var top = rect.bottom + 6; 138 if (top + popRect.height > vh - 8) top = rect.top - popRect.height - 6;
139 if (top < 8) top = 8; 140 popover.style.left = left + "px"; 141 popover.style.top = top + "px"; 142 } 143 144 document.addEventListener("click", function (e) { 145 var abbr = e.target && e.target.closest && e.target.closest(".glossary-term"); 146 if (abbr) { 147 // Terms sometimes sit inside a source-citation <a> (e.g. a "Verizon 148 // DBIR" link) â prevent that ancestor link's default navigation so 149 // tapping the term opens the popover instead of leaving the page. 150 e.preventDefault(); 151 e.stopPropagation(); 152 openPopover(abbr); 153 return; 154 } 155 if (e.target === closeBtn || popover.contains(e.target)) return; 156 closePopover(); 157 }); 158 159 document.addEventListener("keydown", function (e) { 160 if (e.key === "Escape") { 161 closePopover(); 162 return; 163 } 164 if (e.key === "Enter" || e.key === " ") { 165 var el = document.activeElement; 166 if (el && el.classList && el.classList.contains("glossary-term")) { 167 e.preventDefault(); 168 openPopover(el); 169 } 170 } 171 }); 172 173 window.addEventListener("scroll", closePopover, true); 174 window.addEventListener("resize", closePopover); 175 176 // ââ Auto-wrap first use of each term, per page âââââââââââââââââââââ 177 function wrapNode(textNode, term, matchIndex, matchedText) { 178 var parent = textNode.parentNode; 179 if (!parent) return; 180 var value = textNode.nodeValue; 181 var before = value.slice(0, matchIndex); 182 var after = value.slice(matchIndex + matchedText.length); 183 184 var abbr = document.createElement("abbr"); 185 abbr.className = "glossary-term"; 186 abbr.setAttribute("data-term", term); 187 abbr.setAttribute("tabindex", "0"); 188 abbr.setAttribute("aria-expanded", "false"); 189 abbr.title = GLOSSARY[term]; 190 abbr.textContent = matchedText; 191 192 var beforeNode = document.createTextNode(before); 193 var afterNode = document.createTextNode(after); 194 parent.insertBefore(beforeNode, textNode); 195 parent.insertBefore(abbr, textNode); 196 parent.insertBefore(afterNode, textNode); 197 parent.removeChild(textNode); 198 } 199 200 var SKIP_TAGS = { SCRIPT: 1, STYLE: 1, NOSCRIPT: 1, TEXTAREA: 1, ABBR: 1, INPUT: 1 }; 201 202 function findAndWrap(term) { 203 var re = termPattern[term]; 204 var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { 205 acceptNode: function (node) { 206 var p = node.parentElement; 207 if (!p || SKIP_TAGS[p.tagName]) return NodeFilter.FILTER_REJECT; 208 if (p.closest && p.closest(".glossary-popover")) return NodeFilter.FILTER_REJECT; 209 if (!node.nodeValue || !re.test(node.nodeValue)) return NodeFilter.FILTER_SKIP; 210 return NodeFilter.FILTER_ACCEPT; 211 } 212 }); 213 var node = walker.nextNode(); 214 if (!node) return false; 215 var match = re.exec(node.nodeValue); 216 if (!match) return false; 217 wrapNode(node, term, match.index, match[0]); 218 return true; 219 } 220 221 function isTermWrapped(term) { 222 return !!document.querySelector('.glossary-term[data-term="' + term + '"]'); 223 } 224 225 // Re-checks actual DOM presence (rather than a one-time flag) so a term 226 // stays wrapped even if the framework rendering the page (e.g. React) 227 // replaces the subtree that originally held it â such as a live-data 228 // refresh swapping out placeholder ticker text for real content. 229 function autoWrapAll() { 230 TERMS.forEach(function (term) { 231 if (isTermWrapped(term)) return; 232 findAndWrap(term); 233 }); 234 } 235 236 // ââ Footer copyright â appended once to the page's <main> region ââ 237 function ensureCopyright() { 238 if (document.querySelector(".site-copyright")) return; 239 var main = document.querySelector("main"); 240 if (!main) return; 241 var el = document.createElement("div"); 242 el.className = "site-copyright"; 243 el.textContent = "\u00A9 2026 Kuhn Capital. All Rights Reserved."; 244 main.appendChild(el); 245 } 246 247 var observer = null; 248 var pending = false; 249 250 function runPass() { 251 if (observer) observer.disconnect(); 252 autoWrapAll(); 253 ensureCopyright(); 254 if (observer) observer.observe(document.body, { childList: true, subtree: true, characterData: true }); 255 pending = false;
256 } 257 258 function schedulePass() { 259 if (pending) return; 260 pending = true; 261 setTimeout(runPass, 150); 262 } 263 264 observer = new MutationObserver(schedulePass); 265 266 function init() { 267 runPass(); 268 } 269 270 if (document.readyState === "loading") { 271 document.addEventListener("DOMContentLoaded", init); 272 } else { 273 init(); 274 } 275})();
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.