asciidoctor 2.0.10 → 2.0.17

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +294 -30
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +16 -20
  5. data/README-fr.adoc +15 -22
  6. data/README-jp.adoc +15 -26
  7. data/README-zh_CN.adoc +21 -25
  8. data/README.adoc +161 -138
  9. data/asciidoctor.gemspec +6 -13
  10. data/data/locale/attributes-ar.adoc +4 -3
  11. data/data/locale/attributes-be.adoc +23 -0
  12. data/data/locale/attributes-bg.adoc +4 -3
  13. data/data/locale/attributes-ca.adoc +6 -5
  14. data/data/locale/attributes-cs.adoc +4 -3
  15. data/data/locale/attributes-da.adoc +6 -5
  16. data/data/locale/attributes-de.adoc +4 -4
  17. data/data/locale/attributes-en.adoc +4 -4
  18. data/data/locale/attributes-es.adoc +6 -5
  19. data/data/locale/attributes-fa.adoc +4 -3
  20. data/data/locale/attributes-fi.adoc +4 -3
  21. data/data/locale/attributes-fr.adoc +8 -7
  22. data/data/locale/attributes-hu.adoc +4 -3
  23. data/data/locale/attributes-id.adoc +4 -3
  24. data/data/locale/attributes-it.adoc +6 -5
  25. data/data/locale/attributes-ja.adoc +4 -3
  26. data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
  27. data/data/locale/attributes-nb.adoc +4 -3
  28. data/data/locale/attributes-nl.adoc +6 -5
  29. data/data/locale/attributes-nn.adoc +4 -3
  30. data/data/locale/attributes-pl.adoc +8 -7
  31. data/data/locale/attributes-pt.adoc +6 -5
  32. data/data/locale/attributes-pt_BR.adoc +6 -5
  33. data/data/locale/attributes-ro.adoc +4 -3
  34. data/data/locale/attributes-ru.adoc +6 -5
  35. data/data/locale/attributes-sr.adoc +4 -4
  36. data/data/locale/attributes-sr_Latn.adoc +4 -4
  37. data/data/locale/attributes-sv.adoc +4 -4
  38. data/data/locale/attributes-th.adoc +23 -0
  39. data/data/locale/attributes-tr.adoc +4 -3
  40. data/data/locale/attributes-uk.adoc +6 -5
  41. data/data/locale/attributes-vi.adoc +23 -0
  42. data/data/locale/attributes-zh_CN.adoc +4 -3
  43. data/data/locale/attributes-zh_TW.adoc +4 -3
  44. data/data/reference/syntax.adoc +14 -7
  45. data/data/stylesheets/asciidoctor-default.css +76 -76
  46. data/data/stylesheets/coderay-asciidoctor.css +9 -9
  47. data/lib/asciidoctor/abstract_block.rb +20 -13
  48. data/lib/asciidoctor/abstract_node.rb +23 -12
  49. data/lib/asciidoctor/attribute_list.rb +64 -72
  50. data/lib/asciidoctor/block.rb +6 -6
  51. data/lib/asciidoctor/cli/invoker.rb +3 -2
  52. data/lib/asciidoctor/cli/options.rb +32 -31
  53. data/lib/asciidoctor/convert.rb +168 -162
  54. data/lib/asciidoctor/converter/docbook5.rb +49 -34
  55. data/lib/asciidoctor/converter/html5.rb +180 -139
  56. data/lib/asciidoctor/converter/manpage.rb +118 -90
  57. data/lib/asciidoctor/converter/template.rb +15 -13
  58. data/lib/asciidoctor/converter.rb +19 -16
  59. data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
  60. data/lib/asciidoctor/document.rb +77 -86
  61. data/lib/asciidoctor/extensions.rb +22 -16
  62. data/lib/asciidoctor/helpers.rb +20 -15
  63. data/lib/asciidoctor/list.rb +2 -6
  64. data/lib/asciidoctor/load.rb +103 -101
  65. data/lib/asciidoctor/logging.rb +10 -8
  66. data/lib/asciidoctor/parser.rb +211 -220
  67. data/lib/asciidoctor/path_resolver.rb +17 -15
  68. data/lib/asciidoctor/reader.rb +87 -79
  69. data/lib/asciidoctor/rx.rb +9 -7
  70. data/lib/asciidoctor/section.rb +7 -0
  71. data/lib/asciidoctor/substitutors.rb +167 -148
  72. data/lib/asciidoctor/syntax_highlighter/coderay.rb +3 -2
  73. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +13 -5
  74. data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
  75. data/lib/asciidoctor/syntax_highlighter/pygments.rb +19 -11
  76. data/lib/asciidoctor/syntax_highlighter/rouge.rb +35 -20
  77. data/lib/asciidoctor/syntax_highlighter.rb +16 -16
  78. data/lib/asciidoctor/table.rb +70 -43
  79. data/lib/asciidoctor/timings.rb +3 -3
  80. data/lib/asciidoctor/version.rb +1 -1
  81. data/lib/asciidoctor.rb +45 -19
  82. data/man/asciidoctor.1 +29 -31
  83. data/man/asciidoctor.adoc +35 -29
  84. metadata +17 -70
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  # A built-in {Converter} implementation that generates HTML 5 output
4
- # consistent with the html5 backend from AsciiDoc Python.
4
+ # consistent with the html5 backend from AsciiDoc.py.
5
5
  class Converter::Html5Converter < Converter::Base
