asciidoctor 1.5.8 → 2.0.0.rc.1

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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +162 -17
  3. data/LICENSE +1 -1
  4. data/README-de.adoc +12 -13
  5. data/README-fr.adoc +11 -12
  6. data/README-jp.adoc +11 -12
  7. data/README-zh_CN.adoc +12 -13
  8. data/README.adoc +6 -7
  9. data/asciidoctor.gemspec +19 -24
  10. data/bin/asciidoctor +5 -4
  11. data/data/reference/syntax.adoc +283 -0
  12. data/data/stylesheets/asciidoctor-default.css +56 -52
  13. data/data/stylesheets/coderay-asciidoctor.css +7 -9
  14. data/lib/asciidoctor.rb +171 -232
  15. data/lib/asciidoctor/abstract_block.rb +96 -105
  16. data/lib/asciidoctor/abstract_node.rb +118 -139
  17. data/lib/asciidoctor/attribute_list.rb +10 -14
  18. data/lib/asciidoctor/block.rb +20 -19
  19. data/lib/asciidoctor/callouts.rb +4 -2
  20. data/lib/asciidoctor/cli.rb +3 -2
  21. data/lib/asciidoctor/cli/invoker.rb +14 -21
  22. data/lib/asciidoctor/cli/options.rb +64 -54
  23. data/lib/asciidoctor/converter.rb +357 -185
  24. data/lib/asciidoctor/converter/composite.rb +40 -48
  25. data/lib/asciidoctor/converter/docbook5.rb +604 -640
  26. data/lib/asciidoctor/converter/html5.rb +949 -963
  27. data/lib/asciidoctor/converter/manpage.rb +569 -548
  28. data/lib/asciidoctor/converter/template.rb +231 -272
  29. data/lib/asciidoctor/core_ext.rb +5 -18
  30. data/lib/asciidoctor/core_ext/float/truncate.rb +19 -0
  31. data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
  32. data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
  33. data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
  34. data/lib/asciidoctor/document.rb +399 -377
  35. data/lib/asciidoctor/extensions.rb +72 -140
  36. data/lib/asciidoctor/helpers.rb +122 -83
  37. data/lib/asciidoctor/inline.rb +5 -1
  38. data/lib/asciidoctor/list.rb +13 -11
  39. data/lib/asciidoctor/logging.rb +17 -16
  40. data/lib/asciidoctor/parser.rb +390 -423
  41. data/lib/asciidoctor/path_resolver.rb +10 -5
  42. data/lib/asciidoctor/reader.rb +286 -263
  43. data/lib/asciidoctor/rouge_ext.rb +39 -0
  44. data/lib/asciidoctor/section.rb +9 -8
  45. data/lib/asciidoctor/stylesheets.rb +19 -37
  46. data/lib/asciidoctor/substitutors.rb +364 -509
  47. data/lib/asciidoctor/syntax_highlighter.rb +238 -0
  48. data/lib/asciidoctor/syntax_highlighter/coderay.rb +87 -0
  49. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +26 -0
  50. data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
  51. data/lib/asciidoctor/syntax_highlighter/prettify.rb +27 -0
  52. data/lib/asciidoctor/syntax_highlighter/pygments.rb +149 -0
  53. data/lib/asciidoctor/syntax_highlighter/rouge.rb +129 -0
  54. data/lib/asciidoctor/table.rb +73 -66
  55. data/lib/asciidoctor/timings.rb +4 -2
  56. data/lib/asciidoctor/version.rb +2 -1
  57. data/lib/asciidoctor/writer.rb +30 -0
  58. data/man/asciidoctor.1 +19 -15
  59. data/man/asciidoctor.adoc +14 -12
  60. metadata +69 -216
  61. data/CONTRIBUTING.adoc +0 -185
  62. data/Gemfile +0 -60
  63. data/Rakefile +0 -129
  64. data/bin/asciidoctor-safe +0 -15
  65. data/features/open_block.feature +0 -92
  66. data/features/pass_block.feature +0 -66
  67. data/features/step_definitions.rb +0 -49
  68. data/features/text_formatting.feature +0 -57
  69. data/features/xref.feature +0 -1039
  70. data/lib/asciidoctor/converter/base.rb +0 -59
  71. data/lib/asciidoctor/converter/docbook45.rb +0 -93
  72. data/lib/asciidoctor/converter/factory.rb +0 -226
  73. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
  74. data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
  75. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
  76. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
  77. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
  78. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
  79. data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
  80. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
  81. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
  82. data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
  83. data/test/api_test.rb +0 -1240
  84. data/test/attribute_list_test.rb +0 -242
  85. data/test/attributes_test.rb +0 -1623
  86. data/test/blocks_test.rb +0 -3870
  87. data/test/converter_test.rb +0 -470
  88. data/test/document_test.rb +0 -1853
  89. data/test/extensions_test.rb +0 -1560
  90. data/test/fixtures/asciidoc_index.txt +0 -521
  91. data/test/fixtures/basic-docinfo-footer.html +0 -6
  92. data/test/fixtures/basic-docinfo-footer.xml +0 -8
  93. data/test/fixtures/basic-docinfo.html +0 -1
  94. data/test/fixtures/basic-docinfo.xml +0 -4
  95. data/test/fixtures/basic.asciidoc +0 -5
  96. data/test/fixtures/chapter-a.adoc +0 -3
  97. data/test/fixtures/child-include.adoc +0 -5
  98. data/test/fixtures/circle.svg +0 -9
  99. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
  100. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
  101. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
  102. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
  103. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
  104. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
  105. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
  106. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
  107. data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
  108. data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
  109. data/test/fixtures/docinfo-footer.html +0 -1
  110. data/test/fixtures/docinfo-footer.xml +0 -9
  111. data/test/fixtures/docinfo.html +0 -1
  112. data/test/fixtures/docinfo.xml +0 -3
  113. data/test/fixtures/doctime-localtime.adoc +0 -2
  114. data/test/fixtures/dot.gif +0 -0
  115. data/test/fixtures/encoding.asciidoc +0 -13
  116. data/test/fixtures/file-with-missing-include.adoc +0 -1
  117. data/test/fixtures/grandchild-include.adoc +0 -3
  118. data/test/fixtures/hello-asciidoctor.pdf +0 -69
  119. data/test/fixtures/include-file.asciidoc +0 -24
  120. data/test/fixtures/include-file.jsx +0 -8
  121. data/test/fixtures/include-file.ml +0 -3
  122. data/test/fixtures/include-file.xml +0 -5
  123. data/test/fixtures/lists.adoc +0 -96
  124. data/test/fixtures/master.adoc +0 -5
  125. data/test/fixtures/mismatched-end-tag.adoc +0 -7
  126. data/test/fixtures/other-chapters.adoc +0 -11
  127. data/test/fixtures/outer-include.adoc +0 -5
  128. data/test/fixtures/parent-include-restricted.adoc +0 -5
  129. data/test/fixtures/parent-include.adoc +0 -5
  130. data/test/fixtures/sample.asciidoc +0 -30
  131. data/test/fixtures/section-a.adoc +0 -4
  132. data/test/fixtures/stylesheets/custom.css +0 -3
  133. data/test/fixtures/subdir/index.adoc +0 -3
  134. data/test/fixtures/subdir/inner-include.adoc +0 -3
  135. data/test/fixtures/subdir/middle-include.adoc +0 -5
  136. data/test/fixtures/subs-docinfo.html +0 -2
  137. data/test/fixtures/subs.adoc +0 -6
  138. data/test/fixtures/tagged-class-enclosed.rb +0 -25
  139. data/test/fixtures/tagged-class.rb +0 -23
  140. data/test/fixtures/tip.gif +0 -0
  141. data/test/fixtures/unclosed-tag.adoc +0 -3
  142. data/test/fixtures/unexpected-end-tag.adoc +0 -4
  143. data/test/invoker_test.rb +0 -745
  144. data/test/links_test.rb +0 -855
  145. data/test/lists_test.rb +0 -5151
  146. data/test/logger_test.rb +0 -211
  147. data/test/manpage_test.rb +0 -660
  148. data/test/options_test.rb +0 -262
  149. data/test/paragraphs_test.rb +0 -562
  150. data/test/parser_test.rb +0 -742
  151. data/test/paths_test.rb +0 -395
  152. data/test/preamble_test.rb +0 -173
  153. data/test/reader_test.rb +0 -2161
  154. data/test/sections_test.rb +0 -3575
  155. data/test/substitutions_test.rb +0 -2066
  156. data/test/tables_test.rb +0 -2036
  157. data/test/test_helper.rb +0 -447
  158. data/test/text_test.rb +0 -309
