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 linkStyle.setAttribute("id", "quarto-target-style"); 92 window.document.head.appendChild(linkStyle); 93 } 94 while (linkStyle.firstChild) { 95 linkStyle.removeChild(linkStyle.firstChild); 96 } 97 if (topOffset > 0) { 98 linkStyle.appendChild( 99 window.document.createTextNode(` 100 section:target::before { 101 content: ""; 102 display: block; 103 height: ${topOffset}px; 104 margin: -${topOffset}px 0 0; 105 }`) 106 ); 107 } 108 if (init) { 109 window.dispatchEvent(headroomChanged); 110 } 111 init = true; 112 } 113 114 // initialize headroom 115 var header = window.document.querySelector("#quarto-header"); 116 if (header && window.Headroom) { 117 const headroom = new window.Headroom(header, { 118 tolerance: 5, 119 onPin: function () { 120 const sidebars = window.document.querySelectorAll( 121 ".sidebar, .headroom-target" 122 ); 123 sidebars.forEach((sidebar) => { 124 sidebar.classList.remove("sidebar-unpinned"); 125 }); 126 updateDocumentOffset(); 127 }, 128 onUnpin: function () { 129 const sidebars = window.document.querySelectorAll( 130 ".sidebar, .headroom-target" 131 ); 132 sidebars.forEach((sidebar) => { 133 sidebar.classList.add("sidebar-unpinned"); 134 }); 135 updateDocumentOffset(); 136 }, 137 }); 138 headroom.init(); 139 140 let frozen = false; 141 window.quartoToggleHeadroom = function () { 142 if (frozen) { 143 headroom.unfreeze(); 144 frozen = false; 145 } else { 146 headroom.freeze(); 147 frozen = true; 148 } 149 }; 150 } 151 152 // Observe size changed for the header 153 const headerEl = window.document.querySelector("header.fixed-top"); 154 if (headerEl && window.ResizeObserver) { 155 const observer = new window.ResizeObserver( 156 updateDocumentOffsetWithoutAnimation 157 ); 158 observer.observe(headerEl, { 159 attributes: true, 160 childList: true, 161 characterData: true, 162 }); 163 } else { 164 window.addEventListener( 165 "resize", 166 throttle(updateDocumentOffsetWithoutAnimation, 50) 167 ); 168 } 169 setTimeout(updateDocumentOffsetWithoutAnimation, 250); 170 171 // fixup index.html links if we aren't on the filesystem 172 if (window.location.protocol !== "file:") { 173 const links = window.document.querySelectorAll("a"); 174 for (let i = 0; i < links.length; i++) { 175 links[i].href = links[i].href.replace(/\/index\.html/, "/"); 176 } 177 178 // Fixup any sharing links that require urls 179 // Append url to any sharing urls 180 const sharingLinks = window.document.querySelectorAll( 181 "a.sidebar-tools-main-item" 182 ); 183 for (let i = 0; i < sharingLinks.length; i++) { 184 const sharingLink = sharingLinks[i]; 185 const href = sharingLink.getAttribute("href"); 186 if (href) { 187 sharingLink.setAttribute( 188 "href", 189 href.replace("|url|", window.location.href) 190 ); 191 } 192 } 193 194 // Scroll the active navigation item into view, if necessary 195 const navSidebar = window.document.querySelector("nav#quarto-sidebar"); 196 if (navSidebar) { 197 // Find the active item 198 const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); 199 if (activeItem) { 200 // Wait for the scroll height and height to resolve by observing size changes on the 201 // nav element that is scrollable 202 const resizeObserver = new ResizeObserver((_entries) => { 203 // The bottom of the element 204 const elBottom = activeItem.offsetTop;
205 const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; 206 207 // The element height and scroll height are the same, then we are still loading 208 if (viewBottom !== navSidebar.scrollHeight) { 209 // Determine if the item isn't visible and scroll to it 210 if (elBottom >= viewBottom) { 211 navSidebar.scrollTop = elBottom; 212 } 213 214 // stop observing now since we've completed the scroll 215 resizeObserver.unobserve(navSidebar); 216 } 217 }); 218 resizeObserver.observe(navSidebar); 219 } 220 } 221 } 222});
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.