124 lines
3.6 KiB
JavaScript
124 lines
3.6 KiB
JavaScript
jQuery(function($) {
|
|
// tocItems are headings in the main content area that have a representation in the TOC
|
|
// (not all headings are present in the TOC).
|
|
let tocItems = $('#page-content-wrapper .toc-item');
|
|
|
|
function getCurrentlyVisibleSection(scrollPosition) {
|
|
// Walk the list from the bottom up and check;
|
|
// each TOC item is the immediate child of a <div> that
|
|
// carries the TOC item's ID.
|
|
for (let i = tocItems.length - 1; i >= 0; --i) {
|
|
let item = $(tocItems.get(i));
|
|
let offsetTop = item.offset().top - 50;
|
|
|
|
if (scrollPosition >= offsetTop) {
|
|
return item.parent().attr('id');
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function updateNavigationState(visibleSection) {
|
|
let selectedLink = $('#navigation a.selected');
|
|
let selectedSection = selectedLink.length === 0 ? null : selectedLink.attr('href').replace(/#/, '');
|
|
|
|
// nothing to do :)
|
|
if (visibleSection === selectedSection) {
|
|
return;
|
|
}
|
|
|
|
// un-select whatever was previously selected
|
|
if (selectedLink.length > 0) {
|
|
selectedLink.removeClass('selected');
|
|
}
|
|
|
|
if (visibleSection !== null) {
|
|
// show the leaf node in the navigation for the activeSection, plus
|
|
// all nodes that lead to it (in a->b->c->d, if c is active, show
|
|
// b and c because a is always shown already).
|
|
let activeSectionLink = '#' + visibleSection;
|
|
let link = $('#navigation a[href="' + activeSectionLink + '"]');
|
|
let listItem = link.parent();
|
|
|
|
link.addClass('selected');
|
|
|
|
while (listItem.data('level') > 1) {
|
|
let ul = listItem.parent();
|
|
ul.show('fast');
|
|
listItem = ul.parent();
|
|
}
|
|
|
|
// expand the currently selected item, i.e. do not just show the
|
|
// parent path, but also the immediate child <ul>
|
|
link.parent().children('ul').show('fast');
|
|
}
|
|
|
|
// collapse all sub tree that are not required to show the currently
|
|
// selected section (i.e. has no .selected in its children, is not
|
|
// the direct descendant of the currently selected item (which we
|
|
// just expanded) and is not the top level).
|
|
$('#navigation ul:visible').each(function() {
|
|
let list = $(this);
|
|
if (list.find('.selected').length > 0) {
|
|
return;
|
|
}
|
|
|
|
let listItem = list.parent();
|
|
if (listItem.data('level') <= 1) {
|
|
return;
|
|
}
|
|
|
|
let link = listItem.children('a');
|
|
if (link.hasClass('selected')) {
|
|
return;
|
|
}
|
|
|
|
list.hide('fast');
|
|
});
|
|
}
|
|
|
|
let navScrollTimeout;
|
|
function scrollToNav(currentSection) {
|
|
// debounce to prevent too many scrolls in the sidebar
|
|
if (navScrollTimeout) {
|
|
clearTimeout(navScrollTimeout);
|
|
}
|
|
|
|
navScrollTimeout = setTimeout(function() {
|
|
let navNode = $('#navigation a[href="#' + currentSection + '"]');
|
|
$('#sidebar-wrapper').scrollTo(navNode, {duration: 'fast', axis: 'y', over: {top: -1}});
|
|
}, 200);
|
|
}
|
|
|
|
function repaint() {
|
|
let scrollPosition = $(window).scrollTop();
|
|
let currentSection = getCurrentlyVisibleSection(scrollPosition);
|
|
updateNavigationState(currentSection);
|
|
scrollToNav(currentSection);
|
|
}
|
|
|
|
// update navigation whenever scrolling happens
|
|
$(window).on('scroll', repaint);
|
|
|
|
// perform an initial update on the navigation
|
|
repaint();
|
|
});
|
|
|
|
/* handle dark/light mode */
|
|
jQuery(function($) {
|
|
let button = $('.switch-theme');
|
|
button.show();
|
|
|
|
button.on('click', function() {
|
|
$('body').toggleClass('theme-dark');
|
|
});
|
|
|
|
// enable dark mode if desired by the user agent
|
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
$('body').toggleClass('theme-dark');
|
|
}
|
|
|
|
$('body').toggleClass('theme-auto');
|
|
});
|