asciidoctor 2.0.15 → 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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +86 -11
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +9 -12
  5. data/README-fr.adoc +9 -12
  6. data/README-jp.adoc +10 -13
  7. data/README-zh_CN.adoc +9 -12
  8. data/README.adoc +33 -18
  9. data/asciidoctor.gemspec +2 -9
  10. data/data/locale/attributes-fr.adoc +2 -2
  11. data/data/locale/attributes-th.adoc +23 -0
  12. data/data/locale/attributes-vi.adoc +23 -0
  13. data/data/stylesheets/asciidoctor-default.css +54 -53
  14. data/data/stylesheets/coderay-asciidoctor.css +9 -9
  15. data/lib/asciidoctor/abstract_block.rb +11 -9
  16. data/lib/asciidoctor/abstract_node.rb +9 -8
  17. data/lib/asciidoctor/block.rb +6 -6
  18. data/lib/asciidoctor/cli/invoker.rb +1 -2
  19. data/lib/asciidoctor/cli/options.rb +25 -25
  20. data/lib/asciidoctor/convert.rb +1 -0
  21. data/lib/asciidoctor/converter/docbook5.rb +20 -22
  22. data/lib/asciidoctor/converter/html5.rb +112 -94
  23. data/lib/asciidoctor/converter/manpage.rb +61 -52
  24. data/lib/asciidoctor/converter/template.rb +12 -13
  25. data/lib/asciidoctor/converter.rb +6 -4
  26. data/lib/asciidoctor/core_ext/hash/merge.rb +1 -1
  27. data/lib/asciidoctor/document.rb +39 -41
  28. data/lib/asciidoctor/extensions.rb +20 -12
  29. data/lib/asciidoctor/list.rb +2 -6
  30. data/lib/asciidoctor/load.rb +10 -9
  31. data/lib/asciidoctor/logging.rb +10 -8
  32. data/lib/asciidoctor/parser.rb +172 -189
  33. data/lib/asciidoctor/path_resolver.rb +3 -3
  34. data/lib/asciidoctor/reader.rb +71 -72
  35. data/lib/asciidoctor/rx.rb +3 -2
  36. data/lib/asciidoctor/section.rb +7 -0
  37. data/lib/asciidoctor/substitutors.rb +101 -103
  38. data/lib/asciidoctor/syntax_highlighter/coderay.rb +2 -1
  39. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
  40. data/lib/asciidoctor/syntax_highlighter/pygments.rb +14 -5
  41. data/lib/asciidoctor/syntax_highlighter/rouge.rb +2 -1
  42. data/lib/asciidoctor/syntax_highlighter.rb +8 -11
  43. data/lib/asciidoctor/table.rb +18 -20
  44. data/lib/asciidoctor/timings.rb +3 -3
  45. data/lib/asciidoctor/version.rb +1 -1
  46. data/lib/asciidoctor.rb +7 -7
  47. data/man/asciidoctor.1 +26 -28
  48. data/man/asciidoctor.adoc +33 -27
  49. metadata +8 -62
@@ -21,7 +21,7 @@ class Converter::Html5Converter < Converter::Base
21
21
  #latexmath: INLINE_MATH_DELIMITERS[:latexmath] + [false],
22
22
  }).default = ['', '']
23
23
 
