diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2019-03-18 05:10:25 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-03-18 05:10:25 -0600 |
| commit | ee9421003d7b59d0e10e1789aa6c0871fb4c5df6 (patch) | |
| tree | eadf18ba6ee33be26978b87b39142bc0b036b3bb | |
| parent | 0c5534062949ef9463d10122d5838cb43f9980e4 (diff) | |
when resolving a convert method for a node, only consider methods prefixed with convert_ (PR #3150)
- prefix convert methods on built-in converters with convert_
- drop leading underscore from helper methods on built-in converters
- add adapter for older converters that rely on unprefixed convert methods
- use optimize convert handler dispatcher in HTML 5 converter
- only pass options to convert handler method if value is non-nil
- update RDoc
| -rw-r--r-- | lib/asciidoctor/converter.rb | 31 | ||||
| -rw-r--r-- | lib/asciidoctor/converter/docbook5.rb | 204 | ||||
| -rw-r--r-- | lib/asciidoctor/converter/html5.rb | 209 | ||||
| -rw-r--r-- | lib/asciidoctor/converter/manpage.rb | 162 | ||||
| -rw-r--r-- | test/converter_test.rb | 16 | ||||
| -rw-r--r-- | test/document_test.rb | 4 |
6 files changed, 339 insertions, 287 deletions
diff --git a/lib/asciidoctor/converter.rb b/lib/asciidoctor/converter.rb index c0061b95..a440ff8b 100644 --- a/lib/asciidoctor/converter.rb +++ b/lib/asciidoctor/converter.rb @@ -36,7 +36,7 @@ module Asciidoctor # # class Html5Converter < (Asciidoctor::Converter.for 'html5') # register_for 'html5' -# def paragraph node +# def convert_paragraph node # %(<p>#{node.content}</p>) # end # end @@ -73,13 +73,13 @@ module Converter raise ::NotImplementedError, %(#{self.class} (backend: #{@backend}) must implement the ##{__method__} method) end - # Public: Reports whether the current converter is able to convert this node (by its name). Used by the + # Public: Reports whether the current converter is able to convert this node (by its transform name). Used by the # {CompositeConverter} to select which converter to use to handle a given node. Returns true by default. # - # name - the String name of the node to convert. + # transform - the String name of the node transformation (typically the node name). # - # Returns a [Boolean] indicating whether this converter can handle the specified node by name. - def handles? name + # Returns a [Boolean] indicating whether this converter can handle the specified transform. + def handles? transform true end @@ -368,35 +368,36 @@ module Converter # Public: Converts an {AbstractNode} by delegating to a method that matches the transform value. # - # This method looks for a method that matches the name of the transform to dispatch to. If the +opts+ argument is - # non-nil, this method assumes the dispatch method accepts two arguments, the node and an options Hash. The options - # Hash may be used by converters to delegate back to the top-level converter. Currently, it's used for the outline - # transform. If the +opts+ argument is nil, this method assumes the dispatch method accepts the node as its only - # argument. To distiguish from node dispatch methods, the convention is to prefix the name of helper method with - # underscore and mark them as private. Implementations may override this method to provide different behavior. + # This method looks for a method whose name matches the transform prefixed with "convert_" to dispatch to. If the + # +opts+ argument is non-nil, this method assumes the dispatch method accepts two arguments, the node and an options + # Hash. The options Hash may be used by converters to delegate back to the top-level converter. Currently, this + # feature is used for the outline transform. If the +opts+ argument is nil, this method assumes the dispatch method + # accepts the node as its only argument. # # See {Converter#convert} for details about the arguments and return value. def convert node, transform = node.node_name, opts = nil - opts.nil_or_empty? ? (send transform, node) : (send transform, node, opts) + opts ? (send 'convert_' + transform, node, opts) : (send 'convert_' + transform, node) rescue raise unless ::NoMethodError === (ex = $!) && ex.receiver == self && ex.name.to_s == transform logger.warn %(missing convert handler for #{ex.name} node in #{@backend} backend (#{self.class})) nil end - alias handles? respond_to? + def handles? transform + respond_to? %(convert_#{transform}) + end # Public: Converts the {AbstractNode} using only its converted content. # # Returns the converted [String] content. - def _content_only node + def content_only node node.content end # Public: Skips conversion of the {AbstractNode}. # # Returns nothing. - def _skip node; end + def skip node; end end extend DefaultFactory # exports static methods diff --git a/lib/asciidoctor/converter/docbook5.rb b/lib/asciidoctor/converter/docbook5.rb index a87e1ca5..a9c43655 100644 --- a/lib/asciidoctor/converter/docbook5.rb +++ b/lib/asciidoctor/converter/docbook5.rb @@ -33,7 +33,7 @@ class Converter::DocBook5Converter < Converter::Base init_backend_traits basebackend: 'docbook', filetype: 'xml', outfilesuffix: '.xml', supports_templates: true end - def document node + def convert_document node result = ['<?xml version="1.0" encoding="UTF-8"?>'] if node.attr? 'toc' if node.attr? 'toclevels' @@ -53,8 +53,8 @@ class Converter::DocBook5Converter < Converter::Base if (root_tag_name = node.doctype) == 'manpage' root_tag_name = 'refentry' end - result << %(<#{root_tag_name} xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"#{lang_attribute}#{_common_attributes node.id}>) - result << (_document_info_tag node) unless node.noheader + result << %(<#{root_tag_name} xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"#{lang_attribute}#{common_attributes node.id}>) + result << (document_info_tag node) unless node.noheader unless (docinfo_content = node.docinfo :header).empty? result << docinfo_content end @@ -66,31 +66,31 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - alias embedded _content_only + alias convert_embedded content_only - def section node + def convert_section node if node.document.doctype == 'manpage' tag_name = MANPAGE_SECTION_TAGS[tag_name = node.sectname] || tag_name else tag_name = node.sectname end title_el = node.special && (node.option? 'untitled') ? '' : %(<title>#{node.title}</title>\n) - %(<#{tag_name}#{_common_attributes node.id, node.role, node.reftext}> + %(<#{tag_name}#{common_attributes node.id, node.role, node.reftext}> #{title_el}#{node.content} </#{tag_name}>) end - def admonition node - %(<#{tag_name = node.attr 'name'}#{_common_attributes node.id, node.role, node.reftext}> -#{_title_tag node}#{_enclose_content node} + def convert_admonition node + %(<#{tag_name = node.attr 'name'}#{common_attributes node.id, node.role, node.reftext}> +#{title_tag node}#{enclose_content node} </#{tag_name}>) end - alias audio _skip + alias convert_audio skip - def colist node + def convert_colist node result = [] - result << %(<calloutlist#{_common_attributes node.id, node.role, node.reftext}>) + result << %(<calloutlist#{common_attributes node.id, node.role, node.reftext}>) result << %(<title>#{node.title}</title>) if node.title? node.items.each do |item| result << %(<callout arearefs="#{item.attr 'coids'}">) @@ -102,11 +102,11 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - def dlist node + def convert_dlist node result = [] if node.style == 'horizontal' - result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{_common_attributes node.id, node.role, node.reftext} tabstyle="horizontal" frame="none" colsep="0" rowsep="0"> -#{_title_tag node}<tgroup cols="2"> + result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext} tabstyle="horizontal" frame="none" colsep="0" rowsep="0"> +#{title_tag node}<tgroup cols="2"> <colspec colwidth="#{node.attr 'labelwidth', 15}*"/> <colspec colwidth="#{node.attr 'itemwidth', 85}*"/> <tbody valign="top">) @@ -134,7 +134,7 @@ class Converter::DocBook5Converter < Converter::Base term_tag = tags[:term] item_tag = tags[:item] if list_tag - result << %(<#{list_tag}#{_common_attributes node.id, node.role, node.reftext}>) + result << %(<#{list_tag}#{common_attributes node.id, node.role, node.reftext}>) result << %(<title>#{node.title}</title>) if node.title? end @@ -158,24 +158,24 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - def example node + def convert_example node if node.title? - %(<example#{_common_attributes node.id, node.role, node.reftext}> + %(<example#{common_attributes node.id, node.role, node.reftext}> <title>#{node.title}</title> -#{_enclose_content node} +#{enclose_content node} </example>) else - %(<informalexample#{_common_attributes node.id, node.role, node.reftext}> -#{_enclose_content node} + %(<informalexample#{common_attributes node.id, node.role, node.reftext}> +#{enclose_content node} </informalexample>) end end - def floating_title node - %(<bridgehead#{_common_attributes node.id, node.role, node.reftext} renderas="sect#{node.level}">#{node.title}</bridgehead>) + def convert_floating_title node + %(<bridgehead#{common_attributes node.id, node.role, node.reftext} renderas="sect#{node.level}">#{node.title}</bridgehead>) end - def image node + def convert_image node # NOTE according to the DocBook spec, content area, scaling, and scaling to fit are mutually exclusive # See http://tdg.docbook.org/tdg/4.5/imagedata-x.html#d0e79635 if node.attr? 'scaledwidth' @@ -202,20 +202,20 @@ class Converter::DocBook5Converter < Converter::Base </mediaobject>) if node.title? - %(<figure#{_common_attributes node.id, node.role, node.reftext}> + %(<figure#{common_attributes node.id, node.role, node.reftext}> <title>#{node.title}</title> #{mediaobject} </figure>) else - %(<informalfigure#{_common_attributes node.id, node.role, node.reftext}> + %(<informalfigure#{common_attributes node.id, node.role, node.reftext}> #{mediaobject} </informalfigure>) end end - def listing node + def convert_listing node informal = !node.title? - common_attrs = _common_attributes node.id, node.role, node.reftext + common_attrs = common_attributes node.id, node.role, node.reftext if node.style == 'source' if (attrs = node.attributes).key? 'linenums' numbering_attrs = (attrs.key? 'start') ? %( linenumbering="numbered" startinglinenumber="#{attrs['start'].to_i}") : ' linenumbering="numbered"' @@ -238,22 +238,22 @@ class Converter::DocBook5Converter < Converter::Base </formalpara>) end - def literal node + def convert_literal node if node.title? - %(<formalpara#{_common_attributes node.id, node.role, node.reftext}> + %(<formalpara#{common_attributes node.id, node.role, node.reftext}> <title>#{node.title}</title> <para> <literallayout class="monospaced">#{node.content}</literallayout> </para> </formalpara>) else - %(<literallayout#{_common_attributes node.id, node.role, node.reftext} class="monospaced">#{node.content}</literallayout>) + %(<literallayout#{common_attributes node.id, node.role, node.reftext} class="monospaced">#{node.content}</literallayout>) end end - alias pass _content_only + alias convert_pass content_only - def stem node + def convert_stem node if (idx = node.subs.index :specialcharacters) node.subs.delete_at idx equation = node.content || '' @@ -263,33 +263,33 @@ class Converter::DocBook5Converter < Converter::Base end if node.style == 'asciimath' # NOTE fop requires jeuclid to process mathml markup - equation_data = _asciimath_available? ? ((::AsciiMath.parse equation).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML') : %(<mathphrase><![CDATA[#{equation}]]></mathphrase>) + equation_data = asciimath_available? ? ((::AsciiMath.parse equation).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML') : %(<mathphrase><![CDATA[#{equation}]]></mathphrase>) else # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math equation_data = %(<alt><![CDATA[#{equation}]]></alt> <mathphrase><![CDATA[#{equation}]]></mathphrase>) end if node.title? - %(<equation#{_common_attributes node.id, node.role, node.reftext}> + %(<equation#{common_attributes node.id, node.role, node.reftext}> <title>#{node.title}</title> #{equation_data} </equation>) else # WARNING dblatex displays the <informalequation> element inline instead of block as documented (except w/ mathml) - %(<informalequation#{_common_attributes node.id, node.role, node.reftext}> + %(<informalequation#{common_attributes node.id, node.role, node.reftext}> #{equation_data} </informalequation>) end end - def olist node + def convert_olist node result = [] num_attribute = node.style ? %( numeration="#{node.style}") : '' start_attribute = (node.attr? 'start') ? %( startingnumber="#{node.attr 'start'}") : '' - result << %(<orderedlist#{_common_attributes node.id, node.role, node.reftext}#{num_attribute}#{start_attribute}>) + result << %(<orderedlist#{common_attributes node.id, node.role, node.reftext}#{num_attribute}#{start_attribute}>) result << %(<title>#{node.title}</title>) if node.title? node.items.each do |item| - result << %(<listitem#{_common_attributes item.id, item.role}>) + result << %(<listitem#{common_attributes item.id, item.role}>) result << %(<simpara>#{item.text}</simpara>) result << item.content if item.blocks? result << '</listitem>' @@ -298,7 +298,7 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - def open node + def convert_open node case node.style when 'abstract' if node.parent == node.document && node.document.doctype == 'book' @@ -306,7 +306,7 @@ class Converter::DocBook5Converter < Converter::Base '' else %(<abstract> -#{_title_tag node}#{_enclose_content node} +#{title_tag node}#{enclose_content node} </abstract>) end when 'partintro' @@ -314,72 +314,72 @@ class Converter::DocBook5Converter < Converter::Base logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.' '' else - %(<partintro#{_common_attributes node.id, node.role, node.reftext}> -#{_title_tag node}#{_enclose_content node} + %(<partintro#{common_attributes node.id, node.role, node.reftext}> +#{title_tag node}#{enclose_content node} </partintro>) end else reftext = node.reftext if (id = node.id) role = node.role if node.title? - %(<formalpara#{_common_attributes id, role, reftext}> + %(<formalpara#{common_attributes id, role, reftext}> <title>#{node.title}</title> <para>#{content_spacer = node.content_model == :compound ? LF : ''}#{node.content}#{content_spacer}</para> </formalpara>) elsif id || role if node.content_model == :compound - %(<para#{_common_attributes id, role, reftext}> + %(<para#{common_attributes id, role, reftext}> #{node.content} </para>) else - %(<simpara#{_common_attributes id, role, reftext}>#{node.content}</simpara>) + %(<simpara#{common_attributes id, role, reftext}>#{node.content}</simpara>) end else - _enclose_content node + enclose_content node end end end - def page_break node + def convert_page_break node '<simpara><?asciidoc-pagebreak?></simpara>' end - def paragraph node + def convert_paragraph node if node.title? - %(<formalpara#{_common_attributes node.id, node.role, node.reftext}> + %(<formalpara#{common_attributes node.id, node.role, node.reftext}> <title>#{node.title}</title> <para>#{node.content}</para> </formalpara>) else - %(<simpara#{_common_attributes node.id, node.role, node.reftext}>#{node.content}</simpara>) + %(<simpara#{common_attributes node.id, node.role, node.reftext}>#{node.content}</simpara>) end end - def preamble node + def convert_preamble node if node.document.doctype == 'book' - %(<preface#{_common_attributes node.id, node.role, node.reftext}> -#{_title_tag node, false}#{node.content} + %(<preface#{common_attributes node.id, node.role, node.reftext}> +#{title_tag node, false}#{node.content} </preface>) else node.content end end - def quote node - _blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { _enclose_content node } + def convert_quote node + blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { enclose_content node } end - def thematic_break node + def convert_thematic_break node '<simpara><?asciidoc-hr?></simpara>' end - def sidebar node - %(<sidebar#{_common_attributes node.id, node.role, node.reftext}> -#{_title_tag node}#{_enclose_content node} + def convert_sidebar node + %(<sidebar#{common_attributes node.id, node.role, node.reftext}> +#{title_tag node}#{enclose_content node} </sidebar>) end - def table node + def convert_table node has_body = false result = [] pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : '' @@ -387,7 +387,7 @@ class Converter::DocBook5Converter < Converter::Base frame = 'topbot' end grid = node.attr 'grid', nil, 'table-grid' - result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{_common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{frame}" rowsep="#{['none', 'cols'].include?(grid) ? 0 : 1}" colsep="#{['none', 'rows'].include?(grid) ? 0 : 1}"#{(node.attr? 'orientation', 'landscape', 'table-orientation') ? ' orient="land"' : ''}>) + result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{frame}" rowsep="#{['none', 'cols'].include?(grid) ? 0 : 1}" colsep="#{['none', 'rows'].include?(grid) ? 0 : 1}"#{(node.attr? 'orientation', 'landscape', 'table-orientation') ? ' orient="land"' : ''}>) if (node.option? 'unbreakable') result << '<?dbfo keep-together="always"?>' elsif (node.option? 'breakable') @@ -447,12 +447,12 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - alias toc _skip + alias convert_toc skip - def ulist node + def convert_ulist node result = [] if node.style == 'bibliography' - result << %(<bibliodiv#{_common_attributes node.id, node.role, node.reftext}>) + result << %(<bibliodiv#{common_attributes node.id, node.role, node.reftext}>) result << %(<title>#{node.title}</title>) if node.title? node.items.each do |item| result << '<bibliomixed>' @@ -464,11 +464,11 @@ class Converter::DocBook5Converter < Converter::Base else mark_type = (checklist = node.option? 'checklist') ? 'none' : node.style mark_attribute = mark_type ? %( mark="#{mark_type}") : '' - result << %(<itemizedlist#{_common_attributes node.id, node.role, node.reftext}#{mark_attribute}>) + result << %(<itemizedlist#{common_attributes node.id, node.role, node.reftext}#{mark_attribute}>) result << %(<title>#{node.title}</title>) if node.title? node.items.each do |item| text_marker = (item.attr? 'checked') ? '✓ ' : '❏ ' if checklist && (item.attr? 'checkbox') - result << %(<listitem#{_common_attributes item.id, item.role}>) + result << %(<listitem#{common_attributes item.id, item.role}>) result << %(<simpara>#{text_marker || ''}#{item.text}</simpara>) result << item.content if item.blocks? result << '</listitem>' @@ -478,16 +478,16 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - def verse node - _blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { %(<literallayout>#{node.content}</literallayout>) } + def convert_verse node + blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { %(<literallayout>#{node.content}</literallayout>) } end - alias video _skip + alias convert_video skip - def inline_anchor node + def convert_inline_anchor node case node.type when :ref - %(<anchor#{_common_attributes((id = node.id), nil, node.reftext || %([#{id}]))}/>) + %(<anchor#{common_attributes((id = node.id), nil, node.reftext || %([#{id}]))}/>) when :xref if (path = node.attributes['path']) # QUESTION should we use refid as fallback text instead? (like the html5 backend?) @@ -499,34 +499,34 @@ class Converter::DocBook5Converter < Converter::Base when :link %(<link xl:href="#{node.target}">#{node.text}</link>) when :bibref - %(<anchor#{_common_attributes node.id, nil, "[#{node.reftext || node.id}]"}/>#{text}) + %(<anchor#{common_attributes node.id, nil, "[#{node.reftext || node.id}]"}/>#{text}) else logger.warn %(unknown anchor type: #{node.type.inspect}) nil end end - def inline_break node + def convert_inline_break node %(#{node.text}<?asciidoc-br?>) end - def inline_button node + def convert_inline_button node %(<guibutton>#{node.text}</guibutton>) end - def inline_callout node - %(<co#{_common_attributes node.id}/>) + def convert_inline_callout node + %(<co#{common_attributes node.id}/>) end - def inline_footnote node + def convert_inline_footnote node if node.type == :xref %(<footnoteref linkend="#{node.target}"/>) else - %(<footnote#{_common_attributes node.id}><simpara>#{node.text}</simpara></footnote>) + %(<footnote#{common_attributes node.id}><simpara>#{node.text}</simpara></footnote>) end end - def inline_image node + def convert_inline_image node width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : '' depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : '' %(<inlinemediaobject> @@ -537,7 +537,7 @@ class Converter::DocBook5Converter < Converter::Base </inlinemediaobject>) end - def inline_indexterm node + def convert_inline_indexterm node if node.type == :visible %(<indexterm><primary>#{node.text}</primary></indexterm>#{node.text}) else @@ -560,7 +560,7 @@ class Converter::DocBook5Converter < Converter::Base end end - def inline_kbd node + def convert_inline_kbd node if (keys = node.attr 'keys').size == 1 %(<keycap>#{keys[0]}</keycap>) else @@ -568,7 +568,7 @@ class Converter::DocBook5Converter < Converter::Base end end - def inline_menu node + def convert_inline_menu node menu = node.attr 'menu' if (submenus = node.attr 'submenus').empty? if (menuitem = node.attr 'menuitem') @@ -581,10 +581,10 @@ class Converter::DocBook5Converter < Converter::Base end end - def inline_quoted node + def convert_inline_quoted node if (type = node.type) == :asciimath # NOTE fop requires jeuclid to process mathml markup - _asciimath_available? ? %(<inlineequation>#{(::AsciiMath.parse node.text).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'}</inlineequation>) : %(<inlineequation><mathphrase><![CDATA[#{node.text}]]></mathphrase></inlineequation>) + asciimath_available? ? %(<inlineequation>#{(::AsciiMath.parse node.text).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'}</inlineequation>) : %(<inlineequation><mathphrase><![CDATA[#{node.text}]]></mathphrase></inlineequation>) elsif type == :latexmath # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math %(<inlineequation><alt><![CDATA[#{equation = node.text}]]></alt><mathphrase><![CDATA[#{equation}]]></mathphrase></inlineequation>) @@ -601,13 +601,13 @@ class Converter::DocBook5Converter < Converter::Base quoted_text = %(#{open}#{text}#{close}) end - node.id ? %(<anchor#{_common_attributes node.id, nil, text}/>#{quoted_text}) : quoted_text + node.id ? %(<anchor#{common_attributes node.id, nil, text}/>#{quoted_text}) : quoted_text end end private - def _common_attributes id, role = nil, reftext = nil + def common_attributes id, role = nil, reftext = nil if id attrs = %( xml:id="#{id}"#{role ? %[ role="#{role}"] : ''}) elsif role @@ -626,7 +626,7 @@ class Converter::DocBook5Converter < Converter::Base end end - def _author_tag author + def author_tag author result = [] result << '<author>' result << '<personname>' @@ -639,7 +639,7 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - def _document_info_tag doc + def document_info_tag doc result = ['<info>'] unless doc.notitle if (title = doc.doctitle partition: true, use_fallback: true).subtitle? @@ -663,10 +663,10 @@ class Converter::DocBook5Converter < Converter::Base unless (authors = doc.authors).empty? if authors.size > 1 result << '<authorgroup>' - authors.each {|author| result << (_author_tag author) } + authors.each {|author| result << (author_tag author) } result << '</authorgroup>' else - result << _author_tag(author = authors[0]) + result << author_tag(author = authors[0]) result << %(<authorinitials>#{author.initials}</authorinitials>) if author.initials end end @@ -681,10 +681,10 @@ class Converter::DocBook5Converter < Converter::Base </revhistory>) end if (doc.attr? 'front-cover-image') || (doc.attr? 'back-cover-image') - if (back_cover_tag = _cover_tag doc, 'back') - result << (_cover_tag doc, 'front', true) + if (back_cover_tag = cover_tag doc, 'back') + result << (cover_tag doc, 'front', true) result << back_cover_tag - elsif (front_cover_tag = _cover_tag doc, 'front') + elsif (front_cover_tag = cover_tag doc, 'front') result << front_cover_tag end end @@ -712,15 +712,15 @@ class Converter::DocBook5Converter < Converter::Base end # FIXME this should be handled through a template mechanism - def _enclose_content node + def enclose_content node node.content_model == :compound ? node.content : %(<simpara>#{node.content}</simpara>) end - def _title_tag node, optional = true + def title_tag node, optional = true !optional || node.title? ? %(<title>#{node.title}</title>\n) : '' end - def _cover_tag doc, face, use_placeholder = false + def cover_tag doc, face, use_placeholder = false if (cover_image = doc.attr %(#{face}-cover-image)) width_attr = '' depth_attr = '' @@ -749,13 +749,13 @@ class Converter::DocBook5Converter < Converter::Base end end - def _blockquote_tag node, tag_name = nil + def blockquote_tag node, tag_name = nil if tag_name start_tag, end_tag = %(<#{tag_name}), %(</#{tag_name}>) else start_tag, end_tag = '<blockquote', '</blockquote>' end - result = [%(#{start_tag}#{_common_attributes node.id, node.role, node.reftext}>)] + result = [%(#{start_tag}#{common_attributes node.id, node.role, node.reftext}>)] result << %(<title>#{node.title}</title>) if node.title? if (node.attr? 'attribution') || (node.attr? 'citetitle') result << '<attribution>' @@ -768,11 +768,11 @@ class Converter::DocBook5Converter < Converter::Base result.join LF end - def _asciimath_available? - (@asciimath_status ||= _load_asciimath) == :loaded + def asciimath_available? + (@asciimath_status ||= load_asciimath) == :loaded end - def _load_asciimath + def load_asciimath (defined? ::AsciiMath.parse) ? :loaded : (Helpers.require_library 'asciimath', true, :warn).nil? ? :unavailable : :loaded end end diff --git a/lib/asciidoctor/converter/html5.rb b/lib/asciidoctor/converter/html5.rb index 5c075e09..1c22acb8 100644 --- a/lib/asciidoctor/converter/html5.rb +++ b/lib/asciidoctor/converter/html5.rb @@ -41,7 +41,49 @@ class Converter::Html5Converter < Converter::Base init_backend_traits basebackend: 'html', filetype: 'html', htmlsyntax: syntax, outfilesuffix: '.html', supports_templates: true end - def document node + def convert node, transform = node.node_name, opts = nil + if transform == 'inline_quoted'; return convert_inline_quoted node + elsif transform == 'paragraph'; return convert_paragraph node + elsif transform == 'inline_anchor'; return convert_inline_anchor node + elsif transform == 'section'; return convert_section node + elsif transform == 'listing'; return convert_listing node + elsif transform == 'literal'; return convert_literal node + elsif transform == 'ulist'; return convert_ulist node + elsif transform == 'olist'; return convert_olist node + elsif transform == 'dlist'; return convert_dlist node + elsif transform == 'admonition'; return convert_admonition node + elsif transform == 'colist'; return convert_colist node + elsif transform == 'embedded'; return convert_embedded node + elsif transform == 'example'; return convert_example node + elsif transform == 'floating_title'; return convert_floating_title node + elsif transform == 'image'; return convert_image node + elsif transform == 'inline_break'; return convert_inline_break node + elsif transform == 'inline_button'; return convert_inline_button node + elsif transform == 'inline_callout'; return convert_inline_callout node + elsif transform == 'inline_footnote'; return convert_inline_footnote node + elsif transform == 'inline_image'; return convert_inline_image node + elsif transform == 'inline_indexterm'; return convert_inline_indexterm node + elsif transform == 'inline_kbd'; return convert_inline_kbd node + elsif transform == 'inline_menu'; return convert_inline_menu node + elsif transform == 'open'; return convert_open node + elsif transform == 'page_break'; return convert_page_break node + elsif transform == 'preamble'; return convert_preamble node + elsif transform == 'quote'; return convert_quote node + elsif transform == 'sidebar'; return convert_sidebar node + elsif transform == 'stem'; return convert_stem node + elsif transform == 'table'; return convert_table node + elsif transform == 'thematic_break'; return convert_thematic_break node + elsif transform == 'verse'; return convert_verse node + elsif transform == 'video'; return convert_video node + elsif transform == 'document'; return convert_document node + elsif transform == 'toc'; return convert_toc node + elsif transform == 'pass'; return convert_pass node + elsif transform == 'audio'; return convert_audio node + else; return super + end + end + + def convert_document node br = %(<br#{slash = @void_element_slash}>) unless (asset_uri_scheme = (node.attr 'asset-uri-scheme', 'https')).empty? asset_uri_scheme = %(#{asset_uri_scheme}:) @@ -132,10 +174,10 @@ class Converter::Html5Converter < Converter::Base if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto') result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}"> <div id="toctitle">#{node.attr 'toc-title'}</div> -#{outline node} +#{convert_outline node} </div>) end - result << (_generate_manname_section node) if node.attr? 'manpurpose' + result << (generate_manname_section node) if node.attr? 'manpurpose' else if node.header? result << %(<h1>#{node.header.title}</h1>) unless node.notitle @@ -165,7 +207,7 @@ class Converter::Html5Converter < Converter::Base if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto') result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}"> <div id="toctitle">#{node.attr 'toc-title'}</div> -#{outline node} +#{convert_outline node} </div>) end end @@ -235,7 +277,7 @@ TeX: {#{eqnums_opt}} result.join LF end - def embedded node + def convert_embedded node result = [] if node.doctype == 'manpage' # QUESTION should notitle control the manual page title? @@ -243,7 +285,7 @@ TeX: {#{eqnums_opt}} id_attr = node.id ? %( id="#{node.id}") : '' result << %(<h1#{id_attr}>#{node.doctitle} Manual Page</h1>) end - result << (_generate_manname_section node) if node.attr? 'manpurpose' + result << (generate_manname_section node) if node.attr? 'manpurpose' elsif node.header? && !node.notitle id_attr = node.id ? %( id="#{node.id}") : '' result << %(<h1#{id_attr}>#{node.header.title}</h1>) @@ -252,7 +294,7 @@ TeX: {#{eqnums_opt}} if node.sections? && (node.attr? 'toc') && (toc_p = node.attr 'toc-placement') != 'macro' && toc_p != 'preamble' result << %(<div id="toc" class="toc"> <div id="toctitle">#{node.attr 'toc-title'}</div> -#{outline node} +#{convert_outline node} </div>) end @@ -272,7 +314,7 @@ TeX: {#{eqnums_opt}} result.join LF end - def outline node, opts = {} + def convert_outline node, opts = {} return unless node.sections? sectnumlevels = opts[:sectnumlevels] || (node.document.attributes['sectnumlevels'] || 3).to_i toclevels = opts[:toclevels] || (node.document.attributes['toclevels'] || 2).to_i @@ -299,7 +341,7 @@ TeX: {#{eqnums_opt}} stitle = section.title end stitle = stitle.gsub DropAnchorRx, '' if stitle.include? '<a' - if slevel < toclevels && (child_toc_level = outline section, toclevels: toclevels, sectnumlevels: sectnumlevels) + if slevel < toclevels && (child_toc_level = convert_outline section, toclevels: toclevels, sectnumlevels: sectnumlevels) result << %(<li><a href="##{section.id}">#{stitle}</a>) result << child_toc_level result << '</li>' @@ -311,7 +353,7 @@ TeX: {#{eqnums_opt}} result.join LF end - def section node + def convert_section node doc_attrs = node.document.attributes level = node.level if node.caption @@ -360,7 +402,7 @@ TeX: {#{eqnums_opt}} end end - def admonition node + def convert_admonition node id_attr = node.id ? %( id="#{node.id}") : '' name = node.attr 'name' title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : '' @@ -387,7 +429,7 @@ TeX: {#{eqnums_opt}} </div>) end - def audio node + def convert_audio node xml = @xml_mode id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['audioblock', node.role].compact @@ -398,14 +440,14 @@ TeX: {#{eqnums_opt}} time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : '' %(<div#{id_attribute}#{class_attribute}> #{title_element}<div class="content"> -<audio src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{(node.option? 'autoplay') ? (_append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'nocontrols') ? '' : (_append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (_append_boolean_attribute 'loop', xml) : ''}> +<audio src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}> Your browser does not support the audio tag. </audio> </div> </div>) end - def colist node + def convert_colist node result = [] id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['colist', node.style, node.role].compact @@ -444,7 +486,7 @@ Your browser does not support the audio tag. result.join LF end - def dlist node + def convert_dlist node result = [] id_attribute = node.id ? %( id="#{node.id}") : '' @@ -527,7 +569,7 @@ Your browser does not support the audio tag. result.join LF end - def example node + def convert_example node id_attribute = node.id ? %( id="#{node.id}") : '' title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) : '' @@ -538,29 +580,29 @@ Your browser does not support the audio tag. </div>) end - def floating_title node + def convert_floating_title node tag_name = %(h#{node.level + 1}) id_attribute = node.id ? %( id="#{node.id}") : '' classes = [node.style, node.role].compact %(<#{tag_name}#{id_attribute} class="#{classes.join ' '}">#{node.title}</#{tag_name}>) end - def image node + def convert_image node target = node.attr 'target' width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : '' height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : '' if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive'))) if svg - img = (_read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>) + img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>) elsif obj - fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{_encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>) + fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>) img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{width_attr}#{height_attr}>#{fallback}</object>) end end - img ||= %(<img src="#{node.image_uri target}" alt="#{_encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) + img ||= %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) if node.attr? 'link' - img = %(<a class="image" href="#{node.attr 'link'}"#{(_append_link_constraint_attrs node).join}>#{img}</a>) + img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) end id_attr = node.id ? %( id="#{node.id}") : '' classes = ['imageblock'] @@ -576,7 +618,7 @@ Your browser does not support the audio tag. </div>) end - def listing node + def convert_listing node nowrap = (node.option? 'nowrap') || !(node.document.attr? 'prewrap') if node.style == 'source' lang = node.attr 'language' @@ -603,7 +645,7 @@ Your browser does not support the audio tag. </div>) end - def literal node + def convert_literal node id_attribute = node.id ? %( id="#{node.id}") : '' title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : '' nowrap = !(node.document.attr? 'prewrap') || (node.option? 'nowrap') @@ -614,9 +656,7 @@ Your browser does not support the audio tag. </div>) end - alias pass _content_only - - def stem node + def convert_stem node id_attribute = node.id ? %( id="#{node.id}") : '' title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : '' open, close = BLOCK_MATH_DELIMITERS[style = node.style.to_sym] @@ -638,7 +678,7 @@ Your browser does not support the audio tag. </div>) end - def olist node + def convert_olist node result = [] id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['olist', node.style, node.role].compact @@ -649,7 +689,7 @@ Your browser does not support the audio tag. type_attribute = (keyword = node.list_marker_keyword) ? %( type="#{keyword}") : '' start_attribute = (node.attr? 'start') ? %( start="#{node.attr 'start'}") : '' - reversed_attribute = (node.option? 'reversed') ? (_append_boolean_attribute 'reversed', @xml_mode) : '' + reversed_attribute = (node.option? 'reversed') ? (append_boolean_attribute 'reversed', @xml_mode) : '' result << %(<ol class="#{node.style}"#{type_attribute}#{start_attribute}#{reversed_attribute}>) node.items.each do |item| @@ -670,7 +710,7 @@ Your browser does not support the audio tag. result.join LF end - def open node + def convert_open node if (style = node.style) == 'abstract' if node.parent == node.document && node.document.doctype == 'book' logger.warn 'abstract block cannot be used in a document without a title when doctype is book. Excluding block content.' @@ -698,11 +738,11 @@ Your browser does not support the audio tag. end end - def page_break node + def convert_page_break node '<div style="page-break-after: always;"></div>' end - def paragraph node + def convert_paragraph node if node.role attributes = %(#{node.id ? %[ id="#{node.id}"] : ''} class="paragraph #{node.role}") elsif node.id @@ -722,12 +762,14 @@ Your browser does not support the audio tag. end end - def preamble node + alias convert_pass content_only + + def convert_preamble node if (doc = node.document).attr?('toc-placement', 'preamble') && doc.sections? && (doc.attr? 'toc') toc = %( <div id="toc" class="#{doc.attr 'toc-class', 'toc'}"> <div id="toctitle">#{doc.attr 'toc-title'}</div> -#{outline doc} +#{convert_outline doc} </div>) else toc = '' @@ -740,7 +782,7 @@ Your browser does not support the audio tag. </div>) end - def quote node + def convert_quote node id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['quoteblock', node.role].compact class_attribute = %( class="#{classes.join ' '}") @@ -762,11 +804,11 @@ Your browser does not support the audio tag. </div>) end - def thematic_break node + def convert_thematic_break node %(<hr#{@void_element_slash}>) end - def sidebar node + def convert_sidebar node id_attribute = node.id ? %( id="#{node.id}") : '' title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : '' %(<div#{id_attribute} class="sidebarblock#{(role = node.role) ? " #{role}" : ''}"> @@ -776,7 +818,7 @@ Your browser does not support the audio tag. </div>) end - def table node + def convert_table node result = [] id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['tableblock', %(frame-#{node.attr 'frame', 'all', 'table-frame'}), %(grid-#{node.attr 'grid', 'all', 'table-grid'})] @@ -847,7 +889,7 @@ Your browser does not support the audio tag. result.join LF end - def toc node + def convert_toc node unless (doc = node.document).attr?('toc-placement', 'macro') && doc.sections? && (doc.attr? 'toc') return '<!-- toc disabled -->' end @@ -865,11 +907,11 @@ Your browser does not support the audio tag. %(<div#{id_attr} class="#{role}"> <div#{title_id_attr} class="title">#{title}</div> -#{outline doc, toclevels: levels} +#{convert_outline doc, toclevels: levels} </div>) end - def ulist node + def convert_ulist node result = [] id_attribute = node.id ? %( id="#{node.id}") : '' div_classes = ['ulist', node.style, node.role].compact @@ -921,7 +963,7 @@ Your browser does not support the audio tag. result.join LF end - def verse node + def convert_verse node id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['verseblock', node.role].compact class_attribute = %( class="#{classes.join ' '}") @@ -941,7 +983,7 @@ Your browser does not support the audio tag. </div>) end - def video node + def convert_video node xml = @xml_mode id_attribute = node.id ? %( id="#{node.id}") : '' classes = ['videoblock'] @@ -964,7 +1006,7 @@ Your browser does not support the audio tag. muted_param = (node.option? 'muted') ? %(#{delimiter.pop || '&'}muted=1) : '' %(<div#{id_attribute}#{class_attribute}>#{title_element} <div class="content"> -<iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{node.attr 'target'}#{autoplay_param}#{loop_param}#{muted_param}#{start_anchor}" frameborder="0"#{(node.option? 'nofullscreen') ? '' : (_append_boolean_attribute 'allowfullscreen', xml)}></iframe> +<iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{node.attr 'target'}#{autoplay_param}#{loop_param}#{muted_param}#{start_anchor}" frameborder="0"#{(node.option? 'nofullscreen') ? '' : (append_boolean_attribute 'allowfullscreen', xml)}></iframe> </div> </div>) when 'youtube' @@ -985,7 +1027,7 @@ Your browser does not support the audio tag. fs_attribute = '' else fs_param = '' - fs_attribute = _append_boolean_attribute 'allowfullscreen', xml + fs_attribute = append_boolean_attribute 'allowfullscreen', xml end modest_param = (node.option? 'modest') ? '&modestbranding=1' : '' theme_param = (node.attr? 'theme') ? %(&theme=#{node.attr 'theme'}) : '' @@ -1020,7 +1062,7 @@ Your browser does not support the audio tag. time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : '' %(<div#{id_attribute}#{class_attribute}>#{title_element} <div class="content"> -<video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (_append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'nocontrols') ? '' : (_append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (_append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}> +<video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}> Your browser does not support the video tag. </video> </div> @@ -1028,11 +1070,11 @@ Your browser does not support the video tag. end end - def inline_anchor node + def convert_inline_anchor node case node.type when :xref if (path = node.attributes['path']) - attrs = (_append_link_constraint_attrs node, node.role ? [%( class="#{node.role}")] : []).join + attrs = (append_link_constraint_attrs node, node.role ? [%( class="#{node.role}")] : []).join text = node.text || path else attrs = node.role ? %( class="#{node.role}") : '' @@ -1052,7 +1094,7 @@ Your browser does not support the video tag. attrs = node.id ? [%( id="#{node.id}")] : [] attrs << %( class="#{node.role}") if node.role attrs << %( title="#{node.attr 'title'}") if node.attr? 'title' - %(<a href="#{node.target}"#{(_append_link_constraint_attrs node, attrs).join}>#{node.text}</a>) + %(<a href="#{node.target}"#{(append_link_constraint_attrs node, attrs).join}>#{node.text}</a>) when :bibref %(<a id="#{node.id}"></a>[#{node.reftext || node.id}]) else @@ -1061,15 +1103,15 @@ Your browser does not support the video tag. end end - def inline_break node + def convert_inline_break node %(#{node.text}<br#{@void_element_slash}>) end - def inline_button node + def convert_inline_button node %(<b class="button">#{node.text}</b>) end - def inline_callout node + def convert_inline_callout node if node.document.attr? 'icons', 'font' %(<i class="conum" data-value="#{node.text}"></i><b>(#{node.text})</b>) elsif node.document.attr? 'icons' @@ -1080,7 +1122,7 @@ Your browser does not support the video tag. end end - def inline_footnote node + def convert_inline_footnote node if (index = node.attr 'index') if node.type == :xref %(<sup class="footnoteref">[<a class="footnote" href="#_footnotedef_#{index}" title="View footnote.">#{index}</a>]</sup>) @@ -1093,7 +1135,7 @@ Your browser does not support the video tag. end end - def inline_image node + def convert_inline_image node if (type = node.type) == 'icon' && (node.document.attr? 'icons', 'font') class_attr_val = %(fa fa-#{node.target}) { 'size' => 'fa-', 'rotate' => 'fa-rotate-', 'flip' => 'fa-flip-' }.each do |key, prefix| @@ -1109,16 +1151,16 @@ Your browser does not support the video tag. if type != 'icon' && ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive'))) if svg - img = (_read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>) + img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>) elsif obj - fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{_encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>) + fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>) img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{attrs}>#{fallback}</object>) end end - img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{_encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) + img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) end if node.attr? 'link' - img = %(<a class="image" href="#{node.attr 'link'}"#{(_append_link_constraint_attrs node).join}>#{img}</a>) + img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) end if (role = node.role) if node.attr? 'float' @@ -1134,11 +1176,11 @@ Your browser does not support the video tag. %(<span class="#{class_attr_val}">#{img}</span>) end - def inline_indexterm node + def convert_inline_indexterm node node.type == :visible ? node.text : '' end - def inline_kbd node + def convert_inline_kbd node if (keys = node.attr 'keys').size == 1 %(<kbd>#{keys[0]}</kbd>) else @@ -1146,7 +1188,7 @@ Your browser does not support the video tag. end end - def inline_menu node + def convert_inline_menu node caret = (node.document.attr? 'icons', 'font') ? ' <i class="fa fa-angle-right caret"></i> ' : ' <b class="caret">›</b> ' submenu_joiner = %(</b>#{caret}<b class="submenu">) menu = node.attr 'menu' @@ -1161,7 +1203,7 @@ Your browser does not support the video tag. end end - def inline_quoted node + def convert_inline_quoted node open, close, tag = QUOTE_TAGS[node.type] if node.id class_attr = node.role ? %( class="#{node.role}") : '' @@ -1181,18 +1223,31 @@ Your browser does not support the video tag. end end - # NOTE export _read_svg_contents as read_svg_contents for Bespoke converter + # NOTE expose read_svg_contents for Bespoke converter def read_svg_contents node, target - _read_svg_contents node, target + if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG') + svg = svg.sub SvgPreambleRx, '' unless svg.start_with? '<svg' + old_start_tag = new_start_tag = nil + # NOTE width, height and style attributes are removed if either width or height is specified + ['width', 'height'].each do |dim| + if node.attr? dim + new_start_tag = (old_start_tag = (svg.match SvgStartTagRx)[0]).gsub DimensionAttributeRx, '' unless new_start_tag + # QUESTION should we add px since it's already the default? + new_start_tag = %(#{new_start_tag.chop} #{dim}="#{node.attr dim}px">) + end + end + svg = %(#{new_start_tag}#{svg[old_start_tag.length..-1]}) if new_start_tag + end + svg end private - def _append_boolean_attribute name, xml + def append_boolean_attribute name, xml xml ? %( #{name}="#{name}") : %( #{name}) end - def _append_link_constraint_attrs node, attrs = [] + def append_link_constraint_attrs node, attrs = [] rel = 'nofollow' if node.option? 'nofollow' if (window = node.attributes['window']) attrs << %( target="#{window}") @@ -1203,11 +1258,11 @@ Your browser does not support the video tag. attrs end - def _encode_attribute_value val + def encode_attribute_value val (val.include? '"') ? (val.gsub '"', '"') : val end - def _generate_manname_section node + def generate_manname_section node manname_title = node.attr 'manname-title', 'Name' if (next_section = node.sections[0]) && (next_section_title = next_section.title) == next_section_title.upcase manname_title = manname_title.upcase @@ -1219,21 +1274,9 @@ Your browser does not support the video tag. </div>) end - def _read_svg_contents node, target - if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG') - svg = svg.sub SvgPreambleRx, '' unless svg.start_with? '<svg' - old_start_tag = new_start_tag = nil - # NOTE width, height and style attributes are removed if either width or height is specified - ['width', 'height'].each do |dim| - if node.attr? dim - new_start_tag = (old_start_tag = (svg.match SvgStartTagRx)[0]).gsub DimensionAttributeRx, '' unless new_start_tag - # QUESTION should we add px since it's already the default? - new_start_tag = %(#{new_start_tag.chop} #{dim}="#{node.attr dim}px">) - end - end - svg = %(#{new_start_tag}#{svg[old_start_tag.length..-1]}) if new_start_tag - end - svg + # NOTE adapt to older converters that relied on unprefixed method names + def method_missing id, *params + !((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *params) : super end end end diff --git a/lib/asciidoctor/converter/manpage.rb b/lib/asciidoctor/converter/manpage.rb index 975f78c4..4573f6ab 100644 --- a/lib/asciidoctor/converter/manpage.rb +++ b/lib/asciidoctor/converter/manpage.rb @@ -28,7 +28,7 @@ class Converter::ManPageConverter < Converter::Base init_backend_traits basebackend: 'manpage', filetype: 'man', outfilesuffix: '.man', supports_templates: true end - def document node + def convert_document node unless node.attr? 'mantitle' raise 'asciidoctor: ERROR: doctype must be set to manpage when using manpage backend' end @@ -49,7 +49,7 @@ class Converter::ManPageConverter < Converter::Base .\\" Language: English .\\") # TODO add document-level setting to disable capitalization of manname - result << %(.TH "#{_manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{mansource ? (_manify mansource) : '\ \&'}" "#{manmanual ? (_manify manmanual) : '\ \&'}") + result << %(.TH "#{manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{mansource ? (manify mansource) : '\ \&'}" "#{manmanual ? (manify manmanual) : '\ \&'}") # define portability settings # see http://bugs.debian.org/507673 # see http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html @@ -91,7 +91,7 @@ class Converter::ManPageConverter < Converter::Base if node.attr? 'manpurpose' mannames = node.attr 'mannames', [manname] result << %(.SH "#{(node.attr 'manname-title', 'NAME').upcase}" -#{mannames.map {|n| _manify n }.join ', '} \\- #{_manify node.attr('manpurpose'), whitespace: :normalize}) +#{mannames.map {|n| manify n }.join ', '} \\- #{manify node.attr('manpurpose'), whitespace: :normalize}) end end @@ -121,7 +121,7 @@ class Converter::ManPageConverter < Converter::Base end # NOTE embedded doesn't really make sense in the manpage backend - def embedded node + def convert_embedded node result = [node.content] if node.footnotes? && !(node.attr? 'nofootnotes') @@ -134,7 +134,7 @@ class Converter::ManPageConverter < Converter::Base result.join LF end - def section node + def convert_section node result = [] if node.level > 1 macro = 'SS' @@ -144,12 +144,12 @@ class Converter::ManPageConverter < Converter::Base macro = 'SH' stitle = node.title.upcase end - result << %(.#{macro} "#{_manify stitle}" + result << %(.#{macro} "#{manify stitle}" #{node.content}) result.join LF end - def admonition node + def convert_admonition node result = [] result << %(.if n .sp .RS 4 @@ -158,19 +158,19 @@ class Converter::ManPageConverter < Converter::Base .nr an-break-flag 1 .br .ps +1 -.B #{node.attr 'textlabel'}#{node.title? ? "\\fP: #{_manify node.title}" : ''} +.B #{node.attr 'textlabel'}#{node.title? ? "\\fP: #{manify node.title}" : ''} .ps -1 .br -#{_enclose_content node} +#{enclose_content node} .sp .5v .RE) result.join LF end - def colist node + def convert_colist node result = [] result << %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) if node.title? result << '.TS tab(:); @@ -179,7 +179,7 @@ r lw(\n(.lu*75u/100u).' num = 0 node.items.each do |item| result << %(\\fB(#{num += 1})\\fP\\h'-2n':T{) - result << (_manify item.text, whitespace: :normalize) + result << (manify item.text, whitespace: :normalize) result << item.content if item.blocks? result << 'T}' end @@ -188,10 +188,10 @@ r lw(\n(.lu*75u/100u).' end # TODO implement horizontal (if it makes sense) - def dlist node + def convert_dlist node result = [] result << %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) if node.title? counter = 0 node.items.each do |terms, dd| @@ -199,15 +199,15 @@ r lw(\n(.lu*75u/100u).' case node.style when 'qanda' result << %(.sp -#{counter}. #{_manify terms.map {|dt| dt.text }.join ' '} +#{counter}. #{manify terms.map {|dt| dt.text }.join ' '} .RS 4) else result << %(.sp -#{_manify terms.map {|dt| dt.text }.join(', '), whitespace: :normalize} +#{manify terms.map {|dt| dt.text }.join(', '), whitespace: :normalize} .RS 4) end if dd - result << (_manify dd.text, whitespace: :normalize) if dd.text? + result << (manify dd.text, whitespace: :normalize) if dd.text? result << dd.content if dd.blocks? end result << '.RE' @@ -215,73 +215,73 @@ r lw(\n(.lu*75u/100u).' result.join LF end - def example node + def convert_example node result = [] result << (node.title? ? %(.sp -.B #{_manify node.captioned_title} +.B #{manify node.captioned_title} .br) : '.sp') result << %(.RS 4 -#{_enclose_content node} +#{enclose_content node} .RE) result.join LF end - def floating_title node - %(.SS "#{_manify node.title}") + def convert_floating_title node + %(.SS "#{manify node.title}") end - def image node + def convert_image node result = [] result << (node.title? ? %(.sp -.B #{_manify node.captioned_title} +.B #{manify node.captioned_title} .br) : '.sp') result << %([#{node.alt}]) result.join LF end - def listing node + def convert_listing node result = [] result << %(.sp -.B #{_manify node.captioned_title} +.B #{manify node.captioned_title} .br) if node.title? result << %(.sp .if n .RS 4 .nf -#{_manify node.content, whitespace: :preserve} +#{manify node.content, whitespace: :preserve} .fi .if n .RE) result.join LF end - def literal node + def convert_literal node result = [] result << %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) if node.title? result << %(.sp .if n .RS 4 .nf -#{_manify node.content, whitespace: :preserve} +#{manify node.content, whitespace: :preserve} .fi .if n .RE) result.join LF end - def sidebar node + def convert_sidebar node result = [] result << (node.title? ? %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) : '.sp') result << %(.RS 4 -#{_enclose_content node} +#{enclose_content node} .RE) result.join LF end - def olist node + def convert_olist node result = [] result << %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) if node.title? node.items.each_with_index do |item, idx| @@ -294,46 +294,46 @@ r lw(\n(.lu*75u/100u).' . sp -1 . IP " #{idx + 1}." 4.2 .\\} -#{_manify item.text, whitespace: :normalize}) +#{manify item.text, whitespace: :normalize}) result << item.content if item.blocks? result << '.RE' end result.join LF end - def open node + def convert_open node case node.style when 'abstract', 'partintro' - _enclose_content node + enclose_content node else node.content end end # TODO use Page Control https://www.gnu.org/software/groff/manual/html_node/Page-Control.html#Page-Control - alias page_break _skip + alias convert_page_break skip - def paragraph node + def convert_paragraph node if node.title? %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br -#{_manify node.content, whitespace: :normalize}) +#{manify node.content, whitespace: :normalize}) else %(.sp -#{_manify node.content, whitespace: :normalize}) +#{manify node.content, whitespace: :normalize}) end end - alias pass _content_only - alias preamble _content_only + alias convert_pass content_only + alias convert_preamble content_only - def quote node + def convert_quote node result = [] if node.title? result << %(.sp .RS 3 -.B #{_manify node.title} +.B #{manify node.title} .br .RE) end @@ -341,7 +341,7 @@ r lw(\n(.lu*75u/100u).' attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil result << %(.RS 3 .ll -.6i -#{_enclose_content node} +#{enclose_content node} .br .RE .ll) @@ -355,16 +355,16 @@ r lw(\n(.lu*75u/100u).' result.join LF end - def stem node + def convert_stem node result = [] result << (node.title? ? %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) : '.sp') open, close = BLOCK_MATH_DELIMITERS[node.style.to_sym] if ((equation = node.content).start_with? open) && (equation.end_with? close) equation = equation.slice open.length, equation.length - open.length - close.length end - result << %(#{_manify equation, whitespace: :preserve} (#{node.style})) + result << %(#{manify equation, whitespace: :preserve} (#{node.style})) result.join LF end @@ -374,7 +374,7 @@ r lw(\n(.lu*75u/100u).' # create empty cells as placeholders of the span. # To fix this, asciidoctor needs to provide an API to tell the user if a # given cell is being used as a colspan or rowspan. - def table node + def convert_table node result = [] if node.title? result << %(.sp @@ -382,7 +382,7 @@ r lw(\n(.lu*75u/100u).' .nr an-no-space-flag 1 .nr an-break-flag 1 .br -.B #{_manify node.captioned_title} +.B #{manify node.captioned_title} ) end result << '.TS @@ -419,7 +419,7 @@ allbox tab(:);' row_header[row_index][cell_index + 1] ||= [] row_header[row_index][cell_index + 1] << %(#{cell_halign}tB) end - row_text[row_index] << %(#{_manify cell.text, whitespace: :normalize}#{LF}) + row_text[row_index] << %(#{manify cell.text, whitespace: :normalize}#{LF}) elsif tsec == :body if row_header[row_index].empty? || row_header[row_index][cell_index].empty? row_header[row_index][cell_index] << %(#{cell_halign}t) @@ -431,9 +431,9 @@ allbox tab(:);' when :asciidoc cell_content = cell.content when :literal - cell_content = %(.nf#{LF}#{_manify cell.text, whitespace: :preserve}#{LF}.fi) + cell_content = %(.nf#{LF}#{manify cell.text, whitespace: :preserve}#{LF}.fi) else - cell_content = _manify cell.content.join, whitespace: :normalize + cell_content = manify cell.content.join, whitespace: :normalize end row_text[row_index] << %(#{cell_content}#{LF}) elsif tsec == :foot @@ -443,7 +443,7 @@ allbox tab(:);' row_header[row_index][cell_index + 1] ||= [] row_header[row_index][cell_index + 1] << %(#{cell_halign}tB) end - row_text[row_index] << %(#{_manify cell.text, whitespace: :normalize}#{LF}) + row_text[row_index] << %(#{manify cell.text, whitespace: :normalize}#{LF}) end if cell.colspan && cell.colspan > 1 (cell.colspan - 1).times do |i| @@ -496,18 +496,18 @@ allbox tab(:);' result.join end - def thematic_break node + def convert_thematic_break node '.sp .ce \l\'\n(.lu*25u/100u\(ap\'' end - alias toc _skip + alias convert_toc skip - def ulist node + def convert_ulist node result = [] result << %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) if node.title? node.items.map do |item| result << %[.sp @@ -519,7 +519,7 @@ allbox tab(:);' . sp -1 . IP \\(bu 2.3 .\\} -#{_manify item.text, whitespace: :normalize}] +#{manify item.text, whitespace: :normalize}] result << item.content if item.blocks? result << '.RE' end @@ -527,16 +527,16 @@ allbox tab(:);' end # FIXME git uses [verse] for the synopsis; detect this special case - def verse node + def convert_verse node result = [] result << (node.title? ? %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) : '.sp') attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil result << %(.sp .nf -#{_manify node.content, whitespace: :preserve} +#{manify node.content, whitespace: :preserve} .fi .br) if attribution_line @@ -549,18 +549,18 @@ allbox tab(:);' result.join LF end - def video node + def convert_video node start_param = (node.attr? 'start') ? %(&start=#{node.attr 'start'}) : '' end_param = (node.attr? 'end') ? %(&end=#{node.attr 'end'}) : '' result = [] result << (node.title? ? %(.sp -.B #{_manify node.title} +.B #{manify node.title} .br) : '.sp') result << %(<#{node.media_uri(node.attr 'target')}#{start_param}#{end_param}> (video)) result.join LF end - def inline_anchor node + def convert_inline_anchor node target = node.target case node.type when :link @@ -596,20 +596,20 @@ allbox tab(:);' end end - def inline_break node + def convert_inline_break node %(#{node.text}#{LF}#{ESC_FS}br) end - def inline_button node + def convert_inline_button node %(#{ESC_BS}fB[#{ESC_BS}0#{node.text}#{ESC_BS}0]#{ESC_BS}fP) end - def inline_callout node + def convert_inline_callout node %(#{ESC_BS}fB(#{node.text})#{ESC_BS}fP) end # TODO supposedly groff has footnotes, but we're in search of an example - def inline_footnote node + def convert_inline_footnote node if (index = node.attr 'index') %([#{index}]) elsif node.type == :xref @@ -617,15 +617,15 @@ allbox tab(:);' end end - def inline_image node + def convert_inline_image node (node.attr? 'link') ? %([#{node.alt}] <#{node.attr 'link'}>) : %([#{node.alt}]) end - def inline_indexterm node + def convert_inline_indexterm node node.type == :visible ? node.text : '' end - def inline_kbd node + def convert_inline_kbd node if (keys = node.attr 'keys').size == 1 keys[0] else @@ -633,7 +633,7 @@ allbox tab(:);' end end - def inline_menu node + def convert_inline_menu node caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0] menu = node.attr 'menu' if !(submenus = node.attr 'submenus').empty? @@ -647,7 +647,7 @@ allbox tab(:);' end # NOTE use fake <BOUNDARY> element to prevent creating artificial word boundaries - def inline_quoted node + def convert_inline_quoted node case node.type when :emphasis %(#{ESC_BS}fI<BOUNDARY>#{node.text}</BOUNDARY>#{ESC_BS}fP) @@ -680,7 +680,7 @@ allbox tab(:);' # Converts HTML entity references back to their original form, escapes # special man characters and strips trailing whitespace. # - # It's crucial that text only ever pass through _manify once. + # It's crucial that text only ever pass through manify once. # # str - the String to convert # opts - an Hash of options to control processing (default: {}) @@ -689,7 +689,7 @@ allbox tab(:);' # (remove spaces around newlines); :collapse - collapse adjacent whitespace to a single # space (default: :collapse) # * :append_newline a Boolean that indicates whether to append a newline to the result (default: false) - def _manify str, opts = {} + def manify str, opts = {} case opts.fetch :whitespace, :collapse when :preserve str = str.gsub TAB, ET @@ -732,8 +732,8 @@ allbox tab(:);' opts[:append_newline] ? %(#{str}#{LF}) : str end - def _enclose_content node - node.content_model == :compound ? node.content : %(.sp#{LF}#{_manify node.content, whitespace: :normalize}) + def enclose_content node + node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, whitespace: :normalize}) end end end diff --git a/test/converter_test.rb b/test/converter_test.rb index 4f2ef2a8..e30bb651 100644 --- a/test/converter_test.rb +++ b/test/converter_test.rb @@ -381,7 +381,7 @@ context 'Converter' do EOS my_converter_class = Class.new Asciidoctor::Converter::Base do - def document node + def convert_document node 'document' end end @@ -435,17 +435,17 @@ context 'Converter' do end end - test 'should map handles? method on converter to respond_to? by default' do + test 'should map handles? method on converter to respond_to? implementation by default' do class CustomConverterC include Asciidoctor::Converter - def paragraph node + def convert_paragraph node 'paragraph' end end converter = CustomConverterC.new 'myhtml' assert_respond_to converter, :handles? - assert converter.handles?(:paragraph) + assert converter.handles?(:convert_paragraph) end test 'should not configure converter to support templates by default' do @@ -494,6 +494,8 @@ context 'Converter' do send transform, node end + alias handles? respond_to? + def document node ['<!DOCTYPE html>', '<html>', '<body>', node.content, '</body>', '</html>'] * %(\n) end @@ -591,6 +593,12 @@ context 'Converter' do assert_equal MyConverter, (factory.for 'mine') end + test 'should delegate to method on HTML 5 converter with convert_ prefix if called without prefix' do + doc = document_from_string 'paragraph' + result = doc.converter.paragraph doc.blocks[0] + assert_css 'p', result, 1 + end + test 'can call read_svg_contents on built-in HTML5 converter' do doc = document_from_string 'image::circle.svg[]', base_dir: fixturedir result = doc.converter.read_svg_contents doc.blocks[0], 'circle.svg' diff --git a/test/document_test.rb b/test/document_test.rb index 05509903..0270ef67 100644 --- a/test/document_test.rb +++ b/test/document_test.rb @@ -405,7 +405,7 @@ context 'Document' do converter = doc.converter assert_kind_of Asciidoctor::Converter::Html5Converter, converter BUILT_IN_ELEMENTS.each do |element| - assert_respond_to converter, element + assert_respond_to converter, %(convert_#{element}) end end @@ -419,7 +419,7 @@ context 'Document' do converter = doc.converter assert_kind_of Asciidoctor::Converter::DocBook5Converter, converter BUILT_IN_ELEMENTS.each do |element| - assert_respond_to converter, element + assert_respond_to converter, %(convert_#{element}) end end |
