1- require 'nokogiri'
1+ require 'nokogiri'
22
3- module Jekyll
4- module TOCGenerator
5- TOGGLE_HTML = '<div id="toctitle"><h2>%1</h2>%2</div>'
6- TOC_CONTAINER_HTML = '<div id="toc-container"><table class="toc" id="toc"><tbody><tr><td>%1<ul>%2</ul></td></tr></tbody></table></div>'
7- HIDE_HTML = '<span class="toctoggle">[<a id="toctogglelink" class="internal" href="#">%1</a>]</span>'
3+ module Jekyll
4+ module TOCGenerator
5+ TOGGLE_HTML = '<div id="toctitle"><h2>%1</h2>%2</div>'
6+ TOC_CONTAINER_HTML = '<div id="toc-container"><table class="toc" id="toc"><tbody><tr><td>%1<ul>%2</ul></td></tr></tbody></table></div>'
7+ HIDE_HTML = '<span class="toctoggle">[<a id="toctogglelink" class="internal" href="#">%1</a>]</span>'
88
9- def toc_generate ( html )
10- pageContext = @context . environments . first [ "page" ]
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 ;
1113
12- anchorPrefix = pageContext [ "anchorPrefix" ] || 'tocAnchor-'
13- showAlways = pageContext [ "showAlways" ] || false
14- saveShowStatus = pageContext [ "saveShowStatus" ] || true
14+ if no_toc
15+ return html
16+ end
1517
16- # Text labels
17- contentsLabel = pageContext [ "contentsLabel" ] || 'Contents'
18- hideLabel = pageContext [ "hideLabel" ] || 'hide'
19- showText = pageContext [ "showText" ] || 'show'
20- showHideButton = pageContext [ "showHideButton" ] || true ;
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
2121
22- noToc = pageContext [ "noToc "] || false ;
22+ anchor_prefix = config [ "anchorPrefix "] || 'tocAnchor-'
2323
24- if noToc
25- return html
26- end
24+ # Text labels
25+ contents_label = config [ "contentsLabel" ] || 'Contents'
26+ hide_label = config [ "hideLabel" ] || 'hide'
27+ show_label = config [ "showLabel" ] || 'show'
28+ show_toggle_button = config [ "showToggleButton" ]
29+
30+ toc_html = ''
31+ toc_level = 1
32+ toc_section = 1
33+ item_number = 1
34+ level_html = ''
35+
36+ doc = Nokogiri ::HTML ( html )
2737
28- tocHTML = ''
29- tocLevel = 1
30- tocSection = 1
31- itemNumber = 1
32- levelHTML = ''
33-
34- doc = Nokogiri ::HTML ( html )
35-
36- # Find H1 tag and all its H2 siblings until next H1
37- # TODO This XPATH expression can greatly improved
38- doc . css ( 'h1' ) . each do |h1 |
39- ct = h1 . xpath ( 'count(following-sibling::h1)' )
40- h2s = h1 . xpath ( "following-sibling::h2[count(following-sibling::h1)=#{ ct } ]" )
41-
42- levelHTML = '' ;
43- innerSection = 0 ;
44-
45- h2s . map . each do |h2 |
46- innerSection += 1 ;
47- anchorId = anchorPrefix + tocLevel . to_s + '-' + tocSection . to_s + '-' + innerSection . to_s
48- # Insert before element the anchor used by TOC
49- h2 . before ( "<a name=\" #{ anchorId } \" ></a>" )
50-
51- levelHTML += createLevelHTML ( anchorId ,
52- tocLevel + 1 ,
53- tocSection + innerSection ,
54- itemNumber . to_s + '.' + innerSection . to_s ,
55- h2 . text ,
56- '' )
57- end
58- if levelHTML . length > 0
59- levelHTML = '<ul>' + levelHTML + '</ul>' ;
60- end
61- anchorId = anchorPrefix + tocLevel . to_s + '-' + tocSection . to_s ;
38+ # Find H1 tag and all its H2 siblings until next H1
39+ doc . css ( 'h1' ) . each do |h1 |
40+ # TODO This XPATH expression can greatly improved
41+ ct = h1 . xpath ( 'count(following-sibling::h1)' )
42+ h2s = h1 . xpath ( "following-sibling::h2[count(following-sibling::h1)=#{ ct } ]" )
43+
44+ level_html = '' ;
45+ inner_section = 0 ;
46+
47+ h2s . map . each do |h2 |
48+ inner_section += 1 ;
49+ anchor_id = anchor_prefix + toc_level . to_s + '-' + toc_section . to_s + '-' + inner_section . to_s
6250 # Insert before element the anchor used by TOC
63- h1 . before ( "<a name=\" #{ anchorId } \" ></a>" )
64-
65- tocHTML += createLevelHTML ( anchorId ,
66- tocLevel ,
67- tocSection ,
68- itemNumber ,
69- h1 . text ,
70- levelHTML ) ;
71-
72- tocSection += 1 + innerSection ;
73- itemNumber += 1 ;
74- end
75-
76- if tocHTML . length > 0
77- hideHTML = '' ;
78- if ( showHideButton )
79- hideHTML = HIDE_HTML . gsub ( '%1' , hideLabel )
80- end
81-
82- hasOnlyOneTocItem = tocLevel == 1 && tocSection <= 2 ;
83- show = showAlways ? true : !hasOnlyOneTocItem ;
84-
85- if ( show )
86- replacedToggleHTML = TOGGLE_HTML
87- . gsub ( '%1' , contentsLabel )
88- . gsub ( '%2' , hideHTML ) ;
89- tocTable = TOC_CONTAINER_HTML
90- . gsub ( '%1' , replacedToggleHTML )
91- . gsub ( '%2' , tocHTML ) ;
92- doc . css ( 'body' ) . children . before ( tocTable )
93- end
94- doc . css ( 'body' ) . children . to_xhtml ( indent :3 , indent_text :" " )
95- else
96- return html
51+ h2 . before ( "<a name=\" #{ anchor_id } \" ></a>" )
52+
53+ level_html += create_level_html ( anchor_id ,
54+ toc_level + 1 ,
55+ toc_section + inner_section ,
56+ item_number . to_s + '.' + inner_section . to_s ,
57+ h2 . text ,
58+ '' )
59+ end
60+ if level_html . length > 0
61+ level_html = '<ul>' + level_html + '</ul>' ;
62+ end
63+ anchor_id = anchor_prefix + toc_level . to_s + '-' + toc_section . to_s ;
64+ # Insert before element the anchor used by TOC
65+ h1 . before ( "<a name=\" #{ anchor_id } \" ></a>" )
66+
67+ toc_html += create_level_html ( anchor_id ,
68+ toc_level ,
69+ toc_section ,
70+ item_number ,
71+ h1 . text ,
72+ level_html ) ;
73+
74+ toc_section += 1 + inner_section ;
75+ item_number += 1 ;
76+ end
77+
78+ # for convenience item_number starts from 1
79+ # so we decrement it to obtain the index count
80+ toc_index_count = item_number - 1
81+
82+ if toc_html . length > 0
83+ hide_html = '' ;
84+ if ( show_toggle_button )
85+ hide_html = HIDE_HTML . gsub ( '%1' , hide_label )
86+ end
87+
88+ if min_items_to_show_toc <= toc_index_count
89+ replaced_toggle_html = TOGGLE_HTML
90+ . gsub ( '%1' , contents_label )
91+ . gsub ( '%2' , hide_html ) ;
92+ toc_table = TOC_CONTAINER_HTML
93+ . gsub ( '%1' , replaced_toggle_html )
94+ . gsub ( '%2' , toc_html ) ;
95+ doc . css ( 'body' ) . children . before ( toc_table )
9796 end
98- end
99-
100- private
101-
102- def createLevelHTML ( anchorId , tocLevel , tocSection , tocNumber , tocText , tocInner )
103- link = '<a href="#%1"><span class="tocnumber">%2</span> <span class="toctext">%3</span></a>%4'
104- . gsub ( '%1' , anchorId . to_s )
105- . gsub ( '%2' , tocNumber . to_s )
106- . gsub ( '%3' , tocText )
107- . gsub ( '%4' , tocInner ? tocInner : '' ) ;
108- '<li class="toclevel-%1 tocsection-%2">%3</li>'
109- . gsub ( '%1' , tocLevel . to_s )
110- . gsub ( '%2' , tocSection . to_s )
111- . gsub ( '%3' , link )
97+ doc . css ( 'body' ) . children . to_xhtml ( indent :3 , indent_text :" " )
98+ else
99+ return html
112100 end
113- end
101+ end
102+
103+ private
104+
105+ def create_level_html ( anchor_id , toc_level , toc_section , tocNumber , tocText , tocInner )
106+ link = '<a href="#%1"><span class="tocnumber">%2</span> <span class="toctext">%3</span></a>%4'
107+ . gsub ( '%1' , anchor_id . to_s )
108+ . gsub ( '%2' , tocNumber . to_s )
109+ . gsub ( '%3' , tocText )
110+ . gsub ( '%4' , tocInner ? tocInner : '' ) ;
111+ '<li class="toc_level-%1 toc_section-%2">%3</li>'
112+ . gsub ( '%1' , toc_level . to_s )
113+ . gsub ( '%2' , toc_section . to_s )
114+ . gsub ( '%3' , link )
114115 end
116+ end
117+ end
115118
116- Liquid ::Template . register_filter ( Jekyll ::TOCGenerator )
119+ Liquid ::Template . register_filter ( Jekyll ::TOCGenerator )
0 commit comments