24
- DropAnchorRx = /<(?:a\b[^>]*|\/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
@@ -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
 
@@ -350,9 +351,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
350
351
  stitle = section.captioned_title
351
352
  elsif section.numbered && slevel <= sectnumlevels
352
353
  if slevel < 2 && node.document.doctype == 'book'
353
- if section.sectname == 'chapter'
354
+ case section.sectname
355
+ when 'chapter'
354
356
  stitle = %(#{(signifier = node.document.attributes['chapter-signifier']) ? "#{signifier} " : ''}#{section.sectnum} #{section.title})
355
- elsif section.sectname == 'part'
357
+ when 'part'
356
358
  stitle = %(#{(signifier = node.document.attributes['part-signifier']) ? "#{signifier} " : ''}#{section.sectnum nil, ':'} #{section.title})
357
359
  else
358
360
  stitle = %(#{section.sectnum} #{section.title})
@@ -383,9 +385,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
383
385
  title = node.captioned_title
384
386
  elsif node.numbered && level <= (doc_attrs['sectnumlevels'] || 3).to_i
385
387
  if level < 2 && node.document.doctype == 'book'
386
- if node.sectname == 'chapter'
388
+ case node.sectname
389
+ when 'chapter'
387
390
  title = %(#{(signifier = doc_attrs['chapter-signifier']) ? "#{signifier} " : ''}#{node.sectnum} #{node.title})
388
- elsif node.sectname == 'part'
391
+ when 'part'
389
392
  title = %(#{(signifier = doc_attrs['part-signifier']) ? "#{signifier} " : ''}#{node.sectnum nil, ':'} #{node.title})
390
393
  else
391
394
  title = %(#{node.sectnum} #{node.title})
@@ -513,16 +516,16 @@ Your browser does not support the audio tag.
513
516
  result = []
514
517
  id_attribute = node.id ? %( id="#{node.id}") : ''
515
518
 
516
- classes = case node.style
519
+ case node.style
517
520
  when 'qanda'
518
- ['qlist', 'qanda', node.role]
521
+ classes = ['qlist', 'qanda', node.role]
519
522
  when 'horizontal'
520
- ['hdlist', node.role]
523
+ classes = ['hdlist', node.role]
521
524
  else
522
- ['dlist', node.style, node.role]
523
- end.compact
525
+ classes = ['dlist', node.style, node.role]
526
+ end
524
527
 
525
- class_attribute = %( class="#{classes.join ' '}")
528
+ class_attribute = %( class="#{classes.compact.join ' '}")
526
529
 
527
530
  result << %(<div#{id_attribute}#{class_attribute}>)
528
531
  result << %(<div class="title">#{node.title}</div>) if node.title?
@@ -578,12 +581,11 @@ Your browser does not support the audio tag.
578
581
  terms.each do |dt|
579
582
  result << %(<dt#{dt_style_attribute}>#{dt.text}</dt>)
580
583
  end
581
- if dd
582
- result << '<dd>'
583
- result << %(<p>#{dd.text}</p>) if dd.text?
584
- result << dd.content if dd.blocks?
585
- result << '</dd>'
586
- 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>'
587
589
  end
588
590
  result << '</dl>'
589
591
  end
@@ -624,16 +626,18 @@ Your browser does not support the audio tag.
624
626
  target = node.attr 'target'
625
627
  width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
626
628
  height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : ''
627
- if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE &&
628
- ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
629
- if svg
629
+ if ((node.attr? 'format', 'svg') || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
630
+ if node.option? 'inline'
630
631
  img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>)
631
- elsif obj
632
- 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>)
633
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}>)
634
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}>)
635
640
  end
636
- img ||= %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
637
641
  img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
638
642
  id_attr = node.id ? %( id="#{node.id}") : ''
639
643
  classes = ['imageblock']
@@ -759,8 +763,8 @@ Your browser does not support the audio tag.
759
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.'
760
764
  ''
761
765
  else
762
- id_attr = node.id ? %( id="#{node.id}") : ''
763
- 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) : ''
764
768
  %(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role) ? " #{role}" : ''}">
765
769
  #{title_el}<div class="content">
766
770
  #{node.content}
@@ -857,20 +861,19 @@ Your browser does not support the audio tag.
857
861
  if (stripes = node.attr 'stripes', nil, 'table-stripes')
858
862
  classes << %(stripes-#{stripes})
859
863
  end
860
- styles = []
864
+ style_attribute = ''
861
865
  if (autowidth = node.option? 'autowidth') && !(node.attr? 'width')
862
866
  classes << 'fit-content'
863
867
  elsif (tablewidth = node.attr 'tablepcwidth') == 100
864
868
  classes << 'stretch'
865
869
  else
866
- styles << %(width: #{tablewidth}%;)
870
+ style_attribute = %( style="width: #{tablewidth}%;")
867
871
  end
868
872
  classes << (node.attr 'float') if node.attr? 'float'
869
873
  if (role = node.role)
870
874
  classes << role
871
875
  end
872
876
  class_attribute = %( class="#{classes.join ' '}")
873
- style_attribute = styles.empty? ? '' : %( style="#{styles.join ' '}")
874
877
 
875
878
  result << %(<table#{id_attribute}#{class_attribute}#{style_attribute}>)
876
879
  result << %(<caption class="title">#{node.captioned_title}</caption>) if node.title?
@@ -1033,12 +1036,14 @@ Your browser does not support the audio tag.
1033
1036
  end
1034
1037
  start_anchor = (node.attr? 'start') ? %(#at=#{node.attr 'start'}) : ''
1035
1038
  delimiter = ['?']
1039
+ target, hash = (node.attr 'target').split '/', 2
1040
+ hash_param = (hash ||= node.attr 'hash') ? %(#{delimiter.pop || '&amp;'}h=#{hash}) : ''
1036
1041
  autoplay_param = (node.option? 'autoplay') ? %(#{delimiter.pop || '&amp;'}autoplay=1) : ''
1037
1042
  loop_param = (node.option? 'loop') ? %(#{delimiter.pop || '&amp;'}loop=1) : ''
1038
1043
  muted_param = (node.option? 'muted') ? %(#{delimiter.pop || '&amp;'}muted=1) : ''
1039
1044
  %(<div#{id_attribute}#{class_attribute}>#{title_element}
1040
1045
  <div class="content">
1041
- <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>
1042
1047
  </div>
1043
1048
  </div>)
1044
1049
  when 'youtube'
@@ -1074,7 +1079,7 @@ Your browser does not support the audio tag.
1074
1079
  target, playlist = target.split ',', 2
1075
1080
  if (playlist ||= (node.attr 'playlist'))
1076
1081
  # INFO playlist bar doesn't appear in Firefox unless showinfo=1 and modestbranding=1
1077
- list_param = %(&amp;playlist=#{playlist})
1082
+ list_param = %(&amp;playlist=#{target},#{playlist})
1078
1083
  else
1079
1084
  # NOTE for loop to work, playlist must be specified; use VIDEO_ID if there's no explicit playlist
1080
1085
  list_param = has_loop_param ? %(&amp;playlist=#{target}) : ''
@@ -1178,40 +1183,49 @@ Your browser does not support the video tag.
1178
1183
  end
1179
1184
 
1180
1185
  def convert_inline_image node
1181
- if (type = node.type || 'image') == 'icon' && (node.document.attr? 'icons', 'font')
1182
- class_attr_val = %(fa fa-#{node.target})
1183
- { 'size' => 'fa-', 'rotate' => 'fa-rotate-', 'flip' => 'fa-flip-' }.each do |key, prefix|
1184
- 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;)
1185
1205
  end
1186
- title_attr = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : ''
1187
- img = %(<i class="#{class_attr_val}"#{title_attr}></i>)
1188
- elsif type == 'icon' && !(node.document.attr? 'icons')
1189
- img = %([#{node.alt}])
1190
1206
  else
1191
- target = node.target
1192
- attrs = ['width', 'height', 'title'].map {|name| (node.attr? name) ? %( #{name}="#{node.attr name}") : '' }.join
1193
- if type != 'icon' && ((node.attr? 'format', 'svg') || (target.include? '.svg')) &&
1194
- node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
1195
- 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'
1196
1212
  img = (read_svg_contents node, target) || %(<span class="alt">#{node.alt}</span>)
1197
- elsif obj
1198
- 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>)
1199
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}>)
1200
1218
  end
1219
+ else
1220
+ img = %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1201
1221
  end
1202
- img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1203
1222
  end
1204
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
1205
1225
  if (role = node.role)
1206
- if node.attr? 'float'
1207
- class_attr_val = %(#{type} #{node.attr 'float'} #{role})
1208
- else
1209
- class_attr_val = %(#{type} #{role})
1210
- end
1226
+ class_attr_val = (node.attr? 'float') ? %(#{class_attr_val} #{node.attr 'float'} #{role}) : %(#{class_attr_val} #{role})
1211
1227
  elsif node.attr? 'float'
1212
- class_attr_val = %(#{type} #{node.attr 'float'})
1213
- else
1214
- class_attr_val = type
1228
+ class_attr_val = %(#{class_attr_val} #{node.attr 'float'})
1215
1229
  end
1216
1230
  %(<span class="#{class_attr_val}">#{img}</span>)
1217
1231
  end
@@ -1325,8 +1339,12 @@ Your browser does not support the video tag.
1325
1339
  end
1326
1340
 
1327
1341
  # NOTE adapt to older converters that relied on unprefixed method names
1328
- def method_missing id, *params
1329
- !((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)
1330
1348
  end
1331
1349
  end
1332
1350
  end
@@ -23,7 +23,7 @@ class Converter::ManPageConverter < Converter::Base
23
23
  LeadingPeriodRx = /^\./
24
24
  EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) "#{CC_ANY}*?" "#{CC_ANY}*?" )( |[^\s]*)(#{CC_ANY}*?)(?: *#{ESC}\\c)?$/
25
25
  MalformedEscapedMacroRx = /(#{ESC}\\c) (#{ESC}\.(?:URL|MTO) )/
26
- MockMacroRx = /<\/?(#{ESC}\\[^>]+)>/
26
+ MockMacroRx = %r(</?(#{ESC}\\[^>]+)>)
27
27
  EmDashCharRefRx = /&#8212;(?:&#8203;)?/
28
28
  EllipsisCharRefRx = /&#8230;(?:&#8203;)?/
29
29
  WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
@@ -419,15 +419,7 @@ allbox tab(:);'
419
419
  end
420
420
  row_text[row_index] << %(T{#{LF}.sp#{LF})
421
421
  cell_halign = (cell.attr 'halign', 'left').chr
422
- if tsec == :head
423
- if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
424
- row_header[row_index][cell_index] << %(#{cell_halign}tB)
425
- else
426
- row_header[row_index][cell_index + 1] ||= []
427
- row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
428
- end
429
- row_text[row_index] << %(#{manify cell.text, whitespace: :normalize}#{LF})
430
- elsif tsec == :body
422
+ if tsec == :body
431
423
  if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
432
424
  row_header[row_index][cell_index] << %(#{cell_halign}t)
433
425
  else
@@ -443,7 +435,7 @@ allbox tab(:);'
443
435
  cell_content = manify cell.content.join, whitespace: :normalize
444
436
  end
445
437
  row_text[row_index] << %(#{cell_content}#{LF})
446
- elsif tsec == :foot
438
+ else # tsec == :head || tsec == :foot
447
439
  if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
448
440
  row_header[row_index][cell_index] << %(#{cell_halign}tB)
449
441
  else
@@ -587,8 +579,16 @@ allbox tab(:);'
587
579
  %(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
588
580
  when :xref
589
581
  unless (text = node.text)
590
- refid = node.attributes['refid']
591
- text = %([#{refid}]) unless AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid]) && (@resolving_xref ||= outer = true) && outer && (text = ref.xreftext node.attr 'xrefstyle', nil, true)
582
+ if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid = node.attributes['refid']] || (refid.nil_or_empty? ? (top = get_root_document node) : nil))
583
+ if (@resolving_xref ||= (outer = true)) && outer && (text = ref.xreftext node.attr 'xrefstyle', nil, true)
584
+ text = uppercase_pcdata text if ref.context === :section && ref.level < 2 && text == ref.title
585
+ else
586
+ text = top ? '[^top]' : %([#{refid}])
587
+ end
588
+ @resolving_xref = nil if outer
589
+ else
590
+ text = %([#{refid}])
591
+ end
592
592
  end
593
593
  text
594
594
  when :ref, :bibref
@@ -664,13 +664,12 @@ allbox tab(:);'
664
664
  end
665
665
 
666
666
  def self.write_alternate_pages mannames, manvolnum, target
667
- if mannames && mannames.size > 1
668
- mannames.shift
669
- manvolext = %(.#{manvolnum})
670
- dir, basename = ::File.split target
671
- mannames.each do |manname|
672
- ::File.write ::File.join(dir, %(#{manname}#{manvolext})), %(.so #{basename}), mode: FILE_WRITE_MODE
673
- end
667
+ return unless mannames && mannames.size > 1
668
+ mannames.shift
669
+ manvolext = %(.#{manvolnum})
670
+ dir, basename = ::File.split target
671
+ mannames.each do |manname|
672
+ ::File.write ::File.join(dir, %(#{manname}#{manvolext})), %(.so #{basename}), mode: FILE_WRITE_MODE
674
673
  end
675
674
  end
676
675
 
@@ -679,7 +678,7 @@ allbox tab(:);'
679
678
  def append_footnotes result, node
680
679
  if node.footnotes? && !(node.attr? 'nofootnotes')
681
680
  result << '.SH "NOTES"'
682
- node.footnotes.each_with_index do |fn, idx|
681
+ node.footnotes.each do |fn|
683
682
  result << %(.IP [#{fn.index}])
684
683
  # NOTE restore newline in escaped macro that gets removed by normalize_text in substitutor
685
684
  if (text = fn.text).include? %(#{ESC}\\c #{ESC}.)
@@ -713,37 +712,40 @@ allbox tab(:);'
713
712
  else
714
713
  str = str.tr_s WHITESPACE, ' '
715
714
  end
716
- str = str.
717
- gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' }. # literal backslash (not a troff escape sequence)
718
- gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
719
- gsub(LeadingPeriodRx, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
720
- # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
721
- gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#{$2.rstrip}"#{LF}#{rest}) }.
722
- gsub('-', '\-').
723
- gsub('&lt;', '<').
724
- gsub('&gt;', '>').
725
- gsub('&#160;', '\~'). # non-breaking space
726
- gsub('&#169;', '\(co'). # copyright sign
727
- gsub('&#174;', '\(rg'). # registered sign
728
- gsub('&#8482;', '\(tm'). # trademark sign
729
- gsub('&#8201;', ' '). # thin space
730
- gsub('&#8211;', '\(en'). # en dash
731
- gsub(EmDashCharRefRx, '\(em'). # em dash
732
- gsub('&#8216;', '\(oq'). # left single quotation mark
733
- gsub('&#8217;', '\(cq'). # right single quotation mark
734
- gsub('&#8220;', '\(lq'). # left double quotation mark
735
- gsub('&#8221;', '\(rq'). # right double quotation mark
736
- gsub('&#8592;', '\(<-'). # leftwards arrow
737
- gsub('&#8594;', '\(->'). # rightwards arrow
738
- gsub('&#8656;', '\(lA'). # leftwards double arrow
739
- gsub('&#8658;', '\(rA'). # rightwards double arrow
740
- gsub('&#8203;', '\:'). # zero width space
741
- gsub('&amp;', '&'). # literal ampersand (NOTE must take place after any other replacement that includes &)
742
- gsub('\'', '\(aq'). # apostrophe-quote
743
- gsub(MockMacroRx, '\1'). # mock boundary
744
- gsub(ESC_BS, '\\'). # unescape troff backslash (NOTE update if more escapes are added)
745
- gsub(ESC_FS, '.'). # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
746
- rstrip # strip trailing space
715
+ str = str
716
+ .gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' } # literal backslash (not a troff escape sequence)
717
+ .gsub(EllipsisCharRefRx, '...') # horizontal ellipsis
718
+ .gsub(LeadingPeriodRx, '\\\&.') # leading . is used in troff for macro call or other formatting; replace with \&.
719
+ .gsub(EscapedMacroRx) do # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
720
+ (rest = $3.lstrip).empty? ? %(.#{$1}"#{$2}") : %(.#{$1}"#{$2.rstrip}"#{LF}#{rest})
721
+ end
722
+ .gsub('-', '\-')
723
+ .gsub('&lt;', '<')
724
+ .gsub('&gt;', '>')
725
+ .gsub('&#43;', '+') # plus sign; alternately could use \c(pl
726
+ .gsub('&#160;', '\~') # non-breaking space
727
+ .gsub('&#169;', '\(co') # copyright sign
728
+ .gsub('&#174;', '\(rg') # registered sign
729
+ .gsub('&#8482;', '\(tm') # trademark sign
730
+ .gsub('&#176;', '\(de') # degree sign
731
+ .gsub('&#8201;', ' ') # thin space
732
+ .gsub('&#8211;', '\(en') # en dash
733
+ .gsub(EmDashCharRefRx, '\(em') # em dash
734
+ .gsub('&#8216;', '\(oq') # left single quotation mark
735
+ .gsub('&#8217;', '\(cq') # right single quotation mark
736
+ .gsub('&#8220;', '\(lq') # left double quotation mark
737
+ .gsub('&#8221;', '\(rq') # right double quotation mark
738
+ .gsub('&#8592;', '\(<-') # leftwards arrow
739
+ .gsub('&#8594;', '\(->') # rightwards arrow
740
+ .gsub('&#8656;', '\(lA') # leftwards double arrow
741
+ .gsub('&#8658;', '\(rA') # rightwards double arrow
742
+ .gsub('&#8203;', '\:') # zero width space
743
+ .gsub('&amp;', '&') # literal ampersand (NOTE must take place after any other replacement that includes &)
744
+ .gsub('\'', '\*(Aq') # apostrophe / neutral single quote
745
+ .gsub(MockMacroRx, '\1') # mock boundary
746
+ .gsub(ESC_BS, '\\') # unescape troff backslash (NOTE update if more escapes are added)
747
+ .gsub(ESC_FS, '.') # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
748
+ .rstrip # strip trailing space
747
749
  opts[:append_newline] ? %(#{str}#{LF}) : str
748
750
  end
749
751
 
@@ -754,5 +756,12 @@ allbox tab(:);'
754
756
  def enclose_content node
755
757
  node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, whitespace: :normalize})
756
758
  end
759
+
760
+ def get_root_document node
761
+ while (node = node.document).nested?
762
+ node = node.parent_document
763
+ end
764
+ node
765
+ end
757
766
  end
758
767
  end
@@ -40,13 +40,13 @@ class Converter::TemplateConverter < Converter::Base
40
40
  @caches = { scans: {}, templates: {} }
41
41
  end
42
42
 
43
- def self.caches
44
- @caches
45
- end
43
+ class << self
44
+ attr_reader :caches
46
45
 
47
- def self.clear_caches
48
- @caches[:scans].clear if @caches[:scans]
49
- @caches[:templates].clear if @caches[:templates]
46
+ def clear_caches
47
+ @caches[:scans].clear
48
+ @caches[:templates].clear
49
+ end
50
50
  end
51
51
 
52
52
  def initialize backend, template_dirs, opts = {}
@@ -134,12 +134,10 @@ class Converter::TemplateConverter < Converter::Base
134
134
  #
135
135
  # Returns the Tilt template object
136
136
  def register name, template
137
- @templates[name] = if (template_cache = @caches[:templates])
137
+ if (template_cache = @caches[:templates])
138
138
  template_cache[template.file] = template
139
- else
140
- template
141
139
  end
142
- #create_handler name, template
140
+ @templates[name] = template
143
141
  end
144
142
 
145
143
  private
@@ -155,6 +153,7 @@ class Converter::TemplateConverter < Converter::Base
155
153
  engine = @engine
156
154
  @template_dirs.each do |template_dir|
157
155
  # FIXME need to think about safe mode restrictions here
156
+ # Ruby 2.3 requires the extra brackets around the path_resolver.system_path method call
158
157
  next unless ::File.directory?(template_dir = (path_resolver.system_path template_dir))
159
158
 
160
159
  if engine
@@ -186,8 +185,8 @@ class Converter::TemplateConverter < Converter::Base
186
185
  else
187
186
  @templates.update scan_dir(template_dir, pattern, @caches[:templates])
188
187
  end
189
- nil
190
188
  end
189
+ nil
191
190
  end
192
191
 
193
192
  # Internal: Scan the specified directory for template files matching pattern and instantiate
@@ -197,7 +196,7 @@ class Converter::TemplateConverter < Converter::Base
197
196
  def scan_dir template_dir, pattern, template_cache = nil
198
197
  result, helpers = {}, nil
199
198
  # Grab the files in the top level of the directory (do not recurse)
200
- ::Dir.glob(pattern).select {|match| ::File.file? match }.each do |file|
199
+ ::Dir.glob(pattern).keep_if {|match| ::File.file? match }.each do |file|
201
200
  if (basename = ::File.basename file) == 'helpers.rb'
202
201
  helpers = file
203
202
  next
@@ -262,7 +261,7 @@ class Converter::TemplateConverter < Converter::Base
262
261
  [::Tilt::ErubiTemplate, {}]
263
262
  elsif name == 'erubis'
264
263
  Helpers.require_library 'erubis' unless defined? ::Erubis::FastEruby
265
- [::Tilt::ErubisTemplate, { engine_class: ::Erubis::FastEruby }]
264
+ [::Tilt::ErubisTemplate, engine_class: ::Erubis::FastEruby]
266
265
  else
267
266
  raise ::ArgumentError, %(Unknown ERB implementation: #{name})
268
267
  end
@@ -42,7 +42,7 @@ module Asciidoctor
42
42
  # puts Asciidoctor.convert_file 'sample.adoc', safe: :safe
43
43
  module Converter
44
44
  autoload :CompositeConverter, %(#{__dir__}/converter/composite)
45
- autoload :TemplateConverter, %(#{__dir__}/converter/template)
45
+ autoload :TemplateConverter, %(#{__dir__}/converter/template) unless RUBY_ENGINE == 'opal'
46
46
 
47
47
  # Public: The String backend name that this converter is handling.
48
48
  attr_reader :backend
@@ -366,15 +366,17 @@ module Converter
366
366
  # into - The Class into which the {Converter} module is being included.
367
367
  #
368
368
  # Returns nothing.
369
- private_class_method def self.included into
369
+ def self.included into
370
370
  into.send :include, BackendTraits
371
371
  into.extend Config
372
- end || :included
372
+ end
373
+ private_class_method :included # use separate declaration for Ruby 2.0.x
373
374
 
374
375
  # An abstract base class for defining converters that can be used to convert {AbstractNode} objects in a parsed
375
376
  # AsciiDoc document to a backend format such as HTML or DocBook.
376
377
  class Base
377
- include Converter, Logging
378
+ include Logging
379
+ include Converter
378
380
 
379
381
  # Public: Converts an {AbstractNode} by delegating to a method that matches the transform value.
380
382
  #
@@ -3,6 +3,6 @@
3
3
  # NOTE use `send :prepend` to be nice to Ruby 2.0
4
4
  Hash.send :prepend, (Module.new do
5
5
  def merge *args
6
- (len = args.length) < 1 ? super({}) : (len > 1 ? args.inject(self) {|acc, arg| acc.merge arg } : (super args[0]))
6
+ (len = args.length) < 1 ? dup : (len > 1 ? args.inject(self) {|acc, arg| acc.merge arg } : (super args[0]))
7
7
  end
8
8
  end) if (Hash.instance_method :merge).arity == 1