asciidoctor 1.5.8 → 2.0.0.rc.1

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