UNPKG

markdown-it-toc-done-right

Version:

Table of contents (TOC) for markdown-it markdown parser with focus on semantic and security.

1 lines 10.8 kB
{"version":3,"file":"markdownItTocDoneRight.mjs","sources":["../index.js"],"sourcesContent":["'use strict'\n\nfunction slugify (x) {\n return encodeURIComponent(String(x).trim().toLowerCase().replace(/\\s+/g, '-'))\n}\n\nfunction htmlencode (x) {\n/*\n // safest, delegate task to native -- IMPORTANT: enabling this breaks both jest and runkit, but with browserify it's fine\n if (document && document.createElement) {\n const el = document.createElement(\"div\")\n el.innerText = x\n return el.innerHTML\n }\n*/\n\n return String(x)\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nfunction tocPlugin (md, options) {\n options = Object.assign({}, {\n placeholder: '(\\\\$\\\\{toc\\\\}|\\\\[\\\\[?_?toc_?\\\\]?\\\\]|\\\\$\\\\<toc(\\\\{[^}]*\\\\})\\\\>)',\n slugify: slugify,\n uniqueSlugStartIndex: 1,\n containerClass: 'table-of-contents',\n containerId: undefined,\n listClass: undefined,\n itemClass: undefined,\n linkClass: undefined,\n level: 1,\n listType: 'ol',\n format: undefined,\n callback: undefined/* function(html, ast) {} */\n }, options)\n\n let ast\n const pattern = new RegExp('^' + options.placeholder + '$', 'i')\n\n function toc (state, startLine, endLine, silent) {\n let token\n const pos = state.bMarks[startLine] + state.tShift[startLine]\n const max = state.eMarks[startLine]\n\n // use whitespace as a line tokenizer and extract the first token\n // to test against the placeholder anchored pattern, rejecting if false\n const lineFirstToken = state.src.slice(pos, max).split(' ')[0]\n if (!pattern.test(lineFirstToken)) return false\n\n if (silent) return true\n\n const matches = pattern.exec(lineFirstToken)\n let inlineOptions = {}\n if (matches !== null && matches.length === 3) {\n try {\n inlineOptions = JSON.parse(matches[2])\n } catch (ex) {\n // silently ignore inline options\n }\n }\n\n state.line = startLine + 1\n\n token = state.push('tocOpen', 'nav', 1)\n token.markup = ''\n token.map = [startLine, state.line]\n token.inlineOptions = inlineOptions\n\n token = state.push('tocBody', '', 0)\n token.markup = ''\n token.map = [startLine, state.line]\n token.inlineOptions = inlineOptions\n token.children = []\n\n token = state.push('tocClose', 'nav', -1)\n token.markup = ''\n\n return true\n }\n\n md.renderer.rules.tocOpen = function (tokens, idx/* , options, env, renderer */) {\n let _options = Object.assign({}, options)\n if (tokens && idx >= 0) {\n const token = tokens[idx]\n _options = Object.assign(_options, token.inlineOptions)\n }\n const id = _options.containerId ? ` id=\"${htmlencode(_options.containerId)}\"` : ''\n return `<nav${id} class=\"${htmlencode(_options.containerClass)}\">`\n }\n\n md.renderer.rules.tocClose = function (/* tokens, idx, options, env, renderer */) {\n return '</nav>'\n }\n\n md.renderer.rules.tocBody = function (tokens, idx/* , options, env, renderer */) {\n let _options = Object.assign({}, options)\n if (tokens && idx >= 0) {\n const token = tokens[idx]\n _options = Object.assign(_options, token.inlineOptions)\n }\n\n const uniques = {}\n function unique (s) {\n let u = s\n let i = _options.uniqueSlugStartIndex\n while (Object.prototype.hasOwnProperty.call(uniques, u)) u = `${s}-${i++}`\n uniques[u] = true\n return u\n }\n\n const isLevelSelectedNumber = selection => level => level >= selection\n const isLevelSelectedArray = selection => level => selection.includes(level)\n\n const isLevelSelected = Array.isArray(_options.level)\n ? isLevelSelectedArray(_options.level)\n : isLevelSelectedNumber(_options.level)\n\n function ast2html (tree) {\n const listClass = _options.listClass ? ` class=\"${htmlencode(_options.listClass)}\"` : ''\n const itemClass = _options.itemClass ? ` class=\"${htmlencode(_options.itemClass)}\"` : ''\n const linkClass = _options.linkClass ? ` class=\"${htmlencode(_options.linkClass)}\"` : ''\n\n if (tree.c.length === 0) return ''\n\n let buffer = ''\n if (tree.l === 0 || isLevelSelected(tree.l)) {\n buffer += (`<${htmlencode(_options.listType) + listClass}>`)\n }\n tree.c.forEach(node => {\n if (isLevelSelected(node.l)) {\n buffer += (`<li${itemClass}><a${linkClass} href=\"#${unique(options.slugify(node.n))}\">${typeof _options.format === 'function' ? _options.format(node.n, htmlencode) : htmlencode(node.n)}</a>${ast2html(node)}</li>`)\n } else {\n buffer += ast2html(node)\n }\n })\n if (tree.l === 0 || isLevelSelected(tree.l)) {\n buffer += (`</${htmlencode(_options.listType)}>`)\n }\n return buffer\n }\n\n return ast2html(ast)\n }\n\n function headings2ast (tokens) {\n const ast = { l: 0, n: '', c: [] }\n const stack = [ast]\n\n for (let i = 0, iK = tokens.length; i < iK; i++) {\n const token = tokens[i]\n if (token.type === 'heading_open') {\n const key = (\n tokens[i + 1]\n .children\n .filter(function (token) { return token.type === 'text' || token.type === 'code_inline' })\n .reduce(function (s, t) { return s + t.content }, '')\n )\n\n const node = {\n l: parseInt(token.tag.substr(1), 10),\n n: key,\n c: []\n }\n\n if (node.l > stack[0].l) {\n stack[0].c.push(node)\n stack.unshift(node)\n } else if (node.l === stack[0].l) {\n stack[1].c.push(node)\n stack[0] = node\n } else {\n while (node.l <= stack[0].l) stack.shift()\n stack[0].c.push(node)\n stack.unshift(node)\n }\n }\n }\n\n return ast\n }\n\n md.core.ruler.push('generateTocAst', function (state) {\n const tokens = state.tokens\n ast = headings2ast(tokens)\n\n if (typeof options.callback === 'function') {\n options.callback(\n md.renderer.rules.tocOpen() + md.renderer.rules.tocBody() + md.renderer.rules.tocClose(),\n ast\n )\n }\n })\n\n md.block.ruler.before('heading', 'toc', toc, {\n alt: ['paragraph', 'reference', 'blockquote']\n })\n}\n\nexport default tocPlugin\n"],"names":["slugify","x","encodeURIComponent","String","trim","toLowerCase","replace","htmlencode","md","options","ast","Object","assign","placeholder","uniqueSlugStartIndex","containerClass","containerId","undefined","listClass","itemClass","linkClass","level","listType","format","callback","pattern","RegExp","renderer","rules","tocOpen","tokens","idx","_options","inlineOptions","tocClose","tocBody","selection","uniques","isLevelSelected","Array","isArray","includes","isLevelSelectedNumber","ast2html","tree","c","length","buffer","l","forEach","node","s","u","i","prototype","hasOwnProperty","call","unique","n","core","ruler","push","state","stack","iK","token","type","key","children","filter","reduce","t","content","parseInt","tag","substr","unshift","shift","headings2ast","block","before","startLine","endLine","silent","lineFirstToken","src","slice","bMarks","tShift","eMarks","split","test","matches","exec","JSON","parse","ex","line","markup","map","alt"],"mappings":"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"}