asciidoctor 1.5.6.2 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +330 -143
- data/README-fr.adoc +441 -0
- data/README-jp.adoc +418 -0
- data/README-zh_CN.adoc +430 -0
- data/README.adoc +454 -0
- data/Rakefile +57 -0
- data/asciidoctor.gemspec +7 -1
- data/data/locale/attributes-ar.adoc +22 -0
- data/data/locale/attributes-bg.adoc +22 -0
- data/data/locale/attributes-ca.adoc +22 -0
- data/data/locale/attributes-cs.adoc +22 -0
- data/data/locale/attributes-da.adoc +22 -0
- data/data/locale/attributes-de.adoc +22 -0
- data/data/locale/attributes-en.adoc +23 -0
- data/data/locale/attributes-es.adoc +22 -0
- data/data/locale/attributes-fa.adoc +22 -0
- data/data/locale/attributes-fi.adoc +22 -0
- data/data/locale/attributes-fr.adoc +22 -0
- data/data/locale/attributes-hu.adoc +22 -0
- data/data/locale/attributes-id.adoc +22 -0
- data/data/locale/attributes-it.adoc +22 -0
- data/data/locale/attributes-ja.adoc +22 -0
- data/data/locale/attributes-kr.adoc +22 -0
- data/data/locale/attributes-nb.adoc +22 -0
- data/data/locale/attributes-nl.adoc +22 -0
- data/data/locale/attributes-nn.adoc +22 -0
- data/data/locale/attributes-pl.adoc +22 -0
- data/data/locale/attributes-pt.adoc +22 -0
- data/data/locale/attributes-pt_BR.adoc +22 -0
- data/data/locale/attributes-ro.adoc +22 -0
- data/data/locale/attributes-ru.adoc +22 -0
- data/data/locale/attributes-sr.adoc +22 -0
- data/data/locale/attributes-sr_Latn.adoc +22 -0
- data/data/locale/attributes-tr.adoc +22 -0
- data/data/locale/attributes-uk.adoc +22 -0
- data/data/locale/attributes-zh_CN.adoc +22 -0
- data/data/locale/attributes-zh_TW.adoc +22 -0
- data/data/locale/attributes.adoc +8 -649
- data/data/stylesheets/asciidoctor-default.css +77 -72
- data/features/xref.feature +366 -7
- data/lib/asciidoctor.rb +107 -93
- data/lib/asciidoctor/abstract_block.rb +247 -239
- data/lib/asciidoctor/abstract_node.rb +56 -58
- data/lib/asciidoctor/block.rb +3 -3
- data/lib/asciidoctor/callouts.rb +1 -1
- data/lib/asciidoctor/cli/invoker.rb +36 -9
- data/lib/asciidoctor/cli/options.rb +63 -25
- data/lib/asciidoctor/converter.rb +23 -13
- data/lib/asciidoctor/converter/base.rb +4 -0
- data/lib/asciidoctor/converter/docbook45.rb +16 -9
- data/lib/asciidoctor/converter/docbook5.rb +115 -97
- data/lib/asciidoctor/converter/factory.rb +29 -31
- data/lib/asciidoctor/converter/html5.rb +229 -192
- data/lib/asciidoctor/converter/manpage.rb +72 -50
- data/lib/asciidoctor/converter/template.rb +12 -12
- data/lib/asciidoctor/core_ext.rb +5 -1
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
- data/lib/asciidoctor/document.rb +168 -77
- data/lib/asciidoctor/extensions.rb +79 -47
- data/lib/asciidoctor/helpers.rb +33 -11
- data/lib/asciidoctor/inline.rb +3 -2
- data/lib/asciidoctor/list.rb +2 -1
- data/lib/asciidoctor/logging.rb +122 -0
- data/lib/asciidoctor/parser.rb +406 -382
- data/lib/asciidoctor/path_resolver.rb +169 -162
- data/lib/asciidoctor/reader.rb +166 -121
- data/lib/asciidoctor/section.rb +45 -28
- data/lib/asciidoctor/stylesheets.rb +13 -5
- data/lib/asciidoctor/substitutors.rb +328 -254
- data/lib/asciidoctor/table.rb +105 -48
- data/lib/asciidoctor/timings.rb +34 -6
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +41 -23
- data/man/asciidoctor.adoc +14 -8
- data/test/api_test.rb +1004 -0
- data/test/attributes_test.rb +241 -50
- data/test/blocks_test.rb +549 -124
- data/test/converter_test.rb +170 -78
- data/test/document_test.rb +208 -767
- data/test/extensions_test.rb +188 -53
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
- data/test/fixtures/file-with-missing-include.adoc +1 -0
- data/test/fixtures/include-file.jsx +8 -0
- data/test/fixtures/lists.adoc +96 -0
- data/test/fixtures/other-chapters.adoc +11 -0
- data/test/fixtures/outer-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +5 -1
- data/test/fixtures/subdir/index.adoc +3 -0
- data/test/fixtures/subdir/inner-include.adoc +3 -0
- data/test/fixtures/subdir/middle-include.adoc +5 -0
- data/test/fixtures/tagged-class-enclosed.rb +0 -1
- data/test/fixtures/unclosed-tag.adoc +3 -0
- data/test/fixtures/unexpected-end-tag.adoc +4 -0
- data/test/invoker_test.rb +101 -40
- data/test/links_test.rb +266 -72
- data/test/lists_test.rb +243 -45
- data/test/logger_test.rb +211 -0
- data/test/manpage_test.rb +124 -6
- data/test/options_test.rb +46 -1
- data/test/paragraphs_test.rb +23 -10
- data/test/parser_test.rb +30 -1
- data/test/paths_test.rb +115 -33
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +337 -81
- data/test/sections_test.rb +656 -72
- data/test/substitutions_test.rb +182 -57
- data/test/tables_test.rb +324 -57
- data/test/test_helper.rb +77 -32
- data/test/text_test.rb +7 -7
- metadata +67 -3
| @@ -37,7 +37,7 @@ module Asciidoctor | |
| 37 37 | 
             
                    # the registry of converters is initialized using a normal Hash.
         | 
| 38 38 | 
             
                    #
         | 
| 39 39 | 
             
                    # initialize_singleton - A Boolean to indicate whether the singleton should
         | 
| 40 | 
            -
                    #                        be  | 
| 40 | 
            +
                    #                        be initialized if it has not already been created.
         | 
| 41 41 | 
             
                    #                        If false, and a singleton has not been previously
         | 
| 42 42 | 
             
                    #                        initialized, a fresh instance is returned.
         | 
| 43 43 | 
             
                    #
         | 
| @@ -46,10 +46,12 @@ module Asciidoctor | |
| 46 46 | 
             
                      return @__default__ || new unless initialize_singleton
         | 
| 47 47 | 
             
                      # FIXME this assignment is not thread_safe, may need to use a ::Threadsafe helper here
         | 
| 48 48 | 
             
                      @__default__ ||= begin
         | 
| 49 | 
            +
                        # NOTE .to_s hides require from Opal
         | 
| 49 50 | 
             
                        require 'thread_safe'.to_s unless defined? ::ThreadSafe
         | 
| 50 51 | 
             
                        new ::ThreadSafe::Cache.new
         | 
| 51 52 | 
             
                      rescue ::LoadError
         | 
| 52 | 
            -
                         | 
| 53 | 
            +
                        include Logging unless include? Logging
         | 
| 54 | 
            +
                        logger.warn 'gem \'thread_safe\' is not installed. This gem is recommended when registering custom converters.'
         | 
| 53 55 | 
             
                        new
         | 
| 54 56 | 
             
                      end
         | 
| 55 57 | 
             
                    end
         | 
| @@ -183,41 +185,37 @@ module Asciidoctor | |
| 183 185 | 
             
                  # Returns the [Converter] object
         | 
| 184 186 | 
             
                  def create backend, opts = {}
         | 
| 185 187 | 
             
                    if (converter = resolve backend)
         | 
| 186 | 
            -
                       | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
                        require 'asciidoctor/converter/html5'.to_s
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                       | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
                         | 
| 198 | 
            -
                       | 
| 199 | 
            -
             | 
| 200 | 
            -
             | 
| 201 | 
            -
             | 
| 202 | 
            -
             | 
| 188 | 
            +
                      base_converter = ::Class === converter ? (converter.new backend, opts) : converter
         | 
| 189 | 
            +
                      return base_converter unless Converter::BackendInfo === base_converter && base_converter.supports_templates?
         | 
| 190 | 
            +
                    else
         | 
| 191 | 
            +
                      case backend
         | 
| 192 | 
            +
                      when 'html5'
         | 
| 193 | 
            +
                        # NOTE .to_s hides require from Opal
         | 
| 194 | 
            +
                        require 'asciidoctor/converter/html5'.to_s unless defined? ::Asciidoctor::Converter::Html5Converter
         | 
| 195 | 
            +
                        base_converter = Html5Converter.new backend, opts
         | 
| 196 | 
            +
                      when 'docbook5'
         | 
| 197 | 
            +
                        # NOTE .to_s hides require from Opal
         | 
| 198 | 
            +
                        require 'asciidoctor/converter/docbook5'.to_s unless defined? ::Asciidoctor::Converter::DocBook5Converter
         | 
| 199 | 
            +
                        base_converter = DocBook5Converter.new backend, opts
         | 
| 200 | 
            +
                      when 'docbook45'
         | 
| 201 | 
            +
                        # NOTE .to_s hides require from Opal
         | 
| 202 | 
            +
                        require 'asciidoctor/converter/docbook45'.to_s unless defined? ::Asciidoctor::Converter::DocBook45Converter
         | 
| 203 | 
            +
                        base_converter = DocBook45Converter.new backend, opts
         | 
| 204 | 
            +
                      when 'manpage'
         | 
| 205 | 
            +
                        # NOTE .to_s hides require from Opal
         | 
| 206 | 
            +
                        require 'asciidoctor/converter/manpage'.to_s unless defined? ::Asciidoctor::Converter::ManPageConverter
         | 
| 207 | 
            +
                        base_converter = ManPageConverter.new backend, opts
         | 
| 203 208 | 
             
                      end
         | 
