Spaces:
Running
Running
const headroomChanged = new CustomEvent("quarto-hrChanged", { | |
detail: {}, | |
bubbles: true, | |
cancelable: false, | |
composed: false, | |
}); | |
window.document.addEventListener("DOMContentLoaded", function () { | |
let init = false; | |
// Manage the back to top button, if one is present. | |
let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop; | |
const scrollDownBuffer = 5; | |
const scrollUpBuffer = 35; | |
const btn = document.getElementById("quarto-back-to-top"); | |
const hideBackToTop = () => { | |
btn.style.display = "none"; | |
}; | |
const showBackToTop = () => { | |
btn.style.display = "inline-block"; | |
}; | |
if (btn) { | |
window.document.addEventListener( | |
"scroll", | |
function () { | |
const currentScrollTop = | |
window.pageYOffset || document.documentElement.scrollTop; | |
// Shows and hides the button 'intelligently' as the user scrolls | |
if (currentScrollTop - scrollDownBuffer > lastScrollTop) { | |
hideBackToTop(); | |
lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; | |
} else if (currentScrollTop < lastScrollTop - scrollUpBuffer) { | |
showBackToTop(); | |
lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; | |
} | |
// Show the button at the bottom, hides it at the top | |
if (currentScrollTop <= 0) { | |
hideBackToTop(); | |
} else if ( | |
window.innerHeight + currentScrollTop >= | |
document.body.offsetHeight | |
) { | |
showBackToTop(); | |
} | |
}, | |
false | |
); | |
} | |
function throttle(func, wait) { | |
var timeout; | |
return function () { | |
const context = this; | |
const args = arguments; | |
const later = function () { | |
clearTimeout(timeout); | |
timeout = null; | |
func.apply(context, args); | |
}; | |
if (!timeout) { | |
timeout = setTimeout(later, wait); | |
} | |
}; | |
} | |
function headerOffset() { | |
// Set an offset if there is are fixed top navbar | |
const headerEl = window.document.querySelector("header.fixed-top"); | |
if (headerEl) { | |
return headerEl.clientHeight; | |
} else { | |
return 0; | |
} | |
} | |
function footerOffset() { | |
const footerEl = window.document.querySelector("footer.footer"); | |
if (footerEl) { | |
return footerEl.clientHeight; | |
} else { | |
return 0; | |
} | |
} | |
function updateDocumentOffsetWithoutAnimation() { | |
updateDocumentOffset(false); | |
} | |
function updateDocumentOffset(animated) { | |
// set body offset | |
const topOffset = headerOffset(); | |
const bodyOffset = topOffset + footerOffset(); | |
const bodyEl = window.document.body; | |
bodyEl.setAttribute("data-bs-offset", topOffset); | |
bodyEl.style.paddingTop = topOffset + "px"; | |
// deal with sidebar offsets | |
const sidebars = window.document.querySelectorAll( | |
".sidebar, .headroom-target" | |
); | |
sidebars.forEach((sidebar) => { | |
if (!animated) { | |
sidebar.classList.add("notransition"); | |
// Remove the no transition class after the animation has time to complete | |
setTimeout(function () { | |
sidebar.classList.remove("notransition"); | |
}, 201); | |
} | |
if (window.Headroom && sidebar.classList.contains("sidebar-unpinned")) { | |
sidebar.style.top = "0"; | |
sidebar.style.maxHeight = "100vh"; | |
} else { | |
sidebar.style.top = topOffset + "px"; | |
sidebar.style.maxHeight = "calc(100vh - " + topOffset + "px)"; | |
} | |
}); | |
// allow space for footer | |
const mainContainer = window.document.querySelector(".quarto-container"); | |
if (mainContainer) { | |
mainContainer.style.minHeight = "calc(100vh - " + bodyOffset + "px)"; | |
} | |
// link offset | |
let linkStyle = window.document.querySelector("#quarto-target-style"); | |
if (!linkStyle) { | |
linkStyle = window.document.createElement("style"); | |
linkStyle.setAttribute("id", "quarto-target-style"); | |
window.document.head.appendChild(linkStyle); | |
} | |
while (linkStyle.firstChild) { | |
linkStyle.removeChild(linkStyle.firstChild); | |
} | |
if (topOffset > 0) { | |
linkStyle.appendChild( | |
window.document.createTextNode(` | |
section:target::before { | |
content: ""; | |
display: block; | |
height: ${topOffset}px; | |
margin: -${topOffset}px 0 0; | |
}`) | |
); | |
} | |
if (init) { | |
window.dispatchEvent(headroomChanged); | |
} | |
init = true; | |
} | |
// initialize headroom | |
var header = window.document.querySelector("#quarto-header"); | |
if (header && window.Headroom) { | |
const headroom = new window.Headroom(header, { | |
tolerance: 5, | |
onPin: function () { | |
const sidebars = window.document.querySelectorAll( | |
".sidebar, .headroom-target" | |
); | |
sidebars.forEach((sidebar) => { | |
sidebar.classList.remove("sidebar-unpinned"); | |
}); | |
updateDocumentOffset(); | |
}, | |
onUnpin: function () { | |
const sidebars = window.document.querySelectorAll( | |
".sidebar, .headroom-target" | |
); | |
sidebars.forEach((sidebar) => { | |
sidebar.classList.add("sidebar-unpinned"); | |
}); | |
updateDocumentOffset(); | |
}, | |
}); | |
headroom.init(); | |
let frozen = false; | |
window.quartoToggleHeadroom = function () { | |
if (frozen) { | |
headroom.unfreeze(); | |
frozen = false; | |
} else { | |
headroom.freeze(); | |
frozen = true; | |
} | |
}; | |
} | |
window.addEventListener( | |
"hashchange", | |
function (e) { | |
if ( | |
getComputedStyle(document.documentElement).scrollBehavior !== "smooth" | |
) { | |
window.scrollTo(0, window.pageYOffset - headerOffset()); | |
} | |
}, | |
false | |
); | |
// Observe size changed for the header | |
const headerEl = window.document.querySelector("header.fixed-top"); | |
if (headerEl && window.ResizeObserver) { | |
const observer = new window.ResizeObserver( | |
updateDocumentOffsetWithoutAnimation | |
); | |
observer.observe(headerEl, { | |
attributes: true, | |
childList: true, | |
characterData: true, | |
}); | |
} else { | |
window.addEventListener( | |
"resize", | |
throttle(updateDocumentOffsetWithoutAnimation, 50) | |
); | |
} | |
setTimeout(updateDocumentOffsetWithoutAnimation, 250); | |
// fixup index.html links if we aren't on the filesystem | |
if (window.location.protocol !== "file:") { | |
const links = window.document.querySelectorAll("a"); | |
for (let i = 0; i < links.length; i++) { | |
if (links[i].href) { | |
links[i].href = links[i].href.replace(/\/index\.html/, "/"); | |
} | |
} | |
// Fixup any sharing links that require urls | |
// Append url to any sharing urls | |
const sharingLinks = window.document.querySelectorAll( | |
"a.sidebar-tools-main-item" | |
); | |
for (let i = 0; i < sharingLinks.length; i++) { | |
const sharingLink = sharingLinks[i]; | |
const href = sharingLink.getAttribute("href"); | |
if (href) { | |
sharingLink.setAttribute( | |
"href", | |
href.replace("|url|", window.location.href) | |
); | |
} | |
} | |
// Scroll the active navigation item into view, if necessary | |
const navSidebar = window.document.querySelector("nav#quarto-sidebar"); | |
if (navSidebar) { | |
// Find the active item | |
const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); | |
if (activeItem) { | |
// Wait for the scroll height and height to resolve by observing size changes on the | |
// nav element that is scrollable | |
const resizeObserver = new ResizeObserver((_entries) => { | |
// The bottom of the element | |
const elBottom = activeItem.offsetTop; | |
const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; | |
// The element height and scroll height are the same, then we are still loading | |
if (viewBottom !== navSidebar.scrollHeight) { | |
// Determine if the item isn't visible and scroll to it | |
if (elBottom >= viewBottom) { | |
navSidebar.scrollTop = elBottom; | |
} | |
// stop observing now since we've completed the scroll | |
resizeObserver.unobserve(navSidebar); | |
} | |
}); | |
resizeObserver.observe(navSidebar); | |
} | |
} | |
} | |
}); | |