kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/web/site/_plugins/toc_generator.rb (about) 1 require 'nokogiri' 2 3 module Jekyll 4 class TOCGenerator < Jekyll::Generator 5 def generate(site) 6 site.pages.each do |page| 7 maybeTOC page 8 end 9 site.collections.each do |name, coll| 10 coll.docs.each do |page| 11 maybeTOC page 12 end 13 end 14 end 15 16 private 17 18 def maybeTOC(page) 19 if page.data['toc'] 20 maxDepth = page.data['toclevels'] ? page.data['toclevels'].to_i : 2 21 toc = parseTOC(Nokogiri::HTML(page.content), maxDepth) 22 page.data['toc'] = renderTOC(toc) 23 end 24 end 25 26 def renderTOC(toc) 27 html = '<div class="toc">' 28 html += tocList(toc) 29 return html + '</div>' 30 end 31 32 def tocList(toc) 33 html = '<ul>' 34 toc.each do |h| 35 header = h['header'] 36 children = h['children'] 37 38 html += '<li><a href="#' + header['id'] + '">' + header.children.text + '</a>' 39 if not children.empty? then 40 html += tocList children 41 end 42 html += '</li>' 43 end 44 return html + '</ul>' 45 end 46 47 def parseTOC(doc, maxDepth) 48 1.upto(6).each do |level| 49 hdrs = doc.css("h#{level}") 50 unless hdrs.empty? 51 return hdrs.map {|h| toc(h, level, level+maxDepth-1) } 52 end 53 end 54 [] 55 end 56 57 def toc(h, level, maxLevel) 58 return {'header'=>h, 'children'=>[]} if level == maxLevel 59 # Number of following headers at the same level 60 ct = h.xpath("count(following::h#{level})") 61 # All headers of the next level before the next header of the same level 62 children = h.xpath("following::h#{level+1}[count(following::h#{level})=#{ct}]") 63 {'header'=>h, 'children'=>children.map {|c| toc(c, level+1, maxLevel) }} 64 end 65 end 66 end