1'use strict'; 2 3{ 4 function setupTheme() { 5 const kCustomPreference = 'customDarkTheme'; 6 const userSettings = sessionStorage.getItem(kCustomPreference); 7 const themeToggleButton = document.getElementById('theme-toggle-btn'); 8 9 if (userSettings === null && window.matchMedia) { 10 const mq = window.matchMedia('(prefers-color-scheme: dark)'); 11 12 if ('onchange' in mq) { 13 function mqChangeListener(e) { 14 document.documentElement.classList.toggle('dark-mode', e.matches); 15 } 16 mq.addEventListener('change', mqChangeListener); 17 if (themeToggleButton) { 18 themeToggleButton.addEventListener( 19 'click', 20 function() { 21 mq.removeEventListener('change', mqChangeListener); 22 }, 23 { once: true }, 24 ); 25 } 26 } 27 28 if (mq.matches) { 29 document.documentElement.classList.add('dark-mode'); 30 } 31 } else if (userSettings === 'true') { 32 document.documentElement.classList.add('dark-mode'); 33 } 34 35 if (themeToggleButton) { 36 themeToggleButton.hidden = false; 37 themeToggleButton.addEventListener('click', function() { 38 sessionStorage.setItem( 39 kCustomPreference, 40 document.documentElement.classList.toggle('dark-mode'), 41 ); 42 }); 43 } 44 } 45 46 function setupPickers() { 47 function closeAllPickers() { 48 for (const picker of pickers) { 49 picker.parentNode.classList.remove('expanded'); 50 } 51 52 window.removeEventListener('click', closeAllPickers); 53 window.removeEventListener('keydown', onKeyDown); 54 } 55 56 function onKeyDown(e) { 57 if (e.key === 'Escape') { 58 closeAllPickers(); 59 } 60 } 61 62 const pickers = document.querySelectorAll('.picker-header > a'); 63 64 for (const picker of pickers) { 65 const parentNode = picker.parentNode; 66 67 picker.addEventListener('click', function(e) { 68 e.preventDefault(); 69 70 /* 71 closeAllPickers as window event trigger already closed all the pickers, 72 if it already closed there is nothing else to do here 73 */ 74 if (parentNode.classList.contains('expanded')) { 75 return; 76 } 77 78 /* 79 In the next frame reopen the picker if needed and also setup events 80 to close pickers if needed. 81 */ 82 83 requestAnimationFrame(function() { 84 parentNode.classList.add('expanded'); 85 window.addEventListener('click', closeAllPickers); 86 window.addEventListener('keydown', onKeyDown); 87 }); 88 }); 89 } 90 } 91 92 function setupStickyHeaders() { 93 const header = document.querySelector('.header'); 94 let ignoreNextIntersection = false; 95 96 new IntersectionObserver( 97 function(e) { 98 const currentStatus = header.classList.contains('is-pinned'); 99 const newStatus = e[0].intersectionRatio < 1; 100 101 // Same status, do nothing 102 if (currentStatus === newStatus) { 103 return; 104 } else if (ignoreNextIntersection) { 105 ignoreNextIntersection = false; 106 return; 107 } 108 109 /* 110 To avoid flickering, ignore the next changes event that is triggered 111 as the visible elements in the header change once we pin it. 112 113 The timer is reset anyway after few milliseconds. 114 */ 115 ignoreNextIntersection = true; 116 setTimeout(function() { 117 ignoreNextIntersection = false; 118 }, 50); 119 120 header.classList.toggle('is-pinned', newStatus); 121 }, 122 { threshold: [1] }, 123 ).observe(header); 124 } 125 126 function setupAltDocsLink() { 127 const linkWrapper = document.getElementById('alt-docs'); 128 129 function updateHashes() { 130 for (const link of linkWrapper.querySelectorAll('a')) { 131 link.hash = location.hash; 132 } 133 } 134 135 addEventListener('hashchange', updateHashes); 136 updateHashes(); 137 } 138 139 function setupCopyButton() { 140 const buttons = document.querySelectorAll('.copy-button'); 141 buttons.forEach((button) => { 142 button.addEventListener('click', (el) => { 143 const parentNode = el.target.parentNode; 144 145 const flavorSelector = parentNode.querySelector('.js-flavor-selector'); 146 147 let code = ''; 148 149 if (flavorSelector) { 150 if (flavorSelector.checked) { 151 code = parentNode.querySelector('.mjs').textContent; 152 } else { 153 code = parentNode.querySelector('.cjs').textContent; 154 } 155 } else { 156 code = parentNode.querySelector('code').textContent; 157 } 158 159 button.textContent = 'Copied'; 160 navigator.clipboard.writeText(code); 161 162 setTimeout(() => { 163 button.textContent = 'Copy'; 164 }, 2500); 165 }); 166 }); 167 } 168 169 function bootstrap() { 170 // Check if we have JavaScript support. 171 document.documentElement.classList.add('has-js'); 172 173 // Restore user mode preferences. 174 setupTheme(); 175 176 // Handle pickers with click/taps rather than hovers. 177 setupPickers(); 178 179 // Track when the header is in sticky position. 180 setupStickyHeaders(); 181 182 // Make link to other versions of the doc open to the same hash target (if it exists). 183 setupAltDocsLink(); 184 185 setupCopyButton(); 186 } 187 188 if (document.readyState === 'loading') { 189 document.addEventListener('DOMContentLoaded', bootstrap, { once: true }); 190 } else { 191 bootstrap(); 192 } 193} 194