6
6
  register_for 'html5'
7
7
 
@@ -21,15 +21,15 @@ class Converter::Html5Converter < Converter::Base
21
21
  #latexmath: INLINE_MATH_DELIMITERS[:latexmath] + [false],
22
22
  }).default = ['', '']
23
23
 
24
- DropAnchorRx = /<(?:a[^>+]+|\/a)>/
24
+ DropAnchorRx = %r(<(?:a\b[^>]*|/a)>)
25
25
  StemBreakRx = / *\\\n(?:\\?\n)*|\n\n+/
26
26
  if RUBY_ENGINE == 'opal'
27
27
  # NOTE In JavaScript, ^ matches the start of the string when the m flag is not set
28
- SvgPreambleRx = /^#{CC_ALL}*?(?=<svg\b)/
29
- SvgStartTagRx = /^<svg[^>]*>/
28
+ SvgPreambleRx = /^#{CC_ALL}*?(?=<svg[\s>])/
29
+ SvgStartTagRx = /^<svg(?:\s[^>]*)?>/
30
30
  else
31
- SvgPreambleRx = /\A.*?(?=<svg\b)/m
32
- SvgStartTagRx = /\A<svg[^>]*>/
31
+ SvgPreambleRx = /\A.*?(?=<svg[\s>])/m
32
+ SvgStartTagRx = /\A<svg(?:\s[^>]*)?>/
33
33
  end