@@ -1,51 +1,57 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  class AbstractBlock < AbstractNode
4
- # Public: Get the Array of Asciidoctor::AbstractBlock sub-blocks for this block
4
+ # Public: Get the Array of {AbstractBlock} child blocks for this block. Only applies if content model is :compound.
5
5
  attr_reader :blocks
6
6
 
7
- # Public: Set the caption for this block
7
+ # Public: Set the caption for this block.
8
8
  attr_writer :caption
9
9
 
10
- # Public: The types of content that this block can accomodate
10
+ # Public: Describes the type of content this block accepts and how it should be converted. Acceptable values are:
11
+ # * :compound - this block contains other blocks
12
+ # * :simple - this block holds a paragraph of prose that receives normal substitutions
13
+ # * :verbatim - this block holds verbatim text (displayed "as is") that receives verbatim substitutions
14
+ # * :raw - this block holds unprocessed content passed directly to the output with no sustitutions applied
15
+ # * :empty - this block has no content
11
16
  attr_accessor :content_model
12
17
 
13
- # Public: Set the Integer level of this Section or the Section level in which this Block resides
18
+ # Public: Set the Integer level of this {Section} or the level of the Section to which this {AbstractBlock} belongs.
14
19
  attr_accessor :level
15
20
 
16
- # Public: Get/Set the numeral of this block (if section, relative to parent, otherwise absolute)
17
- # Only assigned to section if automatic section numbering is enabled
18
- # Only assigned to formal block (block with title) if corresponding caption attribute is present
21
+ # Public: Get/Set the numeral of this block (if section, relative to parent, otherwise absolute).
22
+ # Only assigned to section if automatic section numbering is enabled.
23
+ # Only assigned to formal block (block with title) if corresponding caption attribute is present.
19
24
  attr_accessor :numeral
20
25
 
