Skip to content

Commit

Permalink
docs: include a table of contents
Browse files Browse the repository at this point in the history
  • Loading branch information
ethomson committed Oct 24, 2020
1 parent 84f42af commit c3f56cf
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 2 deletions.
71 changes: 69 additions & 2 deletions docs/dockhand.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,15 @@ function translate(childPath) {
const html = template.replace(/\{\{\s*([\w\.]+)\s*\}\}/g, (token, key) => {
switch (key) {
case 'content':
return content;
return `<div id="_content">${content}</div>`;
case 'path':
return childPath;
case 'url_path':
return encodeURI(childPath);

case 'toc':
return '<div id="_table_of_contents"></div>';

case 'title':
case 'section':
case 'description':
Expand Down Expand Up @@ -141,20 +144,84 @@ function translate(childPath) {
let headerId = headerText;
let headerIncrement = 1;

while (headerIds.includes(headerId)) {
while (document.getElementById(headerId) !== null) {
headerId = headerText + (++headerIncrement);
}

headerIds.push(headerId);
header.setAttribute('id', headerId);
}

// Walk the dom and build a table of contents
const toc = document.getElementById('_table_of_contents');

if (toc) {
toc.appendChild(generateTableOfContents(document));
}

// Write the final output
const output = dom.serialize();

mkdirp.sync(path.dirname(outputPath));
fs.writeFileSync(outputPath, output);
}

function generateTableOfContents(document) {
const headers = [ ];
walkHeaders(document.getElementById('_content'), headers);

let parent = null;

// The nesting depth of headers are not necessarily the header level.
// (eg, h1 > h3 > h5 is a depth of three even though there's an h5.)
const hierarchy = [ ];
for (let header of headers) {
const level = headerLevel(header);

while (hierarchy.length && hierarchy[hierarchy.length - 1].headerLevel > level) {
hierarchy.pop();
}

if (!hierarchy.length || hierarchy[hierarchy.length - 1].headerLevel < level) {
const newList = document.createElement('ul');
newList.headerLevel = level;

if (hierarchy.length) {
hierarchy[hierarchy.length - 1].appendChild(newList);
}

hierarchy.push(newList);
}

const element = document.createElement('li');

const link = document.createElement('a');
link.setAttribute('href', `#${header.getAttribute('id')}`);
link.innerHTML = header.innerHTML;
element.appendChild(link);

const list = hierarchy[hierarchy.length - 1];
list.appendChild(element);
}

return hierarchy[0];
}

function walkHeaders(element, headers) {
for (let child of element.childNodes) {
if (headerLevel(child)) {
headers.push(child);
}

walkHeaders(child, headers);
}
}

function headerLevel(node) {
const level = node.tagName ? node.tagName.match(/^[Hh]([123456])$/) : null;
return level ? level[1] : 0;
}

function debug(str) {
console.log(str);
}
26 changes: 26 additions & 0 deletions docs/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@
padding: 0 4em;
}

#table_of_contents > h2 {
font-size: 1.17em;
}
#table_of_contents ul:first-child {
border: solid 1px #e1e4e8;
border-radius: 6px;
padding: 1em;
background-color: #f6f8fa;
color: #393a34;
}
#table_of_contents ul {
list-style-type: none;
padding-left: 1.5em;
}
#table_of_contents li {
font-size: 0.9em;
}
#table_of_contents li a {
color: #000000;
}

header.title {
border-bottom: solid 1px #e1e4e8;
}
Expand Down Expand Up @@ -119,6 +140,11 @@ <h1>{{ title }}</h1>
<span class="description">{{ description }}</span>
</header>

<section id="table_of_contents">
<h2>Table of contents</h2>
{{ toc }}
</section>

{{ content }}

<footer id="edit">
Expand Down

0 comments on commit c3f56cf

Please sign in to comment.