asciidoctor 2.0.15 → 2.0.17

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