From ab9669e821432397b60cbb41bc779e9d32274120 Mon Sep 17 00:00:00 2001 From: Dan Allen Date: Mon, 19 Nov 2018 14:07:56 -0700 Subject: [PATCH] add JavaScript that generates the "on this page" sidebar This script was originally developed for Couchbase by OpenDevise. This commit contributes the script to the Antora project on behalf of Couchbase. The script will now be licensed under the terms of the MPL 2.0 license to match the license of the Antora project itself. --- src/js/02-on-this-page.js | 100 +++++++++++++++++++++ ...02-fragment-jumper.js => 03-fragment-jumper.js} | 0 .../{03-page-versions.js => 04-page-versions.js} | 0 .../{04-mobile-navbar.js => 05-mobile-navbar.js} | 0 4 files changed, 100 insertions(+) create mode 100644 src/js/02-on-this-page.js rename src/js/{02-fragment-jumper.js => 03-fragment-jumper.js} (100%) rename src/js/{03-page-versions.js => 04-page-versions.js} (100%) rename src/js/{04-mobile-navbar.js => 05-mobile-navbar.js} (100%) diff --git a/src/js/02-on-this-page.js b/src/js/02-on-this-page.js new file mode 100644 index 0000000..60f40e9 --- /dev/null +++ b/src/js/02-on-this-page.js @@ -0,0 +1,100 @@ +/* Copyright (c) 2018 OpenDevise Inc. and individual contributors. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +;(function () { + 'use strict' + + var sidebar = document.querySelector('aside.toc.sidebar') + if (!sidebar) return + var doc + var headings + if ( + document.querySelector('.body.-toc') || + !(headings = find('h1[id].sect0, .sect1 > h2[id]', (doc = document.querySelector('article.doc')))).length + ) { + sidebar.parentNode.removeChild(sidebar) + return + } + var lastActiveFragment + var links = {} + var menu + + var list = headings.reduce(function (accum, heading) { + var link = toArray(heading.childNodes).reduce(function (target, child) { + if (child.nodeName !== 'A') target.appendChild(child.cloneNode(true)) + return target + }, document.createElement('a')) + links[(link.href = '#' + heading.id)] = link + var listItem = document.createElement('li') + listItem.appendChild(link) + accum.appendChild(listItem) + return accum + }, document.createElement('ul')) + + if (!(menu = sidebar && sidebar.querySelector('.toc-menu'))) { + menu = document.createElement('div') + menu.className = 'toc-menu' + } + + var title = document.createElement('h3') + title.textContent = 'On This Page' + menu.appendChild(title) + menu.appendChild(list) + + if (sidebar) { + window.addEventListener('load', function () { + onScroll() + window.addEventListener('scroll', onScroll) + }) + } + + var startOfContent = doc.querySelector('h1.page ~ :not(.labels)') + if (startOfContent) { + var embeddedToc = document.createElement('aside') + embeddedToc.className = 'toc embedded' + embeddedToc.appendChild(menu.cloneNode(true)) + doc.insertBefore(embeddedToc, startOfContent) + } + + function onScroll () { + // NOTE doc.parentNode.offsetTop ~= doc.parentNode.getBoundingClientRect().top + window.pageYOffset + //var targetPosition = doc.parentNode.offsetTop + // NOTE no need to compensate wheen using spacer above [id] elements + var targetPosition = 0 + var activeFragment + headings.some(function (heading) { + if (Math.floor(heading.getBoundingClientRect().top) <= targetPosition) { + activeFragment = '#' + heading.id + } else { + return true + } + }) + if (activeFragment) { + if (activeFragment !== lastActiveFragment) { + if (lastActiveFragment) { + links[lastActiveFragment].classList.remove('is-active') + } + var activeLink = links[activeFragment] + activeLink.classList.add('is-active') + if (menu.scrollHeight > menu.offsetHeight) { + menu.scrollTop = Math.max(0, activeLink.offsetTop + activeLink.offsetHeight - menu.offsetHeight) + } + lastActiveFragment = activeFragment + } + } else if (lastActiveFragment) { + links[lastActiveFragment].classList.remove('is-active') + lastActiveFragment = undefined + } + } + + function find (selector, from) { + return toArray((from || document).querySelectorAll(selector)) + } + + function toArray (collection) { + return [].slice.call(collection) + } +})() diff --git a/src/js/02-fragment-jumper.js b/src/js/03-fragment-jumper.js similarity index 100% rename from src/js/02-fragment-jumper.js rename to src/js/03-fragment-jumper.js diff --git a/src/js/03-page-versions.js b/src/js/04-page-versions.js similarity index 100% rename from src/js/03-page-versions.js rename to src/js/04-page-versions.js diff --git a/src/js/04-mobile-navbar.js b/src/js/05-mobile-navbar.js similarity index 100% rename from src/js/04-mobile-navbar.js rename to src/js/05-mobile-navbar.js -- 2.11.0