21
- # Deprecated: Legacy property to get/set the numeral of this block
26
+ # Deprecated: Legacy property to get/set the numeral of this block.
22
27
  alias number numeral
23
28
  alias number= numeral=
24
29
 
25
- # Public: Gets/Sets the location in the AsciiDoc source where this block begins
30
+ # Public: Gets/Sets the location in the AsciiDoc source where this block begins.
26
31
  attr_accessor :source_location
27
32
 
28
33
  # Public: Get/Set the String style (block type qualifier) for this block.
29
34
  attr_accessor :style
30
35
 
31
- # Public: Substitutions to be applied to content in this block
36
+ # Public: Substitutions to be applied to content in this block.
32
37
  attr_reader :subs
33
38
 
39
+ # Internal: Set the default subs applied to this block; used during block creation
40
+ attr_writer :default_subs
41
+
34
42
  def initialize parent, context, opts = {}
35
43
  super
36
44
  @content_model = :compound
37
45
  @blocks = []
38
46
  @subs = []
39
- @id = @title = @title_converted = @caption = @numeral = @style = @default_subs = @source_location = nil
40
- if context == :document
41
- @level = 0
42
- elsif parent && context != :section
43
- @level = parent.level
47
+ @id = @title = @caption = @numeral = @style = @default_subs = @source_location = nil
48
+ case context
49
+ when :document, :section
50
+ @level = @next_section_index = 0
51
+ @next_section_ordinal = 1
44
52
  else
45
- @level = nil
53
+ @level = AbstractBlock === parent ? parent.level : nil
46
54
  end
47
- @next_section_index = 0
48
- @next_section_ordinal = 1
49
55
  end
50
56
 
51
57
  def block?
@@ -101,10 +107,10 @@ class AbstractBlock < AbstractNode
101
107
  #
102
108
  # Examples
103
109
  #
104
- # block = Block.new(parent, :preamble, :content_model => :compound)
110
+ # block = Block.new(parent, :preamble, content_model: :compound)
105
111
  #
106
- # block << Block.new(block, :paragraph, :source => 'p1')
107
- # block << Block.new(block, :paragraph, :source => 'p2')
112
+ # block << Block.new(block, :paragraph, source: 'p1')
113
+ # block << Block.new(block, :paragraph, source: 'p2')
108
114
  # block.blocks?
109
115
  # # => true
110
116
  # block.blocks.size
@@ -170,56 +176,6 @@ class AbstractBlock < AbstractNode
170
176
 
171
177
  alias query find_by
172
178
 
173
- # Internal: Performs the work for find_by, but does not handle the StopIteration exception.
174
- def find_by_internal selector = {}, result = [], &block
175
- if ((any_context = !(context_selector = selector[:context])) || context_selector == @context) &&
176
- (!(style_selector = selector[:style]) || style_selector == @style) &&
177
- (!(role_selector = selector[:role]) || (has_role? role_selector)) &&
178
- (!(id_selector = selector[:id]) || id_selector == @id)
179
- if id_selector
180
- result.replace block_given? ? ((yield self) ? [self] : []) : [self]
181
- raise ::StopIteration
182
- elsif block_given?
183
- if (verdict = yield self)
184
- case verdict
185
- when :skip_children
186
- result << self
187
- return result
188
- when :skip
189
- return result
190
- else
191
- result << self
192
- end
193
- end
194
- else
195
- result << self
196
- end
197
- end
198
-
199
- # process document header as a section if present
200
- if @context == :document && (any_context || context_selector == :section) && header?
201
- @header.find_by_internal selector, result, &block
202
- end
203
-
204
- unless context_selector == :document # optimization
205
- # yuck, dlist is a special case
206
- if @context == :dlist
207
- if any_context || context_selector != :section # optimization
208
- @blocks.flatten.each do |li|
209
- # NOTE the list item of a dlist can be nil, so we have to check
210
- li.find_by_internal selector, result, &block if li
211
- end
212
- end
213
- elsif
214
- @blocks.each do |b|
215
- next if (context_selector == :section && b.context != :section) # optimization
216
- b.find_by_internal selector, result, &block
217
- end
218
- end
219
- end
220
- result
221
- end
222
-
223
179
  # Move to the next adjacent block in document order. If the current block is the last
224
180
  # item in a list, this method will return the following sibling of the list block.
225
181
  def next_adjacent_block
@@ -234,12 +190,12 @@ class AbstractBlock < AbstractNode
234
190
  #
235
191
  # doc << (sect1 = Section.new doc, 1)
236
192
  # sect1.title = 'Section 1'
237
- # para1 = Block.new sect1, :paragraph, :source => 'Paragraph 1'
238
- # para2 = Block.new sect1, :paragraph, :source => 'Paragraph 2'
193
+ # para1 = Block.new sect1, :paragraph, source: 'Paragraph 1'
194
+ # para2 = Block.new sect1, :paragraph, source: 'Paragraph 2'
239
195
  # sect1 << para1 << para2
240
196
  # sect1 << (sect1_1 = Section.new sect1, 2)
241
197
  # sect1_1.title = 'Section 1.1'
242
- # sect1_1 << (Block.new sect1_1, :paragraph, :source => 'Paragraph 3')
198
+ # sect1_1 << (Block.new sect1_1, :paragraph, source: 'Paragraph 3')
243
199
  # sect1.blocks?
