media/js/base/legal-toc.es6.js (103 lines of code) (raw):

/* * 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 https://mozilla.org/MPL/2.0/. */ let isSticky = false; let resizeTimer; const stickyTOC = {}; const sidebar = document.querySelector('.mzp-l-sidebar'); const sections = document.querySelectorAll( '.mzp-c-article .section1 .section2' ); const sectionObserver = new IntersectionObserver( (sections) => { sections.forEach((section) => { const id = section.target.querySelector('h2').getAttribute('id'); if (section.intersectionRatio > 0) { // try/catch because it errors if there's no matching selector try { document .querySelector(`.c-toc li a[href="#${id}"]`) .parentElement.classList.add('active'); return true; } catch (e) { return false; } } else { // try/catch because it errors if there's no matching selector try { document .querySelector(`.c-toc li a[href="#${id}"]`) .parentElement.classList.remove('active'); return true; } catch (e) { return false; } } }); }, { rootMargin: '-120px 0px' } ); /** * Is the layout large enough to be 2 column? * @returns {Boolean} */ stickyTOC.isTwoColumn = () => { return getComputedStyle(sidebar).getPropertyValue('float') !== 'none'; }; /** * Is there enough vertical space for sticky behavior? * @returns {Boolean} */ stickyTOC.isTallEnough = () => { return sidebar.offsetHeight < window.innerHeight; }; /** * Feature detect for sticky navigation * @returns {Boolean} */ stickyTOC.supportsSticky = () => { if (typeof window.MzpSupports !== 'undefined') { return ( window.MzpSupports.classList && window.MzpSupports.intersectionObserver && window.MzpSupports.matchMedia && CSS.supports('position', 'sticky') ); } else { return false; } }; /** * Add intersection observer & classes */ stickyTOC.createSticky = () => { if (!isSticky) { sidebar.classList.add('toc-is-sticky'); sections.forEach((section) => { sectionObserver.observe(section); }); isSticky = true; } }; /** * Remove intersection observer & classes */ stickyTOC.destroySticky = () => { if (isSticky) { sidebar.classList.remove('toc-is-sticky'); sidebar.querySelectorAll('.active').forEach((active) => { active.classList.remove('active'); }); sections.forEach((section) => { sectionObserver.unobserve(section); }); isSticky = false; } }; /** * Init sticky TOC if conditions are satisfied */ stickyTOC.initSticky = () => { const makeSticky = () => { if ( stickyTOC.isTallEnough() && stickyTOC.isTwoColumn() && !matchMedia('(prefers-reduced-motion: reduce)').matches ) { stickyTOC.createSticky(); } else { stickyTOC.destroySticky(); return false; } }; // react to window resizing if (stickyTOC.supportsSticky()) { makeSticky(); window.addEventListener( 'resize', function () { clearTimeout(resizeTimer); resizeTimer = setTimeout(makeSticky, 250); }, true ); } }; window.addEventListener('DOMContentLoaded', () => { stickyTOC.initSticky(); });