diff --git a/docs/dockhand.js b/docs/dockhand.js
index 5594ece73e24f..41d01b1cff685 100644
--- a/docs/dockhand.js
+++ b/docs/dockhand.js
@@ -75,12 +75,15 @@ function translate(childPath) {
const html = template.replace(/\{\{\s*([\w\.]+)\s*\}\}/g, (token, key) => {
switch (key) {
case 'content':
- return content;
+ return `
${content}
`;
case 'path':
return childPath;
case 'url_path':
return encodeURI(childPath);
+ case 'toc':
+ return '';
+
case 'title':
case 'section':
case 'description':
@@ -141,7 +144,7 @@ function translate(childPath) {
let headerId = headerText;
let headerIncrement = 1;
- while (headerIds.includes(headerId)) {
+ while (document.getElementById(headerId) !== null) {
headerId = headerText + (++headerIncrement);
}
@@ -149,12 +152,76 @@ function translate(childPath) {
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);
}
diff --git a/docs/template.html b/docs/template.html
index 1a44953acb739..fea1eb949517c 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -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;
}
@@ -119,6 +140,11 @@ {{ title }}
{{ description }}
+
+Table of contents
+{{ toc }}
+
+
{{ content }}