lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.8 vs lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.9
- old
+ new
@@ -48,18 +48,19 @@
class ContentConverter
include ::Asciidoctor::Converter
register_for 'epub3-xhtml5'
- EOL = %(\n)
+ LF = ?\n
NoBreakSpace = ' '
ThinNoBreakSpace = ' '
RightAngleQuote = '›'
CalloutStartNum = %(\u2460)
- XmlElementRx = /<\/?.+?>/
CharEntityRx = /&#(\d{2,6});/
+ XmlElementRx = /<\/?.+?>/
+ TrailingPunctRx = /[[:punct:]]$/
FromHtmlSpecialCharsMap = {
'<' => '<',
'>' => '>',
'&' => '&'
@@ -95,10 +96,11 @@
end
end
def document node
docid = node.id
+ pubtype = node.attr 'publication-type', 'book'
if (doctitle = node.doctitle partition: true, use_fallback: true).subtitle?
title = %(#{doctitle.main} )
subtitle = doctitle.subtitle
else
@@ -108,37 +110,37 @@
end
doctitle_sanitized = (node.doctitle sanitize: true, use_fallback: true).to_s
subtitle_formatted = subtitle.split.map {|w| %(<b>#{w}</b>) } * ' '
- if (node.attr 'publication-type', 'book') == 'book'
- byline = nil
+ if pubtype == 'book'
+ byline = ''
else
author = node.attr 'author'
username = node.attr 'username', 'default'
imagesdir = (node.references[:spine].attr 'imagesdir', '.').chomp '/'
- imagesdir = (imagesdir == '.' ? nil : %(#{imagesdir}/))
- byline = %(<p class="byline"><img src="#{imagesdir}avatars/#{username}.jpg"/> <b class="author">#{author}</b></p>#{EOL})
+ imagesdir = imagesdir == '.' ? '' : %(#{imagesdir}/)
+ byline = %(<p class="byline"><img src="#{imagesdir}avatars/#{username}.jpg"/> <b class="author">#{author}</b></p>#{LF})
end
- mark_last_paragraph node
+ mark_last_paragraph node unless pubtype == 'book'
content = node.content
# NOTE must run after content is resolved
# TODO perhaps create dynamic CSS file?
if @icon_names.empty?
- icon_css_head = icon_css_scoped = nil
+ icon_css_head = icon_css_scoped = ''
else
icon_defs = @icon_names.map {|name|
%(.i-#{name}::before { content: "#{FontIconMap[name.tr('-', '_').to_sym]}"; })
- } * EOL
+ } * LF
icon_css_head = %(<style>
#{icon_defs}
</style>
)
# NOTE Namo Pubtree requires icon CSS to be repeated inside <body> (or in a linked stylesheet); wrap in div to hide from Aldiko
- icon_css_scoped = (node.attr? 'ebook-format', 'kf8') ? nil : %(<div style="display: none" aria-hidden="true"><style scoped="scoped">
+ icon_css_scoped = (node.attr? 'ebook-format', 'kf8') ? '' : %(<div style="display: none" aria-hidden="true"><style scoped="scoped">
#{icon_defs}
</style></div>
)
end
@@ -186,27 +188,27 @@
lines << '</section>
</body>
</html>'
- lines * EOL
+ lines * LF
end
# NOTE embedded is used for AsciiDoc table cell content
def embedded node
node.content
end
def section node
hlevel = node.level + 1
- epub_type_attr = node.special ? %( epub:type="#{node.sectname}") : nil
+ epub_type_attr = node.special ? %( epub:type="#{node.sectname}") : ''
div_classes = [%(sect#{node.level}), node.role].compact
title = node.title
title_sanitized = xml_sanitize title
if node.document.header? || node.level != 1 || node != node.document.first_section
%(<section class="#{div_classes * ' '}" title="#{title_sanitized}"#{epub_type_attr}>
-<h#{hlevel} id="#{node.id}">#{title}</h#{hlevel}>#{(content = node.content).empty? ? nil : %[
+<h#{hlevel} id="#{node.id}">#{title}</h#{hlevel}>#{(content = node.content).empty? ? '' : %[
#{content}]}
</section>)
else
# document has no level-0 heading and this heading serves as the document title
node.content
@@ -245,12 +247,11 @@
def paragraph node
role = node.role
# stack-head is the alternative to the default, inline-head (where inline means "run-in")
head_stop = node.attr 'head-stop', (role && (node.has_role? 'stack-head') ? nil : '.')
- # FIXME promote regexp to constant
- head = node.title? ? %(<strong class="head">#{title = node.title}#{head_stop && title !~ /[[:punct:]]$/ ? head_stop : nil}</strong> ) : nil
+ head = node.title? ? %(<strong class="head">#{title = node.title}#{head_stop && title !~ TrailingPunctRx ? head_stop : ''}</strong> ) : ''
if role
node.set_option 'hardbreaks' if node.has_role? 'signature'
%(<p class="#{role}">#{head}#{node.content}</p>)
else
%(<p>#{head}#{node.content}</p>)
@@ -265,20 +266,20 @@
content
end
end
def admonition node
- id_attr = node.id ? %( id="#{node.id}") : nil
+ id_attr = node.id ? %( id="#{node.id}") : ''
if node.title?
title = node.title
title_sanitized = xml_sanitize title
title_attr = %( title="#{node.caption}: #{title_sanitized}")
title_el = %(<h2>#{title}</h2>
)
else
title_attr = %( title="#{node.caption}")
- title_el = nil
+ title_el = ''
end
type = node.attr 'name'
epub_type = case type
when 'tip'
@@ -294,32 +295,32 @@
</div>
</aside>)
end
def example node
- id_attr = node.id ? %( id="#{node.id}") : nil
+ id_attr = node.id ? %( id="#{node.id}") : ''
title_div = node.title? ? %(<div class="example-title">#{node.title}</div>
-) : nil
+) : ''
%(<div#{id_attr} class="example">
#{title_div}<div class="example-content">
#{convert_content node}
</div>
</div>)
end
def floating_title node
tag_name = %(h#{node.level + 1})
- id_attribute = node.id ? %( id="#{node.id}") : nil
+ id_attribute = node.id ? %( id="#{node.id}") : ''
%(<#{tag_name}#{id_attribute} class="#{['discrete', node.role].compact * ' '}">#{node.title}</#{tag_name}>)
end
def listing node
figure_classes = ['listing']
figure_classes << 'coalesce' if node.option? 'unbreakable'
pre_classes = node.style == 'source' ? ['source', %(language-#{node.attr 'language'})] : ['screen']
title_div = node.title? ? %(<figcaption>#{node.captioned_title}</figcaption>
-) : nil
+) : ''
# patches conums to fix extra or missing leading space
# TODO remove patch once upgrading to Asciidoctor 1.5.6
%(<figure class="#{figure_classes * ' '}">
#{title_div}<pre class="#{pre_classes * ' '}"><code>#{(node.content || '').gsub(/(?<! )<i class="conum"| +<i class="conum"/, ' <i class="conum"')}</code></pre>
</figure>)
@@ -354,11 +355,11 @@
if node.title?
footer_content << %(<span class="context">#{node.title}</span>)
end
- footer_tag = footer_content.empty? ? nil : %(
+ footer_tag = footer_content.empty? ? '' : %(
<footer>~ #{footer_content * ' '}</footer>)
content = (convert_content node).strip.
sub(OpenParagraphTagRx, '<p><span class="open-quote">“</span>').
sub(CloseParagraphTagRx, '<span class="close-quote">”</span></p>')
%(<div#{id_attr}#{class_attr}>
@@ -381,11 +382,11 @@
citetitle_sanitized = xml_sanitize citetitle
footer_content << %(<cite title="#{citetitle_sanitized}">#{citetitle}</cite>)
end
footer_tag = footer_content.size > 0 ? %(
-<span class="attribution">~ #{footer_content * ', '}</span>) : nil
+<span class="attribution">~ #{footer_content * ', '}</span>) : ''
%(<div#{id_attr}#{class_attr}>
<pre>#{node.content}#{footer_tag}</pre>
</div>)
end
@@ -397,12 +398,11 @@
title_sanitized = xml_sanitize title
title_attr = %( title="#{title_sanitized}")
title_el = %(<h2>#{title}</h2>
)
else
- title_attr = nil
- title_el = nil
+ title_attr = title_el = ''
end
%(<aside class="#{classes * ' '}"#{title_attr} epub:type="sidebar">
#{title_el}<div class="content">
#{convert_content node}
@@ -411,11 +411,11 @@
end
def table node
lines = [%(<div class="table">)]
lines << %(<div class="content">)
- table_id_attr = node.id ? %( id="#{node.id}") : nil
+ table_id_attr = node.id ? %( id="#{node.id}") : ''
frame_class = {
'all' => 'table-framed',
'topbot' => 'table-framed-topbot',
'sides' => 'table-framed-sides',
'none' => ''
@@ -433,11 +433,11 @@
table_class_attr = %( class="#{table_classes * ' '}")
table_styles = []
unless (node.option? 'autowidth') && !(node.attr? 'width', nil, false)
table_styles << %(width: #{node.attr 'tablepcwidth'}%)
end
- table_style_attr = table_styles.size > 0 ? %( style="#{table_styles * '; '}") : nil
+ table_style_attr = table_styles.size > 0 ? %( style="#{table_styles * '; '}") : ''
lines << %(<table#{table_id_attr}#{table_class_attr}#{table_style_attr}>)
lines << %(<caption>#{node.captioned_title}</caption>) if node.title?
if (node.attr 'rowcount') > 0
lines << '<colgroup>'
@@ -481,25 +481,25 @@
cell_classes << 'halign-left'
end
if (halign = cell.attr 'valign') && halign != 'top'
cell_classes << 'valign-top'
end
- cell_class_attr = cell_classes.size > 0 ? %( class="#{cell_classes * ' '}") : nil
- cell_colspan_attr = cell.colspan ? %( colspan="#{cell.colspan}") : nil
- cell_rowspan_attr = cell.rowspan ? %( rowspan="#{cell.rowspan}") : nil
- cell_style_attr = (node.document.attr? 'cellbgcolor') ? %( style="background-color: #{node.document.attr 'cellbgcolor'}") : nil
+ cell_class_attr = cell_classes.size > 0 ? %( class="#{cell_classes * ' '}") : ''
+ cell_colspan_attr = cell.colspan ? %( colspan="#{cell.colspan}") : ''
+ cell_rowspan_attr = cell.rowspan ? %( rowspan="#{cell.rowspan}") : ''
+ cell_style_attr = (node.document.attr? 'cellbgcolor') ? %( style="background-color: #{node.document.attr 'cellbgcolor'}") : ''
lines << %(<#{cell_tag_name}#{cell_class_attr}#{cell_colspan_attr}#{cell_rowspan_attr}#{cell_style_attr}>#{cell_content}</#{cell_tag_name}>)
end
lines << '</tr>'
end
lines << %(</t#{tsec}>)
end
end
lines << '</table>
</div>
</div>'
- lines * EOL
+ lines * LF
end
def colist node
lines = ['<div class="callout-list">
<ol>']
@@ -520,22 +520,22 @@
list_tag_name = (style == 'itemized' ? 'ul' : 'ol')
role = node.role
subject_stop = node.attr 'subject-stop', (role && (node.has_role? 'stack') ? nil : ':')
# QUESTION should we just use itemized-list and ordered-list as the class here? or just list?
div_classes = [%(#{style}-list), role].compact
- list_class_attr = (node.option? 'brief') ? ' class="brief"' : nil
+ list_class_attr = (node.option? 'brief') ? ' class="brief"' : ''
lines << %(<div class="#{div_classes * ' '}">
-<#{list_tag_name}#{list_class_attr}#{list_tag_name == 'ol' && (node.option? 'reversed') ? ' reversed="reversed"' : nil}>)
+<#{list_tag_name}#{list_class_attr}#{list_tag_name == 'ol' && (node.option? 'reversed') ? ' reversed="reversed"' : ''}>)
node.items.each do |subjects, dd|
# consists of one term (a subject) and supporting content
subject = [*subjects].first.text
subject_plain = xml_sanitize subject, :plain
- subject_element = %(<strong class="subject">#{subject}#{subject_stop && subject_plain !~ /[[:punct:]]$/ ? subject_stop : nil}</strong>)
+ subject_element = %(<strong class="subject">#{subject}#{subject_stop && subject_plain !~ TrailingPunctRx ? subject_stop : ''}</strong>)
lines << '<li>'
if dd
# NOTE: must wrap remaining text in a span to help webkit justify the text properly
- lines << %(<span class="principal">#{subject_element}#{dd.text? ? %[ <span class="supporting">#{dd.text}</span>] : nil}</span>)
+ lines << %(<span class="principal">#{subject_element}#{dd.text? ? %[ <span class="supporting">#{dd.text}</span>] : ''}</span>)
lines << dd.content if dd.blocks?
else
lines << %(<span class="principal">#{subject_element}</span>)
end
lines << '</li>'
@@ -563,23 +563,23 @@
end
end
lines << '</dl>
</div>'
end
- lines * EOL
+ lines * LF
end
- # TODO support start attribute
def olist node
complex = false
div_classes = ['ordered-list', node.style, node.role].compact
ol_classes = [node.style, ((node.option? 'brief') ? 'brief' : nil)].compact
- ol_class_attr = ol_classes.empty? ? nil : %( class="#{ol_classes * ' '}")
- id_attribute = node.id ? %( id="#{node.id}") : nil
+ ol_class_attr = ol_classes.empty? ? '' : %( class="#{ol_classes * ' '}")
+ ol_start_attr = (node.attr? 'start') ? %( start="#{node.attr 'start'}") : ''
+ id_attribute = node.id ? %( id="#{node.id}") : ''
lines = [%(<div#{id_attribute} class="#{div_classes * ' '}">)]
lines << %(<h3 class="list-heading">#{node.title}</h3>) if node.title?
- lines << %(<ol#{ol_class_attr}#{(node.option? 'reversed') ? ' reversed="reversed"' : nil}>)
+ lines << %(<ol#{ol_class_attr}#{ol_start_attr}#{(node.option? 'reversed') ? ' reversed="reversed"' : ''}>)
node.items.each do |item|
lines << %(<li>
<span class="principal">#{item.text}</span>)
if item.blocks?
lines << item.content
@@ -591,19 +591,19 @@
div_classes << 'complex'
lines[0] = %(<div class="#{div_classes * ' '}">)
end
lines << '</ol>
</div>'
- lines * EOL
+ lines * LF
end
def ulist node
complex = false
div_classes = ['itemized-list', node.style, node.role].compact
ul_classes = [node.style, ((node.option? 'brief') ? 'brief' : nil)].compact
- ul_class_attr = ul_classes.empty? ? nil : %( class="#{ul_classes * ' '}")
- id_attribute = node.id ? %( id="#{node.id}") : nil
+ ul_class_attr = ul_classes.empty? ? '' : %( class="#{ul_classes * ' '}")
+ id_attribute = node.id ? %( id="#{node.id}") : ''
lines = [%(<div#{id_attribute} class="#{div_classes * ' '}">)]
lines << %(<h3 class="list-heading">#{node.title}</h3>) if node.title?
lines << %(<ul#{ul_class_attr}>)
node.items.each do |item|
lines << %(<li>
@@ -618,11 +618,11 @@
div_classes << 'complex'
lines[0] = %(<div class="#{div_classes * ' '}">)
end
lines << '</ul>
</div>'
- lines * EOL
+ lines * LF
end
def image node
target = node.attr 'target'
type = (::File.extname target)[1..-1]
@@ -658,11 +658,11 @@
=end
%(<figure#{id_attr} class="image#{prepend_space node.role}">
<div class="content">
<img src="#{node.image_uri node.attr('target')}" #{img_attrs * ' '}/>
</div>#{node.title? ? %[
-<figcaption>#{node.captioned_title}</figcaption>] : nil}
+<figcaption>#{node.captioned_title}</figcaption>] : ''}
</figure>)
end
def inline_anchor node
target = node.target
@@ -686,11 +686,11 @@
refdoc_id = refdoc_refid = refid
# inflate key to spine item root (e.g., transform chapter-id to chapter-id#chapter-id)
refid = %(#{refid}##{refid})
id_attr = %( id="xref--#{refdoc_id}")
end
- id_attr = nil unless @xrefs_seen.add? refid
+ id_attr = '' unless @xrefs_seen.add? refid
refdoc = doc.references[:spine_items].find {|it| refdoc_id == (it.id || (it.attr 'docname')) }
if refdoc
# QUESTION should we invoke xreftext for references in other documents?
if (refs = refdoc.references[:refs]) && ::Asciidoctor::Document === (ref = refs[refdoc_refid])
text ||= (ref.attr 'docreftext') || ref.doctitle
@@ -701,10 +701,10 @@
end
else
warn %(asciidoctor: WARNING: #{::File.basename(doc.attr 'docfile')}: invalid reference to anchor in unknown chapter: #{refdoc_id})
end
else
- id_attr = (@xrefs_seen.add? refid) ? %( id="xref-#{refid}") : nil
+ id_attr = (@xrefs_seen.add? refid) ? %( id="xref-#{refid}") : ''
if (refs = doc.references[:refs])
if ::Asciidoctor::AbstractNode === (ref = refs[refid])
xreftext = text || ref.xreftext((@xrefstyle ||= (doc.attr 'xrefstyle')))
end
else