├── .eslintignore ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── __test__ └── test.js ├── demo.html ├── dist ├── markdownItTocDoneRight.js ├── markdownItTocDoneRight.js.map ├── markdownItTocDoneRight.mjs ├── markdownItTocDoneRight.mjs.map ├── markdownItTocDoneRight.modern.js ├── markdownItTocDoneRight.modern.js.map ├── markdownItTocDoneRight.umd.js └── markdownItTocDoneRight.umd.js.map ├── index.js ├── package-lock.json ├── package.json ├── runkit.js ├── tdd.helper.html ├── types └── index.d.ts ├── uslug.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | lib 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | - "14" 5 | after_success: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 6 | cache: 7 | directories: 8 | - "node_modules" 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [4.2.0] - 2020-11-19 10 | ### Added 11 | - implemented `uniqueSlugStartIndex` 12 | 13 | ## [4.1.0] - 2019-10-17 14 | ### Added 15 | - accept options in the placeholder [#16](https://github.com/nagaozen/markdown-it-toc-done-right/issues/16) 16 | - `callback` option [#18](https://github.com/nagaozen/markdown-it-toc-done-right/issues/18) 17 | ### Changed 18 | - fixed [#20](https://github.com/nagaozen/markdown-it-toc-done-right/issues/20) 19 | - fixed [#22](https://github.com/nagaozen/markdown-it-toc-done-right/issues/22) 20 | - `test.js` to match the changes from `markdown-it-anchor` 21 | - `headings2ast` removed options dependency (will generate a tree with all headings) 22 | - `ast2html` modified to hold and apply almost all options, making `inlineOptions` possible 23 | 24 | ## [4.0.2] - 2019-07-11 25 | ### Changed 26 | - fixed [#14](https://github.com/nagaozen/markdown-it-toc-done-right/issues/14) 27 | - updated dependencies, especially microbundle. Fix [#12](https://github.com/nagaozen/markdown-it-toc-done-right/issues/12)? 28 | - executed `npm audit fix --force` to fix dependencies vunerabilities 29 | 30 | ## [4.0.1] - 2019-06-03 31 | ### Changed 32 | - executed `npm audit fix --force` to fix dependencies vunerabilities 33 | 34 | ## [4.0.0] - 2019-05-19 35 | ### Changed 36 | - browser global changed from `markdownitTocDoneRight` to `markdownItTocDoneRight` 37 | - filename changed from `markdown-it-toc-made-right.min.js` to `markdownItTocDoneRight.umd.js` 38 | ### Removed 39 | - `babel` 40 | - `babelify` 41 | - `browserify` 42 | - `eslint` 43 | - `uglify` 44 | ### Added 45 | - `microbundle` 46 | - `standard` 47 | 48 | ## [3.0.0] - 2019-03-18 49 | ### Added 50 | - `listClass`, `itemClass` and `linkClass` options. 51 | ### Changed 52 | - placeholder as a regular expression pattern 53 | 54 | ## [2.1.0] - 2019-02-23 55 | ### Added 56 | - `level` option. Default is 1 57 | - test for using `level` option 58 | ### Changed 59 | - update `demo.html` to test the new level option 60 | 61 | ## [2.0.3] - 2018-06-19 62 | ### Changed 63 | - update to keep compat with `markdown-it-anchor` `v5.0.2` 64 | 65 | ## [2.0.2] - 2018-06-15 66 | ### Changed 67 | - tests cover 100% 68 | 69 | ## [2.0.0] - 2018-06-14 70 | ### Changed 71 | - dropped package `string` as dependency 72 | 73 | ## [1.0.5] - 2018-06-12 74 | ### Added 75 | - `markdown-it` as `peerDependencies` 76 | 77 | ### Changed 78 | - better `headings_ast(tokens)` with support for skipping heading ranks 79 | 80 | ## [1.0.4] - 2018-06-11 81 | ### Fixed 82 | - tests 83 | 84 | ## [1.0.3] - 2018-06-08 85 | ### Changed 86 | - **WAI-ARIA** `role='navigation'` isn't required 87 | 88 | ## [1.0.2] - 2018-06-04 89 | ### Added 90 | - travis 91 | 92 | ## [1.0.1] - 2018-06-04 93 | ### Added 94 | - linting 95 | - testing 96 | - runkit 97 | - unicode support example 98 | 99 | [Unreleased]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v4.1.0...HEAD 100 | [4.1.0]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v4.0.2...v4.1.0 101 | [4.0.2]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v4.0.1...v4.0.2 102 | [4.0.1]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v4.0.0...v4.0.1 103 | [4.0.0]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v3.0.0...v4.0.0 104 | [3.0.0]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v2.1.0...v3.0.0 105 | [2.1.0]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v2.0.3...v2.1.0 106 | [2.0.3]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v2.0.2...v2.0.3 107 | [2.0.2]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v2.0.0...v2.0.2 108 | [2.0.0]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v1.0.5...v2.0.0 109 | [1.0.5]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v1.0.4...v1.0.5 110 | [1.0.4]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v1.0.3...v1.0.4 111 | [1.0.3]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v1.0.2...v1.0.3 112 | [1.0.2]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v1.0.1...v1.0.2 113 | [1.0.1]: https://github.com/nagaozen/markdown-it-toc-done-right/compare/v1.0.0...v1.0.1 114 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Fabio Zendhi Nagao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NPM_PACKAGE := $(shell node -e 'process.stdout.write(require("./package.json").name)') 2 | NPM_VERSION := $(shell node -e 'process.stdout.write(require("./package.json").version)') 3 | 4 | GITHUB_PROJ := https://github.com//nagaozen/${NPM_PACKAGE} 5 | 6 | 7 | 8 | build: 9 | rm -rf ./dist 10 | mkdir dist 11 | # Browserify 12 | ( printf "/*! ${NPM_PACKAGE} ${NPM_VERSION} ${GITHUB_PROJ} @license MIT */" ; \ 13 | npx browserify -s markdownitTocDoneRight -t babelify --presets [ "@babel/preset-env" ] . \ 14 | ) > dist/markdown-it-toc-made-right.js 15 | # Minify 16 | npx uglifyjs dist/markdown-it-toc-made-right.js -c -m \ 17 | --preamble "/*! ${NPM_PACKAGE} ${NPM_VERSION} ${GITHUB_PROJ} @license MIT */" \ 18 | > dist/markdown-it-toc-made-right.min.js 19 | 20 | upddemo: 21 | rm -rf ./lib 22 | mkdir lib 23 | curl -o lib/markdown-it-anchor.js https://wzrd.in/standalone/markdown-it-anchor@latest 24 | curl -o lib/uslug.js https://wzrd.in/standalone/uslug@latest 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # markdown-it-toc-done-right 2 | 3 | A table of contents (TOC) plugin for [Markdown-it](https://github.com/markdown-it/markdown-it) with focus on semantic and security. Made to work gracefully with [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor). 4 | 5 | [![Build Status](https://img.shields.io/travis/nagaozen/markdown-it-toc-done-right/master.svg?style=flat)](https://travis-ci.org/nagaozen/markdown-it-toc-done-right) 6 | [![Coverage Status](https://coveralls.io/repos/github/nagaozen/markdown-it-toc-done-right/badge.svg?branch=master)](https://coveralls.io/github/nagaozen/markdown-it-toc-done-right?branch=master) 7 | [![NPM version](https://img.shields.io/npm/v/markdown-it-toc-done-right.svg?style=flat)](vhttps://www.npmjs.org/package/markdown-it-toc-done-right) 8 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 9 | [![Try markdown-it-toc-done-right on RunKit](https://badge.runkitcdn.com/markdown-it-toc-done-right.svg)](https://npm.runkit.com/markdown-it-toc-done-right) 10 | 11 | ## tl;dr 12 | 13 | If you are drowning in table of contents plugins options, just pick this one. It delivers an **accessible**, **semantic**, **SEO friendly** and **safe** TOC. Place one of: `${toc}`, `[[toc]]`, `[toc]`, `[[_toc_]]` in your markdown and, BOOM, the `'\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 += (`${typeof _options.format === 'function' ? _options.format(node.n, htmlencode) : htmlencode(node.n)}${ast2html(node)}`)\n } else {\n buffer += ast2html(node)\n }\n })\n if (tree.l === 0 || isLevelSelected(tree.l)) {\n buffer += (``)\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":"AAEA,SAASA,EAASC,GAChB,OAAOC,mBAAmBC,OAAOF,GAAGG,OAAOC,cAAcC,QAAQ,OAAQ,MAG3E,SAASC,EAAYN,GAUnB,OAAOE,OAAOF,GACXK,QAAQ,KAAM,SACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,uBAGnB,SAAoBE,EAAIC,GAgBtB,IAAIC,EAfJD,EAAUE,OAAOC,OAAO,GAAI,CAC1BC,YAAa,iEACbb,QAASA,EACTc,qBAAsB,EACtBC,eAAgB,oBAChBC,iBAAaC,EACbC,eAAWD,EACXE,eAAWF,EACXG,eAAWH,EACXI,MAAO,EACPC,SAAU,KACVC,YAAQN,EACRO,cAAUP,GACTR,GAGH,IAAMgB,EAAU,IAAIC,OAAO,IAAMjB,EAAQI,YAAc,IAAK,KA2C5DL,EAAGmB,SAASC,MAAMC,QAAU,SAAUC,EAAQC,GAC5C,IAAIC,EAAWrB,OAAOC,OAAO,GAAIH,GAMjC,OALIqB,GAAUC,GAAO,IAEnBC,EAAWrB,OAAOC,OAAOoB,EADXF,EAAOC,GACoBE,wBAEhCD,EAAShB,oBAAsBT,EAAWyB,EAAShB,iBAAkB,eACrDT,EAAWyB,EAASjB,sBAGjDP,EAAGmB,SAASC,MAAMM,SAAW,WAC3B,MAAO,UAGT1B,EAAGmB,SAASC,MAAMO,QAAU,SAAUL,EAAQC,GAC5C,IAAIC,EAAWrB,OAAOC,OAAO,GAAIH,GAC7BqB,GAAUC,GAAO,IAEnBC,EAAWrB,OAAOC,OAAOoB,EADXF,EAAOC,GACoBE,gBAG3C,IAU6BG,EAVvBC,EAAU,GAYVC,EAAkBC,MAAMC,QAAQR,EAASX,QAFlBe,EAGJJ,EAASX,eAHQA,UAASe,EAAUK,SAASpB,KADxC,SAAAe,mBAAaf,UAASA,GAASe,GAKzDM,CAAsBV,EAASX,OA0BnC,OAxBA,SAASsB,EAAUC,GACjB,IAAM1B,EAAYc,EAASd,qBAAuBX,EAAWyB,EAASd,eAAgB,GAChFC,EAAYa,EAASb,qBAAuBZ,EAAWyB,EAASb,eAAgB,GAChFC,EAAYY,EAASZ,qBAAuBb,EAAWyB,EAASZ,eAAgB,GAEtF,GAAsB,IAAlBwB,EAAKC,EAAEC,OAAc,MAAO,GAEhC,IAAIC,EAAS,GAcb,OAbe,IAAXH,EAAKI,GAAWV,EAAgBM,EAAKI,MACvCD,QAAexC,EAAWyB,EAASV,UAAYJ,QAEjD0B,EAAKC,EAAEI,QAAQ,SAAAC,GACTZ,EAAgBY,EAAKF,GACvBD,SAAiB5B,QAAeC,aA5BtC,SAAiB+B,GAGf,IAFA,IAAIC,EAAID,EACJE,EAAIrB,EAASlB,qBACVH,OAAO2C,UAAUC,eAAeC,KAAKnB,EAASe,IAAIA,EAAOD,MAAKE,IAErE,OADAhB,EAAQe,IAAK,EACNA,EAuBiDK,CAAOhD,EAAQT,QAAQkD,EAAKQ,UAAmC,mBAApB1B,EAAST,OAAwBS,EAAST,OAAO2B,EAAKQ,EAAGnD,GAAcA,EAAW2C,EAAKQ,WAASf,EAASO,WAExMH,GAAUJ,EAASO,MAGR,IAAXN,EAAKI,GAAWV,EAAgBM,EAAKI,MACvCD,QAAgBxC,EAAWyB,EAASV,eAE/ByB,EAGFJ,CAASjC,IAwClBF,EAAGmD,KAAKC,MAAMC,KAAK,iBAAkB,SAAUC,GAE7CpD,EAvCF,SAAuBoB,GAIrB,IAHA,IAAMpB,EAAM,CAAEsC,EAAG,EAAGU,EAAG,GAAIb,EAAG,IACxBkB,EAAQ,CAACrD,GAEN2C,EAAI,EAAGW,EAAKlC,EAAOgB,OAAQO,EAAIW,EAAIX,IAAK,CAC/C,IAAMY,EAAQnC,EAAOuB,GACrB,GAAmB,iBAAfY,EAAMC,KAAyB,CACjC,IAAMC,EACJrC,EAAOuB,EAAI,GACRe,SACAC,OAAO,SAAUJ,GAAS,MAAsB,SAAfA,EAAMC,MAAkC,gBAAfD,EAAMC,OAChEI,OAAO,SAAUnB,EAAGoB,GAAK,OAAOpB,EAAIoB,EAAEC,SAAW,IAGhDtB,EAAO,CACXF,EAAGyB,SAASR,EAAMS,IAAIC,OAAO,GAAI,IACjCjB,EAAGS,EACHtB,EAAG,IAGL,GAAIK,EAAKF,EAAIe,EAAM,GAAGf,EACpBe,EAAM,GAAGlB,EAAEgB,KAAKX,GAChBa,EAAMa,QAAQ1B,WACLA,EAAKF,IAAMe,EAAM,GAAGf,EAC7Be,EAAM,GAAGlB,EAAEgB,KAAKX,GAChBa,EAAM,GAAKb,MACN,CACL,KAAOA,EAAKF,GAAKe,EAAM,GAAGf,GAAGe,EAAMc,QACnCd,EAAM,GAAGlB,EAAEgB,KAAKX,GAChBa,EAAMa,QAAQ1B,KAKpB,OAAOxC,EAKDoE,CADShB,EAAMhC,QAGW,mBAArBrB,EAAQe,UACjBf,EAAQe,SACNhB,EAAGmB,SAASC,MAAMC,UAAYrB,EAAGmB,SAASC,MAAMO,UAAY3B,EAAGmB,SAASC,MAAMM,WAC9ExB,KAKNF,EAAGuE,MAAMnB,MAAMoB,OAAO,UAAW,MA1JjC,SAAclB,EAAOmB,EAAWC,EAASC,GACvC,IAAIlB,EAMEmB,EAAiBtB,EAAMuB,IAAIC,MALrBxB,EAAMyB,OAAON,GAAanB,EAAM0B,OAAOP,GACvCnB,EAAM2B,OAAOR,IAIwBS,MAAM,KAAK,GAC5D,IAAKjE,EAAQkE,KAAKP,GAAiB,SAEnC,GAAID,EAAQ,SAEZ,IAAMS,EAAUnE,EAAQoE,KAAKT,GACzBnD,EAAgB,GACpB,GAAgB,OAAZ2D,GAAuC,IAAnBA,EAAQ9C,OAC9B,IACEb,EAAgB6D,KAAKC,MAAMH,EAAQ,IACnC,MAAOI,IAqBX,OAhBAlC,EAAMmC,KAAOhB,EAAY,GAEzBhB,EAAQH,EAAMD,KAAK,UAAW,MAAO,IAC/BqC,OAAS,GACfjC,EAAMkC,IAAM,CAAClB,EAAWnB,EAAMmC,MAC9BhC,EAAMhC,cAAgBA,GAEtBgC,EAAQH,EAAMD,KAAK,UAAW,GAAI,IAC5BqC,OAAS,GACfjC,EAAMkC,IAAM,CAAClB,EAAWnB,EAAMmC,MAC9BhC,EAAMhC,cAAgBA,EACtBgC,EAAMG,SAAW,IAEjBH,EAAQH,EAAMD,KAAK,WAAY,OAAQ,IACjCqC,OAAS,OAsH4B,CAC3CE,IAAK,CAAC,YAAa,YAAa"} -------------------------------------------------------------------------------- /dist/markdownItTocDoneRight.mjs: -------------------------------------------------------------------------------- 1 | function e(e){return encodeURIComponent(String(e).trim().toLowerCase().replace(/\s+/g,"-"))}function n(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}export default function(r,t){var l;t=Object.assign({},{placeholder:"(\\$\\{toc\\}|\\[\\[?_?toc_?\\]?\\]|\\$\\)",slugify:e,uniqueSlugStartIndex:1,containerClass:"table-of-contents",containerId:void 0,listClass:void 0,itemClass:void 0,linkClass:void 0,level:1,listType:"ol",format:void 0,callback:void 0},t);var i=new RegExp("^"+t.placeholder+"$","i");r.renderer.rules.tocOpen=function(e,r){var l=Object.assign({},t);return e&&r>=0&&(l=Object.assign(l,e[r].inlineOptions)),"'},r.renderer.rules.tocClose=function(){return""},r.renderer.rules.tocBody=function(e,r){var i=Object.assign({},t);e&&r>=0&&(i=Object.assign(i,e[r].inlineOptions));var s,a={},c=Array.isArray(i.level)?(s=i.level,function(e){return s.includes(e)}):function(e){return function(n){return n>=e}}(i.level);return function e(r){var l=i.listClass?' class="'+n(i.listClass)+'"':"",s=i.itemClass?' class="'+n(i.itemClass)+'"':"",o=i.linkClass?' class="'+n(i.linkClass)+'"':"";if(0===r.c.length)return"";var u="";return(0===r.l||c(r.l))&&(u+="<"+(n(i.listType)+l)+">"),r.c.forEach(function(r){c(r.l)?u+="'+("function"==typeof i.format?i.format(r.n,n):n(r.n))+""+e(r)+"":u+=e(r)}),(0===r.l||c(r.l))&&(u+=""),u}(l)},r.core.ruler.push("generateTocAst",function(e){l=function(e){for(var n={l:0,n:"",c:[]},r=[n],t=0,l=e.length;tr[0].l)r[0].c.push(a),r.unshift(a);else if(a.l===r[0].l)r[1].c.push(a),r[0]=a;else{for(;a.l<=r[0].l;)r.shift();r[0].c.push(a),r.unshift(a)}}}return n}(e.tokens),"function"==typeof t.callback&&t.callback(r.renderer.rules.tocOpen()+r.renderer.rules.tocBody()+r.renderer.rules.tocClose(),l)}),r.block.ruler.before("heading","toc",function(e,n,r,t){var l,s=e.src.slice(e.bMarks[n]+e.tShift[n],e.eMarks[n]).split(" ")[0];if(!i.test(s))return!1;if(t)return!0;var a=i.exec(s),c={};if(null!==a&&3===a.length)try{c=JSON.parse(a[2])}catch(e){}return e.line=n+1,(l=e.push("tocOpen","nav",1)).markup="",l.map=[n,e.line],l.inlineOptions=c,(l=e.push("tocBody","",0)).markup="",l.map=[n,e.line],l.inlineOptions=c,l.children=[],(l=e.push("tocClose","nav",-1)).markup="",!0},{alt:["paragraph","reference","blockquote"]})} 2 | //# sourceMappingURL=markdownItTocDoneRight.mjs.map 3 | -------------------------------------------------------------------------------- /dist/markdownItTocDoneRight.mjs.map: -------------------------------------------------------------------------------- 1 | {"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, '&')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(//g, '>')\n}\n\nfunction tocPlugin (md, options) {\n options = Object.assign({}, {\n placeholder: '(\\\\$\\\\{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 ``\n }\n\n md.renderer.rules.tocClose = function (/* tokens, idx, options, env, renderer */) {\n return ''\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 += (`${typeof _options.format === 'function' ? _options.format(node.n, htmlencode) : htmlencode(node.n)}${ast2html(node)}`)\n } else {\n buffer += ast2html(node)\n }\n })\n if (tree.l === 0 || isLevelSelected(tree.l)) {\n buffer += (``)\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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"} -------------------------------------------------------------------------------- /dist/markdownItTocDoneRight.modern.js: -------------------------------------------------------------------------------- 1 | function e(e){return encodeURIComponent(String(e).trim().toLowerCase().replace(/\s+/g,"-"))}function n(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}export default function(t,l){let r;l=Object.assign({},{placeholder:"(\\$\\{toc\\}|\\[\\[?_?toc_?\\]?\\]|\\$\\)",slugify:e,uniqueSlugStartIndex:1,containerClass:"table-of-contents",containerId:void 0,listClass:void 0,itemClass:void 0,linkClass:void 0,level:1,listType:"ol",format:void 0,callback:void 0},l);const s=new RegExp("^"+l.placeholder+"$","i");t.renderer.rules.tocOpen=function(e,t){let r=Object.assign({},l);return e&&t>=0&&(r=Object.assign(r,e[t].inlineOptions)),``},t.renderer.rules.tocClose=function(){return""},t.renderer.rules.tocBody=function(e,t){let s=Object.assign({},l);e&&t>=0&&(s=Object.assign(s,e[t].inlineOptions));const c={},i=Array.isArray(s.level)?(o=s.level,e=>o.includes(e)):(e=>n=>n>=e)(s.level);var o;return function e(t){const r=s.listClass?` class="${n(s.listClass)}"`:"",o=s.itemClass?` class="${n(s.itemClass)}"`:"",a=s.linkClass?` class="${n(s.linkClass)}"`:"";if(0===t.c.length)return"";let u="";return(0===t.l||i(t.l))&&(u+=`<${n(s.listType)+r}>`),t.c.forEach(t=>{i(t.l)?u+=`${"function"==typeof s.format?s.format(t.n,n):n(t.n)}${e(t)}`:u+=e(t)}),(0===t.l||i(t.l))&&(u+=``),u}(r)},t.core.ruler.push("generateTocAst",function(e){r=function(e){const n={l:0,n:"",c:[]},t=[n];for(let n=0,l=e.length;nt[0].l)t[0].c.push(s),t.unshift(s);else if(s.l===t[0].l)t[1].c.push(s),t[0]=s;else{for(;s.l<=t[0].l;)t.shift();t[0].c.push(s),t.unshift(s)}}}return n}(e.tokens),"function"==typeof l.callback&&l.callback(t.renderer.rules.tocOpen()+t.renderer.rules.tocBody()+t.renderer.rules.tocClose(),r)}),t.block.ruler.before("heading","toc",function(e,n,t,l){let r;const c=e.src.slice(e.bMarks[n]+e.tShift[n],e.eMarks[n]).split(" ")[0];if(!s.test(c))return!1;if(l)return!0;const i=s.exec(c);let o={};if(null!==i&&3===i.length)try{o=JSON.parse(i[2])}catch(e){}return e.line=n+1,r=e.push("tocOpen","nav",1),r.markup="",r.map=[n,e.line],r.inlineOptions=o,r=e.push("tocBody","",0),r.markup="",r.map=[n,e.line],r.inlineOptions=o,r.children=[],r=e.push("tocClose","nav",-1),r.markup="",!0},{alt:["paragraph","reference","blockquote"]})} 2 | //# sourceMappingURL=markdownItTocDoneRight.modern.js.map 3 | -------------------------------------------------------------------------------- /dist/markdownItTocDoneRight.modern.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"markdownItTocDoneRight.modern.js","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, '&')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(//g, '>')\n}\n\nfunction tocPlugin (md, options) {\n options = Object.assign({}, {\n placeholder: '(\\\\$\\\\{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 ``\n }\n\n md.renderer.rules.tocClose = function (/* tokens, idx, options, env, renderer */) {\n return ''\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 += (`${typeof _options.format === 'function' ? _options.format(node.n, htmlencode) : htmlencode(node.n)}${ast2html(node)}`)\n } else {\n buffer += ast2html(node)\n }\n })\n if (tree.l === 0 || isLevelSelected(tree.l)) {\n buffer += (``)\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","uniques","isLevelSelected","Array","isArray","selection","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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"} -------------------------------------------------------------------------------- /dist/markdownItTocDoneRight.umd.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).markdownItTocDoneRight=n()}(this,function(){function e(e){return encodeURIComponent(String(e).trim().toLowerCase().replace(/\s+/g,"-"))}function n(e){return String(e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}return function(t,r){var l;r=Object.assign({},{placeholder:"(\\$\\{toc\\}|\\[\\[?_?toc_?\\]?\\]|\\$\\)",slugify:e,uniqueSlugStartIndex:1,containerClass:"table-of-contents",containerId:void 0,listClass:void 0,itemClass:void 0,linkClass:void 0,level:1,listType:"ol",format:void 0,callback:void 0},r);var i=new RegExp("^"+r.placeholder+"$","i");t.renderer.rules.tocOpen=function(e,t){var l=Object.assign({},r);return e&&t>=0&&(l=Object.assign(l,e[t].inlineOptions)),"'},t.renderer.rules.tocClose=function(){return""},t.renderer.rules.tocBody=function(e,t){var i=Object.assign({},r);e&&t>=0&&(i=Object.assign(i,e[t].inlineOptions));var o,s={},c=Array.isArray(i.level)?(o=i.level,function(e){return o.includes(e)}):function(e){return function(n){return n>=e}}(i.level);return function e(t){var l=i.listClass?' class="'+n(i.listClass)+'"':"",o=i.itemClass?' class="'+n(i.itemClass)+'"':"",a=i.linkClass?' class="'+n(i.linkClass)+'"':"";if(0===t.c.length)return"";var u="";return(0===t.l||c(t.l))&&(u+="<"+(n(i.listType)+l)+">"),t.c.forEach(function(t){c(t.l)?u+="'+("function"==typeof i.format?i.format(t.n,n):n(t.n))+""+e(t)+"":u+=e(t)}),(0===t.l||c(t.l))&&(u+=""),u}(l)},t.core.ruler.push("generateTocAst",function(e){l=function(e){for(var n={l:0,n:"",c:[]},t=[n],r=0,l=e.length;rt[0].l)t[0].c.push(s),t.unshift(s);else if(s.l===t[0].l)t[1].c.push(s),t[0]=s;else{for(;s.l<=t[0].l;)t.shift();t[0].c.push(s),t.unshift(s)}}}return n}(e.tokens),"function"==typeof r.callback&&r.callback(t.renderer.rules.tocOpen()+t.renderer.rules.tocBody()+t.renderer.rules.tocClose(),l)}),t.block.ruler.before("heading","toc",function(e,n,t,r){var l,o=e.src.slice(e.bMarks[n]+e.tShift[n],e.eMarks[n]).split(" ")[0];if(!i.test(o))return!1;if(r)return!0;var s=i.exec(o),c={};if(null!==s&&3===s.length)try{c=JSON.parse(s[2])}catch(e){}return e.line=n+1,(l=e.push("tocOpen","nav",1)).markup="",l.map=[n,e.line],l.inlineOptions=c,(l=e.push("tocBody","",0)).markup="",l.map=[n,e.line],l.inlineOptions=c,l.children=[],(l=e.push("tocClose","nav",-1)).markup="",!0},{alt:["paragraph","reference","blockquote"]})}}); 2 | //# sourceMappingURL=markdownItTocDoneRight.umd.js.map 3 | -------------------------------------------------------------------------------- /dist/markdownItTocDoneRight.umd.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"markdownItTocDoneRight.umd.js","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, '&')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(//g, '>')\n}\n\nfunction tocPlugin (md, options) {\n options = Object.assign({}, {\n placeholder: '(\\\\$\\\\{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 ``\n }\n\n md.renderer.rules.tocClose = function (/* tokens, idx, options, env, renderer */) {\n return ''\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 += (`${typeof _options.format === 'function' ? _options.format(node.n, htmlencode) : htmlencode(node.n)}${ast2html(node)}`)\n } else {\n buffer += ast2html(node)\n }\n })\n if (tree.l === 0 || isLevelSelected(tree.l)) {\n buffer += (``)\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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"} -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function slugify (x) { 4 | return encodeURIComponent(String(x).trim().toLowerCase().replace(/\s+/g, '-')) 5 | } 6 | 7 | function htmlencode (x) { 8 | /* 9 | // safest, delegate task to native -- IMPORTANT: enabling this breaks both jest and runkit, but with browserify it's fine 10 | if (document && document.createElement) { 11 | const el = document.createElement("div") 12 | el.innerText = x 13 | return el.innerHTML 14 | } 15 | */ 16 | 17 | return String(x) 18 | .replace(/&/g, '&') 19 | .replace(/"/g, '"') 20 | .replace(/'/g, ''') 21 | .replace(//g, '>') 23 | } 24 | 25 | function tocPlugin (md, options) { 26 | options = Object.assign({}, { 27 | placeholder: '(\\$\\{toc\\}|\\[\\[?_?toc_?\\]?\\]|\\$\\)', 28 | slugify: slugify, 29 | uniqueSlugStartIndex: 1, 30 | containerClass: 'table-of-contents', 31 | containerId: undefined, 32 | listClass: undefined, 33 | itemClass: undefined, 34 | linkClass: undefined, 35 | level: 1, 36 | listType: 'ol', 37 | format: undefined, 38 | callback: undefined/* function(html, ast) {} */ 39 | }, options) 40 | 41 | let ast 42 | const pattern = new RegExp('^' + options.placeholder + '$', 'i') 43 | 44 | function toc (state, startLine, endLine, silent) { 45 | let token 46 | const pos = state.bMarks[startLine] + state.tShift[startLine] 47 | const max = state.eMarks[startLine] 48 | 49 | // use whitespace as a line tokenizer and extract the first token 50 | // to test against the placeholder anchored pattern, rejecting if false 51 | const lineFirstToken = state.src.slice(pos, max).split(' ')[0] 52 | if (!pattern.test(lineFirstToken)) return false 53 | 54 | if (silent) return true 55 | 56 | const matches = pattern.exec(lineFirstToken) 57 | let inlineOptions = {} 58 | if (matches !== null && matches.length === 3) { 59 | try { 60 | inlineOptions = JSON.parse(matches[2]) 61 | } catch (ex) { 62 | // silently ignore inline options 63 | } 64 | } 65 | 66 | state.line = startLine + 1 67 | 68 | token = state.push('tocOpen', 'nav', 1) 69 | token.markup = '' 70 | token.map = [startLine, state.line] 71 | token.inlineOptions = inlineOptions 72 | 73 | token = state.push('tocBody', '', 0) 74 | token.markup = '' 75 | token.map = [startLine, state.line] 76 | token.inlineOptions = inlineOptions 77 | token.children = [] 78 | 79 | token = state.push('tocClose', 'nav', -1) 80 | token.markup = '' 81 | 82 | return true 83 | } 84 | 85 | md.renderer.rules.tocOpen = function (tokens, idx/* , options, env, renderer */) { 86 | let _options = Object.assign({}, options) 87 | if (tokens && idx >= 0) { 88 | const token = tokens[idx] 89 | _options = Object.assign(_options, token.inlineOptions) 90 | } 91 | const id = _options.containerId ? ` id="${htmlencode(_options.containerId)}"` : '' 92 | return `` 93 | } 94 | 95 | md.renderer.rules.tocClose = function (/* tokens, idx, options, env, renderer */) { 96 | return '' 97 | } 98 | 99 | md.renderer.rules.tocBody = function (tokens, idx/* , options, env, renderer */) { 100 | let _options = Object.assign({}, options) 101 | if (tokens && idx >= 0) { 102 | const token = tokens[idx] 103 | _options = Object.assign(_options, token.inlineOptions) 104 | } 105 | 106 | const uniques = {} 107 | function unique (s) { 108 | let u = s 109 | let i = _options.uniqueSlugStartIndex 110 | while (Object.prototype.hasOwnProperty.call(uniques, u)) u = `${s}-${i++}` 111 | uniques[u] = true 112 | return u 113 | } 114 | 115 | const isLevelSelectedNumber = selection => level => level >= selection 116 | const isLevelSelectedArray = selection => level => selection.includes(level) 117 | 118 | const isLevelSelected = Array.isArray(_options.level) 119 | ? isLevelSelectedArray(_options.level) 120 | : isLevelSelectedNumber(_options.level) 121 | 122 | function ast2html (tree) { 123 | const listClass = _options.listClass ? ` class="${htmlencode(_options.listClass)}"` : '' 124 | const itemClass = _options.itemClass ? ` class="${htmlencode(_options.itemClass)}"` : '' 125 | const linkClass = _options.linkClass ? ` class="${htmlencode(_options.linkClass)}"` : '' 126 | 127 | if (tree.c.length === 0) return '' 128 | 129 | let buffer = '' 130 | if (tree.l === 0 || isLevelSelected(tree.l)) { 131 | buffer += (`<${htmlencode(_options.listType) + listClass}>`) 132 | } 133 | tree.c.forEach(node => { 134 | if (isLevelSelected(node.l)) { 135 | buffer += (`${typeof _options.format === 'function' ? _options.format(node.n, htmlencode) : htmlencode(node.n)}${ast2html(node)}`) 136 | } else { 137 | buffer += ast2html(node) 138 | } 139 | }) 140 | if (tree.l === 0 || isLevelSelected(tree.l)) { 141 | buffer += (``) 142 | } 143 | return buffer 144 | } 145 | 146 | return ast2html(ast) 147 | } 148 | 149 | function headings2ast (tokens) { 150 | const ast = { l: 0, n: '', c: [] } 151 | const stack = [ast] 152 | 153 | for (let i = 0, iK = tokens.length; i < iK; i++) { 154 | const token = tokens[i] 155 | if (token.type === 'heading_open') { 156 | const key = ( 157 | tokens[i + 1] 158 | .children 159 | .filter(function (token) { return token.type === 'text' || token.type === 'code_inline' }) 160 | .reduce(function (s, t) { return s + t.content }, '') 161 | ) 162 | 163 | const node = { 164 | l: parseInt(token.tag.substr(1), 10), 165 | n: key, 166 | c: [] 167 | } 168 | 169 | if (node.l > stack[0].l) { 170 | stack[0].c.push(node) 171 | stack.unshift(node) 172 | } else if (node.l === stack[0].l) { 173 | stack[1].c.push(node) 174 | stack[0] = node 175 | } else { 176 | while (node.l <= stack[0].l) stack.shift() 177 | stack[0].c.push(node) 178 | stack.unshift(node) 179 | } 180 | } 181 | } 182 | 183 | return ast 184 | } 185 | 186 | md.core.ruler.push('generateTocAst', function (state) { 187 | const tokens = state.tokens 188 | ast = headings2ast(tokens) 189 | 190 | if (typeof options.callback === 'function') { 191 | options.callback( 192 | md.renderer.rules.tocOpen() + md.renderer.rules.tocBody() + md.renderer.rules.tocClose(), 193 | ast 194 | ) 195 | } 196 | }) 197 | 198 | md.block.ruler.before('heading', 'toc', toc, { 199 | alt: ['paragraph', 'reference', 'blockquote'] 200 | }) 201 | } 202 | 203 | export default tocPlugin 204 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-it-toc-done-right", 3 | "version": "4.2.0", 4 | "description": "Table of contents (TOC) for markdown-it markdown parser with focus on semantic and security.", 5 | "source": "index.js", 6 | "main": "dist/markdownItTocDoneRight.js", 7 | "module": "dist/markdownItTocDoneRight.mjs", 8 | "unpkg": "dist/markdownItTocDoneRight.umd.js", 9 | "scripts": { 10 | "build": "microbundle", 11 | "dev": "microbundle watch", 12 | "lint": "standard --verbose index.js __test__/test.js | snazzy", 13 | "test": "jest --coverage" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/nagaozen/markdown-it-toc-done-right.git" 18 | }, 19 | "keywords": [ 20 | "markdown-it-plugin", 21 | "markdown-it", 22 | "markdown", 23 | "toc", 24 | "table of contents" 25 | ], 26 | "author": "Fabio Zendhi Nagao ", 27 | "license": "MIT", 28 | "files": [ 29 | "README.md", 30 | "LICENSE", 31 | "dist/*", 32 | "runkit.js", 33 | "types/*" 34 | ], 35 | "types": "types/index.d.ts", 36 | "bugs": { 37 | "url": "https://github.com/nagaozen/markdown-it-toc-done-right/issues" 38 | }, 39 | "homepage": "https://github.com/nagaozen/markdown-it-toc-done-right#readme", 40 | "jest": { 41 | "verbose": true 42 | }, 43 | "runkitExampleFilename": "runkit.js", 44 | "dependencies": {}, 45 | "devDependencies": { 46 | "@types/markdown-it": "^10.0.0", 47 | "coveralls": "^3.0.7", 48 | "jest": "^26.6.3", 49 | "markdown-it": "^10.0.0", 50 | "markdown-it-anchor": "^6.0.0", 51 | "microbundle": "^0.12.4", 52 | "snazzy": "^8.0.0", 53 | "standard": "^13.1.0", 54 | "uslug": "^1.0.4" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /runkit.js: -------------------------------------------------------------------------------- 1 | const uslug = require('uslug') 2 | function uslugify (x) { 3 | return uslug(x) 4 | } 5 | const umd = require('markdown-it')({ 6 | html: false, 7 | xhtmlOut: true, 8 | typographer: true 9 | }).use(require('markdown-it-anchor'), { permalink: true, permalinkBefore: true, permalinkSymbol: '§', slugify: uslugify }) 10 | .use(require('markdown-it-toc-done-right'), { slugify: uslugify }) 11 | 12 | console.log(umd.render('${toc}\n\n# こんにちは RunKit')) 13 | -------------------------------------------------------------------------------- /tdd.helper.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | markdown-it-toc-done-right 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 182 | 194 | 195 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'markdown-it-toc-done-right' { 2 | import { PluginWithOptions } from 'markdown-it'; 3 | 4 | export interface TocOptions { 5 | placeholder: string 6 | slugify: (s: string) => string 7 | uniqueSlugStartIndex: number 8 | containerClass: string 9 | containerId: string 10 | listClass: string 11 | itemClass: string 12 | linkClass: string 13 | level: number | number[] 14 | listType: 'ol' | 'ul' 15 | format: (s: string) => string 16 | callback: (tocCode: string, ast: TocAst) => void 17 | } 18 | 19 | export interface TocAst { 20 | l: number 21 | n: string 22 | c: TocAst[] 23 | } 24 | 25 | const markdownItTocDoneRight: PluginWithOptions> 26 | 27 | export default markdownItTocDoneRight 28 | } 29 | --------------------------------------------------------------------------------