| 204 | 
            -
                      DocBook45Converter.new backend, opts
         | 
| 205 | 
            -
                    when 'manpage'
         | 
| 206 | 
            -
                      unless defined? ::Asciidoctor::Converter::ManPageConverter
         | 
| 207 | 
            -
                        require 'asciidoctor/converter/manpage'.to_s
         | 
| 208 | 
            -
                      end
         | 
| 209 | 
            -
                      ManPageConverter.new backend, opts
         | 
| 210 209 | 
             
                    end
         | 
| 211 210 |  | 
| 212 211 | 
             
                    return base_converter unless opts.key? :template_dirs
         | 
| 213 212 |  | 
| 214 | 
            -
                     | 
| 215 | 
            -
             | 
| 216 | 
            -
                    end
         | 
| 217 | 
            -
                    unless defined? ::Asciidoctor::Converter::CompositeConverter
         | 
| 218 | 
            -
                      require 'asciidoctor/converter/composite'.to_s
         | 
| 219 | 
            -
                    end
         | 
| 213 | 
            +
                    # NOTE .to_s hides require from Opal
         | 
| 214 | 
            +
                    require 'asciidoctor/converter/template'.to_s unless defined? ::Asciidoctor::Converter::TemplateConverter
         | 
| 220 215 | 
             
                    template_converter = TemplateConverter.new backend, opts[:template_dirs], opts
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    # NOTE .to_s hides require from Opal
         | 
| 218 | 
            +
                    require 'asciidoctor/converter/composite'.to_s unless defined? ::Asciidoctor::Converter::CompositeConverter
         | 
| 221 219 | 
             
                    # QUESTION should we omit the composite converter if built_in_converter is nil?
         | 
| 222 220 | 
             
                    CompositeConverter.new backend, template_converter, base_converter
         | 
| 223 221 | 
             
                  end
         | 
| @@ -19,6 +19,8 @@ module Asciidoctor | |
| 19 19 | 
             
                  #:latexmath   => INLINE_MATH_DELIMITERS[:latexmath] + [false]
         | 
| 20 20 | 
             
                }).default = ['', '', false]
         | 
| 21 21 |  | 
| 22 | 
            +
                DropAnchorRx = /<(?:a[^>+]+|\/a)>/
         | 
| 23 | 
            +
                StemBreakRx = / *\\\n(?:\\?\n)*|\n\n+/
         | 
| 22 24 | 
             
                SvgPreambleRx = /\A.*?(?=<svg\b)/m
         | 
| 23 25 | 
             
                SvgStartTagRx = /\A<svg[^>]*>/
         | 
