File: toc_filter.rb

package info (click to toggle)
ruby-html-pipeline 2.14.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 424 kB
  • sloc: ruby: 2,265; sh: 13; makefile: 6
file content (69 lines) | stat: -rw-r--r-- 2,362 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# frozen_string_literal: true

HTML::Pipeline.require_dependency('escape_utils', 'TableOfContentsFilter')

module HTML
  class Pipeline
    # HTML filter that adds an 'id' attribute to all headers
    # in a document, so they can be accessed from a table of contents.
    #
    # Generates the Table of Contents, with links to each header.
    #
    # Examples
    #
    #  TocPipeline =
    #    HTML::Pipeline.new [
    #      HTML::Pipeline::TableOfContentsFilter
    #    ]
    #  # => #<HTML::Pipeline:0x007fc13c4528d8...>
    #  orig = %(<h1>Ice cube</h1><p>is not for the pop chart</p>)
    #  # => "<h1>Ice cube</h1><p>is not for the pop chart</p>"
    #  result = {}
    #  # => {}
    #  TocPipeline.call(orig, {}, result)
    #  # => {:toc=> ...}
    #  result[:toc]
    #  # => "<ul class=\"section-nav\">\n<li><a href=\"#ice-cube\">...</li><ul>"
    #  result[:output].to_s
    #  # => "<h1>\n<a id=\"ice-cube\" class=\"anchor\" href=\"#ice-cube\">..."
    class TableOfContentsFilter < Filter
      PUNCTUATION_REGEXP = RUBY_VERSION > '1.9' ? /[^\p{Word}\- ]/u : /[^\w\- ]/

      # The icon that will be placed next to an anchored rendered markdown header
      def anchor_icon
        context[:anchor_icon] || '<span aria-hidden="true" class="octicon octicon-link"></span>'
      end

      def call
        result[:toc] = String.new('')

        headers = Hash.new(0)
        doc.css('h1, h2, h3, h4, h5, h6').each do |node|
          text = node.text
          id = ascii_downcase(text)
          id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
          id.tr!(' ', '-') # replace spaces with dash

          uniq = headers[id] > 0 ? "-#{headers[id]}" : ''
          headers[id] += 1
          if header_content = node.children.first
            result[:toc] << %(<li><a href="##{id}#{uniq}">#{CGI.escape_html(text)}</a></li>\n)
            header_content.add_previous_sibling(%(<a id="#{id}#{uniq}" class="anchor" href="##{id}#{uniq}" aria-hidden="true">#{anchor_icon}</a>))
          end
        end
        result[:toc] = %(<ul class="section-nav">\n#{result[:toc]}</ul>) unless result[:toc].empty?
        doc
      end

      if RUBY_VERSION >= '2.4'
        def ascii_downcase(str)
          str.downcase(:ascii)
        end
      else
        def ascii_downcase(str)
          str.downcase
        end
      end
    end
  end
end