├── TOC-Sample.png ├── .gitignore ├── css └── toc.css ├── js └── jquery.tocLight.js ├── README.md └── _plugins └── tocGenerator.rb /TOC-Sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icai/jekyll-toc-generator/master/TOC-Sample.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | 15 | # YARD artifacts 16 | .yardoc 17 | _yardoc 18 | doc/ 19 | -------------------------------------------------------------------------------- /css/toc.css: -------------------------------------------------------------------------------- 1 | 2 | #toc, .toc, .mw-warning { 3 | background-color: #F9F9F9; 4 | border: 1px solid #AAAAAA; 5 | font-size: 95%; 6 | padding: 5px; 7 | } 8 | #toc h2, .toc h2 { 9 | border: medium none; 10 | display: inline; 11 | font-size: 100%; 12 | font-weight: bold; 13 | padding: 0; 14 | } 15 | #toc #toctitle, .toc #toctitle, #toc .toctitle, .toc .toctitle { 16 | text-align: center; 17 | margin:0 8px 4px 14px; 18 | padding-top:8px; 19 | line-height: 1.5; 20 | overflow:hidden; 21 | } 22 | 23 | 24 | #toc ul, .toc ul { 25 | list-style-image: none; 26 | list-style-type: none; 27 | margin:0 8px 7px 14px; 28 | padding-left: 0; 29 | text-align: left; 30 | } 31 | #toc ul ul, .toc ul ul { 32 | margin: 0 0 0 2em; 33 | } 34 | #toc .toctoggle, .toc .toctoggle { 35 | font-size: 94%; 36 | } 37 | 38 | #toc ul li { 39 | list-style-type: none; 40 | padding-left: 0; 41 | } 42 | 43 | #toc-container { 44 | margin-bottom: 10px; 45 | } -------------------------------------------------------------------------------- /js/jquery.tocLight.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Table of Content Generator Support for Jekyll v1.0 3 | * 4 | * https://github.com/dafi/jekyll-tocmd-generator 5 | * Examples and documentation at: https://github.com/dafi/jekyll-tocmd-generator 6 | * 7 | * Requires: jQuery v1.7+ 8 | * 9 | * Copyright (c) 2013 Davide Ficano 10 | * 11 | * Dual licensed under the MIT and GPL licenses: 12 | * http://www.opensource.org/licenses/mit-license.php 13 | * http://www.gnu.org/licenses/gpl.html 14 | */ 15 | (function($) { 16 | $.toc = {}; 17 | $.toc.clickHideButton = function(settings) { 18 | var config = { 19 | saveShowStatus: false, 20 | hideText: 'hide', 21 | showText: 'show'}; 22 | 23 | if (settings) { 24 | $.extend(config, settings); 25 | } 26 | 27 | $('#toctogglelink').click(function() { 28 | var ul = $($('#toc ul')[0]); 29 | 30 | if (ul.is(':visible')) { 31 | ul.hide(); 32 | $(this).text(config.showText); 33 | if (config.saveShowStatus) { 34 | $.cookie('toc-hide', '1', { expires: 365, path: '/' }); 35 | } 36 | $('#toc').addClass('tochidden'); 37 | } else { 38 | ul.show(); 39 | $(this).text(config.hideText); 40 | if (config.saveShowStatus) { 41 | $.removeCookie('toc-hide', { path: '/' }); 42 | } 43 | $('#toc').removeClass('tochidden'); 44 | } 45 | return false; 46 | }); 47 | 48 | if (config.saveShowStatus && $.cookie('toc-hide')) { 49 | var ul = $($('#toc ul')[0]); 50 | 51 | ul.hide(); 52 | $('#toctogglelink').text(config.showText); 53 | $('#toc').addClass('tochidden'); 54 | } 55 | 56 | } 57 | })(jQuery); 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jekyll-toc-generator 2 | ==================== 3 | 4 | Liquid filter to generate Table of Content into Jeklyll pages 5 | 6 | This filter adds to jekyll pages a Table of Content (TOC), it generates the necessary HTML code. 7 | 8 | # Example 9 | 10 | The image below shows the result for the markdown page [alignRules.md](https://raw.github.com/visualdiffer/visualdiffer.github.com/master/alignRules.md) 11 | 12 | ![image](TOC-Sample.png) 13 | 14 | # Installation 15 | 16 | ### Prerequisites 17 | 18 | To use tocGenerator.rb you need [nokogiri](http://nokogiri.org/) 19 | 20 | 1. copy the file `tocGenerator.rb` into the `_plugins` folder 21 | 2. copy the file `css/toc.css` to you css site and include into `_layouts` (this is recommended but not necessary) 22 | 2. finished 23 | 24 | # How to use 25 | 26 | You must replace the `{{ content }}` directive with `{{ content | toc_generate }}` 27 | 28 | The output contains the HTML code with the TOC and anchor's targets with the `id` attribute 29 | 30 | Example 31 | 32 | 33 | 34 | 35 | 36 | {{ page.title }} 37 | 38 | 39 | 40 | 41 |
42 | {{ content | toc_generate }} 43 |
44 | 45 | 46 | 47 | ## Style the TOC table 48 | 49 | The TOC must have some CSS style to appear correctly, you can find a default implementation into file `css/toc.css` 50 | The style is compatible with mediawiki themes 51 | 52 | # Disable generation per page 53 | 54 | It is possible to suppress the TOC generation in specific pages, for example the index page normally doesn't have a TOC. 55 | This can be done into the [Front Matter](http://jekyllrb.com/docs/frontmatter/) section used by jekyll 56 | 57 | 58 | You must add the `noToc: true` directive 59 | 60 | --- 61 | permalink: index.html 62 | layout: default 63 | title: Main Page with TOC 64 | noToc: true 65 | --- 66 | 67 | 68 | # Advanced configuration 69 | 70 | Normally no configuration is necessary but you can set some parameter into you `_config.yml` file 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 100 | 101 | 102 |
Parameter nameDescriptionDefault value
minItemsToShowTocMinimum number of items to show the TOC
Suppose you want to generated the TOC only if there are at least 3 H1
0 (no limit)
tocTopTagThe top level tag given nokogiri to find
Suppose you want to generated the TOC start form h1 to h5
h1
anchorPrefixThe prefix used to generate the anchor nametocAnchor-
showToggleButtonThe TOC has a button used to collapse/expand the list, this requires a little of Javascript 99 |
This package contains a jQuery plugin to handle the click
false
103 | 104 | # Toggling the button via jQuery 105 | 106 | If the toogle button is visible you can use the jquery plugin included in this repository 107 | 108 | Below is shown an usage example 109 | 110 | 111 | 112 | 113 | {{ page.title }} 114 | 115 | 116 | 117 | 118 | 119 | 120 | 125 | 126 | 127 | 128 | {{ content | toc_generate }} 129 | 130 | 131 | 132 | 133 | # How it works 134 | 135 | The filter converts all `H1` and `H2` elements in a parent-child TOC based on how the markdown code is traslated in HTML. 136 | Markdown generates `H1` and `H2` as siblings 137 | 138 | For example the markdown code shown below 139 | 140 | heading 1 141 | ========= 142 | 143 | blah blah 144 | 145 | heading 1.1 146 | ----------- 147 | 148 | Generates the following HTML code 149 | 150 |

