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 = '&#xa0;' ThinNoBreakSpace = '&#x202f;' RightAngleQuote = '&#x203a;' CalloutStartNum = %(\u2460) - XmlElementRx = /<\/?.+?>/ CharEntityRx = /&#(\d{2,6});/ + XmlElementRx = /<\/?.+?>/ + TrailingPunctRx = /[[:punct:]]$/ FromHtmlSpecialCharsMap = { '&lt;' => '<', '&gt;' => '>', '&amp;' => '&' @@ -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