| 24 26 | 
             
                DimensionAttributeRx = /\s(?:width|height|style)=(["']).*?\1/
         | 
| @@ -36,10 +38,10 @@ module Asciidoctor | |
| 36 38 | 
             
                    asset_uri_scheme = %(#{asset_uri_scheme}:)
         | 
| 37 39 | 
             
                  end
         | 
| 38 40 | 
             
                  cdn_base = %(#{asset_uri_scheme}//cdnjs.cloudflare.com/ajax/libs)
         | 
| 39 | 
            -
                  linkcss = node. | 
| 41 | 
            +
                  linkcss = node.attr? 'linkcss'
         | 
| 40 42 | 
             
                  result = ['<!DOCTYPE html>']
         | 
| 41 | 
            -
                  lang_attribute = (node.attr? 'nolang') ?  | 
| 42 | 
            -
                  result << %(<html#{@xml_mode ? ' xmlns="http://www.w3.org/1999/xhtml"' :  | 
| 43 | 
            +
                  lang_attribute = (node.attr? 'nolang') ? '' : %( lang="#{node.attr 'lang', 'en'}")
         | 
| 44 | 
            +
                  result << %(<html#{@xml_mode ? ' xmlns="http://www.w3.org/1999/xhtml"' : ''}#{lang_attribute}>)
         | 
| 43 45 | 
             
                  result << %(<head>
         | 
| 44 46 | 
             
            <meta charset="#{node.attr 'encoding', 'UTF-8'}"#{slash}>
         | 
| 45 47 | 
             
            <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"#{slash}><![endif]-->
         | 
| @@ -56,7 +58,7 @@ module Asciidoctor | |
| 56 58 | 
             
                    else
         | 
| 57 59 | 
             
                      icon_type = (icon_ext = ::File.extname icon_href) == '.ico' ? 'image/x-icon' : %(image/#{icon_ext[1..-1]})
         | 
| 58 60 | 
             
                    end
         | 
| 59 | 
            -
                    result << %(<link rel=" | 
| 61 | 
            +
                    result << %(<link rel="icon" type="#{icon_type}" href="#{icon_href}"#{slash}>)
         | 
| 60 62 | 
             
                  end
         | 
| 61 63 | 
             
                  result << %(<title>#{node.doctitle :sanitize => true, :use_fallback => true}</title>)
         | 
| 62 64 |  | 
| @@ -81,7 +83,7 @@ module Asciidoctor | |
| 81 83 |  | 
| 82 84 | 
             
                  if node.attr? 'icons', 'font'
         | 
| 83 85 | 
             
                    if node.attr? 'iconfont-remote'
         | 
| 84 | 
            -
                      result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/ | 
| 86 | 
            +
                      result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/#{FONT_AWESOME_VERSION}/css/font-awesome.min.css]}"#{slash}>)
         | 
| 85 87 | 
             
                    else
         | 
| 86 88 | 
             
                      iconfont_stylesheet = %(#{node.attr 'iconfont-name', 'font-awesome'}.css)
         | 
| 87 89 | 
             
                      result << %(<link rel="stylesheet" href="#{node.normalize_web_path iconfont_stylesheet, (node.attr 'stylesdir', ''), false}"#{slash}>)
         | 
| @@ -113,13 +115,14 @@ module Asciidoctor | |
| 113 115 | 
             
                  end
         | 
| 114 116 |  | 
| 115 117 | 
             
                  result << '</head>'
         | 
| 116 | 
            -
                  body_attrs = []
         | 
| 117 | 
            -
                  body_attrs << %(id="#{node.id}") if node.id
         | 
| 118 | 
            +
                  body_attrs = node.id ? [%(id="#{node.id}")] : []
         | 
| 118 119 | 
             
                  if (sectioned = node.sections?) && (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
         | 
| 119 | 
            -
                     | 
| 120 | 
            +
                    classes = [node.doctype, (node.attr 'toc-class'), %(toc-#{node.attr 'toc-position', 'header'})]
         | 
| 120 121 | 
             
                  else
         | 
| 121 | 
            -
                     | 
| 122 | 
            +
                    classes = [node.doctype]
         | 
| 122 123 | 
             
                  end
         | 
| 124 | 
            +
                  classes << (node.attr 'docrole') if node.attr? 'docrole'
         | 
| 125 | 
            +
                  body_attrs << %(class="#{classes * ' '}")
         | 
| 123 126 | 
             
                  body_attrs << %(style="max-width: #{node.attr 'max-width'};") if node.attr? 'max-width'
         | 
| 124 127 | 
             
                  result << %(<body #{body_attrs * ' '}>)
         | 
| 125 128 |  | 
| @@ -133,11 +136,7 @@ module Asciidoctor | |
| 133 136 | 
             
            #{outline node}
         | 
| 134 137 | 
             
            </div>)
         | 
| 135 138 | 
             
                      end
         | 
| 136 | 
            -
                       | 
| 137 | 
            -
                      result << %(<h2>#{node.attr 'manname-title'}</h2>
         | 
| 138 | 
            -
            <div class="sectionbody">
         | 
| 139 | 
            -
            <p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
         | 
| 140 | 
            -
            </div>)
         | 
| 139 | 
            +
                      result << (generate_manname_section node) if node.attr? 'manpurpose'
         | 
| 141 140 | 
             
                    else
         | 
| 142 141 | 
             
                      if node.has_header?
         | 
| 143 142 | 
             
                        result << %(<h1>#{node.header.title}</h1>) unless node.notitle
         | 
| @@ -190,7 +189,7 @@ module Asciidoctor | |
| 190 189 | 
             
                    result << %(<div id="footnotes">
         | 
| 191 190 | 
             
            <hr#{slash}>)
         | 
| 192 191 | 
             
                    node.footnotes.each do |footnote|
         | 
| 193 | 
            -
                      result << %(<div class="footnote" id=" | 
| 192 | 
            +
                      result << %(<div class="footnote" id="_footnotedef_#{footnote.index}">
         | 
| 194 193 | 
             
            <a href="#_footnoteref_#{footnote.index}">#{footnote.index}</a>. #{footnote.text}
         | 
| 195 194 | 
             
            </div>)
         | 
| 196 195 | 
             
                    end
         | 
| @@ -245,7 +244,7 @@ MathJax.Hub.Config({ | |
| 245 244 | 
             
              TeX: {#{eqnums_opt}}
         | 
| 246 245 | 
             
            });
         | 
| 247 246 | 
             
            </script>
         | 
| 248 | 
            -
            <script src="#{cdn_base}/mathjax/ | 
| 247 | 
            +
            <script src="#{cdn_base}/mathjax/#{MATHJAX_VERSION}/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>)
         | 
| 249 248 | 
             
                  end
         | 
| 250 249 |  | 
| 251 250 | 
             
                  result << '</body>'
         | 
| @@ -258,17 +257,13 @@ MathJax.Hub.Config({ | |
| 258 257 | 
             
                  if node.doctype == 'manpage'
         | 
| 259 258 | 
             
                    # QUESTION should notitle control the manual page title?
         | 
| 260 259 | 
             
                    unless node.notitle
         | 
| 261 | 
            -
                      id_attr = node.id ? %( id="#{node.id}") :  | 
| 260 | 
            +
                      id_attr = node.id ? %( id="#{node.id}") : ''
         | 
| 262 261 | 
             
                      result << %(<h1#{id_attr}>#{node.doctitle} Manual Page</h1>)
         | 
| 263 262 | 
             
                    end
         | 
| 264 | 
            -
                     | 
| 265 | 
            -
                    result << %(<h2>#{node.attr 'manname-title'}</h2>
         | 
| 266 | 
            -
            <div class="sectionbody">
         | 
| 267 | 
            -
            <p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
         | 
| 268 | 
            -
            </div>)
         | 
| 263 | 
            +
                    result << (generate_manname_section node) if node.attr? 'manpurpose'
         | 
| 269 264 | 
             
                  else
         | 
| 270 265 | 
             
                    if node.has_header? && !node.notitle
         | 
| 271 | 
            -
                      id_attr = node.id ? %( id="#{node.id}") :  | 
| 266 | 
            +
                      id_attr = node.id ? %( id="#{node.id}") : ''
         | 
| 272 267 | 
             
                      result << %(<h1#{id_attr}>#{node.header.title}</h1>)
         | 
| 273 268 | 
             
                    end
         | 
| 274 269 | 
             
                  end
         | 
| @@ -286,7 +281,7 @@ MathJax.Hub.Config({ | |
| 286 281 | 
             
                    result << %(<div id="footnotes">
         | 
| 287 282 | 
             
            <hr#{@void_element_slash}>)
         | 
| 288 283 | 
             
                    node.footnotes.each do |footnote|
         | 
| 289 | 
            -
                      result << %(<div class="footnote" id=" | 
| 284 | 
            +
                      result << %(<div class="footnote" id="_footnotedef_#{footnote.index}">
         | 
| 290 285 | 
             
            <a href="#_footnoteref_#{footnote.index}">#{footnote.index}</a>. #{footnote.text}
         | 
| 291 286 | 
             
            </div>)
         | 
| 292 287 | 
             
                    end
         | 
| @@ -312,6 +307,7 @@ MathJax.Hub.Config({ | |
| 312 307 | 
             
                    else
         | 
| 313 308 | 
             
                      stitle = section.title
         | 
| 314 309 | 
             
                    end
         | 
| 310 | 
            +
                    stitle = stitle.gsub DropAnchorRx, '' if stitle.include? '<a'
         | 
| 315 311 | 
             
                    if slevel < toclevels && (child_toc_level = outline section, :toclevels => toclevels, :secnumlevels => sectnumlevels)
         | 
| 316 312 | 
             
                      result << %(<li><a href="##{section.id}">#{stitle}</a>)
         | 
| 317 313 | 
             
                      result << child_toc_level
         | 
| @@ -325,45 +321,46 @@ MathJax.Hub.Config({ | |
| 325 321 | 
             
                end
         | 
| 326 322 |  | 
| 327 323 | 
             
                def section node
         | 
| 328 | 
            -
                   | 
| 329 | 
            -
             | 
| 330 | 
            -
             | 
| 324 | 
            +
                  if (level = node.level) == 0
         | 
| 325 | 
            +
                    sect0 = true
         | 
| 326 | 
            +
                    title = node.numbered && level <= (node.document.attr 'sectnumlevels', 3).to_i ? %(#{node.sectnum} #{node.title}) : node.title
         | 
| 327 | 
            +
                  else
         | 
| 328 | 
            +
                    title = node.numbered && !node.caption && level <= (node.document.attr 'sectnumlevels', 3).to_i ? %(#{node.sectnum} #{node.title}) : node.captioned_title
         | 
| 329 | 
            +
                  end
         | 
| 331 330 | 
             
                  if node.id
         | 
| 332 331 | 
             
                    id_attr = %( id="#{id = node.id}")
         | 
| 333 | 
            -
                    if ( | 
| 334 | 
            -
                       | 
| 335 | 
            -
                      # possible idea - anchor icons GitHub-style
         | 
| 336 | 
            -
                      #if doc.attr? 'icons', 'font'
         | 
| 337 | 
            -
                      #  anchor = %(<a class="anchor" href="##{id}"><i class="fa fa-anchor"></i></a>)
         | 
| 338 | 
            -
                      #else
         | 
| 339 | 
            -
                      #  anchor = %(<a class="anchor" href="##{id}"></a>)
         | 
| 340 | 
            -
                      #end
         | 
| 332 | 
            +
                    if (doc_attrs = node.document.attributes).key? 'sectlinks'
         | 
| 333 | 
            +
                      title = %(<a class="link" href="##{id}">#{title}</a>)
         | 
| 341 334 | 
             
                    end
         | 
| 342 | 
            -
                    if  | 
| 343 | 
            -
                       | 
| 344 | 
            -
                       | 
| 335 | 
            +
                    if doc_attrs.key? 'sectanchors'
         | 
| 336 | 
            +
                      # QUESTION should we add a font-based icon in anchor if icons=font?
         | 
| 337 | 
            +
                      if doc_attrs['sectanchors'] == 'after'
         | 
| 338 | 
            +
                        title = %(#{title}<a class="anchor" href="##{id}"></a>)
         | 
| 339 | 
            +
                      else
         | 
| 340 | 
            +
                        title = %(<a class="anchor" href="##{id}"></a>#{title})
         | 
| 341 | 
            +
                      end
         | 
| 345 342 | 
             
                    end
         | 
| 343 | 
            +
                  else
         | 
| 344 | 
            +
                    id_attr = ''
         | 
| 346 345 | 
             
                  end
         | 
| 347 346 |  | 
| 348 | 
            -
                  if  | 
| 349 | 
            -
                    %(<h1#{id_attr} class="sect0" | 
| 347 | 
            +
                  if sect0
         | 
| 348 | 
            +
                    %(<h1#{id_attr} class="sect0#{(role = node.role) ? " #{role}" : ''}">#{title}</h1>
         | 
| 350 349 | 
             
            #{node.content})
         | 
| 351 350 | 
             
                  else
         | 
| 352 | 
            -
                     | 
| 353 | 
            -
             | 
| 354 | 
            -
             | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 357 | 
            -
            <#{htag}#{id_attr}>#{anchor}#{link_start}#{sectnum}#{node.captioned_title}#{link_end}</#{htag}>
         | 
| 358 | 
            -
            #{slevel == 1 ? %[<div class="sectionbody">\n#{node.content}\n</div>] : node.content}
         | 
| 351 | 
            +
                    %(<div class="sect#{level}#{(role = node.role) ? " #{role}" : ''}">
         | 
| 352 | 
            +
            <h#{level + 1}#{id_attr}>#{title}</h#{level + 1}>
         | 
| 353 | 
            +
            #{level == 1 ? %[<div class="sectionbody">
         | 
| 354 | 
            +
            #{node.content}
         | 
| 355 | 
            +
            </div>] : node.content}
         | 
| 359 356 | 
             
            </div>)
         | 
| 360 357 | 
             
                  end
         | 
| 361 358 | 
             
                end
         | 
| 362 359 |  | 
| 363 360 | 
             
                def admonition node
         | 
| 364 | 
            -
                  id_attr = node.id ? %( id="#{node.id}") :  | 
| 361 | 
            +
                  id_attr = node.id ? %( id="#{node.id}") : ''
         | 
| 365 362 | 
             
                  name = node.attr 'name'
         | 
| 366 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 363 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 367 364 | 
             
                  if node.document.attr? 'icons'
         | 
| 368 365 | 
             
                    if (node.document.attr? 'icons', 'font') && !(node.attr? 'icon')
         | 
| 369 366 | 
             
                      label = %(<i class="fa icon-#{name}" title="#{node.attr 'textlabel'}"></i>)
         | 
| @@ -373,7 +370,7 @@ MathJax.Hub.Config({ | |
| 373 370 | 
             
                  else
         | 
| 374 371 | 
             
                    label = %(<div class="title">#{node.attr 'textlabel'}</div>)
         | 
| 375 372 | 
             
                  end
         | 
| 376 | 
            -
                  %(<div#{id_attr} class="admonitionblock #{name}#{(role = node.role)  | 
| 373 | 
            +
                  %(<div#{id_attr} class="admonitionblock #{name}#{(role = node.role) ? " #{role}" : ''}">
         | 
| 377 374 | 
             
            <table>
         | 
| 378 375 | 
             
            <tr>
         | 
| 379 376 | 
             
            <td class="icon">
         | 
| @@ -389,16 +386,16 @@ MathJax.Hub.Config({ | |
| 389 386 |  | 
| 390 387 | 
             
                def audio node
         | 
| 391 388 | 
             
                  xml = @xml_mode
         | 
| 392 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 389 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 393 390 | 
             
                  classes = ['audioblock', node.role].compact
         | 
| 394 391 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 395 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 392 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 396 393 | 
             
                  start_t = node.attr 'start', nil, false
         | 
| 397 394 | 
             
                  end_t = node.attr 'end', nil, false
         | 
| 398 | 
            -
                  time_anchor = (start_t || end_t) ? %(#t=#{start_t}#{end_t ?  | 
| 395 | 
            +
                  time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : ''
         | 
| 399 396 | 
             
                  %(<div#{id_attribute}#{class_attribute}>
         | 
| 400 397 | 
             
            #{title_element}<div class="content">
         | 
| 401 | 
            -
            <audio src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) :  | 
| 398 | 
            +
            <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) : ''}>
         | 
| 402 399 | 
             
            Your browser does not support the audio tag.
         | 
| 403 400 | 
             
            </audio>
         | 
| 404 401 | 
             
            </div>
         | 
| @@ -407,7 +404,7 @@ Your browser does not support the audio tag. | |
| 407 404 |  | 
| 408 405 | 
             
                def colist node
         | 
| 409 406 | 
             
                  result = []
         | 
| 410 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 407 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 411 408 | 
             
                  classes = ['colist', node.style, node.role].compact
         | 
| 412 409 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 413 410 |  | 
| @@ -446,7 +443,7 @@ Your browser does not support the audio tag. | |
| 446 443 |  | 
| 447 444 | 
             
                def dlist node
         | 
| 448 445 | 
             
                  result = []
         | 
| 449 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 446 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 450 447 |  | 
| 451 448 | 
             
                  classes = case node.style
         | 
| 452 449 | 
             
                  when 'qanda'
         | 
| @@ -481,15 +478,15 @@ Your browser does not support the audio tag. | |
| 481 478 | 
             
                    result << '<table>'
         | 
| 482 479 | 
             
                    if (node.attr? 'labelwidth') || (node.attr? 'itemwidth')
         | 
| 483 480 | 
             
                      result << '<colgroup>'
         | 
| 484 | 
            -
                      col_style_attribute = (node.attr? 'labelwidth') ? %( style="width: #{(node.attr 'labelwidth').chomp '%'}%;") :  | 
| 481 | 
            +
                      col_style_attribute = (node.attr? 'labelwidth') ? %( style="width: #{(node.attr 'labelwidth').chomp '%'}%;") : ''
         | 
| 485 482 | 
             
                      result << %(<col#{col_style_attribute}#{slash}>)
         | 
| 486 | 
            -
                      col_style_attribute = (node.attr? 'itemwidth') ? %( style="width: #{(node.attr 'itemwidth').chomp '%'}%;") :  | 
| 483 | 
            +
                      col_style_attribute = (node.attr? 'itemwidth') ? %( style="width: #{(node.attr 'itemwidth').chomp '%'}%;") : ''
         | 
| 487 484 | 
             
                      result << %(<col#{col_style_attribute}#{slash}>)
         | 
| 488 485 | 
             
                      result << '</colgroup>'
         | 
| 489 486 | 
             
                    end
         | 
| 490 487 | 
             
                    node.items.each do |terms, dd|
         | 
| 491 488 | 
             
                      result << '<tr>'
         | 
| 492 | 
            -
                      result << %(<td class="hdlist1#{(node.option? 'strong') ? ' strong' :  | 
| 489 | 
            +
                      result << %(<td class="hdlist1#{(node.option? 'strong') ? ' strong' : ''}">)
         | 
| 493 490 | 
             
                      terms_array = [*terms]
         | 
| 494 491 | 
             
                      last_term = terms_array[-1]
         | 
| 495 492 | 
             
                      terms_array.each do |dt|
         | 
| @@ -508,7 +505,7 @@ Your browser does not support the audio tag. | |
| 508 505 | 
             
                    result << '</table>'
         | 
| 509 506 | 
             
                  else
         | 
| 510 507 | 
             
                    result << '<dl>'
         | 
| 511 | 
            -
                    dt_style_attribute = node.style ?  | 
| 508 | 
            +
                    dt_style_attribute = node.style ? '' : ' class="hdlist1"'
         | 
| 512 509 | 
             
                    node.items.each do |terms, dd|
         | 
| 513 510 | 
             
                      [*terms].each do |dt|
         | 
| 514 511 | 
             
                        result << %(<dt#{dt_style_attribute}>#{dt.text}</dt>)
         | 
| @@ -528,10 +525,10 @@ Your browser does not support the audio tag. | |
| 528 525 | 
             
                end
         | 
| 529 526 |  | 
| 530 527 | 
             
                def example node
         | 
| 531 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 532 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) :  | 
| 528 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 529 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) : ''
         | 
| 533 530 |  | 
| 534 | 
            -
                  %(<div#{id_attribute} class="exampleblock#{(role = node.role)  | 
| 531 | 
            +
                  %(<div#{id_attribute} class="exampleblock#{(role = node.role) ? " #{role}" : ''}">
         | 
| 535 532 | 
             
            #{title_element}<div class="content">
         | 
| 536 533 | 
             
            #{node.content}
         | 
| 537 534 | 
             
            </div>
         | 
| @@ -540,15 +537,15 @@ Your browser does not support the audio tag. | |
| 540 537 |  | 
| 541 538 | 
             
                def floating_title node
         | 
| 542 539 | 
             
                  tag_name = %(h#{node.level + 1})
         | 
| 543 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 540 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 544 541 | 
             
                  classes = [node.style, node.role].compact
         | 
| 545 542 | 
             
                  %(<#{tag_name}#{id_attribute} class="#{classes * ' '}">#{node.title}</#{tag_name}>)
         | 
| 546 543 | 
             
                end
         | 
| 547 544 |  | 
| 548 545 | 
             
                def image node
         | 
| 549 546 | 
             
                  target = node.attr 'target'
         | 
| 550 | 
            -
                  width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") :  | 
| 551 | 
            -
                  height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") :  | 
| 547 | 
            +
                  width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
         | 
| 548 | 
            +
                  height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : ''
         | 
| 552 549 | 
             
                  if ((node.attr? 'format', 'svg', false) || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE &&
         | 
| 553 550 | 
             
                      ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
         | 
| 554 551 | 
             
                    if svg
         | 
| @@ -559,18 +556,17 @@ Your browser does not support the audio tag. | |
| 559 556 | 
             
                    end
         | 
| 560 557 | 
             
                  end
         | 
| 561 558 | 
             
                  img ||= %(<img src="#{node.image_uri target}" alt="#{encode_quotes node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
         | 
| 562 | 
            -
                  if node.attr? 'link'
         | 
| 563 | 
            -
                     | 
| 564 | 
            -
                    img = %(<a class="image" href="#{node.attr 'link'}"#{window_attr}>#{img}</a>)
         | 
| 559 | 
            +
                  if node.attr? 'link', nil, false
         | 
| 560 | 
            +
                    img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
         | 
| 565 561 | 
             
                  end
         | 
| 566 | 
            -
                  id_attr = node.id ? %( id="#{node.id}") :  | 
| 562 | 
            +
                  id_attr = node.id ? %( id="#{node.id}") : ''
         | 
| 567 563 | 
             
                  classes = ['imageblock', node.role].compact
         | 
| 568 564 | 
             
                  class_attr = %( class="#{classes * ' '}")
         | 
| 569 565 | 
             
                  styles = []
         | 
| 570 566 | 
             
                  styles << %(text-align: #{node.attr 'align'}) if node.attr? 'align'
         | 
| 571 567 | 
             
                  styles << %(float: #{node.attr 'float'}) if node.attr? 'float'
         | 
| 572 | 
            -
                  style_attr = styles.empty? ?  | 
| 573 | 
            -
                  title_el = node.title? ? %(\n<div class="title">#{node.captioned_title}</div>) :  | 
| 568 | 
            +
                  style_attr = styles.empty? ? '' : %( style="#{styles * ';'}")
         | 
| 569 | 
            +
                  title_el = node.title? ? %(\n<div class="title">#{node.captioned_title}</div>) : ''
         | 
| 574 570 | 
             
                  %(<div#{id_attr}#{class_attr}#{style_attr}>
         | 
| 575 571 | 
             
            <div class="content">
         | 
| 576 572 | 
             
            #{img}
         | 
| @@ -584,36 +580,41 @@ Your browser does not support the audio tag. | |
| 584 580 | 
             
                    if (language = node.attr 'language', nil, false)
         | 
| 585 581 | 
             
                      code_attrs = %( data-lang="#{language}")
         | 
| 586 582 | 
             
                    else
         | 
| 587 | 
            -
                      code_attrs =  | 
| 583 | 
            +
                      code_attrs = ''
         | 
| 588 584 | 
             
                    end
         | 
| 589 585 | 
             
                    case node.document.attr 'source-highlighter'
         | 
| 590 586 | 
             
                    when 'coderay'
         | 
| 591 | 
            -
                      pre_class = %( class="CodeRay highlight#{nowrap ? ' nowrap' :  | 
| 587 | 
            +
                      pre_class = %( class="CodeRay highlight#{nowrap ? ' nowrap' : ''}")
         | 
| 592 588 | 
             
                    when 'pygments'
         | 
| 593 | 
            -
                       | 
| 589 | 
            +
                      if (node.document.attr? 'pygments-css', 'inline')
         | 
| 590 | 
            +
                        @pygments_bg = @stylesheets.pygments_background(node.document.attr 'pygments-style') unless defined? @pygments_bg
         | 
| 591 | 
            +
                        pre_class = %( class="pygments highlight#{nowrap ? ' nowrap' : ''}" style="background: #{@pygments_bg}")
         | 
| 592 | 
            +
                      else
         | 
| 593 | 
            +
                        pre_class = %( class="pygments highlight#{nowrap ? ' nowrap' : ''}")
         | 
| 594 | 
            +
                      end
         | 
| 594 595 | 
             
                    when 'highlightjs', 'highlight.js'
         | 
| 595 | 
            -
                      pre_class = %( class="highlightjs highlight#{nowrap ? ' nowrap' :  | 
| 596 | 
            +
                      pre_class = %( class="highlightjs highlight#{nowrap ? ' nowrap' : ''}")
         | 
| 596 597 | 
             
                      code_attrs = %( class="language-#{language} hljs"#{code_attrs}) if language
         | 
| 597 598 | 
             
                    when 'prettify'
         | 
| 598 | 
            -
                      pre_class = %( class="prettyprint highlight#{nowrap ? ' nowrap' :  | 
| 599 | 
            +
                      pre_class = %( class="prettyprint highlight#{nowrap ? ' nowrap' : ''}#{(node.attr? 'linenums', nil, false) ? ' linenums' : ''}")
         | 
| 599 600 | 
             
                      code_attrs = %( class="language-#{language}"#{code_attrs}) if language
         | 
| 600 601 | 
             
                    when 'html-pipeline'
         | 
| 601 | 
            -
                      pre_class = language ? %( lang="#{language}") :  | 
| 602 | 
            -
                      code_attrs =  | 
| 602 | 
            +
                      pre_class = language ? %( lang="#{language}") : ''
         | 
| 603 | 
            +
                      code_attrs = ''
         | 
| 603 604 | 
             
                    else
         | 
| 604 | 
            -
                      pre_class = %( class="highlight#{nowrap ? ' nowrap' :  | 
| 605 | 
            +
                      pre_class = %( class="highlight#{nowrap ? ' nowrap' : ''}")
         | 
| 605 606 | 
             
                      code_attrs = %( class="language-#{language}"#{code_attrs}) if language
         | 
| 606 607 | 
             
                    end
         | 
| 607 608 | 
             
                    pre_start = %(<pre#{pre_class}><code#{code_attrs}>)
         | 
| 608 609 | 
             
                    pre_end = '</code></pre>'
         | 
| 609 610 | 
             
                  else
         | 
| 610 | 
            -
                    pre_start = %(<pre#{nowrap ? ' class="nowrap"' :  | 
| 611 | 
            +
                    pre_start = %(<pre#{nowrap ? ' class="nowrap"' : ''}>)
         | 
| 611 612 | 
             
                    pre_end = '</pre>'
         | 
| 612 613 | 
             
                  end
         | 
| 613 614 |  | 
| 614 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 615 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) :  | 
| 616 | 
            -
                  %(<div#{id_attribute} class="listingblock#{(role = node.role)  | 
| 615 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 616 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) : ''
         | 
| 617 | 
            +
                  %(<div#{id_attribute} class="listingblock#{(role = node.role) ? " #{role}" : ''}">
         | 
| 617 618 | 
             
            #{title_element}<div class="content">
         | 
| 618 619 | 
             
            #{pre_start}#{node.content}#{pre_end}
         | 
| 619 620 | 
             
            </div>
         | 
| @@ -621,26 +622,32 @@ Your browser does not support the audio tag. | |
| 621 622 | 
             
                end
         | 
| 622 623 |  | 
| 623 624 | 
             
                def literal node
         | 
| 624 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 625 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 625 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 626 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 626 627 | 
             
                  nowrap = !(node.document.attr? 'prewrap') || (node.option? 'nowrap')
         | 
| 627 | 
            -
                  %(<div#{id_attribute} class="literalblock#{(role = node.role)  | 
| 628 | 
            +
                  %(<div#{id_attribute} class="literalblock#{(role = node.role) ? " #{role}" : ''}">
         | 
| 628 629 | 
             
            #{title_element}<div class="content">
         | 
| 629 | 
            -
            <pre#{nowrap ? ' class="nowrap"' :  | 
| 630 | 
            +
            <pre#{nowrap ? ' class="nowrap"' : ''}>#{node.content}</pre>
         | 
| 630 631 | 
             
            </div>
         | 
| 631 632 | 
             
            </div>)
         | 
| 632 633 | 
             
                end
         | 
| 633 634 |  | 
| 634 635 | 
             
                def stem node
         | 
| 635 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 636 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 637 | 
            -
                  open, close = BLOCK_MATH_DELIMITERS[node.style.to_sym]
         | 
| 636 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 637 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 638 | 
            +
                  open, close = BLOCK_MATH_DELIMITERS[style = node.style.to_sym]
         | 
| 639 | 
            +
                  equation = node.content
         | 
| 640 | 
            +
             | 
| 641 | 
            +
                  if style == :asciimath && (equation.include? LF)
         | 
| 642 | 
            +
                    br = %(<br#{@void_element_slash}>#{LF})
         | 
| 643 | 
            +
                    equation = equation.gsub(StemBreakRx) { %(#{close}#{br * ($&.count LF)}#{open}) }
         | 
| 644 | 
            +
                  end
         | 
| 638 645 |  | 
| 639 | 
            -
                  unless ( | 
| 646 | 
            +
                  unless (equation.start_with? open) && (equation.end_with? close)
         | 
| 640 647 | 
             
                    equation = %(#{open}#{equation}#{close})
         | 
| 641 648 | 
             
                  end
         | 
| 642 649 |  | 
| 643 | 
            -
                  %(<div#{id_attribute} class="stemblock#{(role = node.role)  | 
| 650 | 
            +
                  %(<div#{id_attribute} class="stemblock#{(role = node.role) ? " #{role}" : ''}">
         | 
| 644 651 | 
             
            #{title_element}<div class="content">
         | 
| 645 652 | 
             
            #{equation}
         | 
| 646 653 | 
             
            </div>
         | 
| @@ -649,16 +656,16 @@ Your browser does not support the audio tag. | |
| 649 656 |  | 
| 650 657 | 
             
                def olist node
         | 
| 651 658 | 
             
                  result = []
         | 
| 652 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 659 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 653 660 | 
             
                  classes = ['olist', node.style, node.role].compact
         | 
| 654 661 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 655 662 |  | 
| 656 663 | 
             
                  result << %(<div#{id_attribute}#{class_attribute}>)
         | 
| 657 664 | 
             
                  result << %(<div class="title">#{node.title}</div>) if node.title?
         | 
| 658 665 |  | 
| 659 | 
            -
                  type_attribute = (keyword = node.list_marker_keyword) ? %( type="#{keyword}") :  | 
| 660 | 
            -
                  start_attribute = (node.attr? 'start') ? %( start="#{node.attr 'start'}") :  | 
| 661 | 
            -
                  reversed_attribute = (node.option? 'reversed') ? (append_boolean_attribute 'reversed', @xml_mode) :  | 
| 666 | 
            +
                  type_attribute = (keyword = node.list_marker_keyword) ? %( type="#{keyword}") : ''
         | 
| 667 | 
            +
                  start_attribute = (node.attr? 'start') ? %( start="#{node.attr 'start'}") : ''
         | 
| 668 | 
            +
                  reversed_attribute = (node.option? 'reversed') ? (append_boolean_attribute 'reversed', @xml_mode) : ''
         | 
| 662 669 | 
             
                  result << %(<ol class="#{node.style}"#{type_attribute}#{start_attribute}#{reversed_attribute}>)
         | 
| 663 670 |  | 
| 664 671 | 
             
                  node.items.each do |item|
         | 
| @@ -676,24 +683,24 @@ Your browser does not support the audio tag. | |
| 676 683 | 
             
                def open node
         | 
| 677 684 | 
             
                  if (style = node.style) == 'abstract'
         | 
| 678 685 | 
             
                    if node.parent == node.document && node.document.doctype == 'book'
         | 
| 679 | 
            -
                      warn ' | 
| 686 | 
            +
                      logger.warn 'abstract block cannot be used in a document without a title when doctype is book. Excluding block content.'
         | 
| 680 687 | 
             
                      ''
         | 
| 681 688 | 
             
                    else
         | 
| 682 | 
            -
                      id_attr = node.id ? %( id="#{node.id}") :  | 
| 683 | 
            -
                      title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 684 | 
            -
                      %(<div#{id_attr} class="quoteblock abstract#{(role = node.role)  | 
| 689 | 
            +
                      id_attr = node.id ? %( id="#{node.id}") : ''
         | 
| 690 | 
            +
                      title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 691 | 
            +
                      %(<div#{id_attr} class="quoteblock abstract#{(role = node.role) ? " #{role}" : ''}">
         | 
| 685 692 | 
             
            #{title_el}<blockquote>
         | 
| 686 693 | 
             
            #{node.content}
         | 
| 687 694 | 
             
            </blockquote>
         | 
| 688 695 | 
             
            </div>)
         | 
| 689 696 | 
             
                    end
         | 
| 690 697 | 
             
                  elsif style == 'partintro' && (node.level > 0 || node.parent.context != :section || node.document.doctype != 'book')
         | 
| 691 | 
            -
                     | 
| 698 | 
            +
                    logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
         | 
| 692 699 | 
             
                    ''
         | 
| 693 700 | 
             
                  else
         | 
| 694 | 
            -
                      id_attr = node.id ? %( id="#{node.id}") :  | 
| 695 | 
            -
                      title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 696 | 
            -
                    %(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role)  | 
| 701 | 
            +
                      id_attr = node.id ? %( id="#{node.id}") : ''
         | 
| 702 | 
            +
                      title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 703 | 
            +
                    %(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role) ? " #{role}" : ''}">
         | 
| 697 704 | 
             
            #{title_el}<div class="content">
         | 
| 698 705 | 
             
            #{node.content}
         | 
| 699 706 | 
             
            </div>
         | 
| @@ -729,7 +736,7 @@ Your browser does not support the audio tag. | |
| 729 736 | 
             
            #{outline doc}
         | 
| 730 737 | 
             
            </div>)
         | 
| 731 738 | 
             
                  else
         | 
| 732 | 
            -
                    toc =  | 
| 739 | 
            +
                    toc = ''
         | 
| 733 740 | 
             
                  end
         | 
| 734 741 |  | 
| 735 742 | 
             
                  %(<div id="preamble">
         | 
| @@ -740,18 +747,18 @@ Your browser does not support the audio tag. | |
| 740 747 | 
             
                end
         | 
| 741 748 |  | 
| 742 749 | 
             
                def quote node
         | 
| 743 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 750 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 744 751 | 
             
                  classes = ['quoteblock', node.role].compact
         | 
| 745 752 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 746 | 
            -
                  title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) :  | 
| 753 | 
            +
                  title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) : ''
         | 
| 747 754 | 
             
                  attribution = (node.attr? 'attribution') ? (node.attr 'attribution') : nil
         | 
| 748 755 | 
             
                  citetitle = (node.attr? 'citetitle') ? (node.attr 'citetitle') : nil
         | 
| 749 756 | 
             
                  if attribution || citetitle
         | 
| 750 | 
            -
                    cite_element = citetitle ? %(<cite>#{citetitle}</cite>) :  | 
| 751 | 
            -
                    attribution_text = attribution ? %(— #{attribution}#{citetitle ? "<br#{@void_element_slash}>\n" :  | 
| 757 | 
            +
                    cite_element = citetitle ? %(<cite>#{citetitle}</cite>) : ''
         | 
| 758 | 
            +
                    attribution_text = attribution ? %(— #{attribution}#{citetitle ? "<br#{@void_element_slash}>\n" : ''}) : ''
         | 
| 752 759 | 
             
                    attribution_element = %(\n<div class="attribution">\n#{attribution_text}#{cite_element}\n</div>)
         | 
| 753 760 | 
             
                  else
         | 
| 754 | 
            -
                    attribution_element =  | 
| 761 | 
            +
                    attribution_element = ''
         | 
| 755 762 | 
             
                  end
         | 
| 756 763 |  | 
| 757 764 | 
             
                  %(<div#{id_attribute}#{class_attribute}>#{title_element}
         | 
| @@ -766,9 +773,9 @@ Your browser does not support the audio tag. | |
| 766 773 | 
             
                end
         | 
| 767 774 |  | 
| 768 775 | 
             
                def sidebar node
         | 
| 769 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 770 | 
            -
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) :  | 
| 771 | 
            -
                  %(<div#{id_attribute} class="sidebarblock#{(role = node.role)  | 
| 776 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 777 | 
            +
                  title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
         | 
| 778 | 
            +
                  %(<div#{id_attribute} class="sidebarblock#{(role = node.role) ? " #{role}" : ''}">
         | 
| 772 779 | 
             
            <div class="content">
         | 
| 773 780 | 
             
            #{title_element}#{node.content}
         | 
| 774 781 | 
             
            </div>
         | 
| @@ -777,36 +784,36 @@ Your browser does not support the audio tag. | |
| 777 784 |  | 
| 778 785 | 
             
                def table node
         | 
| 779 786 | 
             
                  result = []
         | 
| 780 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 787 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 781 788 | 
             
                  classes = ['tableblock', %(frame-#{node.attr 'frame', 'all'}), %(grid-#{node.attr 'grid', 'all'})]
         | 
| 789 | 
            +
                  if (stripes = node.attr 'stripes')
         | 
| 790 | 
            +
                    classes << %(stripes-#{stripes})
         | 
| 791 | 
            +
                  end
         | 
| 782 792 | 
             
                  styles = []
         | 
| 783 | 
            -
                   | 
| 784 | 
            -
                     | 
| 785 | 
            -
             | 
| 786 | 
            -
                     | 
| 787 | 
            -
             | 
| 788 | 
            -
                     | 
| 793 | 
            +
                  if (autowidth = node.attributes['autowidth-option']) && !(node.attr? 'width', nil, false)
         | 
| 794 | 
            +
                    classes << 'fit-content'
         | 
| 795 | 
            +
                  elsif (tablewidth = node.attr 'tablepcwidth') == 100
         | 
| 796 | 
            +
                    classes << 'stretch'
         | 
| 797 | 
            +
                  else
         | 
| 798 | 
            +
                    styles << %(width: #{tablewidth}%;)
         | 
| 789 799 | 
             
                  end
         | 
| 790 800 | 
             
                  if (role = node.role)
         | 
| 791 801 | 
             
                    classes << role
         | 
| 792 802 | 
             
                  end
         | 
| 793 803 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 794 804 | 
             
                  styles << %(float: #{node.attr 'float'};) if node.attr? 'float'
         | 
| 795 | 
            -
                  style_attribute = styles.empty? ?  | 
| 805 | 
            +
                  style_attribute = styles.empty? ? '' : %( style="#{styles * ' '}")
         | 
| 796 806 |  | 
| 797 807 | 
             
                  result << %(<table#{id_attribute}#{class_attribute}#{style_attribute}>)
         | 
| 798 808 | 
             
                  result << %(<caption class="title">#{node.captioned_title}</caption>) if node.title?
         | 
| 799 809 | 
             
                  if (node.attr 'rowcount') > 0
         | 
| 800 810 | 
             
                    slash = @void_element_slash
         | 
| 801 811 | 
             
                    result << '<colgroup>'
         | 
| 802 | 
            -
                    if  | 
| 803 | 
            -
                       | 
| 804 | 
            -
                      node.columns.size.times do
         | 
| 805 | 
            -
                        result << tag
         | 
| 806 | 
            -
                      end
         | 
| 812 | 
            +
                    if autowidth
         | 
| 813 | 
            +
                      result += (Array.new node.columns.size, %(<col#{slash}>))
         | 
| 807 814 | 
             
                    else
         | 
| 808 815 | 
             
                      node.columns.each do |col|
         | 
| 809 | 
            -
                        result << %(<col style="width: #{col.attr 'colpcwidth'}%;"#{slash}>)
         | 
| 816 | 
            +
                        result << (col.attributes['autowidth-option'] ? %(<col#{slash}>) : %(<col style="width: #{col.attr 'colpcwidth'}%;"#{slash}>))
         | 
| 810 817 | 
             
                      end
         | 
| 811 818 | 
             
                    end
         | 
| 812 819 | 
             
                    result << '</colgroup>'
         | 
| @@ -821,7 +828,7 @@ Your browser does not support the audio tag. | |
| 821 828 | 
             
                          else
         | 
| 822 829 | 
             
                            case cell.style
         | 
| 823 830 | 
             
                            when :asciidoc
         | 
| 824 | 
            -
                              cell_content = %(<div>#{cell.content}</div>)
         | 
| 831 | 
            +
                              cell_content = %(<div class="content">#{cell.content}</div>)
         | 
| 825 832 | 
             
                            when :verse
         | 
| 826 833 | 
             
                              cell_content = %(<div class="verse">#{cell.text}</div>)
         | 
| 827 834 | 
             
                            when :literal
         | 
| @@ -834,9 +841,9 @@ Your browser does not support the audio tag. | |
| 834 841 |  | 
| 835 842 | 
             
                          cell_tag_name = (tsec == :head || cell.style == :header ? 'th' : 'td')
         | 
| 836 843 | 
             
                          cell_class_attribute = %( class="tableblock halign-#{cell.attr 'halign'} valign-#{cell.attr 'valign'}")
         | 
| 837 | 
            -
                          cell_colspan_attribute = cell.colspan ? %( colspan="#{cell.colspan}") :  | 
| 838 | 
            -
                          cell_rowspan_attribute = cell.rowspan ? %( rowspan="#{cell.rowspan}") :  | 
| 839 | 
            -
                          cell_style_attribute = (node.document.attr? 'cellbgcolor') ? %( style="background-color: #{node.document.attr 'cellbgcolor'};") :  | 
| 844 | 
            +
                          cell_colspan_attribute = cell.colspan ? %( colspan="#{cell.colspan}") : ''
         | 
| 845 | 
            +
                          cell_rowspan_attribute = cell.rowspan ? %( rowspan="#{cell.rowspan}") : ''
         | 
| 846 | 
            +
                          cell_style_attribute = (node.document.attr? 'cellbgcolor') ? %( style="background-color: #{node.document.attr 'cellbgcolor'};") : ''
         | 
| 840 847 | 
             
                          result << %(<#{cell_tag_name}#{cell_class_attribute}#{cell_colspan_attribute}#{cell_rowspan_attribute}#{cell_style_attribute}>#{cell_content}</#{cell_tag_name}>)
         | 
| 841 848 | 
             
                        end
         | 
| 842 849 | 
             
                        result << '</tr>'
         | 
| @@ -872,10 +879,9 @@ Your browser does not support the audio tag. | |
| 872 879 |  | 
| 873 880 | 
             
                def ulist node
         | 
| 874 881 | 
             
                  result = []
         | 
| 875 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 882 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 876 883 | 
             
                  div_classes = ['ulist', node.style, node.role].compact
         | 
| 877 | 
            -
                  marker_checked =  | 
| 878 | 
            -
                  marker_unchecked = nil
         | 
| 884 | 
            +
                  marker_checked = marker_unchecked = ''
         | 
| 879 885 | 
             
                  if (checklist = node.option? 'checklist')
         | 
| 880 886 | 
             
                    div_classes.unshift div_classes.shift, 'checklist'
         | 
| 881 887 | 
             
                    ul_class_attribute = ' class="checklist"'
         | 
| @@ -897,7 +903,7 @@ Your browser does not support the audio tag. | |
| 897 903 | 
             
                      end
         | 
| 898 904 | 
             
                    end
         | 
| 899 905 | 
             
                  else
         | 
| 900 | 
            -
                    ul_class_attribute = node.style ? %( class="#{node.style}") :  | 
| 906 | 
            +
                    ul_class_attribute = node.style ? %( class="#{node.style}") : ''
         | 
| 901 907 | 
             
                  end
         | 
| 902 908 | 
             
                  result << %(<div#{id_attribute} class="#{div_classes * ' '}">)
         | 
| 903 909 | 
             
                  result << %(<div class="title">#{node.title}</div>) if node.title?
         | 
| @@ -920,18 +926,18 @@ Your browser does not support the audio tag. | |
| 920 926 | 
             
                end
         | 
| 921 927 |  | 
| 922 928 | 
             
                def verse node
         | 
| 923 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 929 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 924 930 | 
             
                  classes = ['verseblock', node.role].compact
         | 
| 925 931 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 926 | 
            -
                  title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) :  | 
| 932 | 
            +
                  title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) : ''
         | 
| 927 933 | 
             
                  attribution = (node.attr? 'attribution') ? (node.attr 'attribution') : nil
         | 
| 928 934 | 
             
                  citetitle = (node.attr? 'citetitle') ? (node.attr 'citetitle') : nil
         | 
| 929 935 | 
             
                  if attribution || citetitle
         | 
| 930 | 
            -
                    cite_element = citetitle ? %(<cite>#{citetitle}</cite>) :  | 
| 931 | 
            -
                    attribution_text = attribution ? %(— #{attribution}#{citetitle ? "<br#{@void_element_slash}>\n" :  | 
| 936 | 
            +
                    cite_element = citetitle ? %(<cite>#{citetitle}</cite>) : ''
         | 
| 937 | 
            +
                    attribution_text = attribution ? %(— #{attribution}#{citetitle ? "<br#{@void_element_slash}>\n" : ''}) : ''
         | 
| 932 938 | 
             
                    attribution_element = %(\n<div class="attribution">\n#{attribution_text}#{cite_element}\n</div>)
         | 
| 933 939 | 
             
                  else
         | 
| 934 | 
            -
                    attribution_element =  | 
| 940 | 
            +
                    attribution_element = ''
         | 
| 935 941 | 
             
                  end
         | 
| 936 942 |  | 
| 937 943 | 
             
                  %(<div#{id_attribute}#{class_attribute}>#{title_element}
         | 
| @@ -941,25 +947,29 @@ Your browser does not support the audio tag. | |
| 941 947 |  | 
| 942 948 | 
             
                def video node
         | 
| 943 949 | 
             
                  xml = @xml_mode
         | 
| 944 | 
            -
                  id_attribute = node.id ? %( id="#{node.id}") :  | 
| 950 | 
            +
                  id_attribute = node.id ? %( id="#{node.id}") : ''
         | 
| 945 951 | 
             
                  classes = ['videoblock', node.role].compact
         | 
| 946 952 | 
             
                  class_attribute = %( class="#{classes * ' '}")
         | 
| 947 | 
            -
                  title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) :  | 
| 948 | 
            -
                  width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") :  | 
| 949 | 
            -
                  height_attribute = (node.attr? 'height') ? %( height="#{node.attr 'height'}") :  | 
| 953 | 
            +
                  title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) : ''
         | 
| 954 | 
            +
                  width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
         | 
| 955 | 
            +
                  height_attribute = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : ''
         | 
| 950 956 | 
             
                  case node.attr 'poster'
         | 
| 951 957 | 
             
                  when 'vimeo'
         | 
| 952 958 | 
             
                    unless (asset_uri_scheme = (node.document.attr 'asset-uri-scheme', 'https')).empty?
         | 
| 953 959 | 
             
                      asset_uri_scheme = %(#{asset_uri_scheme}:)
         | 
| 954 960 | 
             
                    end
         | 
| 955 | 
            -
                    start_anchor = (node.attr? 'start', nil, false) ? %(#at=#{node.attr 'start'}) :  | 
| 961 | 
            +
                    start_anchor = (node.attr? 'start', nil, false) ? %(#at=#{node.attr 'start'}) : ''
         | 
| 956 962 | 
             
                    delimiter = '?'
         | 
| 957 | 
            -
                     | 
| 958 | 
            -
             | 
| 959 | 
            -
             | 
| 963 | 
            +
                    if node.option? 'autoplay'
         | 
| 964 | 
            +
                      autoplay_param = %(#{delimiter}autoplay=1)
         | 
| 965 | 
            +
                      delimiter = '&'
         | 
| 966 | 
            +
                    else
         | 
| 967 | 
            +
                      autoplay_param = ''
         | 
| 968 | 
            +
                    end
         | 
| 969 | 
            +
                    loop_param = (node.option? 'loop') ? %(#{delimiter}loop=1) : ''
         | 
| 960 970 | 
             
                    %(<div#{id_attribute}#{class_attribute}>#{title_element}
         | 
| 961 971 | 
             
            <div class="content">
         | 
| 962 | 
            -
            <iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{node.attr 'target'}#{start_anchor}#{autoplay_param}#{loop_param}" frameborder="0"#{(node.option? 'nofullscreen') ?  | 
| 972 | 
            +
            <iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{node.attr 'target'}#{start_anchor}#{autoplay_param}#{loop_param}" frameborder="0"#{(node.option? 'nofullscreen') ? '' : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
         | 
| 963 973 | 
             
            </div>
         | 
| 964 974 | 
             
            </div>)
         | 
| 965 975 | 
             
                  when 'youtube'
         | 
| @@ -968,22 +978,22 @@ Your browser does not support the audio tag. | |
| 968 978 | 
             
                    end
         | 
| 969 979 | 
             
                    rel_param_val = (node.option? 'related') ? 1 : 0
         | 
| 970 980 | 
             
                    # NOTE start and end must be seconds (t parameter allows XmYs where X is minutes and Y is seconds)
         | 
| 971 | 
            -
                    start_param = (node.attr? 'start', nil, false) ? %(&start=#{node.attr 'start'}) :  | 
| 972 | 
            -
                    end_param = (node.attr? 'end', nil, false) ? %(&end=#{node.attr 'end'}) :  | 
| 973 | 
            -
                    autoplay_param = (node.option? 'autoplay') ? '&autoplay=1' :  | 
| 974 | 
            -
                    loop_param = (node.option? 'loop') ? '&loop=1' :  | 
| 975 | 
            -
                    controls_param = (node.option? 'nocontrols') ? '&controls=0' :  | 
| 981 | 
            +
                    start_param = (node.attr? 'start', nil, false) ? %(&start=#{node.attr 'start'}) : ''
         | 
| 982 | 
            +
                    end_param = (node.attr? 'end', nil, false) ? %(&end=#{node.attr 'end'}) : ''
         | 
| 983 | 
            +
                    autoplay_param = (node.option? 'autoplay') ? '&autoplay=1' : ''
         | 
| 984 | 
            +
                    loop_param = (has_loop_param = node.option? 'loop') ? '&loop=1' : ''
         | 
| 985 | 
            +
                    controls_param = (node.option? 'nocontrols') ? '&controls=0' : ''
         | 
| 976 986 | 
             
                    # cover both ways of controlling fullscreen option
         | 
| 977 987 | 
             
                    if node.option? 'nofullscreen'
         | 
| 978 988 | 
             
                      fs_param = '&fs=0'
         | 
| 979 | 
            -
                      fs_attribute =  | 
| 989 | 
            +
                      fs_attribute = ''
         | 
| 980 990 | 
             
                    else
         | 
| 981 | 
            -
                      fs_param =  | 
| 991 | 
            +
                      fs_param = ''
         | 
| 982 992 | 
             
                      fs_attribute = append_boolean_attribute 'allowfullscreen', xml
         | 
| 983 993 | 
             
                    end
         | 
| 984 | 
            -
                    modest_param = (node.option? 'modest') ? '&modestbranding=1' :  | 
| 985 | 
            -
                    theme_param = (node.attr? 'theme', nil, false) ? %(&theme=#{node.attr 'theme'}) :  | 
| 986 | 
            -
                    hl_param = (node.attr? 'lang') ? %(&hl=#{node.attr 'lang'}) :  | 
| 994 | 
            +
                    modest_param = (node.option? 'modest') ? '&modestbranding=1' : ''
         | 
| 995 | 
            +
                    theme_param = (node.attr? 'theme', nil, false) ? %(&theme=#{node.attr 'theme'}) : ''
         | 
| 996 | 
            +
                    hl_param = (node.attr? 'lang') ? %(&hl=#{node.attr 'lang'}) : ''
         | 
| 987 997 |  | 
| 988 998 | 
             
                    # parse video_id/list_id syntax where list_id (i.e., playlist) is optional
         | 
| 989 999 | 
             
                    target, list = (node.attr 'target').split '/', 2
         | 
| @@ -997,7 +1007,7 @@ Your browser does not support the audio tag. | |
| 997 1007 | 
             
                        list_param = %(&playlist=#{playlist})
         | 
| 998 1008 | 
             
                      else
         | 
| 999 1009 | 
             
                        # NOTE for loop to work, playlist must be specified; use VIDEO_ID if there's no explicit playlist
         | 
| 1000 | 
            -
                        list_param =  | 
| 1010 | 
            +
                        list_param = has_loop_param ? %(&playlist=#{target}) : ''
         | 
| 1001 1011 | 
             
                      end
         | 
| 1002 1012 | 
             
                    end
         | 
| 1003 1013 |  | 
| @@ -1007,14 +1017,14 @@ Your browser does not support the audio tag. | |
| 1007 1017 | 
             
            </div>
         | 
| 1008 1018 | 
             
            </div>)
         | 
| 1009 1019 | 
             
                  else
         | 
| 1010 | 
            -
                    poster_attribute = (val = node.attr 'poster', nil, false).nil_or_empty? ?  | 
| 1011 | 
            -
                    preload_attribute = (val = node.attr 'preload', nil, false).nil_or_empty? ?  | 
| 1020 | 
            +
                    poster_attribute = (val = node.attr 'poster', nil, false).nil_or_empty? ? '' : %( poster="#{node.media_uri val}")
         | 
| 1021 | 
            +
                    preload_attribute = (val = node.attr 'preload', nil, false).nil_or_empty? ? '' : %( preload="#{val}")
         | 
| 1012 1022 | 
             
                    start_t = node.attr 'start', nil, false
         | 
| 1013 1023 | 
             
                    end_t = node.attr 'end', nil, false
         | 
| 1014 | 
            -
                    time_anchor = (start_t || end_t) ? %(#t=#{start_t}#{end_t ?  | 
| 1024 | 
            +
                    time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : ''
         | 
| 1015 1025 | 
             
                    %(<div#{id_attribute}#{class_attribute}>#{title_element}
         | 
| 1016 1026 | 
             
            <div class="content">
         | 
| 1017 | 
            -
            <video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) :  | 
| 1027 | 
            +
            <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}>
         | 
| 1018 1028 | 
             
            Your browser does not support the video tag.
         | 
| 1019 1029 | 
             
            </video>
         | 
| 1020 1030 | 
             
            </div>
         | 
| @@ -1025,29 +1035,34 @@ Your browser does not support the video tag. | |
| 1025 1035 | 
             
                def inline_anchor node
         | 
| 1026 1036 | 
             
                  case node.type
         | 
| 1027 1037 | 
             
                  when :xref
         | 
| 1028 | 
            -
                     | 
| 1029 | 
            -
                       | 
| 1030 | 
            -
             | 
| 1031 | 
            -
             | 
| 1032 | 
            -
             | 
| 1038 | 
            +
                    if (path = node.attributes['path'])
         | 
| 1039 | 
            +
                      attrs = (append_link_constraint_attrs node, node.role ? [%( class="#{node.role}")] : []).join
         | 
| 1040 | 
            +
                      text = node.text || path
         | 
| 1041 | 
            +
                    else
         | 
| 1042 | 
            +
                      attrs = node.role ? %( class="#{node.role}") : ''
         | 
| 1043 | 
            +
                      unless (text = node.text)
         | 
| 1044 | 
            +
                        refid = node.attributes['refid']
         | 
| 1045 | 
            +
                        if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
         | 
| 1046 | 
            +
                          text = (ref.xreftext node.attr('xrefstyle')) || %([#{refid}])
         | 
| 1047 | 
            +
                        else
         | 
| 1048 | 
            +
                          text = %([#{refid}])
         | 
| 1049 | 
            +
                        end
         | 
| 1033 1050 | 
             
                      end
         | 
| 1034 1051 | 
             
                    end
         | 
| 1035 | 
            -
                    %(<a href="#{node.target}">#{text}</a>)
         | 
| 1052 | 
            +
                    %(<a href="#{node.target}"#{attrs}>#{text}</a>)
         | 
| 1036 1053 | 
             
                  when :ref
         | 
| 1037 1054 | 
             
                    %(<a id="#{node.id}"></a>)
         | 
| 1038 1055 | 
             
                  when :link
         | 
| 1039 1056 | 
             
                    attrs = node.id ? [%( id="#{node.id}")] : []
         | 
| 1040 | 
            -
                     | 
| 1041 | 
            -
                      attrs << %( class="#{role}")
         | 
| 1042 | 
            -
                    end
         | 
| 1057 | 
            +
                    attrs << %( class="#{node.role}") if node.role
         | 
| 1043 1058 | 
             
                    attrs << %( title="#{node.attr 'title'}") if node.attr? 'title', nil, false
         | 
| 1044 | 
            -
                     | 
| 1045 | 
            -
                    %(<a href="#{node.target}"#{attrs.join}>#{node.text}</a>)
         | 
| 1059 | 
            +
                    %(<a href="#{node.target}"#{(append_link_constraint_attrs node, attrs).join}>#{node.text}</a>)
         | 
| 1046 1060 | 
             
                  when :bibref
         | 
| 1047 1061 | 
             
                    # NOTE technically node.text should be node.reftext, but subs have already been applied to text
         | 
| 1048 1062 | 
             
                    %(<a id="#{node.id}"></a>#{node.text})
         | 
| 1049 1063 | 
             
                  else
         | 
| 1050 | 
            -
                    warn %( | 
| 1064 | 
            +
                    logger.warn %(unknown anchor type: #{node.type.inspect})
         | 
| 1065 | 
            +
                    nil
         | 
| 1051 1066 | 
             
                  end
         | 
| 1052 1067 | 
             
                end
         | 
| 1053 1068 |  | 
| @@ -1073,10 +1088,10 @@ Your browser does not support the video tag. | |
| 1073 1088 | 
             
                def inline_footnote node
         | 
| 1074 1089 | 
             
                  if (index = node.attr 'index', nil, false)
         | 
| 1075 1090 | 
             
                    if node.type == :xref
         | 
| 1076 | 
            -
                      %(<sup class="footnoteref">[<a class="footnote" href="# | 
| 1091 | 
            +
                      %(<sup class="footnoteref">[<a class="footnote" href="#_footnotedef_#{index}" title="View footnote.">#{index}</a>]</sup>)
         | 
| 1077 1092 | 
             
                    else
         | 
| 1078 | 
            -
                      id_attr = node.id ? %( id="_footnote_#{node.id}") :  | 
| 1079 | 
            -
                      %(<sup class="footnote"#{id_attr}>[<a id="_footnoteref_#{index}" class="footnote" href="# | 
| 1093 | 
            +
                      id_attr = node.id ? %( id="_footnote_#{node.id}") : ''
         | 
| 1094 | 
            +
                      %(<sup class="footnote"#{id_attr}>[<a id="_footnoteref_#{index}" class="footnote" href="#_footnotedef_#{index}" title="View footnote.">#{index}</a>]</sup>)
         | 
| 1080 1095 | 
             
                    end
         | 
| 1081 1096 | 
             
                  elsif node.type == :xref
         | 
| 1082 1097 | 
             
                    %(<sup class="footnoteref red" title="Unresolved footnote reference.">[#{node.text}]</sup>)
         | 
| @@ -1089,13 +1104,13 @@ Your browser does not support the video tag. | |
| 1089 1104 | 
             
                    {'size' => 'fa-', 'rotate' => 'fa-rotate-', 'flip' => 'fa-flip-'}.each do |key, prefix|
         | 
| 1090 1105 | 
             
                      class_attr_val = %(#{class_attr_val} #{prefix}#{node.attr key}) if node.attr? key
         | 
| 1091 1106 | 
             
                    end
         | 
| 1092 | 
            -
                    title_attr = (node.attr? 'title') ? %( title="#{node.attr 'title'}") :  | 
| 1107 | 
            +
                    title_attr = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : ''
         | 
| 1093 1108 | 
             
                    img = %(<i class="#{class_attr_val}"#{title_attr}></i>)
         | 
| 1094 1109 | 
             
                  elsif type == 'icon' && !(node.document.attr? 'icons')
         | 
| 1095 1110 | 
             
                    img = %([#{node.alt}])
         | 
| 1096 1111 | 
             
                  else
         | 
| 1097 1112 | 
             
                    target = node.target
         | 
| 1098 | 
            -
                    attrs = ['width', 'height', 'title'].map {|name| (node.attr? name) ? %( #{name}="#{node.attr name}") :  | 
| 1113 | 
            +
                    attrs = ['width', 'height', 'title'].map {|name| (node.attr? name) ? %( #{name}="#{node.attr name}") : '' }.join
         | 
| 1099 1114 | 
             
                    if type != 'icon' && ((node.attr? 'format', 'svg', false) || (target.include? '.svg')) &&
         | 
| 1100 1115 | 
             
                        node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
         | 
| 1101 1116 | 
             
                      if svg
         | 
| @@ -1107,12 +1122,11 @@ Your browser does not support the video tag. | |
| 1107 1122 | 
             
                    end
         | 
| 1108 1123 | 
             
                    img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_quotes node.alt}"#{attrs}#{@void_element_slash}>)
         | 
| 1109 1124 | 
             
                  end
         | 
| 1110 | 
            -
                  if node.attr? 'link'
         | 
| 1111 | 
            -
                     | 
| 1112 | 
            -
                    img = %(<a class="image" href="#{node.attr 'link'}"#{window_attr}>#{img}</a>)
         | 
| 1125 | 
            +
                  if node.attr? 'link', nil, false
         | 
| 1126 | 
            +
                    img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
         | 
| 1113 1127 | 
             
                  end
         | 
| 1114 1128 | 
             
                  class_attr_val = (role = node.role) ? %(#{type} #{role}) : type
         | 
| 1115 | 
            -
                  style_attr = (node.attr? 'float') ? %( style="float: #{node.attr 'float'}") :  | 
| 1129 | 
            +
                  style_attr = (node.attr? 'float') ? %( style="float: #{node.attr 'float'}") : ''
         | 
| 1116 1130 | 
             
                  %(<span class="#{class_attr_val}"#{style_attr}>#{img}</span>)
         | 
| 1117 1131 | 
             
                end
         | 
| 1118 1132 |  | 
| @@ -1145,17 +1159,17 @@ Your browser does not support the video tag. | |
| 1145 1159 |  | 
| 1146 1160 | 
             
                def inline_quoted node
         | 
| 1147 1161 | 
             
                  open, close, is_tag = QUOTE_TAGS[node.type]
         | 
| 1148 | 
            -
                  if node.role
         | 
| 1162 | 
            +
                  class_attr = %( class="#{node.role}") if node.role
         | 
| 1163 | 
            +
                  id_attr = %( id="#{node.id}") if node.id
         | 
| 1164 | 
            +
                  if class_attr || id_attr
         | 
| 1149 1165 | 
             
                    if is_tag
         | 
| 1150 | 
            -
                       | 
| 1166 | 
            +
                      %(#{open.chop}#{id_attr || ''}#{class_attr || ''}>#{node.text}#{close})
         | 
| 1151 1167 | 
             
                    else
         | 
| 1152 | 
            -
                       | 
| 1168 | 
            +
                      %(<span#{id_attr || ''}#{class_attr || ''}>#{open}#{node.text}#{close}</span>)
         | 
| 1153 1169 | 
             
                    end
         | 
| 1154 1170 | 
             
                  else
         | 
| 1155 | 
            -
                     | 
| 1171 | 
            +
                    %(#{open}#{node.text}#{close})
         | 
| 1156 1172 | 
             
                  end
         | 
| 1157 | 
            -
             | 
| 1158 | 
            -
                  node.id ? %(<a id="#{node.id}"></a>#{quoted_text}) : quoted_text
         | 
| 1159 1173 | 
             
                end
         | 
| 1160 1174 |  | 
| 1161 1175 | 
             
                def append_boolean_attribute name, xml
         | 
| @@ -1166,6 +1180,29 @@ Your browser does not support the video tag. | |
| 1166 1180 | 
             
                  (val.include? '"') ? (val.gsub '"', '"') : val
         | 
| 1167 1181 | 
             
                end
         | 
| 1168 1182 |  | 
| 1183 | 
            +
                def generate_manname_section node
         | 
| 1184 | 
            +
                  manname_title = (node.attr 'manname-title') || 'Name'
         | 
| 1185 | 
            +
                  if (next_section = node.sections[0]) && (next_section_title = next_section.title) == next_section_title.upcase
         | 
| 1186 | 
            +
                    manname_title = manname_title.upcase
         | 
| 1187 | 
            +
                  end
         | 
| 1188 | 
            +
                  manname_id_attr = (manname_id = node.attr 'manname-id') ? %( id="#{manname_id}") : ''
         | 
| 1189 | 
            +
                  %(<h2#{manname_id_attr}>#{manname_title}</h2>
         | 
| 1190 | 
            +
            <div class="sectionbody">
         | 
| 1191 | 
            +
            <p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
         | 
| 1192 | 
            +
            </div>)
         | 
| 1193 | 
            +
                end
         | 
| 1194 | 
            +
             | 
| 1195 | 
            +
                def append_link_constraint_attrs node, attrs = []
         | 
| 1196 | 
            +
                  rel = 'nofollow' if node.option? 'nofollow'
         | 
| 1197 | 
            +
                  if (window = node.attributes['window'])
         | 
| 1198 | 
            +
                    attrs << %( target="#{window}")
         | 
| 1199 | 
            +
                    attrs << (rel ? %( rel="#{rel} noopener") : ' rel="noopener"') if window == '_blank' || (node.option? 'noopener')
         | 
| 1200 | 
            +
                  elsif rel
         | 
| 1201 | 
            +
                    attrs << %( rel="#{rel}")
         | 
| 1202 | 
            +
                  end
         | 
| 1203 | 
            +
                  attrs
         | 
| 1204 | 
            +
                end
         | 
| 1205 | 
            +
             | 
| 1169 1206 | 
             
                def read_svg_contents node, target
         | 
| 1170 1207 | 
             
                  if (svg = node.read_contents target, :start => (node.document.attr 'imagesdir'), :normalize => true, :label => 'SVG')
         | 
| 1171 1208 | 
             
                    svg = svg.sub SvgPreambleRx, '' unless svg.start_with? '<svg'
         |