PageSourceSearch

https://kuhn-cyberpulse.fly.dev/glossary.js

js kuhn-cyberpulse.fly.dev collected 2026-10-03 08:26:23 UTC 13,622 bytes, 275 lines download raw bytes

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.