244
200
  # # => true
245
201
  # sect1.blocks.size
@@ -318,7 +274,7 @@ class AbstractBlock < AbstractNode
318
274
  # Returns the converted String title for this Block, or nil if the source title is falsy
319
275
  def title
320
276
  # prevent substitutions from being applied to title multiple times
321
- @title_converted ? @converted_title : (@converted_title = (@title_converted = true) && @title && (apply_title_subs @title))
277
+ @converted_title ||= @title && (apply_title_subs @title)
322
278
  end
323
279
 
324
280
  # Public: A convenience method that checks whether the title of this block is defined.
@@ -332,7 +288,8 @@ class AbstractBlock < AbstractNode
332
288
  #
333
289
  # Returns the new String title assigned to this Block
334
290
  def title= val
335
- @title, @title_converted = val, nil
291
+ @converted_title = nil
292
+ @title = val
336
293
  end
337
294
 
338
295
  # Public: A convenience method that checks whether the specified
@@ -379,7 +336,7 @@ class AbstractBlock < AbstractNode
379
336
  elsif xrefstyle && @title && @caption
380
337
  case xrefstyle
381
338
  when 'full'
382
- quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title
339
+ quoted_title = sub_placeholder (sub_quotes @document.compat_mode ? %q(``%s'') : '"`%s`"'), title
383
340
  if @numeral && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)])