heading 1

151 |

blah blah

152 |

heading 1.1

153 | 154 | # Github pages can't use plugins 155 | 156 | If you host your jekyll pages on github you can't run plugins, in this scenario you can use a full javascript solution using [TOC Generator for Markdown](https://github.com/dafi/tocmd-generator) 157 | -------------------------------------------------------------------------------- /_plugins/tocGenerator.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | 3 | module Jekyll 4 | module TOCGenerator 5 | TOGGLE_HTML = '

%1

%2
' 6 | TOC_CONTAINER_HTML = '
%1
    %2
' 7 | HIDE_HTML = '[%1]' 8 | 9 | def toc_generate(html) 10 | # No Toc can be specified on every single page 11 | # For example the index page has no table of contents 12 | no_toc = @context.environments.first["page"]["noToc"] || false; 13 | 14 | if no_toc 15 | return html 16 | end 17 | 18 | config = @context.registers[:site].config 19 | # Minimum number of items needed to show TOC, default 0 (0 means no minimum) 20 | min_items_to_show_toc = config["minItemsToShowToc"] || 0 21 | 22 | anchor_prefix = config["anchorPrefix"] || 'tocAnchor-' 23 | 24 | # better for traditional page seo, commonlly use h1 as title 25 | toc_top_tag = config["tocTopTag"] || 'h1' 26 | toc_top_tag = toc_top_tag.gsub(/h/, '').to_i 27 | if toc_top_tag > 5 28 | toc_top_tag = 5 29 | end 30 | toc_sec_tag = toc_top_tag + 1 31 | toc_top_tag = "h#{toc_top_tag}" 32 | toc_sec_tag = "h#{toc_sec_tag}" 33 | 34 | 35 | # Text labels 36 | contents_label = config["contentsLabel"] || 'Contents' 37 | hide_label = config["hideLabel"] || 'hide' 38 | show_label = config["showLabel"] || 'show' 39 | show_toggle_button = config["showToggleButton"] 40 | 41 | toc_html = '' 42 | toc_level = 1 43 | toc_section = 1 44 | item_number = 1 45 | level_html = '' 46 | 47 | doc = Nokogiri::HTML(html) 48 | 49 | # Find H1 tag and all its H2 siblings until next H1 50 | doc.css(toc_top_tag).each do |tag| 51 | # TODO This XPATH expression can greatly improved 52 | ct = tag.xpath("count(following-sibling::#{toc_top_tag})") 53 | sects = tag.xpath("following-sibling::#{toc_sec_tag}[count(following-sibling::#{toc_top_tag})=#{ct}]") 54 | 55 | level_html = ''; 56 | inner_section = 0; 57 | 58 | sects.map.each do |sect| 59 | inner_section += 1; 60 | anchor_id = anchor_prefix + toc_level.to_s + '-' + toc_section.to_s + '-' + inner_section.to_s 61 | sect['id'] = "#{anchor_id}" 62 | 63 | level_html += create_level_html(anchor_id, 64 | toc_level + 1, 65 | toc_section + inner_section, 66 | item_number.to_s + '.' + inner_section.to_s, 67 | sect.text, 68 | '') 69 | end 70 | if level_html.length > 0 71 | level_html = ''; 72 | end 73 | 74 | anchor_id = anchor_prefix + toc_level.to_s + '-' + toc_section.to_s; 75 | tag['id'] = "#{anchor_id}" 76 | 77 | toc_html += create_level_html(anchor_id, 78 | toc_level, 79 | toc_section, 80 | item_number, 81 | tag.text, 82 | level_html); 83 | 84 | toc_section += 1 + inner_section; 85 | item_number += 1; 86 | end 87 | 88 | # for convenience item_number starts from 1 89 | # so we decrement it to obtain the index count 90 | toc_index_count = item_number - 1 91 | 92 | if toc_html.length > 0 93 | hide_html = ''; 94 | if (show_toggle_button) 95 | hide_html = HIDE_HTML.gsub('%1', hide_label) 96 | end 97 | 98 | if min_items_to_show_toc <= toc_index_count 99 | replaced_toggle_html = TOGGLE_HTML 100 | .gsub('%1', contents_label) 101 | .gsub('%2', hide_html); 102 | toc_table = TOC_CONTAINER_HTML 103 | .gsub('%1', replaced_toggle_html) 104 | .gsub('%2', toc_html); 105 | doc.css('body').children.before(toc_table) 106 | end 107 | doc.css('body').children.to_xhtml(indent:3, indent_text:" ") 108 | else 109 | return html 110 | end 111 | end 112 | 113 | private 114 | 115 | def create_level_html(anchor_id, toc_level, toc_section, tocNumber, tocText, tocInner) 116 | link = '%2 %3%4' 117 | .gsub('%1', anchor_id.to_s) 118 | .gsub('%2', tocNumber.to_s) 119 | .gsub('%3', tocText) 120 | .gsub('%4', tocInner ? tocInner : ''); 121 | '
  • %3
  • ' 122 | .gsub('%1', toc_level.to_s) 123 | .gsub('%2', toc_section.to_s) 124 | .gsub('%3', link) 125 | end 126 | end 127 | end 128 | 129 | Liquid::Template.register_filter(Jekyll::TOCGenerator) 130 | --------------------------------------------------------------------------------