summaryrefslogtreecommitdiff
path: root/website/showdown_toc.js
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2019-02-11 17:41:13 -0500
committerGitHub <noreply@github.com>2019-02-11 17:41:13 -0500
commita4dec944bc821d114bfd82debb72d60bd04f836d (patch)
tree5e090d3b19e396baec5dddaa81a6cfa5ff96addb /website/showdown_toc.js
parent489c69f8e1c15c75461d24d91aa3e9c2a756e807 (diff)
web design (#1728)
Diffstat (limited to 'website/showdown_toc.js')
-rw-r--r--website/showdown_toc.js142
1 files changed, 142 insertions, 0 deletions
diff --git a/website/showdown_toc.js b/website/showdown_toc.js
new file mode 100644
index 000000000..ba1f65c71
--- /dev/null
+++ b/website/showdown_toc.js
@@ -0,0 +1,142 @@
+// Extension loading compatible with AMD and CommonJs
+(function(extension) {
+ "use strict";
+
+ if (typeof showdown === "object") {
+ // global (browser or nodejs global)
+ showdown.extension("toc", extension());
+ } else if (typeof define === "function" && define.amd) {
+ // AMD
+ define("toc", extension());
+ } else if (typeof exports === "object") {
+ // Node, CommonJS-like
+ module.exports = extension();
+ } else {
+ // showdown was not found so we throw
+ throw Error("Could not find showdown library");
+ }
+})(function() {
+ function getHeaderEntries(sourceHtml) {
+ if (typeof window === "undefined") {
+ return getHeaderEntriesInNodeJs(sourceHtml);
+ } else {
+ return getHeaderEntriesInBrowser(sourceHtml);
+ }
+ }
+
+ function getHeaderEntriesInNodeJs(sourceHtml) {
+ var cheerio = require("cheerio");
+ var $ = cheerio.load(sourceHtml);
+ var headers = $("h1, h2, h3, h4, h5, h6");
+
+ var headerList = [];
+ for (var i = 0; i < headers.length; i++) {
+ var el = headers[i];
+ headerList.push(new TocEntry(el.name, $(el).text(), $(el).attr("id")));
+ }
+
+ return headerList;
+ }
+
+ function getHeaderEntriesInBrowser(sourceHtml) {
+ // Generate dummy element
+ var source = document.createElement("div");
+ source.innerHTML = sourceHtml;
+
+ // Find headers
+ var headers = source.querySelectorAll("h1, h2, h3, h4, h5, h6");
+ var headerList = [];
+ for (var i = 0; i < headers.length; i++) {
+ var el = headers[i];
+ headerList.push(new TocEntry(el.tagName, el.textContent, el.id));
+ }
+
+ return headerList;
+ }
+
+ function TocEntry(tagName, text, anchor) {
+ this.tagName = tagName;
+ this.text = text;
+ this.anchor = anchor;
+ this.children = [];
+ }
+
+ TocEntry.prototype.childrenToString = function() {
+ if (this.children.length === 0) {
+ return "";
+ }
+ var result = "<ul>\n";
+ for (var i = 0; i < this.children.length; i++) {
+ result += this.children[i].toString();
+ }
+ result += "</ul>\n";
+ return result;
+ };
+
+ TocEntry.prototype.toString = function() {
+ var result = "<li>";
+ if (this.text) {
+ result += '<a href="#' + this.anchor + '">' + this.text + "</a>";
+ }
+ result += this.childrenToString();
+ result += "</li>\n";
+ return result;
+ };
+
+ function sortHeader(tocEntries, level) {
+ level = level || 1;
+ var tagName = "H" + level,
+ result = [],
+ currentTocEntry;
+
+ function push(tocEntry) {
+ if (tocEntry !== undefined) {
+ if (tocEntry.children.length > 0) {
+ tocEntry.children = sortHeader(tocEntry.children, level + 1);
+ }
+ result.push(tocEntry);
+ }
+ }
+
+ for (var i = 0; i < tocEntries.length; i++) {
+ var tocEntry = tocEntries[i];
+ if (tocEntry.tagName.toUpperCase() !== tagName) {
+ if (currentTocEntry === undefined) {
+ currentTocEntry = new TocEntry();
+ }
+ currentTocEntry.children.push(tocEntry);
+ } else {
+ push(currentTocEntry);
+ currentTocEntry = tocEntry;
+ }
+ }
+
+ push(currentTocEntry);
+ return result;
+ }
+
+ return {
+ type: "output",
+ filter: function(sourceHtml) {
+ var headerList = getHeaderEntries(sourceHtml);
+
+ // No header found
+ if (headerList.length === 0) {
+ return sourceHtml;
+ }
+
+ // Sort header
+ headerList = sortHeader(headerList);
+
+ // Skip the title.
+ if (headerList.length == 1) {
+ headerList = headerList[0].children;
+ }
+
+ // Build result and replace all [toc]
+ var result =
+ '<div class="toc">\n<ul>\n' + headerList.join("") + "</ul>\n</div>\n";
+ return sourceHtml.replace(/\[toc\]/gi, result);
+ }
+ };
+});