fic_tracker/lib/fic_tracker/renderers/html.rb

151 lines
4.4 KiB
Ruby

# frozen_string_literal: true
module FicTracker::Renderers
class HTML
attr_accessor :body_only, :cover, :preface
attr_reader :story
def initialize(story, io:, cover: false, preface: true, body_only: false)
@story = story
@io = io
@cover = story.image if cover
@preface = preface
@body_only = body_only
end
def render
logger.info "Rendering html for #{story}"
doc = Nokogiri::HTML5::Document.new
Nokogiri::XML::Builder.with(doc) do |html|
build_html(html) do
html.article class: "story" do
if preface
build_preface(html)
html.hr
end
story.chapters.each do |chapter|
html.article class: 'chapter' do
build_chapter(html, chapter)
end
html.hr unless chapter == story.chapters.last
end
end
end
end
@io.puts doc.to_html
end
def build_preface(html)
html.article class: 'preface' do
html.h1 story.name
html.address do
html << "By "
story.authors.each do |author|
html << 'and ' if author != story.authors.first && author == story.authors.last
if author.url
html.a author.to_s, href: author.url
else
html.i author.to_s
end
html << ', ' unless author == story.authors.last
end
html.i '<Unknown>' if story.authors.empty?
end
html.a "On #{story.backend.full_name}.", href: story.url if story.url
html.span do
html << 'Published '
html.time story.published_at.to_date, datetime: story.published_at.iso8601
if story.updated_at && story.updated_at != story.published_at
html << ", #{story.completed? ? 'completed' : 'updated'} "
html.time story.updated_at.to_date, datetime: story.updated_at.iso8601
end
html << '.'
end
html.br
html.dl do
story.tags&.map { |td| FicTracker::Models::Light::Tag.load td }.sort_by(&:ordering).group_by(&:category).each do |category, tags|
html.dt category.to_s.capitalize
html.dd do
tags.each do |tag|
if tag.important?
html.b tag.name
else
html << tag.name
end
html << ', ' unless tag == tags.last
end
end
end
end
html.article class: 'synopsis' do
html << story.synopsis
end
html.img alt: 'Cover image', src: cover if cover
end
end
def build_chapter(html, chapter)
html.h2 do
html.a chapter, href: chapter.url
end
case chapter.content_type
when 'text/html'
require_relative '../converters/from_html'
html << FicTracker::Converters::FromHTML.to_safe_html(chapter.content)
when 'text/markdown'
require_relative '../converters/from_markdown'
html << FicTracker::Converters::FromMarkdown.to_safe_html(chapter.content)
when 'text/plain'
chapter.content.split("\n\n").each do |para|
html.p para
end
else
raise "Unknown chapter content-type: #{chapter.content_type.inspect}"
end
end
private
def logger
Logging.logger[self]
end
def build_html(html)
return yield if body_only
require_relative '../util/time_extensions'
html.html(lang: story.language || 'en') do
html.head do
html.meta charset: 'utf-8'
html.meta name: 'viewport', content: 'width=device-width, initial-scale=1'
story.authors.each do |aut|
html.meta name: 'author', content: aut.to_s
end
html.meta name: 'generator', content: "FicTracker/#{FicTracker::VERSION}"
html.meta name: 'keywords', content: story.tags.map(&:to_s).join(',')
html.meta name: 'ft-backend', content: story.backend_name
html.meta name: 'ft-story', content: story.slug
html.meta name: 'ft-etag', content: story.etag
html.meta name: 'ft-modified', content: (story.updated_at || story.published_at || Time.now).to_header
html.meta name: 'ft-complete', content: story.completed?
html.title story.to_s
end
html.body do
yield
end
end
end
end
end