1const headroomChanged = new CustomEvent("quarto-hrChanged", { 2 detail: {}, 3 bubbles: true, 4 cancelable: false, 5 composed: false, 6}); 7 8window.document.addEventListener("DOMContentLoaded", function () { 9 let init = false; 10 11 function throttle(func, wait) { 12 var timeout; 13 return function () { 14 const context = this; 15 const args = arguments; 16 const later = function () { 17 clearTimeout(timeout); 18 timeout = null; 19 func.apply(context, args); 20 }; 21 22 if (!timeout) { 23 timeout = setTimeout(later, wait); 24 } 25 }; 26 } 27 28 function headerOffset() { 29 // Set an offset if there is are fixed top navbar 30 const headerEl = window.document.querySelector("header.fixed-top"); 31 if (headerEl) { 32 return headerEl.clientHeight; 33 } else { 34 return 0; 35 } 36 } 37 38 function footerOffset() { 39 const footerEl = window.document.querySelector("footer.footer"); 40 if (footerEl) { 41 return footerEl.clientHeight; 42 } else { 43 return 0; 44 } 45 } 46 47 function updateDocumentOffsetWithoutAnimation() { 48 updateDocumentOffset(false); 49 } 50 51 function updateDocumentOffset(animated) { 52 // set body offset 53 const topOffset = headerOffset(); 54 const bodyOffset = topOffset + footerOffset(); 55 const bodyEl = window.document.body; 56 bodyEl.setAttribute("data-bs-offset", topOffset); 57 bodyEl.style.paddingTop = topOffset + "px"; 58 59 // deal with sidebar offsets 60 const sidebars = window.document.querySelectorAll( 61 ".sidebar, .headroom-target" 62 ); 63 sidebars.forEach((sidebar) => { 64 if (!animated) { 65 sidebar.classList.add("notransition"); 66 // Remove the no transition class after the animation has time to complete 67 setTimeout(function () { 68 sidebar.classList.remove("notransition"); 69 }, 201); 70 } 71 72 if (window.Headroom && sidebar.classList.contains("sidebar-unpinned")) { 73 sidebar.style.top = "0"; 74 sidebar.style.maxHeight = "100vh"; 75 } else { 76 sidebar.style.top = topOffset + "px"; 77 sidebar.style.maxHeight = "calc(100vh - " + topOffset + "px)"; 78 } 79 }); 80 81 // allow space for footer 82 const mainContainer = window.document.querySelector(".quarto-container"); 83 if (mainContainer) { 84 mainContainer.style.minHeight = "calc(100vh - " + bodyOffset + "px)"; 85 } 86 87 // link offset 88 let linkStyle = window.document.querySelector("#quarto-target-style"); 89 if (!linkStyle) { 90 linkStyle = window.document.createElement("style"); 91 window.document.head.appendChild(linkStyle); 92 } 93 while (linkStyle.firstChild) { 94 linkStyle.removeChild(linkStyle.firstChild); 95 } 96 if (topOffset > 0) { 97 linkStyle.appendChild( 98 window.document.createTextNode(` 99 section:target::before { 100 content: ""; 101 display: block; 102 height: ${topOffset}px; 103 margin: -${topOffset}px 0 0; 104 }`) 105 ); 106 } 107 if (init) { 108 window.dispatchEvent(headroomChanged); 109 } 110 init = true; 111 } 112 113 // initialize headroom 114 var header = window.document.querySelector("#quarto-header"); 115 if (header && window.Headroom) { 116 const headroom = new window.Headroom(header, { 117 tolerance: 5, 118 onPin: function () { 119 const sidebars = window.document.querySelectorAll( 120 ".sidebar, .headroom-target" 121 ); 122 sidebars.forEach((sidebar) => { 123 sidebar.classList.remove("sidebar-unpinned"); 124 }); 125 updateDocumentOffset(); 126 }, 127 onUnpin: function () { 128 const sidebars = window.document.querySelectorAll( 129 ".sidebar, .headroom-target" 130 ); 131 sidebars.forEach((sidebar) => { 132 sidebar.classList.add("sidebar-unpinned"); 133 }); 134 updateDocumentOffset(); 135 }, 136 }); 137 headroom.init(); 138 139 let frozen = false; 140 window.quartoToggleHeadroom = function () { 141 if (frozen) { 142 headroom.unfreeze(); 143 frozen = false; 144 } else { 145 headroom.freeze(); 146 frozen = true; 147 } 148 }; 149 } 150 151 // Observe size changed for the header 152 const headerEl = window.document.querySelector("header.fixed-top"); 153 if (headerEl && window.ResizeObserver) { 154 const observer = new window.ResizeObserver( 155 throttle(updateDocumentOffsetWithoutAnimation, 50) 156 ); 157 observer.observe(headerEl, { 158 attributes: true, 159 childList: true, 160 characterData: true, 161 }); 162 } else { 163 window.addEventListener( 164 "resize", 165 throttle(updateDocumentOffsetWithoutAnimation, 50) 166 ); 167 setTimeout(updateDocumentOffsetWithoutAnimation, 500); 168 } 169 170 // fixup index.html links if we aren't on the filesystem 171 if (window.location.protocol !== "file:") { 172 const links = window.document.querySelectorAll("a"); 173 for (let i = 0; i < links.length; i++) { 174 links[i].href = links[i].href.replace(/\/index\.html/, "/"); 175 } 176 177 // Fixup any sharing links that require urls 178 // Append url to any sharing urls 179 const sharingLinks = window.document.querySelectorAll( 180 "a.sidebar-tools-main-item" 181 ); 182 for (let i = 0; i < sharingLinks.length; i++) { 183 const sharingLink = sharingLinks[i]; 184 const href = sharingLink.getAttribute("href"); 185 if (href) { 186 sharingLink.setAttribute( 187 "href", 188 href.replace("|url|", window.location.href) 189 ); 190 } 191 } 192 193 // Scroll the active navigation item into view, if necessary 194 const navSidebar = window.document.querySelector("nav#quarto-sidebar"); 195 if (navSidebar) { 196 // Find the active item 197 const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); 198 if (activeItem) { 199 // Wait for the scroll height and height to resolve by observing size changes on the 200 // nav element that is scrollable 201 const resizeObserver = new ResizeObserver((_entries) => { 202 // The bottom of the element 203 const elBottom = activeItem.offsetTop;
204 const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; 205 206 // The element height and scroll height are the same, then we are still loading 207 if (viewBottom !== navSidebar.scrollHeight) { 208 // Determine if the item isn't visible and scroll to it 209 if (elBottom >= viewBottom) { 210 navSidebar.scrollTop = elBottom; 211 } 212 213 // stop observing now since we've completed the scroll 214 resizeObserver.unobserve(navSidebar); 215 } 216 }); 217 resizeObserver.observe(navSidebar); 218 } 219 } 220 } 221});
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.