1(()=>{'use strict';const $=id=>document.getElementById(id);if(!document.querySelector('.is-shell-shell-header'))return; 2 // Native disclosures retain keyboard and no-JavaScript behavior. 3 const menu = $('site-menu-toggle'); 4 const nav = $('site-navigation'); 5 const header = document.querySelector('.is-shell-shell-header'); 6 const shade = $('nav-shade'); 7 const disclosures = [...nav.querySelectorAll('.is-shell-nav-disclosure')]; 8 const desktopMenu = matchMedia('(min-width: 1101px)'); 9 const finePointer = matchMedia('(hover: hover) and (pointer: fine)'); 10 let hoverTimer; 11 let leaveTimer; 12 let savedOverflow; 13 let suppressHover = false; 14 function updateHeaderSpace() { 15 const rect = header.getBoundingClientRect(); 16 document.documentElement.style.setProperty('--header-height', Math.ceil(rect.height) + 'px'); 17 document.documentElement.style.setProperty('--header-bottom', Math.max(0, Math.ceil(rect.bottom)) + 'px'); 18 } 19 function updateNavShade() { 20 shade.hidden = desktopMenu.matches ? !disclosures.some(item => item.open) : !nav.classList.contains('is-shell-is-open'); 21 } 22 function closeDisclosures(except = null) { 23 clearTimeout(hoverTimer); 24 clearTimeout(leaveTimer); 25 disclosures.forEach(item => { if (item !== except) item.open = false; }); 26 updateNavShade(); 27 } 28 function closeMenu(restoreFocus = false) { 29 closeDisclosures(); 30 nav.classList.remove('is-shell-is-open'); 31 menu.setAttribute('aria-expanded', 'false'); 32 menu.querySelector('span').textContent = 'Menu'; 33 menu.querySelector('svg').innerHTML = '<path d="M4 8h16M4 16h16"/>'; 34 if (savedOverflow !== undefined) { document.body.style.overflow = savedOverflow; savedOverflow = undefined; } 35 updateNavShade(); 36 if (restoreFocus) menu.focus({ preventScroll: true }); 37 } 38 menu.hidden = false; 39 document.documentElement.classList.add('is-shell-nav-ready'); 40 menu.addEventListener('click', () => { 41 if (nav.classList.contains('is-shell-is-open')) { closeMenu(true); return; } 42 updateHeaderSpace(); 43 nav.classList.add('is-shell-is-open'); 44 menu.setAttribute('aria-expanded', 'true'); 45 menu.querySelector('span').textContent = 'Close menu'; 46 menu.querySelector('svg').innerHTML = '<path d="m6 6 12 12M18 6 6 18"/>'; 47 savedOverflow = document.body.style.overflow; 48 document.body.style.overflow = 'hidden'; 49 nav.scrollTop = 0; 50 updateNavShade(); 51 }); 52 disclosures.forEach(item => { 53 const trigger = item.querySelector('summary'); 54 const panel = item.querySelector('.is-shell-mega-panel'); 55 trigger.setAttribute('aria-controls', panel.id); 56 trigger.setAttribute('aria-expanded', String(item.open)); 57 item.addEventListener('toggle', () => { 58 if (item.open) closeDisclosures(item); 59 trigger.setAttribute('aria-expanded', String(item.open)); 60 updateNavShade(); 61 }); 62 trigger.addEventListener('click', () => { suppressHover = true; clearTimeout(hoverTimer); clearTimeout(leaveTimer); }); 63 trigger.addEventListener('keydown', event => { 64 if (event.key !== 'ArrowDown') return; 65 event.preventDefault(); 66 closeDisclosures(item); 67 item.open = true; 68 panel.querySelector('a')?.focus(); 69 }); 70 item.addEventListener('pointerenter', () => { 71 clearTimeout(leaveTimer); 72 if (!desktopMenu.matches || !finePointer.matches || suppressHover) return; 73 // Do not hide a panel containing the keyboard user's current focus. 74 if (disclosures.some(other => other !== item && other.open && other.contains(document.activeElement))) return; 75 hoverTimer = setTimeout(() => { closeDisclosures(item); item.open = true; }, 140); 76 }); 77 item.addEventListener('pointerleave', () => { 78 clearTimeout(hoverTimer); 79 suppressHover = false; 80 if (!desktopMenu.matches || !finePointer.matches) return; 81 leaveTimer = setTimeout(() => { if (!item.contains(document.activeElement)) { item.open = false; updateNavShade(); } }, 220); 82 }); 83 }); 84 nav.querySelectorAll('a').forEach(link => link.addEventListener('click', () => closeMenu())); 85 nav.addEventListener('focusin', event => { 86 const current = event.target.closest('.is-shell-nav-disclosure'); 87 if (desktopMenu.matches && current) closeDisclosures(current); 88 if (!desktopMenu.matches && nav.classList.contains('is-shell-is-open')) { 89 const target = event.target.getBoundingClientRect(); 90 const container = nav.getBoundingClientRect(); 91 if (target.top < container.top + 10) nav.scrollTop += target.top - container.top - 10; 92 else if (target.bottom > container.bottom - 10) nav.scrollTop += target.bottom - container.bottom + 10; 93 } 94 }); 95 document.addEventListener('keydown', event => { 96 if (event.key !== 'Escape') return; 97 const active = disclosures.find(item => item.open); 98 if (active) { 99 event.preventDefault(); 100 suppressHover = true; 101 closeDisclosures(); 102 active.querySelector('summary').focus({ preventScroll: true }); 103 } else if (nav.classList.contains('is-shell-is-open')) { event.preventDefault(); closeMenu(true); } 104 }); 105 document.addEventListener('pointerdown', event => { 106 if (!nav.contains(event.target) && !menu.contains(event.target)) { 107 // Return focus if an outside click closes a panel that held focus. 108 const active = disclosures.find(item => item.open && item.contains(document.activeElement)); 109 const restore = !desktopMenu.matches && nav.contains(document.activeElement); 110 closeMenu(restore); 111 if (desktopMenu.matches && active) active.querySelector('summary').focus({ preventScroll: true }); 112 } 113 }); 114 document.addEventListener('focusin', event => { 115 if (!nav.contains(event.target) && !menu.contains(event.target)) closeMenu(); 116 }); 117 desktopMenu.addEventListener('change', event => { 118 const focused = document.activeElement; 119 const restoreMobile = !event.matches && nav.contains(focused); 120 const restoreDesktop = event.matches && (focused === menu || nav.contains(focused)); 121 closeMenu(restoreMobile); 122 if (restoreDesktop) disclosures[0].querySelector('summary').focus({ preventScroll: true }); 123 updateHeaderSpace(); 124 }); 125 window.addEventListener('resize', updateHeaderSpace); 126 window.addEventListener('scroll', updateHeaderSpace, { passive: true });
127 if (window.visualViewport) window.visualViewport.addEventListener('resize', updateHeaderSpace); 128 if ('ResizeObserver' in window) new ResizeObserver(updateHeaderSpace).observe(header); 129 updateHeaderSpace(); 130 131 document.getElementById('shell-consent-control')?.addEventListener('click',()=>{if(typeof window.cmplz_show_cookiebanner==='function')window.cmplz_show_cookiebanner();else location.href='/your-privacy-choices/';}); 132 const path=location.pathname.replace(/\/$/,'');document.querySelectorAll('.is-shell-shell-header a,.is-shell-shell-footer a').forEach(a=>{if(new URL(a.href,location.href).pathname.replace(/\/$/,'')===path&&a.origin===location.origin)a.setAttribute('aria-current','page');}); 133})(); 134/* Shared image viewer for [data-cl-zoom] buttons. These replaced links that pointed straight at image files. */ 135(()=>{'use strict';const triggers=[...document.querySelectorAll('[data-cl-zoom]')];if(!triggers.length||typeof HTMLDialogElement==='undefined')return; 136 const dialog=document.createElement('dialog');dialog.className='cl-zoom';dialog.setAttribute('aria-label','Image viewer'); 137 dialog.innerHTML='<button type="button">Close image</button><img alt=""><p></p>';document.body.append(dialog); 138 const full=dialog.querySelector('img'),caption=dialog.querySelector('p'),close=dialog.querySelector('button');let opener,overflow; 139 triggers.forEach(trigger=>trigger.addEventListener('click',()=>{ 140 const source=trigger.querySelector('img');opener=trigger; 141 full.src=trigger.dataset.clZoom;full.alt=source?source.alt:'';caption.textContent=full.alt; 142 overflow=document.body.style.overflow;document.body.style.overflow='hidden';dialog.showModal();close.focus(); 143 })); 144 close.addEventListener('click',()=>dialog.close()); 145 dialog.addEventListener('close',()=>{document.body.style.overflow=overflow||'';if(opener&&opener.isConnected)opener.focus({preventScroll:true});}); 146 dialog.addEventListener('click',event=>{if(event.target!==dialog)return;const r=dialog.getBoundingClientRect();if(event.clientX<r.left||event.clientX>r.right||event.clientY<r.top||event.clientY>r.bottom)dialog.close();}); 147})();
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.