packages/redoc-core/lib/plugins/toc.js
import _ from "underscore";
function isBlankString(str) {
return !!(str || "").match(/^\s*$/);
}
function replaceAttr(token, attrName, replace, env) {
token.attrs.forEach(function (attr) {
if (attr[0] === attrName) {
attr[1] = replace(attr[1], env, token);
}
});
}
function slugify(value) {
return value.toLowerCase()
.replace(/[^\w\s-]/g, "") // remove non-word [a-z0-9_], non-whitespace, non-hyphen characters
.replace(/[\s_-]+/g, "-") // swap any length of whitespace, underscore, hyphen characters with a single -
.replace(/^-+|-+$/g, ""); // remove leading, trailing -
}
function TOCParser(md) {
md.core.ruler.after(
"inline",
"replace-link",
function (state) {
// var replace = md.options.replaceLink;
const documentTOC = [];
state.tokens.forEach(function (blockToken, index) {
// Look for open header tags
if (blockToken.type === "heading_open") {
const nextToken = state.tokens[index + 1];
const headingLevel = parseInt(blockToken.tag.replace("h", ""), 10);
if (headingLevel < 2 || headingLevel > 3) {
return;
}
// This is the content of the heading
if (nextToken.type === "inline" && nextToken.children) {
for (const node of nextToken.children) {
if (node.type === "text" || nextToken.type === "inline") {
const data = {
level: headingLevel,
content: node.content,
slug: slugify(node.content)
};
const { alias, repo } = state.env;
const slug = slugify(node.content);
if (_.isFunction(md.options.processTOCHeadings)) {
md.options.processTOCHeadings(data, state.env);
}
blockToken.attrs = [["id", slug]];
if (!isBlankString(node.content)) {
documentTOC.push({
className: `guide-sub-subnav-item level-${headingLevel}`,
label: node.content || "--",
slug
});
}
break;
}
}
}
}
});
if (_.isFunction(md.options.documentTOC)) {
md.options.documentTOC(documentTOC, state.env);
}
return false;
}
);
}
export default TOCParser;