mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Initial docs release
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function IsRightTocEnabled() {
|
||||
var meta = document.querySelector('meta[name="ffu-right-toc"]');
|
||||
if (meta && meta.content && meta.content.toLowerCase() === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function GetHeadings(container) {
|
||||
var headings = container.querySelectorAll('h2, h3');
|
||||
var results = [];
|
||||
|
||||
for (var i = 0; i < headings.length; i++) {
|
||||
var heading = headings[i];
|
||||
|
||||
if (heading.classList.contains('no_toc')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = heading.getAttribute('id');
|
||||
if (!id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var text = (heading.textContent || '').trim();
|
||||
if (!text) {
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({
|
||||
level: heading.tagName.toLowerCase(),
|
||||
id: id,
|
||||
text: text
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function BuildToc(headings) {
|
||||
var nav = document.createElement('nav');
|
||||
nav.className = 'page-toc';
|
||||
nav.setAttribute('aria-label', 'On this page');
|
||||
|
||||
var title = document.createElement('div');
|
||||
title.className = 'page-toc__title';
|
||||
title.textContent = 'In this article';
|
||||
nav.appendChild(title);
|
||||
|
||||
var list = document.createElement('ul');
|
||||
list.className = 'page-toc__list';
|
||||
|
||||
for (var i = 0; i < headings.length; i++) {
|
||||
var item = headings[i];
|
||||
|
||||
var li = document.createElement('li');
|
||||
li.className = 'page-toc__item page-toc__item--' + item.level;
|
||||
|
||||
var a = document.createElement('a');
|
||||
a.className = 'page-toc__link';
|
||||
a.href = '#' + item.id;
|
||||
a.textContent = item.text;
|
||||
|
||||
li.appendChild(a);
|
||||
list.appendChild(li);
|
||||
}
|
||||
|
||||
nav.appendChild(list);
|
||||
return nav;
|
||||
}
|
||||
|
||||
function SetActiveTocLink(toc, activeId) {
|
||||
if (!toc) {
|
||||
return;
|
||||
}
|
||||
|
||||
var links = toc.querySelectorAll('.page-toc__link');
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var link = links[i];
|
||||
var href = link.getAttribute('href') || '';
|
||||
var isActive = ('#' + activeId) === href;
|
||||
|
||||
if (isActive) {
|
||||
link.classList.add('is-active');
|
||||
|
||||
/* Keep the active item visible inside the TOC panel */
|
||||
try {
|
||||
link.scrollIntoView({ block: 'nearest' });
|
||||
} catch (e) {
|
||||
link.scrollIntoView();
|
||||
}
|
||||
} else {
|
||||
link.classList.remove('is-active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SetupScrollSpy(main, toc, headings) {
|
||||
if (!main || !toc || !headings || headings.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var headingElements = [];
|
||||
for (var i = 0; i < headings.length; i++) {
|
||||
var el = document.getElementById(headings[i].id);
|
||||
if (el) {
|
||||
headingElements.push(el);
|
||||
}
|
||||
}
|
||||
|
||||
if (headingElements.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var activeId = null;
|
||||
var ticking = false;
|
||||
var lockActiveUntilMs = 0;
|
||||
|
||||
function IsNearBottomOfPage() {
|
||||
var thresholdPx = 24;
|
||||
var scrollY = window.scrollY || window.pageYOffset || 0;
|
||||
var viewportBottom = scrollY + window.innerHeight;
|
||||
var pageHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
||||
|
||||
return viewportBottom >= (pageHeight - thresholdPx);
|
||||
}
|
||||
|
||||
function GetCurrentHeadingId() {
|
||||
/* If we're at the bottom, force the last heading active (Learn-like behavior) */
|
||||
if (IsNearBottomOfPage()) {
|
||||
return headingElements[headingElements.length - 1].getAttribute('id');
|
||||
}
|
||||
|
||||
/* Choose the heading closest to the top "activation line" */
|
||||
var activationLine = 16;
|
||||
var current = null;
|
||||
|
||||
for (var i = 0; i < headingElements.length; i++) {
|
||||
var rectTop = headingElements[i].getBoundingClientRect().top;
|
||||
|
||||
if (rectTop <= activationLine) {
|
||||
current = headingElements[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null === current) {
|
||||
current = headingElements[i];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (null === current) {
|
||||
current = headingElements[0];
|
||||
}
|
||||
|
||||
return current.getAttribute('id');
|
||||
}
|
||||
|
||||
function Update() {
|
||||
ticking = false;
|
||||
|
||||
if (Date.now() < lockActiveUntilMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentId = GetCurrentHeadingId();
|
||||
if (!currentId || currentId === activeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
activeId = currentId;
|
||||
SetActiveTocLink(toc, activeId);
|
||||
}
|
||||
|
||||
function OnScrollOrResize() {
|
||||
if (ticking) {
|
||||
return;
|
||||
}
|
||||
|
||||
ticking = true;
|
||||
window.requestAnimationFrame(Update);
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', OnScrollOrResize, { passive: true });
|
||||
window.addEventListener('resize', OnScrollOrResize);
|
||||
|
||||
/* Update immediately and also when clicking TOC links */
|
||||
toc.addEventListener('click', function (evt) {
|
||||
var target = evt.target;
|
||||
if (!target || !target.classList || !target.classList.contains('page-toc__link')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var href = target.getAttribute('href') || '';
|
||||
if (href.charAt(0) !== '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
var id = href.substring(1);
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent scrollspy from immediately overriding the clicked section */
|
||||
lockActiveUntilMs = Date.now() + 800;
|
||||
|
||||
activeId = id;
|
||||
SetActiveTocLink(toc, activeId);
|
||||
});
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
function InitRightToc() {
|
||||
if (!IsRightTocEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var main = document.querySelector('.main-content main');
|
||||
if (!main) {
|
||||
return;
|
||||
}
|
||||
|
||||
var headings = GetHeadings(main);
|
||||
if (headings.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
var wrap = document.querySelector('.main-content-wrap');
|
||||
var content = document.querySelector('.main-content');
|
||||
if (!wrap || !content) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wrap.querySelector('.page-toc')) {
|
||||
return;
|
||||
}
|
||||
|
||||
wrap.classList.add('has-page-toc');
|
||||
|
||||
var toc = BuildToc(headings);
|
||||
wrap.appendChild(toc);
|
||||
|
||||
SetupScrollSpy(main, toc, headings);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', InitRightToc);
|
||||
return;
|
||||
}
|
||||
|
||||
InitRightToc();
|
||||
})();
|
||||
Reference in New Issue
Block a user