34
34
  DimensionAttributeRx = /\s(?:width|height|style)=(["'])#{CC_ANY}*?\1/
35
35
 
@@ -48,44 +48,45 @@ class Converter::Html5Converter < Converter::Base
48
48
  end
49
49
 
50
50
  def convert node, transform = node.node_name, opts = nil
51
- if transform == 'inline_quoted'; return convert_inline_quoted node
52
- elsif transform == 'paragraph'; return convert_paragraph node
53
- elsif transform == 'inline_anchor'; return convert_inline_anchor node
54
- elsif transform == 'section'; return convert_section node
55
- elsif transform == 'listing'; return convert_listing node
56
- elsif transform == 'literal'; return convert_literal node
57
- elsif transform == 'ulist'; return convert_ulist node
58
- elsif transform == 'olist'; return convert_olist node
59
- elsif transform == 'dlist'; return convert_dlist node
60
- elsif transform == 'admonition'; return convert_admonition node
61
- elsif transform == 'colist'; return convert_colist node
62
- elsif transform == 'embedded'; return convert_embedded node
63
- elsif transform == 'example'; return convert_example node
64
- elsif transform == 'floating_title'; return convert_floating_title node
65
- elsif transform == 'image'; return convert_image node
66
- elsif transform == 'inline_break'; return convert_inline_break node
67
- elsif transform == 'inline_button'; return convert_inline_button node
68
- elsif transform == 'inline_callout'; return convert_inline_callout node
69
- elsif transform == 'inline_footnote'; return convert_inline_footnote node
70
- elsif transform == 'inline_image'; return convert_inline_image node
71
- elsif transform == 'inline_indexterm'; return convert_inline_indexterm node
72
- elsif transform == 'inline_kbd'; return convert_inline_kbd node
73
- elsif transform == 'inline_menu'; return convert_inline_menu node
74
- elsif transform == 'open'; return convert_open node
75
- elsif transform == 'page_break'; return convert_page_break node
76
- elsif transform == 'preamble'; return convert_preamble node
77
- elsif transform == 'quote'; return convert_quote node
78
- elsif transform == 'sidebar'; return convert_sidebar node
79
- elsif transform == 'stem'; return convert_stem node
80
- elsif transform == 'table'; return convert_table node
81
- elsif transform == 'thematic_break'; return convert_thematic_break node
82
- elsif transform == 'verse'; return convert_verse node
83
- elsif transform == 'video'; return convert_video node
84
- elsif transform == 'document'; return convert_document node
85
- elsif transform == 'toc'; return convert_toc node
86
- elsif transform == 'pass'; return convert_pass node
87
- elsif transform == 'audio'; return convert_audio node
88
- else; return super
51
+ case transform
52
+ when 'inline_quoted' then convert_inline_quoted node
53
+ when 'paragraph' then convert_paragraph node
54
+ when 'inline_anchor' then convert_inline_anchor node
55
+ when 'section' then convert_section node
56
+ when 'listing' then convert_listing node
57
+ when 'literal' then convert_literal node
58
+ when 'ulist' then convert_ulist node
59
+ when 'olist' then convert_olist node
60
+ when 'dlist' then convert_dlist node
61
+ when 'admonition' then convert_admonition node
62
+ when 'colist' then convert_colist node
63
+ when 'embedded' then convert_embedded node
64
+ when 'example' then convert_example node
65
+ when 'floating_title' then convert_floating_title node
66
+ when 'image' then convert_image node
67
+ when 'inline_break' then convert_inline_break node
68
+ when 'inline_button' then convert_inline_button node
69
+ when 'inline_callout' then convert_inline_callout node
70
+ when 'inline_footnote' then convert_inline_footnote node
71
+ when 'inline_image' then convert_inline_image node
72
+ when 'inline_indexterm' then convert_inline_indexterm node
73
+ when 'inline_kbd' then convert_inline_kbd node
74
+ when 'inline_menu' then convert_inline_menu node
75
+ when 'open' then convert_open node
76
+ when 'page_break' then convert_page_break node
77
+ when 'preamble' then convert_preamble node
78
+ when 'quote' then convert_quote node
79
+ when 'sidebar' then convert_sidebar node
80
+ when 'stem' then convert_stem node
81
+ when 'table' then convert_table node
82
+ when 'thematic_break' then convert_thematic_break node
83
+ when 'verse' then convert_verse node
84
+ when 'video' then convert_video node
85
+ when 'document' then convert_document node
86
+ when 'toc' then convert_toc node
87
+ when 'pass' then convert_pass node
88
+ when 'audio' then convert_audio node
89
+ else; super
89
90
  end
90
91
  end
91
92
 
@@ -96,6 +97,7 @@ class Converter::Html5Converter < Converter::Base
96
97
  end
97
98
  cdn_base_url = %(#{asset_uri_scheme}//cdnjs.cloudflare.com/ajax/libs)
98
99
  linkcss = node.attr? 'linkcss'
100
+ max_width_attr = (node.attr? 'max-width') ? %( style="max-width: #{node.attr 'max-width'};") : ''
99
101
  result = ['<!DOCTYPE html>']
100
102
  lang_attribute = (node.attr? 'nolang') ? '' : %( lang="#{node.attr 'lang', 'en'}")
101
103
  result << %(<html#{@xml_mode ? ' xmlns="http://www.w3.org/1999/xhtml"' : ''}#{lang_attribute}>)
@@ -138,7 +140,7 @@ class Converter::Html5Converter < Converter::Base
138
140
  result << %(<link rel="stylesheet" href="#{node.normalize_web_path((node.attr 'stylesheet'), (node.attr 'stylesdir', ''))}"#{slash}>)
139
141
  else
140
142
  result << %(<style>
141
- #{node.read_asset node.normalize_system_path((node.attr 'stylesheet'), (node.attr 'stylesdir', '')), warn_on_failure: true, label: 'stylesheet'}
143
+ #{node.read_contents (node.attr 'stylesheet'), start: (node.attr 'stylesdir'), warn_on_failure: true, label: 'stylesheet'}
142
144
  </style>)
143
145
  end
144
146
  end
@@ -152,8 +154,8 @@ class Converter::Html5Converter < Converter::Base
152
154
  end
153
155
  end
154
156
 
155
- if (syntax_hl = node.syntax_highlighter) && (syntax_hl.docinfo? :head)
156
- result << (syntax_hl.docinfo :head, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash)
157
+ if (syntax_hl = node.syntax_highlighter)
158
+ result << (syntax_hl_docinfo_head_idx = result.size)
157
159
  end
158
160
 
159
161
  unless (docinfo_content = node.docinfo).empty?
@@ -161,29 +163,27 @@ class Converter::Html5Converter < Converter::Base
161
163
  end
162
164
 
163
165
  result << '</head>'
164
- body_attrs = node.id ? [%(id="#{node.id}")] : []
166
+ id_attr = node.id ? %( id="#{node.id}") : ''
165
167
  if (sectioned = node.sections?) && (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
166
168
  classes = [node.doctype, (node.attr 'toc-class'), %(toc-#{node.attr 'toc-position', 'header'})]
167
169
  else
168
170
  classes = [node.doctype]
169
171
  end
170
172
  classes << node.role if node.role?
171
- body_attrs << %(class="#{classes.join ' '}")
172
- body_attrs << %(style="max-width: #{node.attr 'max-width'};") if node.attr? 'max-width'
173
- result << %(<body #{body_attrs.join ' '}>)
173
+ result << %(<body#{id_attr} class="#{classes.join ' '}">)
174
174
 
175
175
  unless (docinfo_content = node.docinfo :header).empty?
176
176
  result << docinfo_content
177
177
  end
178
178
 
179
179
  unless node.noheader
180
- result << '<div id="header">'
180
+ result << %(<div id="header"#{max_width_attr}>)
181
181
  if node.doctype == 'manpage'
182
182
  result << %(<h1>#{node.doctitle} Manual Page</h1>)
183
183
  if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
184
184
  result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
185
185
  <div id="toctitle">#{node.attr 'toc-title'}</div>
186
- #{convert_outline node}
186
+ #{node.converter.convert node, 'outline'}
187
187
  </div>)
188
188
  end
189
189
  result << (generate_manname_section node) if node.attr? 'manpurpose'
@@ -216,19 +216,19 @@ class Converter::Html5Converter < Converter::Base
216
216
  if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
217
217
  result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
218
218
  <div id="toctitle">#{node.attr 'toc-title'}</div>
219
- #{convert_outline node}
219
+ #{node.converter.convert node, 'outline'}
220
220
  </div>)
221
221
  end
222
222
  end
223
223
  result << '</div>'
224
224
  end
225
225
 
226
- result << %(<div id="content">
226
+ result << %(<div id="content"#{max_width_attr}>
227
227
  #{node.content}
228
228
  </div>)
229
229
 
230
230
  if node.footnotes? && !(node.attr? 'nofootnotes')
231
- result << %(<div id="footnotes">
231
+ result << %(<div id="footnotes"#{max_width_attr}>
232
232
  <hr#{slash}>)
233
233
  node.footnotes.each do |footnote|
234
234
  result << %(<div class="footnote" id="_footnotedef_#{footnote.index}">
@@ -239,7 +239,7 @@ class Converter::Html5Converter < Converter::Base
239
239
  end
240
240
 
241
241
  unless node.nofooter
242
- result << '<div id="footer">'
242
+ result << %(<div id="footer"#{max_width_attr}>)
243
243
  result << '<div id="footer-text">'
244
244
  result << %(#{node.attr 'version-label'} #{node.attr 'revnumber'}#{br}) if node.attr? 'revnumber'
245
245
  result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'}) if (node.attr? 'last-update-label') && !(node.attr? 'reproducible')
@@ -250,8 +250,15 @@ class Converter::Html5Converter < Converter::Base
250
250
  # JavaScript (and auxiliary stylesheets) loaded at the end of body for performance reasons
251
251
  # See http://www.html5rocks.com/en/tutorials/speed/script-loading/
252
252
 
253
- if syntax_hl && (syntax_hl.docinfo? :footer)
254
- result << (syntax_hl.docinfo :footer, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash)
253
+ if syntax_hl
254
+ if syntax_hl.docinfo? :head
255
+ result[syntax_hl_docinfo_head_idx] = syntax_hl.docinfo :head, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash
256
+ else
257
+ result.delete_at syntax_hl_docinfo_head_idx
258
+ end
259
+ if syntax_hl.docinfo? :footer
260
+ result << (syntax_hl.docinfo :footer, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash)
261
+ end
255
262
  end
256
263
 
257
264
  if node.attr? 'stem'
@@ -275,7 +282,7 @@ MathJax.Hub.Config({
275
282
  })
276
283
  MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
277
284
  MathJax.InputJax.AsciiMath.postfilterHooks.Add(function (data, node) {
278
- if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains('stemblock')) {
285
+ if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains("stemblock")) {
279
286
  data.math.root.display = "block"
280
287
  }
281
288
  return data
@@ -311,7 +318,7 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
311
318
  if node.sections? && (node.attr? 'toc') && (toc_p = node.attr 'toc-placement') != 'macro' && toc_p != 'preamble'
312
319
  result << %(<div id="toc" class="toc">
313
320
  <div id="toctitle">#{node.attr 'toc-title'}</div>
314
- #{convert_outline node}
321
+ #{node.converter.convert node, 'outline'}
315
322
  </div>)
316
323
  end
317
324
 
@@ -344,9 +351,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
344
351
  stitle = section.captioned_title
345
352
  elsif section.numbered && slevel <= sectnumlevels
346
353
  if slevel < 2 && node.document.doctype == 'book'
347
- if section.sectname == 'chapter'
354
+ case section.sectname
355
+ when 'chapter'
348
356
  stitle = %(#{(signifier = node.document.attributes['chapter-signifier']) ? "#{signifier} " : ''}#{section.sectnum} #{section.title})
349
- elsif section.sectname == 'part'
357
+ when 'part'
350
358
  stitle = %(#{(signifier = node.document.attributes['part-signifier']) ? "#{signifier} " : ''}#{section.sectnum nil, ':'} #{section.title})
351
359
  else
352
360
  stitle = %(#{section.sectnum} #{section.title})
@@ -377,9 +385,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
377
385
  title = node.captioned_title
378
386
  elsif node.numbered && level <= (doc_attrs['sectnumlevels'] || 3).to_i
379
387
  if level < 2 && node.document.doctype == 'book'
380
- if node.sectname == 'chapter'
388
+ case node.sectname
389
+ when 'chapter'
381
390
  title = %(#{(signifier = doc_attrs['chapter-signifier']) ? "#{signifier} " : ''}#{node.sectnum} #{node.title})
382
- elsif node.sectname == 'part'
391
+ when 'part'
383
392
  title = %(#{(signifier = doc_attrs['part-signifier']) ? "#{signifier} " : ''}#{node.sectnum nil, ':'} #{node.title})
384
393
  else
385
394
  title = %(#{node.sectnum} #{node.title})
@@ -507,16 +516,16 @@ Your browser does not support the audio tag.
507
516
  result = []
508
517
  id_attribute = node.id ? %( id="#{node.id}") : ''
509
518
 
510
- classes = case node.style
519
+ case node.style
511
520
  when 'qanda'
512
- ['qlist', 'qanda', node.role]
521
+ classes = ['qlist', 'qanda', node.role]
513
522
  when 'horizontal'
514
- ['hdlist', node.role]
523
+ classes = ['hdlist', node.role]
515
524
  else
516
- ['dlist', node.style, node.role]
517
- end.compact
525
+ classes = ['dlist', node.style, node.role]
526
+ end
518
527
 
519
- class_attribute = %( class="#{classes.join ' '}")
528
+ class_attribute = %( class="#{classes.compact.join ' '}")
520
529
 
521
530
  result << %(<div#{id_attribute}#{class_attribute}>)
522
531
  result << %(<div class="title">#{node.title}</div>) if node.title?
@@ -572,12 +581,11 @@ Your browser does not support the audio tag.
572
581
  terms.each do |dt|
573
582
  result << %(<dt#{dt_style_attribute}>#{dt.text}</dt>)
574
583
  end
575
- if dd
576
- result << '<dd>'
577
- result << %(<p>#{dd.text}</p>) if dd.text?
578
- result << dd.content if dd.blocks?
579
- result << '</dd>'
580
- end
584
+ next unless dd
585
+ result << '<dd>'
586
+ result << %(<p>#{dd.text}</p>) if dd.text?
587
+ result << dd.content if dd.blocks?
588
+ result << '</dd>'
581
589
  end
582
590
  result << '</dl>'
583
591
  end
@@ -618,19 +626,19 @@ Your browser does not support the audio tag.
618
626
  target = node.attr 'target'
619
627
  width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
620
628
  height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : ''
621
- if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE &&
622
- ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
623
- if svg
629
+ if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
630
+ if node.option? 'inline'
624
631
  img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>)
625
- elsif obj
626
- fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>)
632
+ elsif node.option? 'interactive'
633
+ fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri node.attr 'fallback'}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>)
627
634
  img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{width_attr}#{height_attr}>#{fallback}</object>)
635
+ else
636
+ img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
628
637
  end
638
+ else
639
+ img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
629
640
  end
630
- img ||= %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
631
- if node.attr? 'link'
632
- img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
633
- end
641
+ img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
634
642
  id_attr = node.id ? %( id="#{node.id}") : ''
635
643
  classes = ['imageblock']
636
644
  classes << (node.attr 'float') if node.attr? 'float'
@@ -689,8 +697,8 @@ Your browser does not support the audio tag.
689
697
  open, close = BLOCK_MATH_DELIMITERS[style = node.style.to_sym]
690
698
  if (equation = node.content)
691
699
  if style == :asciimath && (equation.include? LF)
692
- br = %(<br#{@void_element_slash}>#{LF})
693
- equation = equation.gsub(StemBreakRx) { %(#{close}#{br * ($&.count LF)}#{open}) }
700
+ br = %(#{LF}<br#{@void_element_slash}>)
701
+ equation = equation.gsub(StemBreakRx) { %(#{close}#{br * (($&.count LF) - 1)}#{LF}#{open}) }
694
702
  end
695
703
  unless (equation.start_with? open) && (equation.end_with? close)
696
704
  equation = %(#{open}#{equation}#{close})
@@ -755,8 +763,8 @@ Your browser does not support the audio tag.
755
763
  logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
756
764
  ''
757
765
  else
758
- id_attr = node.id ? %( id="#{node.id}") : ''
759
- title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
766
+ id_attr = node.id ? %( id="#{node.id}") : ''
767
+ title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
760
768
  %(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role) ? " #{role}" : ''}">
761
769
  #{title_el}<div class="content">
762
770
  #{node.content}
@@ -796,7 +804,7 @@ Your browser does not support the audio tag.
796
804
  toc = %(
797
805
  <div id="toc" class="#{doc.attr 'toc-class', 'toc'}">
798
806
  <div id="toctitle">#{doc.attr 'toc-title'}</div>
799
- #{convert_outline doc}
807
+ #{doc.converter.convert doc, 'outline'}
800
808
  </div>)
801
809
  else
802
810
  toc = ''
@@ -848,24 +856,24 @@ Your browser does not support the audio tag.
848
856
  def convert_table node
849
857
  result = []
850
858
  id_attribute = node.id ? %( id="#{node.id}") : ''
851
- classes = ['tableblock', %(frame-#{node.attr 'frame', 'all', 'table-frame'}), %(grid-#{node.attr 'grid', 'all', 'table-grid'})]
859
+ frame = 'ends' if (frame = node.attr 'frame', 'all', 'table-frame') == 'topbot'
860
+ classes = ['tableblock', %(frame-#{frame}), %(grid-#{node.attr 'grid', 'all', 'table-grid'})]
852
861
  if (stripes = node.attr 'stripes', nil, 'table-stripes')
853
862
  classes << %(stripes-#{stripes})
854
863
  end
855
- styles = []
864
+ style_attribute = ''
856
865
  if (autowidth = node.option? 'autowidth') && !(node.attr? 'width')
857
866
  classes << 'fit-content'
858
867
  elsif (tablewidth = node.attr 'tablepcwidth') == 100
859
868
  classes << 'stretch'
860
869
  else
861
- styles << %(width: #{tablewidth}%;)
870
+ style_attribute = %( style="width: #{tablewidth}%;")
862
871
  end
863
872
  classes << (node.attr 'float') if node.attr? 'float'
864
873
  if (role = node.role)
865
874
  classes << role
866
875
  end
867
876
  class_attribute = %( class="#{classes.join ' '}")
868
- style_attribute = styles.empty? ? '' : %( style="#{styles.join ' '}")
869
877
 
870
878
  result << %(<table#{id_attribute}#{class_attribute}#{style_attribute}>)
871
879
  result << %(<caption class="title">#{node.captioned_title}</caption>) if node.title?
@@ -934,7 +942,7 @@ Your browser does not support the audio tag.
934
942
 
935
943
  %(<div#{id_attr} class="#{role}">
936
944
  <div#{title_id_attr} class="title">#{title}</div>
937
- #{convert_outline doc, toclevels: levels}
945
+ #{doc.converter.convert doc, 'outline', toclevels: levels}
938
946
  </div>)
939
947
  end
940
948
 
@@ -1028,12 +1036,14 @@ Your browser does not support the audio tag.
1028
1036
  end
1029
1037
  start_anchor = (node.attr? 'start') ? %(#at=#{node.attr 'start'}) : ''
1030
1038
  delimiter = ['?']
1039
+ target, hash = (node.attr 'target').split '/', 2
1040
+ hash_param = (hash ||= node.attr 'hash') ? %(#{delimiter.pop || '&amp;'}h=#{hash}) : ''
1031
1041
  autoplay_param = (node.option? 'autoplay') ? %(#{delimiter.pop || '&amp;'}autoplay=1) : ''
1032
1042
  loop_param = (node.option? 'loop') ? %(#{delimiter.pop || '&amp;'}loop=1) : ''
1033
1043
  muted_param = (node.option? 'muted') ? %(#{delimiter.pop || '&amp;'}muted=1) : ''
1034
1044
  %(<div#{id_attribute}#{class_attribute}>#{title_element}
1035
1045
  <div class="content">
1036
- <iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{node.attr 'target'}#{autoplay_param}#{loop_param}#{muted_param}#{start_anchor}" frameborder="0"#{(node.option? 'nofullscreen') ? '' : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
1046
+ <iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{target}#{hash_param}#{autoplay_param}#{loop_param}#{muted_param}#{start_anchor}" frameborder="0"#{(node.option? 'nofullscreen') ? '' : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
1037
1047
  </div>
1038
1048
  </div>)
1039
1049
  when 'youtube'
@@ -1069,7 +1079,7 @@ Your browser does not support the audio tag.
1069
1079
  target, playlist = target.split ',', 2
1070
1080
  if (playlist ||= (node.attr 'playlist'))
1071
1081
  # INFO playlist bar doesn't appear in Firefox unless showinfo=1 and modestbranding=1
1072
- list_param = %(&amp;playlist=#{playlist})
1082
+ list_param = %(&amp;playlist=#{target},#{playlist})
1073
1083
  else
1074
1084
  # NOTE for loop to work, playlist must be specified; use VIDEO_ID if there's no explicit playlist
1075
1085
  list_param = has_loop_param ? %(&amp;playlist=#{target}) : ''
@@ -1089,7 +1099,7 @@ Your browser does not support the audio tag.
1089
1099
  time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : ''
1090
1100
  %(<div#{id_attribute}#{class_attribute}>#{title_element}
1091
1101
  <div class="content">
1092
- <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}>
1102
+ <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? 'muted') ? (append_boolean_attribute 'muted', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}>
1093
1103
  Your browser does not support the video tag.
1094
1104
  </video>
1095
1105
  </div>
@@ -1106,9 +1116,17 @@ Your browser does not support the video tag.
1106
1116
  else
1107
1117
  attrs = node.role ? %( class="#{node.role}") : ''
1108
1118
  unless (text = node.text)
1109
- refid = node.attributes['refid']
1110
- if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
1111
- text = (ref.xreftext node.attr('xrefstyle', nil, true)) || %([#{refid}])
1119
+ if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid = node.attributes['refid']] || (refid.nil_or_empty? ? (top = get_root_document node) : nil))
1120
+ if (@resolving_xref ||= (outer = true)) && outer
1121
+ if (text = ref.xreftext node.attr 'xrefstyle', nil, true)
1122
+ text = text.gsub DropAnchorRx, '' if text.include? '<a'
1123
+ else
1124
+ text = top ? '[^top]' : %([#{refid}])
1125
+ end
1126
+ @resolving_xref = nil
1127
+ else
1128
+ text = top ? '[^top]' : %([#{refid}])
1129
+ end
1112
1130
  else
1113
1131
  text = %([#{refid}])
1114
1132
  end
@@ -1144,8 +1162,10 @@ Your browser does not support the video tag.
1144
1162
  elsif node.document.attr? 'icons'
1145
1163
  src = node.icon_uri("callouts/#{node.text}")
1146
1164
  %(<img src="#{src}" alt="#{node.text}"#{@void_element_slash}>)
1165
+ elsif ::Array === (guard = node.attributes['guard'])
1166
+ %(&lt;!--<b class="conum">(#{node.text})</b>--&gt;)
1147
1167
  else
1148
- %(#{node.attributes['guard']}<b class="conum">(#{node.text})</b>)
1168
+ %(#{guard}<b class="conum">(#{node.text})</b>)
1149
1169
  end
1150
1170
  end
1151
1171
 
@@ -1163,42 +1183,49 @@ Your browser does not support the video tag.
1163
1183
  end
1164
1184
 
1165
1185
  def convert_inline_image node
1166
- if (type = node.type || 'image') == 'icon' && (node.document.attr? 'icons', 'font')
1167
- class_attr_val = %(fa fa-#{node.target})
1168
- { 'size' => 'fa-', 'rotate' => 'fa-rotate-', 'flip' => 'fa-flip-' }.each do |key, prefix|
1169
- class_attr_val = %(#{class_attr_val} #{prefix}#{node.attr key}) if node.attr? key
1186
+ target = node.target
1187
+ if (type = node.type || 'image') == 'icon'
1188
+ if (icons = node.document.attr 'icons') == 'font'
1189
+ i_class_attr_val = %(fa fa-#{target})
1190
+ i_class_attr_val = %(#{i_class_attr_val} fa-#{node.attr 'size'}) if node.attr? 'size'
1191
+ if node.attr? 'flip'
1192
+ i_class_attr_val = %(#{i_class_attr_val} fa-flip-#{node.attr 'flip'})
1193
+ elsif node.attr? 'rotate'
1194
+ i_class_attr_val = %(#{i_class_attr_val} fa-rotate-#{node.attr 'rotate'})
1195
+ end
1196
+ attrs = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : ''
1197
+ img = %(<i class="#{i_class_attr_val}"#{attrs}></i>)
1198
+ elsif icons
1199
+ attrs = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
1200
+ attrs = %(#{attrs} height="#{node.attr 'height'}") if node.attr? 'height'
1201
+ attrs = %(#{attrs} title="#{node.attr 'title'}") if node.attr? 'title'
1202
+ img = %(<img src="#{node.icon_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1203
+ else
1204
+ img = %([#{node.alt}&#93;)
1170
1205
  end
1171
- title_attr = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : ''
1172
- img = %(<i class="#{class_attr_val}"#{title_attr}></i>)
1173
- elsif type == 'icon' && !(node.document.attr? 'icons')
1174
- img = %([#{node.alt}])
1175
1206
  else
1176
- target = node.target
1177
- attrs = ['width', 'height', 'title'].map {|name| (node.attr? name) ? %( #{name}="#{node.attr name}") : '' }.join
1178
- if type != 'icon' && ((node.attr? 'format', 'svg') || (target.include? '.svg')) &&
1179
- node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
1180
- if svg
1207
+ attrs = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
1208
+ attrs = %(#{attrs} height="#{node.attr 'height'}") if node.attr? 'height'
1209
+ attrs = %(#{attrs} title="#{node.attr 'title'}") if node.attr? 'title'
1210
+ if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
1211
+ if node.option? 'inline'
1181
1212
  img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>)
1182
- elsif obj
1183
- fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>)
1213
+ elsif node.option? 'interactive'
1214
+ fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri node.attr 'fallback'}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>) : %(<span class="alt">#{node.alt}</span>)
1184
1215
  img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{attrs}>#{fallback}</object>)
1216
+ else
1217
+ img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1185
1218
  end
1219
+ else
1220
+ img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1186
1221
  end
1187
- img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1188
- end
1189
- if node.attr? 'link'
1190
- img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
1191
1222
  end
1223
+ img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
1224
+ class_attr_val = type
1192
1225
  if (role = node.role)
1193
- if node.attr? 'float'
1194
- class_attr_val = %(#{type} #{node.attr 'float'} #{role})
1195
- else
1196
- class_attr_val = %(#{type} #{role})
1197
- end
1226
+ class_attr_val = (node.attr? 'float') ? %(#{class_attr_val} #{node.attr 'float'} #{role}) : %(#{class_attr_val} #{role})
1198
1227
  elsif node.attr? 'float'
1199
- class_attr_val = %(#{type} #{node.attr 'float'})
1200
- else
1201
- class_attr_val = type
1228
+ class_attr_val = %(#{class_attr_val} #{node.attr 'float'})
1202
1229
  end
1203
1230
  %(<span class="#{class_attr_val}">#{img}</span>)
1204
1231
  end
@@ -1252,16 +1279,19 @@ Your browser does not support the video tag.
1252
1279
 
1253
1280
  # NOTE expose read_svg_contents for Bespoke converter
1254
1281
  def read_svg_contents node, target
1255
- if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG')
1282
+ if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG', warn_if_empty: true)
1283
+ return if svg.empty?
1256
1284
  svg = svg.sub SvgPreambleRx, '' unless svg.start_with? '<svg'
1257
- old_start_tag = new_start_tag = nil
1285
+ old_start_tag = new_start_tag = start_tag_match = nil
1258
1286
  # NOTE width, height and style attributes are removed if either width or height is specified
1259
1287
  ['width', 'height'].each do |dim|
1260
- if node.attr? dim
1261
- new_start_tag = (old_start_tag = (svg.match SvgStartTagRx)[0]).gsub DimensionAttributeRx, '' unless new_start_tag
1262
- # QUESTION should we add px since it's already the default?
1263
- new_start_tag = %(#{new_start_tag.chop} #{dim}="#{node.attr dim}px">)
1288
+ next unless node.attr? dim
1289
+ unless new_start_tag
1290
+ next if (start_tag_match ||= (svg.match SvgStartTagRx) || :no_match) == :no_match
1291
+ new_start_tag = (old_start_tag = start_tag_match[0]).gsub DimensionAttributeRx, ''
1264
1292
  end
1293
+ # NOTE a unitless value in HTML is assumed to be px, so we can pass the value straight through
1294
+ new_start_tag = %(#{new_start_tag.chop} #{dim}="#{node.attr dim}">)
1265
1295
  end
1266
1296
  svg = %(#{new_start_tag}#{svg[old_start_tag.length..-1]}) if new_start_tag
1267
1297
  end
@@ -1297,13 +1327,24 @@ Your browser does not support the video tag.
1297
1327
  manname_id_attr = (manname_id = node.attr 'manname-id') ? %( id="#{manname_id}") : ''
1298
1328
  %(<h2#{manname_id_attr}>#{manname_title}</h2>
1299
1329
  <div class="sectionbody">
1300
- <p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
1330
+ <p>#{(node.attr 'mannames').join ', '} - #{node.attr 'manpurpose'}</p>
1301
1331
  </div>)
1302
1332
  end
1303
1333
 
1334
+ def get_root_document node
1335
+ while (node = node.document).nested?
1336
+ node = node.parent_document
1337
+ end
1338
+ node
1339
+ end
1340
+
1304
1341
  # NOTE adapt to older converters that relied on unprefixed method names
1305
- def method_missing id, *params
1306
- !((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *params) : super
1342
+ def method_missing id, *args
1343
+ !((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *args) : super
1344
+ end
1345
+
1346
+ def respond_to_missing? id, *options
1347
+ !((name = id.to_s).start_with? 'convert_') && (handles? name)
1307
1348
  end
1308
1349
  end
1309
1350
  end