{"version":3,"sources":["gumshoe-5.1.2-patched.js","main.js"],"names":["root","factory","define","amd","exports","_typeof","module","Gumshoe","global","window","emitEvent","type","elem","detail","dispatchEvent","event","CustomEvent","bubbles","cancelable","location","getOffsetTop","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","settings","bottom","bounds","getBoundingClientRect","offset","parseFloat","parseInt","isAtBottom","innerHeight","documentElement","clientHeight","top","useLastItem","pageYOffset","Math","max","document","body","scrollHeight","offsetHeight","getActive","item","length","last","deactivateNested","i","nav","nested","parentNode","li","closest","classList","remove","nestedClass","items","navClass","contentClass","activateNested","add","events","reflow","selector","options","publicAPIs","detect","querySelectorAll","deactivate","current","getElementById","decodeURIComponent","hash","substr","push","active","scrollHandler","timeout","cancelAnimationFrame","link","requestAnimationFrame","removeEventListener","destroy","setup","addEventListener","resizeHandler","navItems","merged","Array","prototype","forEach","call","arguments","obj","key","hasOwnProperty","extend","defaults","tocScroll","header","floor","scrollHandlerForHeader","scrollHandlerForTOC","positionY","scrollTo","ceil","querySelector","mode","error","console","localStorage","setItem","theme","log","concat","currentTheme","prefersDark","matchMedia","setTheme","matches","setupScrollHandler","ticking","e","last_known_scroll_position","scrollY","setupScrollSpy","recursive","setupTheme","from","buttons","getElementsByClassName","btn","cycleThemeOnce","main"],"mappings":"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","file":"main.js","sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1))\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n }\n);\n","////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n var last_known_scroll_position = 0;\n var ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n var spy = new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n });\n}\n\nfunction setupTheme() {\n // Set the current theme to auto.\n setTheme(localStorage.getItem(\"theme\") || \"auto\");\n\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"]}