Skip to content

Commit ea8c825

Browse files
committed
updated plugin
1 parent 91b3d7a commit ea8c825

File tree

1 file changed

+113
-108
lines changed

1 file changed

+113
-108
lines changed

_plugins/tocGenerator.rb

Lines changed: 113 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,134 @@
11
require 'nokogiri'
22

33
module Jekyll
4+
45
module TOCGenerator
6+
57
TOGGLE_HTML = '<div id="toctitle"><h2>%1</h2>%2</div>'
68
TOC_CONTAINER_HTML = '<div id="toc-container"><table class="toc" id="toc"><tbody><tr><td>%1<ul>%2</ul></td></tr></tbody></table></div>'
79
HIDE_HTML = '<span class="toctoggle">[<a id="toctogglelink" class="internal" href="#">%1</a>]</span>'
810

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;
11+
def toc_generate(html)
12+
# No Toc can be specified on every single page
13+
# For example the index page has no table of contents
14+
no_toc = @context.environments.first["page"]["noToc"] || false;
1315

14-
if no_toc
15-
return html
16-
end
16+
return html if no_toc
1717

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
18+
config = @context.registers[:site].config
2119

22-
anchor_prefix = config["anchorPrefix"] || 'tocAnchor-'
20+
# Minimum number of items needed to show TOC, default 0 (0 means no minimum)
21+
min_items_to_show_toc = config["minItemsToShowToc"] || 0
2322

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 = '<ul>' + level_html + '</ul>';
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
23+
anchor_prefix = config["anchorPrefix"] || 'tocAnchor-'
24+
25+
# better for traditional page seo, commonlly use h1 as title
26+
toc_top_tag = config["tocTopTag"] || 'h1'
27+
toc_top_tag = toc_top_tag.gsub(/h/, '').to_i
28+
29+
toc_top_tag = 5 if toc_top_tag > 5
30+
31+
toc_sec_tag = toc_top_tag + 1
32+
toc_top_tag = "h#{toc_top_tag}"
33+
toc_sec_tag = "h#{toc_sec_tag}"
34+
35+
36+
# Text labels
37+
contents_label = config["contentsLabel"] || 'Contents'
38+
hide_label = config["hideLabel"] || 'hide'
39+
# show_label = config["showLabel"] || 'show' # unused
40+
show_toggle_button = config["showToggleButton"]
41+
42+
toc_html = ''
43+
toc_level = 1
44+
toc_section = 1
45+
item_number = 1
46+
level_html = ''
47+
48+
doc = Nokogiri::HTML(html)
49+
50+
# Find H1 tag and all its H2 siblings until next H1
51+
doc.css(toc_top_tag).each do |tag|
52+
# TODO This XPATH expression can greatly improved
53+
ct = tag.xpath("count(following-sibling::#{toc_top_tag})")
54+
sects = tag.xpath("following-sibling::#{toc_sec_tag}[count(following-sibling::#{toc_top_tag})=#{ct}]")
55+
56+
level_html = '';
57+
inner_section = 0;
58+
59+
sects.map.each do |sect|
60+
inner_section += 1;
61+
anchor_id = [
62+
anchor_prefix, toc_level, '-', toc_section, '-',
63+
inner_section
64+
].map(&:to_s).join ''
65+
66+
sect['id'] = "#{anchor_id}"
8767

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
68+
level_html += create_level_html(anchor_id,
69+
toc_level + 1,
70+
toc_section + inner_section,
71+
item_number.to_s + '.' + inner_section.to_s,
72+
sect.text,
73+
'')
11074
end
111-
end
11275

113-
private
114-
76+
level_html = '<ul>' + level_html + '</ul>' if level_html.length > 0
77+
78+
anchor_id = anchor_prefix + toc_level.to_s + '-' + toc_section.to_s;
79+
tag['id'] = "#{anchor_id}"
80+
81+
toc_html += create_level_html(anchor_id,
82+
toc_level,
83+
toc_section,
84+
item_number,
85+
tag.text,
86+
level_html);
87+
88+
toc_section += 1 + inner_section;
89+
item_number += 1;
90+
end
91+
92+
# for convenience item_number starts from 1
93+
# so we decrement it to obtain the index count
94+
toc_index_count = item_number - 1
95+
96+
return html unless toc_html.length > 0
97+
98+
hide_html = '';
99+
hide_html = HIDE_HTML.gsub('%1', hide_label) if (show_toggle_button)
100+
101+
if min_items_to_show_toc <= toc_index_count
102+
replaced_toggle_html = TOGGLE_HTML
103+
.gsub('%1', contents_label)
104+
.gsub('%2', hide_html);
105+
106+
toc_table = TOC_CONTAINER_HTML
107+
.gsub('%1', replaced_toggle_html)
108+
.gsub('%2', toc_html);
109+
110+
doc.css('body').children.before(toc_table)
111+
end
112+
113+
doc.to_xhtml
114+
end
115+
116+
private
117+
115118
def create_level_html(anchor_id, toc_level, toc_section, tocNumber, tocText, tocInner)
116-
link = '<a href="#%1"><span class="tocnumber">%2</span> <span class="toctext">%3</span></a>%4'
117-
.gsub('%1', anchor_id.to_s)
118-
.gsub('%2', tocNumber.to_s)
119-
.gsub('%3', tocText)
120-
.gsub('%4', tocInner ? tocInner : '');
121-
'<li class="toc_level-%1 toc_section-%2">%3</li>'
122-
.gsub('%1', toc_level.to_s)
123-
.gsub('%2', toc_section.to_s)
124-
.gsub('%3', link)
119+
link = '<a href="#%1"><span class="tocnumber">%2</span> <span class="toctext">%3</span></a>%4'
120+
.gsub('%1', anchor_id.to_s)
121+
.gsub('%2', tocNumber.to_s)
122+
.gsub('%3', tocText)
123+
.gsub('%4', tocInner ? tocInner : '');
124+
'<li class="toc_level-%1 toc_section-%2">%3</li>'
125+
.gsub('%1', toc_level.to_s)
126+
.gsub('%2', toc_section.to_s)
127+
.gsub('%3', link)
125128
end
129+
126130
end
131+
127132
end
128133

129134
Liquid::Template.register_filter(Jekyll::TOCGenerator)

0 commit comments

Comments
 (0)