├── package.json ├── README.md ├── LICENSE ├── .gitignore └── dist ├── toc.css └── toc.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docsify-toc", 3 | "version": "1.1.0", 4 | "description": "docsify-toc", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "files": [ 10 | "src", 11 | "dist" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/mrpotatoes/docsify-toc.git" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/mrpotatoes/docsify-toc/issues" 22 | }, 23 | "homepage": "https://github.com/mrpotatoes/docsify-toc#readme" 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docsify Table of Contents 2 | 3 |

4 | 5 |
6 | docsify-toc 7 |

8 | 9 | 10 | **Note**: I won't be adding features but feel free to add pull requests and if they work/pass tests I'll add them in. 11 | 12 | ## To use 13 | Add stylesheet 14 | ```html 15 | 16 | ``` 17 | 18 | Add JS 19 | ```html 20 | 21 | ``` 22 | 23 | Add settings 24 | ```js 25 | window.$docsify = { 26 | toc: { 27 | scope: '.markdown-section', 28 | headings: 'h1, h2, h3, h4, h5, h6', 29 | title: 'Table of Contents', 30 | }, 31 | } 32 | ``` 33 | 34 | # TODO 35 | - [ ] Tests 36 | - [ ] Example 37 | - [x] ~Documentation~ 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andric LibreSinn 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # next.js build output 63 | .next 64 | -------------------------------------------------------------------------------- /dist/toc.css: -------------------------------------------------------------------------------- 1 | .content { 2 | display: flex; 3 | flex-direction: row-reverse; 4 | justify-content: center; 5 | } 6 | .markdown-section { 7 | /* flex: 1 1 0%; */ 8 | margin: 0 48px; 9 | } 10 | .nav { 11 | width: var(--toc-width, 700px); 12 | /* align-self: flex-start; 13 | flex: 0 0 auto; */ 14 | } 15 | aside.nav.nothing { 16 | width: 0; 17 | } 18 | 19 | .page_toc { 20 | position: fixed; 21 | border-left-style: solid; 22 | border-left-width: 1px; 23 | border-left-color: rgba(0, 0, 0, 0.07); 24 | border-image-slice: 1; 25 | padding-left: 5px; 26 | } 27 | 28 | .page_toc a > * { 29 | pointer-events: none; 30 | } 31 | 32 | .page_toc code { 33 | background-color: #f8f8f8; 34 | border-radius: 2px; 35 | color: #e96900; 36 | font-family: 'Roboto Mono', Monaco, courier, monospace; 37 | font-size: 0.8rem; 38 | margin: 0 2px; 39 | padding: 3px 5px; 40 | } 41 | 42 | .page_toc p.title { 43 | margin: 0px 0 0px 9px; 44 | padding-bottom: 5px; 45 | font-weight: 600; 46 | font-size: 1.2em; 47 | } 48 | .page_toc .anchor:hover:after { 49 | content: ""; 50 | } 51 | 52 | .page_toc ul { 53 | list-style-type: none; 54 | margin-top: 0px; 55 | padding-left: 10px; 56 | color: var(--text-color-base, black); 57 | text-decoration: none; 58 | font-weight: 300; 59 | line-height: 1.6em; 60 | } 61 | 62 | .page_toc ul a:hover span { 63 | color: var(--text-color-tertiary, #42b983); 64 | border-bottom: none !important; 65 | text-decoration:none !important; 66 | } 67 | 68 | .page_toc ul a { 69 | color: var(--text-color-base, black); 70 | text-decoration: none; 71 | font-weight: 300; 72 | line-height: 1.6em; 73 | } 74 | 75 | @media screen and (max-width: 1300px) { 76 | .page_toc { 77 | position: relative; 78 | left: 0; 79 | top: -20px; 80 | padding: 10px 0; 81 | border: none; 82 | border-bottom: 1px solid #ddd; 83 | font-size: 1.0em; 84 | } 85 | .page_toc a:before { 86 | content: "- "; 87 | } 88 | .nav { 89 | margin: 0 auto; 90 | width: 800px; 91 | } 92 | .page_toc p.title { 93 | font-weight: 300; 94 | font-size: 1.8em; 95 | } 96 | .content { 97 | display: block; 98 | } 99 | .markdown-section { 100 | margin: 0 auto; 101 | } 102 | } 103 | 104 | .page_toc .active { 105 | border-left: 5px solid; 106 | color: var(--theme-color, #42b983); 107 | padding-left: 10px; 108 | } 109 | -------------------------------------------------------------------------------- /dist/toc.js: -------------------------------------------------------------------------------- 1 | var defaultOptions = { 2 | headings: 'h1, h2', 3 | scope: '.markdown-section', 4 | 5 | // To make work 6 | title: 'Contents', 7 | listType: 'ul', 8 | } 9 | 10 | // Element builders 11 | var tocHeading = function(Title) { 12 | return document.createElement('h2').appendChild( 13 | document.createTextNode(Title) 14 | ) 15 | } 16 | 17 | var aTag = function(src) { 18 | var a = document.createElement('a'); 19 | var content = src.firstChild.innerHTML; 20 | 21 | // Use this to clip text w/ HTML in it. 22 | // https://github.com/arendjr/text-clipper 23 | a.innerHTML = content; 24 | a.href = src.firstChild.href; 25 | a.onclick = tocClick 26 | 27 | // In order to remove this gotta fix the styles. 28 | a.setAttribute('class', 'anchor'); 29 | 30 | return a 31 | }; 32 | 33 | var tocClick = function(e) { 34 | var divs = document.querySelectorAll('.page_toc .active'); 35 | 36 | // Remove the previous classes 37 | [].forEach.call(divs, function(div) { 38 | div.setAttribute('class', 'anchor') 39 | }); 40 | 41 | // Make sure this is attached to the parent not itself 42 | e.currentTarget.setAttribute('class', 'active') 43 | }; 44 | 45 | var createList = function(wrapper, count) { 46 | while (count--) { 47 | if(wrapper){ 48 | wrapper = wrapper.appendChild( 49 | document.createElement('ul') 50 | ); 51 | } 52 | if (count) { 53 | wrapper = wrapper.appendChild( 54 | document.createElement('li') 55 | ); 56 | } 57 | } 58 | 59 | return wrapper; 60 | }; 61 | 62 | //------------------------------------------------------------------------ 63 | 64 | var getHeaders = function(selector) { 65 | var headings2 = document.querySelectorAll(selector); 66 | var ret = []; 67 | 68 | [].forEach.call(headings2, function(heading) { 69 | ret = ret.concat(heading); 70 | }); 71 | 72 | return ret; 73 | }; 74 | 75 | var getLevel = function(header) { 76 | var decs = header.match(/\d/g); 77 | 78 | return decs ? Math.min.apply(null, decs) : 1; 79 | }; 80 | 81 | var jumpBack = function(currentWrapper, offset) { 82 | while (offset--) { 83 | currentWrapper = currentWrapper.parentElement; 84 | } 85 | 86 | return currentWrapper; 87 | }; 88 | 89 | var buildTOC = function(options) { 90 | var ret = document.createElement('ul'); 91 | var wrapper = ret; 92 | var lastLi = null; 93 | var selector = options.scope + ' ' + options.headings 94 | var headers = getHeaders(selector).filter(h => h.id); 95 | 96 | headers.reduce(function(prev, curr, index) { 97 | var currentLevel = getLevel(curr.tagName); 98 | var offset = currentLevel - prev; 99 | 100 | wrapper = (offset > 0) 101 | ? createList(lastLi, offset) 102 | : jumpBack(wrapper, -offset * 2) 103 | 104 | wrapper = wrapper || ret; 105 | 106 | var li = document.createElement('li'); 107 | 108 | wrapper.appendChild(li).appendChild(aTag(curr)); 109 | 110 | lastLi = li; 111 | 112 | return currentLevel; 113 | }, getLevel(options.headings)); 114 | 115 | return ret; 116 | }; 117 | 118 | // Docsify plugin functions 119 | function plugin(hook, vm) { 120 | var userOptions = vm.config.toc; 121 | 122 | hook.mounted(function () { 123 | var content = window.Docsify.dom.find(".content"); 124 | if (content) { 125 | var nav = window.Docsify.dom.create("aside", ""); 126 | window.Docsify.dom.toggleClass(nav, "add", "nav"); 127 | window.Docsify.dom.before(content, nav); 128 | } 129 | }); 130 | 131 | hook.doneEach(function () { 132 | var nav = document.querySelectorAll('.nav')[0] 133 | var t = Array.from(document.querySelectorAll('.nav')) 134 | 135 | if (!nav) { 136 | return; 137 | } 138 | 139 | const toc = buildTOC(userOptions); 140 | 141 | // Just unset it for now. 142 | if (!toc.innerHTML) { 143 | nav.innerHTML = null 144 | return; 145 | } 146 | 147 | // Fix me in the future 148 | var title = document.createElement('p'); 149 | title.innerHTML = userOptions.title; 150 | title.setAttribute('class', 'title'); 151 | 152 | var container = document.createElement('div'); 153 | container.setAttribute('class', 'page_toc'); 154 | 155 | container.appendChild(title); 156 | container.appendChild(toc); 157 | 158 | // Existing TOC 159 | var tocChild = document.querySelectorAll('.nav .page_toc'); 160 | 161 | if (tocChild.length > 0) { 162 | tocChild[0].parentNode.removeChild(tocChild[0]); 163 | } 164 | 165 | nav.appendChild(container); 166 | }); 167 | } 168 | 169 | // Docsify plugin options 170 | window.$docsify['toc'] = Object.assign(defaultOptions, window.$docsify['toc']); 171 | window.$docsify.plugins = [].concat(plugin, window.$docsify.plugins); 172 | --------------------------------------------------------------------------------