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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +162 -17
- data/LICENSE +1 -1
- data/README-de.adoc +12 -13
- data/README-fr.adoc +11 -12
- data/README-jp.adoc +11 -12
- data/README-zh_CN.adoc +12 -13
- data/README.adoc +6 -7
- data/asciidoctor.gemspec +19 -24
- data/bin/asciidoctor +5 -4
- data/data/reference/syntax.adoc +283 -0
- data/data/stylesheets/asciidoctor-default.css +56 -52
- data/data/stylesheets/coderay-asciidoctor.css +7 -9
- data/lib/asciidoctor.rb +171 -232
- data/lib/asciidoctor/abstract_block.rb +96 -105
- data/lib/asciidoctor/abstract_node.rb +118 -139
- data/lib/asciidoctor/attribute_list.rb +10 -14
- data/lib/asciidoctor/block.rb +20 -19
- data/lib/asciidoctor/callouts.rb +4 -2
- data/lib/asciidoctor/cli.rb +3 -2
- data/lib/asciidoctor/cli/invoker.rb +14 -21
- data/lib/asciidoctor/cli/options.rb +64 -54
- data/lib/asciidoctor/converter.rb +357 -185
- data/lib/asciidoctor/converter/composite.rb +40 -48
- data/lib/asciidoctor/converter/docbook5.rb +604 -640
- data/lib/asciidoctor/converter/html5.rb +949 -963
- data/lib/asciidoctor/converter/manpage.rb +569 -548
- data/lib/asciidoctor/converter/template.rb +231 -272
- data/lib/asciidoctor/core_ext.rb +5 -18
- data/lib/asciidoctor/core_ext/float/truncate.rb +19 -0
- data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
- data/lib/asciidoctor/document.rb +399 -377
- data/lib/asciidoctor/extensions.rb +72 -140
- data/lib/asciidoctor/helpers.rb +122 -83
- data/lib/asciidoctor/inline.rb +5 -1
- data/lib/asciidoctor/list.rb +13 -11
- data/lib/asciidoctor/logging.rb +17 -16
- data/lib/asciidoctor/parser.rb +390 -423
- data/lib/asciidoctor/path_resolver.rb +10 -5
- data/lib/asciidoctor/reader.rb +286 -263
- data/lib/asciidoctor/rouge_ext.rb +39 -0
- data/lib/asciidoctor/section.rb +9 -8
- data/lib/asciidoctor/stylesheets.rb +19 -37
- data/lib/asciidoctor/substitutors.rb +364 -509
- data/lib/asciidoctor/syntax_highlighter.rb +238 -0
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +87 -0
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +26 -0
- data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
- data/lib/asciidoctor/syntax_highlighter/prettify.rb +27 -0
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +149 -0
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +129 -0
- data/lib/asciidoctor/table.rb +73 -66
- data/lib/asciidoctor/timings.rb +4 -2
- data/lib/asciidoctor/version.rb +2 -1
- data/lib/asciidoctor/writer.rb +30 -0
- data/man/asciidoctor.1 +19 -15
- data/man/asciidoctor.adoc +14 -12
- metadata +69 -216
- data/CONTRIBUTING.adoc +0 -185
- data/Gemfile +0 -60
- data/Rakefile +0 -129
- data/bin/asciidoctor-safe +0 -15
- data/features/open_block.feature +0 -92
- data/features/pass_block.feature +0 -66
- data/features/step_definitions.rb +0 -49
- data/features/text_formatting.feature +0 -57
- data/features/xref.feature +0 -1039
- data/lib/asciidoctor/converter/base.rb +0 -59
- data/lib/asciidoctor/converter/docbook45.rb +0 -93
- data/lib/asciidoctor/converter/factory.rb +0 -226
- data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
- data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
- data/test/api_test.rb +0 -1240
- data/test/attribute_list_test.rb +0 -242
- data/test/attributes_test.rb +0 -1623
- data/test/blocks_test.rb +0 -3870
- data/test/converter_test.rb +0 -470
- data/test/document_test.rb +0 -1853
- data/test/extensions_test.rb +0 -1560
- data/test/fixtures/asciidoc_index.txt +0 -521
- data/test/fixtures/basic-docinfo-footer.html +0 -6
- data/test/fixtures/basic-docinfo-footer.xml +0 -8
- data/test/fixtures/basic-docinfo.html +0 -1
- data/test/fixtures/basic-docinfo.xml +0 -4
- data/test/fixtures/basic.asciidoc +0 -5
- data/test/fixtures/chapter-a.adoc +0 -3
- data/test/fixtures/child-include.adoc +0 -5
- data/test/fixtures/circle.svg +0 -9
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
- data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
- data/test/fixtures/docinfo-footer.html +0 -1
- data/test/fixtures/docinfo-footer.xml +0 -9
- data/test/fixtures/docinfo.html +0 -1
- data/test/fixtures/docinfo.xml +0 -3
- data/test/fixtures/doctime-localtime.adoc +0 -2
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +0 -13
- data/test/fixtures/file-with-missing-include.adoc +0 -1
- data/test/fixtures/grandchild-include.adoc +0 -3
- data/test/fixtures/hello-asciidoctor.pdf +0 -69
- data/test/fixtures/include-file.asciidoc +0 -24
- data/test/fixtures/include-file.jsx +0 -8
- data/test/fixtures/include-file.ml +0 -3
- data/test/fixtures/include-file.xml +0 -5
- data/test/fixtures/lists.adoc +0 -96
- data/test/fixtures/master.adoc +0 -5
- data/test/fixtures/mismatched-end-tag.adoc +0 -7
- data/test/fixtures/other-chapters.adoc +0 -11
- data/test/fixtures/outer-include.adoc +0 -5
- data/test/fixtures/parent-include-restricted.adoc +0 -5
- data/test/fixtures/parent-include.adoc +0 -5
- data/test/fixtures/sample.asciidoc +0 -30
- data/test/fixtures/section-a.adoc +0 -4
- data/test/fixtures/stylesheets/custom.css +0 -3
- data/test/fixtures/subdir/index.adoc +0 -3
- data/test/fixtures/subdir/inner-include.adoc +0 -3
- data/test/fixtures/subdir/middle-include.adoc +0 -5
- data/test/fixtures/subs-docinfo.html +0 -2
- data/test/fixtures/subs.adoc +0 -6
- data/test/fixtures/tagged-class-enclosed.rb +0 -25
- data/test/fixtures/tagged-class.rb +0 -23
- data/test/fixtures/tip.gif +0 -0
- data/test/fixtures/unclosed-tag.adoc +0 -3
- data/test/fixtures/unexpected-end-tag.adoc +0 -4
- data/test/invoker_test.rb +0 -745
- data/test/links_test.rb +0 -855
- data/test/lists_test.rb +0 -5151
- data/test/logger_test.rb +0 -211
- data/test/manpage_test.rb +0 -660
- data/test/options_test.rb +0 -262
- data/test/paragraphs_test.rb +0 -562
- data/test/parser_test.rb +0 -742
- data/test/paths_test.rb +0 -395
- data/test/preamble_test.rb +0 -173
- data/test/reader_test.rb +0 -2161
- data/test/sections_test.rb +0 -3575
- data/test/substitutions_test.rb +0 -2066
- data/test/tables_test.rb +0 -2036
- data/test/test_helper.rb +0 -447
- data/test/text_test.rb +0 -309
@@ -1,51 +1,57 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
class AbstractBlock < AbstractNode
|
4
|
-
# Public: Get the Array of
|
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:
|
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
|
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 = @
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@
|
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, :
|
110
|
+
# block = Block.new(parent, :preamble, content_model: :compound)
|
105
111
|
#
|
106
|
-
# block << Block.new(block, :paragraph, :
|
107
|
-
# block << Block.new(block, :paragraph, :
|
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, :
|
238
|
-
# para2 = Block.new sect1, :paragraph, :
|
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, :
|
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
|
-
@
|
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
|
-
@
|
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 =
|
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
|
-
#
|
481
|
-
=
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
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
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
-
|
499
|
+
result
|
508
500
|
end
|
509
|
-
=end
|
510
501
|
end
|
511
502
|
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
#
|
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
|
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
|
-
#
|
81
|
-
# this node and return the value of the attribute if found.
|
82
|
-
# this node is
|
83
|
-
# Document node and return
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
|
111
|
-
|
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
|
-
|
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
|
-
#
|
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?
|
162
|
-
@attributes
|
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
|
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
|
-
#
|
174
|
-
def set_option
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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:
|
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']
|
193
|
+
@attributes['role']
|
203
194
|
end
|
204
195
|
|
205
|
-
# Public:
|
196
|
+
# Public: Retrieves the String role names for this node as an Array.
|
206
197
|
#
|
207
|
-
# Returns the role names as
|
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']
|
200
|
+
(val = @attributes['role']) ? val.split : []
|
210
201
|
end
|
211
202
|
|
212
|
-
# Public:
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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:
|
222
|
-
#
|
223
|
-
|
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']
|
221
|
+
(val = @attributes['role']) ? (%( #{val} ).include? %( #{name} )) : false
|
226
222
|
end
|
227
223
|
|
228
|
-
# Public:
|
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
|
232
|
-
if (val = @attributes['role'])
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
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'] =
|
237
|
+
@attributes['role'] = name
|
240
238
|
true
|
241
239
|
end
|
242
240
|
end
|
243
241
|
|
244
|
-
# Public:
|
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
|
248
|
-
if (val = @attributes['role']).
|
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
|
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
|
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.
|
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
|
-
|
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 =
|
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, :
|
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 ::
|
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
|
404
|
+
elsif !RUBY_ENGINE_OPAL
|
408
405
|
# autoload open-uri
|
409
406
|
::OpenURI
|
410
407
|
end
|
411
408
|
|
412
409
|
begin
|
413
|
-
mimetype =
|
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
|
-
|
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 = { :
|
502
|
+
opts = { warn_on_failure: (opts != false) } unless ::Hash === opts
|
511
503
|
if ::File.readable? path
|
512
|
-
if
|
513
|
-
|
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
|
-
|
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, :
|
564
|
-
read_asset target, :
|
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
|
-
#
|
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
|