Kanka Automatic Table of Contents

Automatically adds a table of contents to Kanka entity pages under the Pins sidebar.

目前為 2021-07-15 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Kanka Automatic Table of Contents
// @namespace    http://tampermonkey.net/
// @version      1
// @description  Automatically adds a table of contents to Kanka entity pages under the Pins sidebar.
// @author       Salvatos
// @match        https://kanka.io/*
// @icon         https://www.google.com/s2/favicons?domain=kanka.io
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle(`
.entity-links {
	margin-bottom: 20px;
}
div#toc h3 {
    text-align: center;
    margin: 0 0 10px;
    font-family: Source Sans Pro,Helvetica Neue,Helvetica,Arial,sans-serif;
    font-weight: bold;
    font-size: 17px;
    color: #444;
}
#tableofcontent, #tableofcontent ul {
    list-style: none;
    padding: 0;
    text-indent: -5px;
}
#tableofcontent {
    padding: 0 10px;
    overflow: hidden;
    word-wrap: anywhere;
}
#tableofcontent ul {
    padding-left: 10px;
}
#tableofcontent a {
    font-size: 14px;
    color: #3e456c;
}
#tableofcontent li.toc-level-0 a {
    font-weight: bold;
}
.to-top {
    vertical-align: super;
    font-variant: all-petite-caps;
    font-size: 10px;
}
`);

/* Set arrays */
var headings = [];
var tag_names = { h1:1, h2:1, h3:1, h4:1, h5:1, h6:1 };

/* Walk through DOM looking for selected elements */
function walk( root ) {
    if( root.nodeType === 1 && root.nodeName !== 'script' ) {
        if( tag_names.hasOwnProperty(root.nodeName.toLowerCase()) ) {
            headings.push( root );
        } else {
            for( var i = 0; i < root.childNodes.length; i++ ) {
                walk( root.childNodes[i] );
            }
        }
    }
}
walk( document.getElementsByClassName('entity-story-block')[0] );

/* Start main list */
var level =0;
var past_level = 0;
var hList= "<div id='toc' class='box box-solid' style='padding:10px;'><h3>Table of contents</h3><ul id='tableofcontent'>";

/* Create sublists to reflect heading level */
for( var i = 0; i < headings.length; i++ ) {
    // "Entry" and entity note titles act as level-0 headers
    level = (headings[i].classList.contains("box-title")) ? 0 : headings[i].nodeName.substr(1);

    if (level > past_level) {
        for(var j = 0; j < level - past_level; j++) {
            hList += "<li><ul>";
        }
    }
    else if (level < past_level) {
        for(var j = 0; j < past_level - level; j++) {
            hList += "</ul></li>";
        }
    }

    /* Create ID anchor and links */
    headings[i].id = "h" + level + "-" + headings[i].innerText.replace(/\s/g, "");
    hList += "<li class='toc-level-" + level + "'><a href='#" + headings[i].id + "'>" + headings[i].innerText + "</a></li>";
    headings[i].insertAdjacentHTML("beforeend", "<a class='to-top' href='#toc'> ^ top</a>");
    past_level = level;
}

/* Close sublists per current level */
for(var k = 0; k < past_level; k++) {
    hList += "</li></ul>";
}
hList += "</div>";

/* Insert element after Pins (and entity links) */
document.getElementsByClassName('entity-sidebar-pins')[0].insertAdjacentHTML("beforeend", hList);