Skip to content

Instantly share code, notes, and snippets.

@SJongeJongeJonge
Last active April 27, 2021 13:39
Show Gist options
  • Save SJongeJongeJonge/c6c92707e70f60a0c3187b4e70bd3215 to your computer and use it in GitHub Desktop.
Save SJongeJongeJonge/c6c92707e70f60a0c3187b4e70bd3215 to your computer and use it in GitHub Desktop.
devhints.io TOC userscript
// ==UserScript==
// @name devhints.io TOC
// @description 26-4-2021 23:12:54 Adds a table of contents to the devhint cheatsheets.
// @version 1.0
// @author SJongeJongeJonge
// @match http*://devhints.io/*
// @exclude http*://devhints.io/
// @run-at document-end
// @grant none
// @downloadURL https://gist.github.com/SJongeJongeJonge/c6c92707e70f60a0c3187b4e70bd3215/raw/devhints-toc-userscript.js
// @updateURL https://gist.github.com/SJongeJongeJonge/c6c92707e70f60a0c3187b4e70bd3215/raw/devhints-toc-userscript.js
// @supportURL https://gist.github.com/SJongeJongeJonge/c6c92707e70f60a0c3187b4e70bd3215#comments
// ==/UserScript==
// TODO: make toc-list an actual list element
// TODO: make toc a nav element
const css = `
#toc {
position: fixed;
margin-left: 3em;
}
#toc h2 {
color: #89a;
}
.toc-list > a {
display: block;
line-height: 2em;
}
`
let createStyleNode = cssText => {
let elem = document.createElement('style')
elem.innerText = cssText
return elem
}
let getAnchorRef = anchor => anchor.href
let getSectionName = anchor => anchor.parentElement.textContent
let sectionFromElem = elem => ({ 'name': getSectionName(elem), 'href': getAnchorRef(elem) })
let shouldRemoveFirstSection = anchors => {
if (anchors.length < 1) return false
return !anchors[0]
.parentElement
.parentElement
.classList
.contains('-no-hide')
}
let createTOCItem = ({name, href}) => {
let elem = document.createElement('a')
elem.textContent = name
elem.href = href
return elem
}
let createTOCBase = () => {
let base = document.createElement('div')
base.id = 'toc'
let title = document.createElement('h2')
title.innerText = "Table of Contents"
base.appendChild(title)
return base
}
let createTOCList = () => {
let elem = document.createElement('div')
elem.classList.add('toc-list')
return elem
}
let main = () => {
let topNavs = document.getElementsByClassName('top-nav')
let anchors = document.getElementsByClassName('anchor')
let sections = [{ name: '#Top', href: '#'}]
for (elem of anchors) {
sections.push(sectionFromElem(elem))
}
if (shouldRemoveFirstSection(anchors)) {
sections.splice(1, 1)
}
let tocBase = createTOCBase()
let tocList = createTOCList()
sections.map(createTOCItem)
.forEach(elem => tocList.appendChild(elem))
tocBase.appendChild(tocList)
if (topNavs.length === 0) {
console.error('No top-nav element found, unable to add Table of Contents')
return;
}
let topNav = topNavs[0];
topNav.insertAdjacentElement("afterend", tocBase)
document.head.appendChild(createStyleNode(css))
}
/* Wait for the page to be formatted before reading the sections and adding the toc. */
let observerCallback = (mutationList, observer) => {
mutationList.forEach( mutation => {
if (mutation.target.classList.contains('LoadDone')) {
setTimeout(main)
observer.disconnect()
}
})
}
if (document.documentElement.classList.contains('LoadDone')) {
setTimeout(main)
} else {
let observer = new MutationObserver(observerCallback)
observer.observe(document.documentElement, {
attributeFilter: [ 'class' ]
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment