2082001ff8
* Initial tests set up This adds tests using uvu (we can switch if people want) and restructures things a bit so that it's easier to test. Like in snowpack you set up a little project. In our tests you can say: ```js const result = await runtime.load('/blog/hello-world') ``` And analyze the result. I included a `test-helpers.js` which has a function that will turn HTML into a cheerio instance, for inspecting the result HTML. * Bring snowpack example in * Formatting
115 lines
3.3 KiB
JavaScript
115 lines
3.3 KiB
JavaScript
/**
|
||
* Debounce functions for better performance
|
||
* (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
|
||
* @param {Function} fn The function to debounce
|
||
*/
|
||
function debounce(fn) {
|
||
// Setup a timer
|
||
var timeout;
|
||
// Return a function to run debounced
|
||
return function () {
|
||
// Setup the arguments
|
||
var context = this;
|
||
var args = arguments;
|
||
// If there's a timer, cancel it
|
||
if (timeout) {
|
||
window.cancelAnimationFrame(timeout);
|
||
}
|
||
// Setup the new requestAnimationFrame()
|
||
timeout = window.requestAnimationFrame(function () {
|
||
fn.apply(context, args);
|
||
});
|
||
};
|
||
}
|
||
|
||
function isScrolledIntoView(el) {
|
||
const { top } = el.getBoundingClientRect();
|
||
const halfHeight = window.innerHeight / 2;
|
||
const isVisible = top <= halfHeight;
|
||
return isVisible;
|
||
}
|
||
|
||
function setActiveToc() {
|
||
if (window.innerWidth < 1240) {
|
||
return;
|
||
}
|
||
if (!tableOfContentsEl) {
|
||
return;
|
||
}
|
||
|
||
const headings = [
|
||
...document.querySelectorAll(
|
||
'.content-body h1, .content-body h2, .content-body h3, .content-body h4',
|
||
),
|
||
].filter((el) => !!el.id);
|
||
const scrolledToBeginning = window.scrollY === 0;
|
||
const scrolledToEnd =
|
||
Math.ceil(window.innerHeight + window.scrollY) >=
|
||
Math.ceil(document.body.getBoundingClientRect().height);
|
||
|
||
let el;
|
||
if (scrolledToBeginning) {
|
||
el = headings[0]; // if we‘re at the top of the page, highlight the first item
|
||
} else if (scrolledToEnd) {
|
||
el = headings[headings.length - 1]; // if we’re at the end of the page, highlight the last item
|
||
} else {
|
||
el = headings.reverse().find(isScrolledIntoView); // otherwise highlight the one that’s at least halfway up the page
|
||
}
|
||
|
||
if (!el) return;
|
||
|
||
const elId = el.id;
|
||
const href = `#${elId}`;
|
||
const tocEl = tableOfContentsEl.querySelector(`a[href="${href}"]`);
|
||
// only add the active class once, which will also prevent scroll from re-triggering while scrolling to the same element
|
||
if (!tocEl || tocEl.classList.contains('active')) {
|
||
return;
|
||
}
|
||
|
||
tableOfContentsEl.querySelectorAll(`a.active`).forEach((aEl) => {
|
||
if (aEl.getAttribute('href') !== href) aEl.classList.remove('active');
|
||
});
|
||
|
||
tocEl.classList.add('active');
|
||
|
||
// // update nav on desktop
|
||
// if (window.innerWidth >= 860) {
|
||
// tocEl.scrollIntoView({behavior: 'smooth'});
|
||
// }
|
||
// {
|
||
// top:
|
||
// tocEl.getBoundingClientRect().top + gridTocEl.scrollTop - PADDING_TOP,
|
||
// behavior: 'smooth',
|
||
// });
|
||
}
|
||
|
||
const tableOfContentsEl = document.querySelector('.toc');
|
||
window.addEventListener('scroll', debounce(setActiveToc));
|
||
/* May not be needed:
|
||
window.addEventListener('DOMContentLoaded', (event) => {
|
||
if (!window.location.hash) {
|
||
return;
|
||
}
|
||
const elNeedingScroll = document.getElementById(window.location.hash.substring(1));
|
||
if (!elNeedingScroll) {
|
||
return;
|
||
}
|
||
elNeedingScroll.scrollIntoView();
|
||
elNeedingScroll.classList.add('highlighted');
|
||
});
|
||
*/
|
||
|
||
window.addEventListener('DOMContentLoaded', (event) => {
|
||
if (!tableOfContentsEl) {
|
||
return;
|
||
}
|
||
document.querySelectorAll('.content h3, .content h4').forEach((headerEl) => {
|
||
const linkEl = document.createElement('a');
|
||
// linkEl.setAttribute('target', "_blank");
|
||
linkEl.setAttribute('href', '#' + headerEl.id);
|
||
linkEl.classList.add('header-link');
|
||
linkEl.innerText = '#';
|
||
headerEl.appendChild(linkEl);
|
||
});
|
||
setActiveToc();
|
||
});
|