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