diff options
author | ThibG <thib@sitedethib.com> | 2019-09-30 23:29:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-30 23:29:55 +0200 |
commit | 65db9df011a3e13efa79f4e56343aa69bdad7716 (patch) | |
tree | 1941b85de1f0d1b6f56b73345cddf664a71faf20 /app/lib/toc_generator.rb | |
parent | febcdad2e2c98aee62b55ee21bdf0debf7c6fd6b (diff) | |
parent | 3b855b5c82362783969f748ad78bcaf85f938c9f (diff) |
Merge pull request #1224 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
Diffstat (limited to 'app/lib/toc_generator.rb')
-rw-r--r-- | app/lib/toc_generator.rb | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/app/lib/toc_generator.rb b/app/lib/toc_generator.rb new file mode 100644 index 000000000..0c8f766ca --- /dev/null +++ b/app/lib/toc_generator.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +class TOCGenerator + TARGET_ELEMENTS = %w(h1 h2 h3 h4 h5 h6).freeze + LISTED_ELEMENTS = %w(h2 h3).freeze + + class Section + attr_accessor :depth, :title, :children, :anchor + + def initialize(depth, title, anchor) + @depth = depth + @title = title + @children = [] + @anchor = anchor + end + + delegate :<<, to: :children + end + + def initialize(source_html) + @source_html = source_html + @processed = false + @target_html = '' + @headers = [] + @slugs = Hash.new { |h, k| h[k] = 0 } + end + + def html + parse_and_transform unless @processed + @target_html + end + + def toc + parse_and_transform unless @processed + @headers + end + + private + + def parse_and_transform + return if @source_html.blank? + + parsed_html = Nokogiri::HTML.fragment(@source_html) + + parsed_html.traverse do |node| + next unless TARGET_ELEMENTS.include?(node.name) + + anchor = node['id'] || node.text.parameterize.presence || 'sec' + @slugs[anchor] += 1 + anchor = "#{anchor}-#{@slugs[anchor]}" if @slugs[anchor] > 1 + + node['id'] = anchor + + next unless LISTED_ELEMENTS.include?(node.name) + + depth = node.name[1..-1] + latest_section = @headers.last + + if latest_section.nil? || latest_section.depth >= depth + @headers << Section.new(depth, node.text, anchor) + else + latest_section << Section.new(depth, node.text, anchor) + end + end + + @target_html = parsed_html.to_s + @processed = true + end +end |