class Polites::HtmlFormatter

Takes an AST as produced by {Parser} and generates HTML output from it. This output is not guaranteed to be the exact same as Polites would produce itself, but it should be structurally similar.

Constants

NL

Public Class Methods

new() click to toggle source
# File lib/polites/html_formatter.rb, line 19
def initialize
  @footnotes = []
  @indenter = ListIndenter.new
  @code_grouper = CodeGrouper.new
end

Public Instance Methods

call(obj, indent: false, join: '') click to toggle source

Apply formatting to the given AST.

@param [Object] obj @param [Bool] indent whether to apply indentation when formatting. Used internally. @param [String] join how to join multiple elements together. Used internally. @return [String]

# File lib/polites/html_formatter.rb, line 31
def call(obj, indent: false, join: '')
  case obj
  when Sheet
    content = call(obj.content, indent: true, join: NL)
    content << NL << tag(:ol, NL + footnotes + NL, id: 'footnotes') << NL if @footnotes.any?
    content
  when ListIndenter::List
    nl = obj.children.first.level.positive? ? NL : ''
    tag_name = case obj.children.first
               when Block::OrderedList then 'ol'
               when Block::UnorderedList then 'ul'
               end
    nl + tag(tag_name, NL + call(obj.children, join: NL) + NL) + nl
  when Array
    coll = indent ? @code_grouper.call(@indenter.call(obj)) : obj
    coll.map { |c| call(c) }.join(join)
  when Block::UnorderedList, Block::OrderedList
    tag(:li, call(obj.children))
  when Block::Paragraph
    tag(:p, call(obj.children))
  when Block::Heading1
    tag(:h1, call(obj.children))
  when Block::Heading2
    tag(:h2, call(obj.children))
  when Block::Heading3
    tag(:h3, call(obj.children))
  when Block::Heading4
    tag(:h4, call(obj.children))
  when Block::Heading5
    tag(:h5, call(obj.children))
  when Block::Heading6
    tag(:h6, call(obj.children))
  when Block::Blockquote
    tag(:blockquote, tag(:p, call(obj.children)))
  when Block::CodeBlock
    tag(:pre, tag(:code, CGI.escape_html(call(obj.children)), class: "language-#{obj.syntax}"))
  when Block::Divider
    tag(:hr)
  when Span::Strong
    tag(:strong, call(obj.children))
  when Span::Emph
    tag(:em, call(obj.children))
  when Span::Delete
    tag(:del, call(obj.children))
  when Span::Mark
    tag(:mark, call(obj.children))
  when Span::Code
    tag(:code, call(obj.children))
  when Span::Annotation
    call(obj.children)
  when Span::Footnote
    @footnotes << obj.text
    n = @footnotes.count
    tag(:sup, tag(:a, n, id: "ffn#{n}", href: "#fn#{n}", class: 'footnote'))
  when Span::Image
    figcaption = obj.description ? tag(:figcaption, obj.description) : ''
    tag(:figure, tag(:img, nil, src: image_src(obj), title: obj.title, alt: obj.description, width: obj.width, height: obj.height) + figcaption)
  when Span::Link
    tag(:a, call(obj.children), href: obj.url, title: obj.title)
  when Text
    obj.text
  else
    raise FormattingError, "Unknown obj #{obj.inspect}"
  end
end

Private Instance Methods

footnotes() click to toggle source
# File lib/polites/html_formatter.rb, line 103
def footnotes
  @footnotes
    .compact
    .each_with_index
    .map { |obj, i| "  #{tag(:li, call(obj), id: "fn#{i + 1}")}" }
    .join(NL)
end
image_src(image_element) click to toggle source
# File lib/polites/html_formatter.rb, line 99
def image_src(image_element)
  image_element.filename || image_element.image
end
tag(name, content = nil, attributes = {}) click to toggle source
# File lib/polites/html_formatter.rb, line 111
def tag(name, content = nil, attributes = {})
  html_attributes = attributes.compact.inject('') do |acc, (key, value)|
    acc + %( #{key}="#{value}")
  end
  case name
  when :hr, :img
    "<#{name}#{html_attributes}>"
  else
    "<#{name}#{html_attributes}>#{content}</#{name}>"
  end
end