asciidoctor 2.0.10 → 2.0.17

Sign up to get free protection for your applications and to get access to all the features.
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