151 lines
4.4 KiB
Ruby
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
|