1(function () { 2 'use strict'; 3 4 /* ========================================================================= 5 Mobile menu toggle 6 ========================================================================= */ 7 var hb = document.querySelector('.ij-topnav__hamburger'); 8 var mobile = document.getElementById('ij-mobile'); 9 if (hb && mobile) { 10 hb.addEventListener('click', function () { 11 var open = mobile.hasAttribute('hidden'); 12 if (open) { 13 mobile.removeAttribute('hidden'); 14 hb.setAttribute('aria-expanded', 'true'); 15 } else { 16 mobile.setAttribute('hidden', ''); 17 hb.setAttribute('aria-expanded', 'false'); 18 } 19 }); 20 } 21 22 /* ========================================================================= 23 Page-bottom reading progress (Carbon-native variant) 24 ========================================================================= */ 25 var progressFill = document.querySelector('.ij-progress__fill'); 26 if (progressFill) { 27 var ticking = false; 28 function update() { 29 var doc = document.documentElement; 30 var max = doc.scrollHeight - window.innerHeight; 31 if (max <= 0) { progressFill.style.width = '0%'; ticking = false; return; } 32 var p = Math.max(0, Math.min(1, window.scrollY / max)); 33 progressFill.style.width = (p * 100).toFixed(1) + '%'; 34 ticking = false; 35 } 36 window.addEventListener('scroll', function () { 37 if (!ticking) { requestAnimationFrame(update); ticking = true; } 38 }, { passive: true }); 39 update(); 40 } 41 42 /* ========================================================================= 43 TOC scroll-spy 44 ========================================================================= */ 45 var tocLinks = Array.prototype.slice.call(document.querySelectorAll('.ij-toc__item a')); 46 if (tocLinks.length) { 47 var sections = tocLinks 48 .map(function (a) { return document.getElementById(a.dataset.toc); }) 49 .filter(Boolean); 50 function setActive() { 51 var pos = window.scrollY + 120; 52 var idx = -1; 53 for (var i = 0; i < sections.length; i++) { 54 if (sections[i].offsetTop <= pos) idx = i; 55 } 56 tocLinks.forEach(function (a, i) { 57 a.parentElement.classList.toggle('is-active', i === idx); 58 }); 59 } 60 window.addEventListener('scroll', setActive, { passive: true }); 61 setActive(); 62 } 63 64 /* ========================================================================= 65 Theme toggle (Carbon Gray-100 dark mode) 66 ========================================================================= */ 67 var themeBtn = document.querySelector('.ij-theme-toggle'); 68 if (themeBtn) { 69 themeBtn.addEventListener('click', function () { 70 var current = document.documentElement.getAttribute('data-theme') || 'light'; 71 var next = current === 'dark' ? 'light' : 'dark'; 72 document.documentElement.setAttribute('data-theme', next); 73 try { localStorage.setItem('ij-theme', next); } catch (e) {} 74 themeBtn.setAttribute('aria-pressed', next === 'dark' ? 'true' : 'false'); 75 }); 76 var t = document.documentElement.getAttribute('data-theme'); 77 themeBtn.setAttribute('aria-pressed', t === 'dark' ? 'true' : 'false'); 78 } 79 80 /* ========================================================================= 81 Glossary auto-wrap (IT acronyms â <abbr> with hover tooltip) 82 ========================================================================= */ 83 var GLOSSARY = { 84 // Healthcare IT 85 'EHR': 'Electronic Health Record â the digital version of a patient\'s paper chart.', 86 'EMR': 'Electronic Medical Record â single-practice digital chart.', 87 'HIPAA': 'Health Insurance Portability and Accountability Act â US healthcare data-privacy law (1996).', 88 'HITECH': 'Health Information Technology for Economic and Clinical Health Act â US 2009.', 89 'PHI': 'Protected Health Information â patient data covered by HIPAA.', 90 'FHIR': 'Fast Healthcare Interoperability Resources â HL7 healthcare-data exchange standard.', 91 'HL7': 'Health Level Seven â international healthcare interoperability standards body.', 92 'NHS': 'National Health Service â UK publicly-funded healthcare system.', 93 'ICB': 'Integrated Care Board â NHS regional commissioning body (England).', 94 'RPM': 'Remote Patient Monitoring.', 95 'IoMT': 'Internet of Medical Things â networked medical devices.', 96 'mHealth':'Mobile health â health services delivered via mobile devices.', 97 'CDSS': 'Clinical Decision Support System.', 98 'PACS': 'Picture Archiving and Communication System â medical imaging.', 99 'HIE': 'Health Information Exchange.', 100 // Finance IT 101 'PSD2': 'Payment Services Directive 2 â EU open-banking regulation (2018).', 102 'PSD3': 'Payment Services Directive 3 â EU successor to PSD2 (in legislative process).', 103 'SEPA': 'Single Euro Payments Area â EU/EEA euro payment standard.', 104 'KYC': 'Know Your Customer â identity-verification compliance.', 105 'AML': 'Anti-Money Laundering â financial crime compliance.', 106 'CDD': 'Customer Due Diligence â KYC sub-process.', 107 'DORA': 'Digital Operational Resilience Act â EU financial-sector IT-resil
107ience regulation (2025).', 108 'SWIFT': 'Society for Worldwide Interbank Financial Telecommunication â interbank messaging network.', 109 'ISO20022':'ISO 20022 â modern financial-messaging standard, replacing legacy SWIFT MT.', 110 'FIX': 'Financial Information eXchange â electronic trading protocol.', 111 'BNPL': 'Buy Now Pay Later â deferred-payment lending product.', 112 'DeFi': 'Decentralised Finance â blockchain-based financial services.', 113 'CeFi': 'Centralised Finance â traditional intermediated financial services.', 114 'CBDC': 'Central Bank Digital Currency.', 115 'FX': 'Foreign Exchange.', 116 // Cross-sector regulatory 117 'GDPR': 'General Data Protection Regulation â EU data-protection law (2018).', 118 'NIS2': 'Network and Information Security Directive 2 â EU cybersecurity (2024).', 119 'CCPA': 'California Consumer Privacy Act.', 120 'ICO': 'Information Commissioner\'s Office â UK data-protection regulator.', 121 'CISO': 'Chief Information Security Officer.', 122 'CIO': 'Chief Information Officer.', 123 'CTO': 'Chief Technology Officer.', 124 'CDO': 'Chief Data Officer.', 125 'CPO': 'Chief Privacy Officer.', 126 'COO': 'Chief Operating Officer.', 127 'SaaS': 'Software as a Service.', 128 'PaaS': 'Platform as a Service.', 129 'IaaS': 'Infrastructure as a Service.', 130 'BaaS': 'Banking as a Service.', 131 // Architecture / infra 132 'API': 'Application Programming Interface.', 133 'SDK': 'Software Development Kit.', 134 'REST': 'Representational State Transfer â web API architectural style.', 135 'gRPC': 'gRPC Remote Procedure Calls â modern RPC framework (Google).',
136 'JWT': 'JSON Web Token â compact signed token for auth.', 137 'MFA': 'Multi-Factor Authentication.', 138 '2FA': 'Two-Factor Authentication.', 139 'SSO': 'Single Sign-On.', 140 'IAM': 'Identity and Access Management.', 141 'RBAC': 'Role-Based Access Control.', 142 'ABAC': 'Attribute-Based Access Control.', 143 'TLS': 'Transport Layer Security â encryption protocol for HTTPS and other traffic.', 144 'mTLS': 'Mutual TLS â both client and server authenticate.', 145 'VPN': 'Virtual Private Network.', 146 'SIEM': 'Security Information and Event Management.', 147 'SOC': 'Security Operations Center.', 148 'EDR': 'Endpoint Detection and Response.', 149 'XDR': 'Extended Detection and Response.', 150 'ZTA': 'Zero Trust Architecture.', 151 'SBOM': 'Software Bill of Materials.', 152 'CVE': 'Common Vulnerabilities and Exposures â public vulnerability catalogue.', 153 'RTO': 'Recovery Time Objective â disaster-recovery target.', 154 'RPO': 'Recovery Point Objective â data-loss tolerance target.', 155 // Data / AI 156 'ML': 'Machine Learning.', 157 'LLM': 'Large Language Model.', 158 'RAG': 'Retrieval-Augmented Generation.', 159 'ETL': 'Extract, Transform, Load â data-pipeline pattern.', 160 'ELT': 'Extract, Load, Transform â modern data-pipeline pattern.', 161 'CDC': 'Change Data Capture â incremental data-sync pattern.', 162 'ML Ops': 'Machine Learning Operations â productionising ML systems.' 163 }; 164 165 // Build a sorted-by-length regex so longer acronyms match before shorter substrings 166 var keys = Object.keys(GLOSSARY).sort(function (a, b) { return b.length - a.length; }); 167 var glossRegex = new RegExp('\\b(' + keys.map(function (k) { return k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }).join('|') + ')\\b', 'g'); 168 169 function wrapGlossary(root) { 170 var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { 171 acceptNode: function (node) { 172 // Skip text inside abbr, code, pre, script, style, a, headings, and existing glossary spans 173 if (!node.parentElement) return NodeFilter.FILTER_REJECT; 174 var p = node.parentElement; 175 var skip = ['ABBR','CODE','PRE','SCRIPT','STYLE','A','H1','H2','H3','H4','H5','H6','BUTTON','INPUT','TEXTAREA','SELECT','LABEL']; 176 if (skip.indexOf(p.tagName) !== -1) return NodeFilter.FILTER_REJECT; 177 if (p.closest && (p.closest('.ij-toc') || p.closest('.ij-search') || p.closest('.ij-takeaways__head') || p.closest('.ij-callout__head'))) return NodeFilter.FILTER_REJECT; 178 return glossRegex.test(node.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 179 } 180 }); 181 var nodes = []; 182 var n; 183 while ((n = walker.nextNode())) nodes.push(n); 184 nodes.forEach(function (node) { 185 glossRegex.lastIndex = 0; 186 var html = node.nodeValue.replace(glossRegex, function (match) { 187 var def = GLOSSARY[match]; 188 if (!def) return match; 189 return '<abbr class="ij-gloss" data-def="' + def.replace(/"/g, '"') + '" title="' + def.replace(/"/g, '"') + '" tabindex="0" role="term">' + match + '</abbr>'; 190 }); 191 if (html !== node.nodeValue) { 192 var span = document.createElement('span'); 193 span.innerHTML = html; 194 node.parentNode.replaceChild(span, node); 195 // Unwrap the placeholder span (preserve original structure) 196 while (span.firstChild) span.parentNode.insertBefore(span.firstChild, span); 197 span.parentNode.removeChild(span); 198 } 199 }); 200 } 201 202 var articleBody = document.querySelector('.ij-article__body'); 203 if (articleBody) wrapGlossary(articleBody); 204 205 /* ========================================================================= 206 Search overlay (hybrid keyword + vertical filter) 207 ========================================================================= */ 208 var searchEl = document.getElementById('ij-search'); 209 var searchBtn = document.querySelector('.ij-search-toggle'); 210 if (searchEl && searchBtn) { 211 var input = searchEl.querySelector('.ij-search__input'); 212 var closeBt = searchEl.querySelector('.ij-search__close'); 213 var resBox = searchEl.querySelector('.ij-search__results'); 214 var filters = Array.prototype.slice.call(searchEl.querySelectorAll('.ij-search__filter')); 215 var indexJSON = document.getElementById('ij-search-index'); 216 var index = []; 217 try { index = JSON.parse(indexJSON ? indexJSON.textContent : '[]'); } catch (e) {} 218 var activeFilter = 'all'; 219 var activeIdx = -1; 220 221 function openSearch() { 222 searchEl.removeAttribute('hidden'); 223 searchBtn.setAttribute('aria-expanded', 'true'); 224 document.body.style.overflow = 'hidden'; 225 setTimeout(function () { input.focus(); }, 50); 226 render(); 227 } 228 function closeSearch() { 229 searchEl.setAttribute('hidden', ''); 230 searchBtn.setAttribute('aria-expanded', 'false'); 231 document.body.style.overflow = ''; 232 input.value = ''; 233 activeIdx = -1; 234 } 235 function render() { 236 var q = input.value.trim().toLowerCase(); 237 var matches = index.filter(function (p) {
238 if (activeFilter !== 'all' && p.vert !== activeFilter) return false; 239 if (!q) return true; 240 var hay = (p.title + ' ' + (p.sum || '') + ' ' + (p.vert || '')).toLowerCase(); 241 return q.split(/\s+/).every(function (term) { return hay.indexOf(term) !== -1; }); 242 }).slice(0, 30); 243 activeIdx = -1; 244 if (!matches.length) { 245 resBox.innerHTML = '<p class="ij-search__empty">No briefings match. Try a broader term or switch the vertical filter.</p>'; 246 return; 247 } 248 resBox.innerHTML = matches.map(function (p, i) { 249 return '<a class="ij-search__result" href="' + p.url + '" data-i="' + i + '">' + 250 '<span class="ij-search__result-vert">' + (p.vert || '') + '</span>' + 251 '<span class="ij-search__result-title">' + p.title + '</span>' + 252 '<span class="ij-search__result-sum">' + (p.sum || '').slice(0, 140) + '</span>' + 253 '</a>'; 254 }).join(''); 255 } 256 function highlight() { 257 var items = resBox.querySelectorAll('.ij-search__result'); 258 items.forEach(function (it, i) { it.classList.toggle('is-active', i === activeIdx); }); 259 if (activeIdx >= 0 && items[activeIdx]) items[activeIdx].scrollIntoView({ block: 'nearest' }); 260 } 261 262 searchBtn.addEventListener('click', openSearch); 263 closeBt.addEventListener('click', closeSearch); 264 searchEl.addEventListener('click', function (e) { if (e.target === searchEl) closeSearch(); }); 265 input.addEventListener('input', render); 266 filters.forEach(function (f) { 267 f.addEventListener('click', function () { 268 activeFilter = f.dataset.filter; 269 filters.forEach(function (x) { 270 x.classList.toggle('is-active', x === f); 271 x.setAttribute('aria-selected', x === f ? 'true' : 'false'); 272 }); 273 render(); 274 }); 275 }); 276 document.addEventListener('keydown', function (e) { 277 // Global open on / 278 if ((e.key === '/' || (e.key === 'k' && (e.metaKey || e.ctrlKey))) && searchEl.hasAttribute('hidden')) { 279 e.preventDefault(); 280 openSearch(); 281 return; 282 } 283 if (searchEl.hasAttribute('hidden')) return; 284 if (e.key === 'Escape') { closeSearch(); return; } 285 var items = resBox.querySelectorAll('.ij-search__result'); 286 if (!items.length) return; 287 if (e.key === 'ArrowDown') { e.preventDefault(); activeIdx = (activeIdx + 1) % items.length; highlight(); } 288 if (e.key === 'ArrowUp') { e.preventDefault(); activeIdx = (activeIdx - 1 + items.length) % items.length; highlight(); } 289 if (e.key === 'Enter' && activeIdx >= 0) { e.preventDefault(); window.location = items[activeIdx].href; } 290 }); 291 } 292 293 /* ========================================================================= 294 Section-anchor hover-to-copy (H2/H3 with id) 295 ========================================================================= */ 296 if (articleBody) { 297 articleBody.addEventListener('click', function (e) { 298 var h = e.target.closest('h2[id], h3[id]'); 299 if (!h) return; 300 // only when clicking the ::before "#" 301 var rect = h.getBoundingClientRect(); 302 if (e.clientX < rect.left) { 303 var url = window.location.origin + window.location.pathname + '#' + h.id; 304 if (navigator.clipboard) navigator.clipboard.writeText(url); 305 history.replaceState(null, '', '#' + h.id); 306 } 307 }); 308 } 309})();
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.