123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- /**
- * Site-wide JS that sets up:
- *
- * [1] MathJax rendering on navigation
- * [2] Sidebar toggling
- * [3] Sidebar scroll preserving
- * [4] Keyboard navigation
- * [5] Right sidebar scroll highlighting
- */
- const togglerId = 'js-sidebar-toggle'
- const textbookId = 'js-textbook'
- const togglerActiveClass = 'is-active'
- const textbookActiveClass = 'js-show-sidebar'
- const mathRenderedClass = 'js-mathjax-rendered'
- const icon_path = document.location.origin + `${site_basename}assets`;
- const getToggler = () => document.getElementById(togglerId)
- const getTextbook = () => document.getElementById(textbookId)
- // [1] Run MathJax when Turbolinks navigates to a page.
- // When Turbolinks caches a page, it also saves the MathJax rendering. We mark
- // each page with a CSS class after rendering to prevent double renders when
- // navigating back to a cached page.
- document.addEventListener('turbolinks:load', () => {
- const textbook = getTextbook()
- if (window.MathJax && !textbook.classList.contains(mathRenderedClass)) {
- MathJax.Hub.Queue(['Typeset', MathJax.Hub])
- textbook.classList.add(mathRenderedClass)
- }
- })
- /**
- * [2] Toggles sidebar and menu icon
- */
- const toggleSidebar = () => {
- const toggler = getToggler()
- const textbook = getTextbook()
- if (textbook.classList.contains(textbookActiveClass)) {
- textbook.classList.remove(textbookActiveClass)
- toggler.classList.remove(togglerActiveClass)
- } else {
- textbook.classList.add(textbookActiveClass)
- toggler.classList.add(togglerActiveClass)
- }
- }
- /**
- * Keep the variable below in sync with the tablet breakpoint value in
- * _sass/inuitcss/tools/_tools.mq.scss
- *
- */
- const autoCloseSidebarBreakpoint = 740
- // Set up event listener for sidebar toggle button
- const sidebarButtonHandler = () => {
- getToggler().addEventListener('click', toggleSidebar)
- /**
- * Auto-close sidebar on smaller screens after page load.
- *
- * Having the sidebar be open by default then closing it on page load for
- * small screens gives the illusion that the sidebar closes in response
- * to selecting a page in the sidebar. However, it does cause a bit of jank
- * on the first page load.
- *
- * Since we don't want to persist state in between page navigation, this is
- * the best we can do while optimizing for larger screens where most
- * viewers will read the textbook.
- *
- * The code below assumes that the sidebar is open by default.
- */
- if (window.innerWidth < autoCloseSidebarBreakpoint) toggleSidebar()
- }
- initFunction(sidebarButtonHandler);
- /**
- * [3] Preserve sidebar scroll when navigating between pages
- */
- let sidebarScrollTop = 0
- const getSidebar = () => document.getElementById('js-sidebar')
- document.addEventListener('turbolinks:before-visit', () => {
- sidebarScrollTop = getSidebar().scrollTop
- })
- document.addEventListener('turbolinks:load', () => {
- getSidebar().scrollTop = sidebarScrollTop
- })
- /**
- * Focus textbook page by default so that user can scroll with spacebar
- */
- const focusPage = () => {
- document.querySelector('.c-textbook__page').focus()
- }
- initFunction(focusPage);
- /**
- * [4] Use left and right arrow keys to navigate forward and backwards.
- */
- const LEFT_ARROW_KEYCODE = 37
- const RIGHT_ARROW_KEYCODE = 39
- const getPrevUrl = () => document.getElementById('js-page__nav__prev').href
- const getNextUrl = () => document.getElementById('js-page__nav__next').href
- const initPageNav = (event) => {
- const keycode = event.which
- if (keycode === LEFT_ARROW_KEYCODE) {
- Turbolinks.visit(getPrevUrl())
- } else if (keycode === RIGHT_ARROW_KEYCODE) {
- Turbolinks.visit(getNextUrl())
- }
- };
- var keyboardListener = false;
- const initListener = () => {
- if (keyboardListener === false) {
- document.addEventListener('keydown', initPageNav)
- keyboardListener = true;
- }
- }
- initFunction(initListener);
- /**
- * [5] Right sidebar scroll highlighting
- */
- highlightRightSidebar = function() {
- var position = document.querySelector('.c-textbook__page').scrollTop;
- position = position + (window.innerHeight / 4); // + Manual offset
- // Highlight the "active" menu item
- document.querySelectorAll('.c-textbook__content h2, .c-textbook__content h3').forEach((header, index) => {
- var target = header.offsetTop;
- var id = header.id;
- if (position >= target) {
- var query = 'ul.toc__menu a[href="#' + id + '"]';
- document.querySelectorAll('ul.toc__menu li').forEach((item) => {item.classList.remove('active')});
- document.querySelectorAll(query).forEach((item) => {item.parentElement.classList.add('active')});
- }
- });
- document.querySelector('.c-textbook__page').addEventListener('scroll', highlightRightSidebar);
- };
- initFunction(highlightRightSidebar);
|