384
341
  %(#{prefix} #{@numeral}, #{quoted_title})
385
342
  else
@@ -477,35 +434,69 @@ class AbstractBlock < AbstractNode
477
434
  end
478
435
  end
479
436
 
480
- # stage the Enumerable mixin until we're sure we've got it right
481
- =begin
482
- include ::Enumerable
483
-
484
- # Public: Yield the block on this block node and all its descendant
485
- # block node children to satisfy the Enumerable contract.
486
- #
487
- # Returns nothing
488
- def each &block
489
- # yucky, dlist is a special case
490
- if @context == :dlist
491
- @blocks.flatten.each &block
492
- else
493
- #yield self.header if @context == :document && header?
494
- @blocks.each &block
437
+ # Internal: Performs the work for find_by, but does not handle the StopIteration exception.
438
+ protected def find_by_internal selector = {}, result = [], &block
439
+ if ((any_context = (context_selector = selector[:context]) ? nil : true) || context_selector == @context) &&
440
+ (!(style_selector = selector[:style]) || style_selector == @style) &&
441
+ (!(role_selector = selector[:role]) || (has_role? role_selector)) &&
442
+ (!(id_selector = selector[:id]) || id_selector == @id)
443
+ if id_selector
444
+ block_given? && !(yield self) ? result.clear : (result.replace [self])
445
+ raise ::StopIteration
446
+ elsif block_given?
447
+ if (verdict = yield self)
448
+ case verdict
449
+ when :skip_children
450
+ result << self
451
+ return result
452
+ when :skip
453
+ return result
454
+ else
455
+ result << self
456
+ end
457
+ end
458
+ else
459
+ result << self
460
+ end
495
461
  end
496
- end
497
-
498
- #--
499
- # TODO is there a way to make this lazy?
500
- def each_recursive &block
501
- block = lambda {|node| node } unless block_given?
502
- results = []
503
- self.each do |node|
504
- results << block.call(node)
505
- results.concat(node.each_recursive(&block)) if ::Enumerable === node
462
+ case @context
463
+ when :document
464
+ unless context_selector == :document
465
+ # process document header as a section, if present
466
+ if header? && (any_context || context_selector == :section)
467
+ @header.find_by_internal selector, result, &block
468
+ end
469
+ @blocks.each do |b|
470
+ next if (context_selector == :section && b.context != :section) # optimization
471
+ b.find_by_internal selector, result, &block
472
+ end
473
+ end
474
+ when :dlist
475
+ # dlist has different structure than other blocks
476
+ if any_context || context_selector != :section # optimization
477
+ # NOTE the list item of a dlist can be nil, so we have to check
478
+ @blocks.flatten.each {|b| b.find_by_internal selector, result, &block if b }
479
+ end
480
+ when :table
481
+ if selector[:traverse_documents]
482
+ rows.head.each {|r| r.each {|c| c.find_by_internal selector, result, &block } }
483
+ selector = selector.merge context: :document if context_selector == :inner_document
484
+ (rows.body + rows.foot).each do |r|
485
+ r.each do |c|
486
+ c.find_by_internal selector, result, &block
487
+ c.inner_document.find_by_internal selector, result, &block if c.style == :asciidoc
488
+ end
489
+ end
490
+ else
491
+ (rows.head + rows.body + rows.foot).each {|r| r.each {|c| c.find_by_internal selector, result, &block } }
492
+ end
493
+ else
494
+ @blocks.each do |b|
495
+ next if (context_selector == :section && b.context != :section) # optimization
496
+ b.find_by_internal selector, result, &block
497
+ end
506
498
  end
507
- block_given? ? results : results.to_enum
499
+ result
508
500
  end
509
- =end
510
501
  end
511
502
  end
@@ -1,11 +1,10 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
  module Asciidoctor
3
3
  # Public: An abstract base class that provides state and methods for managing a
4
4
  # node of AsciiDoc content. The state and methods on this class are common to
5
5
  # all content segments in an AsciiDoc document.
6
6
  class AbstractNode
7
- include Logging
8
- include Substitutors
7
+ include Substitutors, Logging
9
8
 
10
9
  # Public: Get the Hash of attributes for this node
11
10
  attr_reader :attributes
@@ -16,13 +15,13 @@ class AbstractNode
16
15
  # Public: Get the Asciidoctor::Document to which this node belongs
17
16
  attr_reader :document
18
17
 
19
- # Public: Get/Set the id of this node
18
+ # Public: Get/Set the String id of this node
20
19
  attr_accessor :id
21
20
 
22
21
  # Public: Get the String name of this node
23
22
  attr_reader :node_name
24
23
 
25
- # Public: Get the element which is the parent of this node
24
+ # Public: Get the AbstractBlock parent element of this node
26
25
  attr_reader :parent
27
26
 
28
27
  def initialize parent, context, opts = {}
@@ -39,7 +38,7 @@ class AbstractNode
39
38
  @node_name = (@context = context).to_s
40
39
  # QUESTION are we correct in duplicating the attributes (seems to be just as fast)
41
40
  @attributes = (opts.key? :attributes) ? opts[:attributes].dup : {}
42
- @passthroughs = {}
41
+ @passthroughs = []
43
42
  end
44
43
 
45
44
  # Public: Returns whether this {AbstractNode} is an instance of {Block}
@@ -75,51 +74,43 @@ class AbstractNode
75
74
  @parent, @document = parent, parent.document
76
75
  end
77
76
 
78
- # Public: Get the value of the specified attribute
79
- #
80
- # Get the value for the specified attribute. First look in the attributes on
81
- # this node and return the value of the attribute if found. Otherwise, if
82
- # this node is a child of the Document node, look in the attributes of the
83
- # Document node and return the value of the attribute if found. Otherwise,
84
- # return the default value, which defaults to nil.
85
- #
86
- # name - the String or Symbol name of the attribute to lookup
87
- # default_val - the Object value to return if the attribute is not found (default: nil)
88
- # inherit - a Boolean indicating whether to check for the attribute on the
89
- # AsciiDoctor::Document if not found on this node (default: false)
90
- #
91
- # return the value of the attribute or the default value if the attribute
92
- # is not found in the attributes of this node or the document node
93
- def attr name, default_val = nil, inherit = true
94
- name = name.to_s
95
- # NOTE if @parent is set, it means @document is also set
96
- @attributes[name] || (inherit && @parent ? @document.attributes[name] || default_val : default_val)
97
- end
98
-
99
- # Public: Check if the attribute is defined, optionally performing a
100
- # comparison of its value if expected is not nil
101
- #
102
- # Check if the attribute is defined. First look in the attributes on this
103
- # node. If not found, and this node is a child of the Document node, look in
104
- # the attributes of the Document node. If the attribute is found and a
105
- # comparison value is specified (not nil), return whether the two values match.
106
- # Otherwise, return whether the attribute was found.
107
- #
108
- # name - the String or Symbol name of the attribute to lookup
109
- # expect_val - the expected Object value of the attribute (default: nil)
110
- # inherit - a Boolean indicating whether to check for the attribute on the
111
- # AsciiDoctor::Document if not found on this node (default: false)
112
- #
113
- # return a Boolean indicating whether the attribute exists and, if a
114
- # comparison value is specified, whether the value of the attribute matches
115
- # the comparison value
116
- def attr? name, expect_val = nil, inherit = true
117
- name = name.to_s
118
- # NOTE if @parent is set, it means @document is also set
119
- if expect_val.nil?
120
- (@attributes.key? name) || (inherit && @parent && (@document.attributes.key? name))
77
+ # Public: Get the value of the specified attribute. If the attribute is not found on this node, fallback_name is set,
78
+ # and this node is not the Document node, get the value of the specified attribute from the Document node.
79
+ #
80
+ # Look for the specified attribute in the attributes on this node and return the value of the attribute, if found.
81
+ # Otherwise, if fallback_name is set (default: same as name) and this node is not the Document node, look for that
82
+ # attribute on the Document node and return its value, if found. Otherwise, return the default value (default: nil).
83
+ #
84
+ # name - The String or Symbol name of the attribute to resolve.
85
+ # default_value - The Object value to return if the attribute is not found (default: nil).
86
+ # fallback_name - The String or Symbol of the attribute to resolve on the Document if the attribute is not found on
87
+ # this node (default: same as name).
88
+ #
89
+ # Returns the [Object] value (typically a String) of the attribute or default_value if the attribute is not found.
90
+ def attr name, default_value = nil, fallback_name = nil
91
+ @attributes[name.to_s] || (fallback_name && @parent && @document.attributes[(fallback_name == true ? name : fallback_name).to_s] || default_value)
92
+ end
93
+
94
+ # Public: Check if the specified attribute is defined using the same logic as {#attr}, optionally performing a
95
+ # comparison with the expected value if specified.
96
+ #
97
+ # Look for the specified attribute in the attributes on this node. If not found, fallback_name is specified (default:
98
+ # same as name), and this node is not the Document node, look for that attribute on the Document node. In either case,
99
+ # if the attribute is found, and the comparison value is truthy, return whether the two values match. Otherwise,
100
+ # return whether the attribute was found.
101
+ #
102
+ # name - The String or Symbol name of the attribute to resolve.
103
+ # expected_value - The expected Object value of the attribute (default: nil).
104
+ # fallback_name - The String or Symbol of the attribute to resolve on the Document if the attribute is not found on
105
+ # this node (default: same as name).
106
+ #
107
+ # Returns a [Boolean] indicating whether the attribute exists and, if a truthy comparison value is specified, whether
108
+ # the value of the attribute matches the comparison value.
109
+ def attr? name, expected_value = nil, fallback_name = nil
110
+ if expected_value
111
+ expected_value == (@attributes[name.to_s] || (fallback_name && @parent ? @document.attributes[(fallback_name == true ? name : fallback_name).to_s] : nil))
121
112
  else
122
- expect_val == (@attributes[name] || (inherit && @parent ? @document.attributes[name] : nil))
113
+ (@attributes.key? name.to_s) || (fallback_name && @parent ? (@document.attributes.key? (fallback_name == true ? name : fallback_name).to_s) : false)
123
114
  end
124
115
  end
125
116
 
@@ -153,34 +144,32 @@ class AbstractNode
153
144
  # enabled on the current node.
154
145
  #
155
146
  # Check if the option is enabled. This method simply checks to see if the
156
- # %name%-option attribute is defined on the current node.
147
+ # <name>-option attribute is defined on the current node.
157
148
  #
158
149
  # name - the String or Symbol name of the option
159
150
  #
160
151
  # return a Boolean indicating whether the option has been specified
161
- def option?(name)
162
- @attributes.key? %(#{name}-option)
152
+ def option? name
153
+ @attributes[%(#{name}-option)] ? true : false
163
154
  end
164
155
 
165
156
  # Public: Set the specified option on this node.
166
157
  #
167
- # This method sets the specified option on this node if not already set.
168
- # It will add the name to the options attribute and set the <name>-option
169
- # attribute.
158
+ # This method sets the specified option on this node by setting the <name>-option attribute.
170
159
  #
171
160
  # name - the String name of the option
172
161
  #
173
- # returns truthy if the option was set or falsey if the option was already set
174
- def set_option(name)
175
- if (attrs = @attributes)['options']
176
- unless attrs[key = %(#{name}-option)]
177
- attrs['options'] += %(,#{name})
178
- attrs[key] = ''
179
- end
180
- else
181
- attrs['options'] = name
182
- attrs[%(#{name}-option)] = ''
183
- end
162
+ # Returns Nothing
163
+ def set_option name
164
+ @attributes[%(#{name}-option)] = ''
165
+ nil
166
+ end
167
+
168
+ # Public: Retrieve the Set of option names that are set on this node
169
+ #
170
+ # Returns a [Set] of option names
171
+ def options
172
+ ::Set.new.tap {|accum| @attributes.each_key {|k| accum << (k.slice 0, k.length - 7) if k.to_s.end_with? '-option' } }
184
173
  end
185
174
 
186
175
  # Public: Update the attributes of this node with the new values in
@@ -197,59 +186,66 @@ class AbstractNode
197
186
  nil
198
187
  end
199
188
 
200
- # Public: A convenience method that returns the value of the role attribute
189
+ # Public: Retrieves the space-separated String role for this node.
190
+ #
191
+ # Returns the role as a space-separated [String].
201
192
  def role
202
- @attributes['role'] || @document.attributes['role']
193
+ @attributes['role']
203
194
  end
204
195
 
205
- # Public: A convenience method that returns the role names as an Array
196
+ # Public: Retrieves the String role names for this node as an Array.
206
197
  #
207
- # Returns the role names as an Array or an empty Array if the role attribute is absent.
198
+ # Returns the role names as a String [Array], which is empty if the role attribute is absent on this node.
208
199
  def roles
209
- (val = @attributes['role'] || @document.attributes['role']).nil_or_empty? ? [] : val.split
200
+ (val = @attributes['role']) ? val.split : []
210
201
  end
211
202
 
212
- # Public: A convenience method that checks if the role attribute is specified
213
- def role? expect_val = nil
214
- if expect_val
215
- expect_val == (@attributes['role'] || @document.attributes['role'])
216
- else
217
- @attributes.key?('role') || @document.attributes.key?('role')
218
- end
203
+ # Public: Checks if the role attribute is set on this node and, if an expected value is given, whether the
204
+ # space-separated role matches that value.
205
+ #
206
+ # expected_value - The expected String value of the role (optional, default: nil)
207
+ #
208
+ # Returns a [Boolean] indicating whether the role attribute is set on this node and, if an expected value is given,
209
+ # whether the space-separated role matches that value.
210
+ def role? expected_value = nil
211
+ expected_value ? expected_value == @attributes['role'] : (@attributes.key? 'role')
219
212
  end
220
213
 
221
- # Public: A convenience method that checks if the specified role is present
222
- # in the list of roles on this node
223
- def has_role?(name)
214
+ # Public: Checks if the specified role is present in the list of roles for this node.
215
+ #
216
+ # name - The String name of the role to find.
217
+ #
218
+ # Returns a [Boolean] indicating whether this node has the specified role.
219
+ def has_role? name
224
220
  # NOTE center + include? is faster than split + include?
225
- (val = @attributes['role'] || @document.attributes['role']) ? %( #{val} ).include?(%( #{name} )) : false
221
+ (val = @attributes['role']) ? (%( #{val} ).include? %( #{name} )) : false
226
222
  end
227
223
 
228
- # Public: A convenience method that adds the given role directly to this node
224
+ # Public: Adds the given role directly to this node.
229
225
  #
230
- # Returns a Boolean indicating whether the role was added.
231
- def add_role(name)
232
- if (val = @attributes['role']).nil_or_empty?
233
- @attributes['role'] = name
234
- true
235
- # NOTE center + include? is faster than split + include?
236
- elsif %( #{val} ).include?(%( #{name} ))
237
- false
226
+ # Returns a [Boolean] indicating whether the role was added.
227
+ def add_role name
228
+ if (val = @attributes['role'])
229
+ # NOTE center + include? is faster than split + include?
230
+ if %( #{val} ).include? %( #{name} )
231
+ false
232
+ else
233
+ @attributes['role'] = %(#{val} #{name})
234
+ true
235
+ end
238
236
  else
239
- @attributes['role'] = %(#{val} #{name})
237
+ @attributes['role'] = name
240
238
  true
241
239
  end
242
240
  end
243
241
 
244
- # Public: A convenience method that removes the given role directly from this node
242
+ # Public: Removes the given role directly from this node.
245
243
  #
246
- # Returns a Boolean indicating whether the role was removed.
247
- def remove_role(name)
248
- if (val = @attributes['role']).nil_or_empty?
249
- false
250
- elsif (val = val.split).delete name
244
+ # Returns a [Boolean] indicating whether the role was removed.
245
+ def remove_role name
246
+ if (val = @attributes['role']) && ((val = val.split).delete name)
251
247
  if val.empty?
252
- @attributes.delete('role')
248
+ @attributes.delete 'role'
253
249
  else
254
250
  @attributes['role'] = val.join ' '
255
251
  end
@@ -273,12 +269,12 @@ class AbstractNode
273
269
  # specified icon name.
274
270
  #
275
271
  # If the 'icon' attribute is set on this block, the name is ignored and the
276
- # value of this attribute is used as the target image path. Otherwise,
272
+ # value of this attribute is used as the target image path. Otherwise,
277
273
  # construct a target image path by concatenating the value of the 'iconsdir'
278
- # attribute, the icon name and the value of the 'icontype' attribute
274
+ # attribute, the icon name, and the value of the 'icontype' attribute
279
275
  # (defaulting to 'png').
280
276
  #
281
- # The target image path is then passed through the #image_uri() method. If
277
+ # The target image path is then passed through the #image_uri() method. If
282
278
  # the 'data-uri' attribute is set on the document, the image will be
283
279
  # safely converted to a data URI.
284
280
  #
@@ -289,7 +285,8 @@ class AbstractNode
289
285
  # Returns A String reference or data URI for an icon image
290
286
  def icon_uri name
291
287
  if attr? 'icon'
292
- if ::File.extname(icon = (attr 'icon')).empty?
288
+ # Ruby 2.3 requires the extra brackets around the attr method call
289
+ if (::File.extname (icon = (attr 'icon'))).empty?
293
290
  # QUESTION should we be adding the extension if the icon is an absolute URI?
294
291
  icon = %(#{icon}.#{@document.attr 'icontype', 'png'})
295
292
  end
@@ -320,7 +317,7 @@ class AbstractNode
320
317
  # Returns A String reference or data URI for the target image
321
318
  def image_uri(target_image, asset_dir_key = 'imagesdir')
322
319
  if (doc = @document).safe < SafeMode::SECURE && (doc.attr? 'data-uri')
323
- if ((Helpers.uriish? target_image) && (target_image = uri_encode_spaces target_image)) ||
320
+ if ((Helpers.uriish? target_image) && (target_image = Helpers.encode_uri target_image)) ||
324
321
  (asset_dir_key && (images_base = doc.attr asset_dir_key) && (Helpers.uriish? images_base) &&
325
322
  (target_image = normalize_web_path target_image, images_base, false))
326
323
  if doc.attr? 'allow-uri-read'
@@ -371,14 +368,14 @@ class AbstractNode
371
368
  # QUESTION what if ext is empty?
372
369
  mimetype = (ext == '.svg' ? 'image/svg+xml' : %(image/#{ext.slice 1, ext.length}))
373
370
  if asset_dir_key
374
- image_path = normalize_system_path(target_image, @document.attr(asset_dir_key), nil, :target_name => 'image')
371
+ image_path = normalize_system_path(target_image, @document.attr(asset_dir_key), nil, target_name: 'image')
375
372
  else
376
373
  image_path = normalize_system_path(target_image)
377
374
  end
378
375
 
379
376
  if ::File.readable? image_path
380
377
  # NOTE base64 is autoloaded by reference to ::Base64
381
- %(data:#{mimetype};base64,#{::Base64.strict_encode64 ::IO.binread image_path})
378
+ %(data:#{mimetype};base64,#{::Base64.strict_encode64 ::File.binread image_path})
382
379
  else
383
380
  logger.warn %(image to embed not found or not readable: #{image_path})
384
381
  %(data:#{mimetype};base64,)
@@ -404,17 +401,13 @@ class AbstractNode
404
401
  # caching requires the open-uri-cached gem to be installed
405
402
  # processing will be automatically aborted if these libraries can't be opened
406
403
  Helpers.require_library 'open-uri/cached', 'open-uri-cached'
407
- elsif !::RUBY_ENGINE_OPAL
404
+ elsif !RUBY_ENGINE_OPAL
408
405
  # autoload open-uri
409
406
  ::OpenURI
410
407
  end
411
408
 
412
409
  begin
413
- mimetype = nil
414
- bindata = open image_uri, 'rb' do |f|
415
- mimetype = f.content_type
416
- f.read
417
- end
410
+ mimetype, bindata = ::OpenURI.open_uri(image_uri, URI_READ_MODE) {|f| [f.content_type, f.read] }
418
411
  # NOTE base64 is autoloaded by reference to ::Base64
419
412
  %(data:#{mimetype};base64,#{::Base64.strict_encode64 bindata})
420
413
  rescue
@@ -432,8 +425,7 @@ class AbstractNode
432
425
  # Delegates to normalize_system_path, with the start path set to the value of
433
426
  # the base_dir instance variable on the Document object.
434
427
  def normalize_asset_path(asset_ref, asset_name = 'path', autocorrect = true)
435
- normalize_system_path(asset_ref, @document.base_dir, nil,
436
- :target_name => asset_name, :recover => autocorrect)
428
+ normalize_system_path(asset_ref, @document.base_dir, nil, target_name: asset_name, recover: autocorrect)
437
429
  end
438
430
 
439
431
  # Public: Resolve and normalize a secure path from the target and start paths
@@ -486,7 +478,7 @@ class AbstractNode
486
478
  # Returns the resolved [String] path
487
479
  def normalize_web_path(target, start = nil, preserve_uri_target = true)
488
480
  if preserve_uri_target && (Helpers.uriish? target)
489
- uri_encode_spaces target
481
+ Helpers.encode_uri target
490
482
  else
491
483
  @document.path_resolver.web_path target, start
492
484
  end
@@ -507,15 +499,10 @@ class AbstractNode
507
499
  # if the file does not exist.
508
500
  def read_asset path, opts = {}
509
501
  # remap opts for backwards compatibility
510
- opts = { :warn_on_failure => (opts != false) } unless ::Hash === opts
502
+ opts = { warn_on_failure: (opts != false) } unless ::Hash === opts
511
503
  if ::File.readable? path
512
- if opts[:normalize]
513
- # NOTE Opal does not yet support File#readlines
514
- (Helpers.normalize_lines_array ::File.open(path, 'rb') {|f| f.each_line.to_a }).join LF
515
- else
516
- # QUESTION should we chomp or rstrip content?
517
- ::IO.read path
518
- end
504
+ # QUESTION should we chomp content if normalize is false?
505
+ opts[:normalize] ? ((Helpers.prepare_source_string ::File.read path, mode: FILE_READ_MODE).join LF) : (::File.read path, mode: FILE_READ_MODE)
519
506
  elsif opts[:warn_on_failure]
520
507
  logger.warn %(#{(attr 'docfile') || '<stdin>'}: #{opts[:label] || 'file'} does not exist or cannot be read: #{path})
521
508
  nil
@@ -546,10 +533,9 @@ class AbstractNode
546
533
  Helpers.require_library 'open-uri/cached', 'open-uri-cached' if doc.attr? 'cache-uri'
547
534
  begin
548
535
  if opts[:normalize]
549
- # NOTE Opal does not yet support File#readlines
550
- (Helpers.normalize_lines_array ::OpenURI.open_uri(target) {|f| f.each_line.to_a }).join LF
536
+ (Helpers.prepare_source_string ::OpenURI.open_uri(target, URI_READ_MODE) {|f| f.read }).join LF
551
537
  else
552
- ::OpenURI.open_uri(target) {|f| f.read }
538
+ ::OpenURI.open_uri(target, URI_READ_MODE) {|f| f.read }
553
539
  end
554
540
  rescue
555
541
  logger.warn %(could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
@@ -560,23 +546,16 @@ class AbstractNode
560
546
  return
561
547
  end
562
548
  else
563
- target = normalize_system_path target, opts[:start], nil, :target_name => (opts[:label] || 'asset')
564
- read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true), :label => opts[:label]
549
+ target = normalize_system_path target, opts[:start], nil, target_name: (opts[:label] || 'asset')
550
+ read_asset target, normalize: opts[:normalize], warn_on_failure: (opts.fetch :warn_on_failure, true), label: opts[:label]
565
551
  end
566
552
  end
567
553
 
568
- # Internal: URI encode spaces in a String
569
- #
570
- # str - the String to encode
571
- #
572
- # Returns the String with all spaces replaced with %20.
573
- def uri_encode_spaces str
574
- (str.include? ' ') ? (str.gsub ' ', '%20') : str
575
- end
576
-
577
- # Public: Check whether the specified String is a URI by
554
+ # Deprecated: Check whether the specified String is a URI by
578
555
  # matching it against the Asciidoctor::UriSniffRx regex.
579
556
  #
557
+ # In use by Asciidoctor PDF
558
+ #
580
559
  # @deprecated Use Helpers.uriish? instead
581
560
  def is_uri? str
582
561
  Helpers.uriish? str