asciidoctor 1.5.5 → 1.5.6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +216 -1
- data/CONTRIBUTING.adoc +2 -2
- data/Gemfile +20 -1
- data/LICENSE.adoc +1 -1
- data/README-fr.adoc +4 -3
- data/README-jp.adoc +11 -10
- data/README-zh_CN.adoc +4 -3
- data/README.adoc +17 -202
- data/Rakefile +41 -25
- data/asciidoctor.gemspec +9 -10
- data/data/locale/attributes.adoc +216 -34
- data/data/stylesheets/asciidoctor-default.css +23 -16
- data/features/step_definitions.rb +15 -19
- data/features/xref.feature +584 -20
- data/lib/asciidoctor.rb +292 -278
- data/lib/asciidoctor/abstract_block.rb +155 -94
- data/lib/asciidoctor/abstract_node.rb +108 -94
- data/lib/asciidoctor/attribute_list.rb +30 -22
- data/lib/asciidoctor/block.rb +7 -7
- data/lib/asciidoctor/cli/invoker.rb +47 -34
- data/lib/asciidoctor/cli/options.rb +22 -11
- data/lib/asciidoctor/converter.rb +3 -3
- data/lib/asciidoctor/converter/base.rb +2 -2
- data/lib/asciidoctor/converter/composite.rb +1 -1
- data/lib/asciidoctor/converter/docbook45.rb +2 -2
- data/lib/asciidoctor/converter/docbook5.rb +132 -87
- data/lib/asciidoctor/converter/factory.rb +0 -1
- data/lib/asciidoctor/converter/html5.rb +116 -98
- data/lib/asciidoctor/converter/manpage.rb +51 -52
- data/lib/asciidoctor/converter/template.rb +47 -36
- data/lib/asciidoctor/core_ext.rb +8 -2
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
- data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
- data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
- data/lib/asciidoctor/document.rb +216 -213
- data/lib/asciidoctor/extensions.rb +318 -185
- data/lib/asciidoctor/helpers.rb +35 -35
- data/lib/asciidoctor/inline.rb +32 -1
- data/lib/asciidoctor/list.rb +22 -6
- data/lib/asciidoctor/parser.rb +1008 -1038
- data/lib/asciidoctor/path_resolver.rb +46 -50
- data/lib/asciidoctor/reader.rb +275 -251
- data/lib/asciidoctor/section.rb +86 -58
- data/lib/asciidoctor/stylesheets.rb +6 -6
- data/lib/asciidoctor/substitutors.rb +567 -649
- data/lib/asciidoctor/table.rb +163 -108
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +18 -16
- data/man/asciidoctor.adoc +15 -13
- data/test/attributes_test.rb +138 -22
- data/test/blocks_test.rb +377 -97
- data/test/converter_test.rb +13 -0
- data/test/document_test.rb +244 -34
- data/test/extensions_test.rb +409 -42
- data/test/fixtures/asciidoc_index.txt +521 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.html +1 -0
- data/test/fixtures/basic-docinfo.xml +4 -0
- data/test/fixtures/basic.asciidoc +5 -0
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/child-include.adoc +5 -0
- data/test/fixtures/circle.svg +9 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
- data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
- data/test/fixtures/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.html +1 -0
- data/test/fixtures/docinfo.xml +3 -0
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +13 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/hello-asciidoctor.pdf +69 -0
- data/test/fixtures/include-file.asciidoc +24 -0
- data/test/fixtures/include-file.ml +3 -0
- data/test/fixtures/include-file.xml +5 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/fixtures/mismatched-end-tag.adoc +7 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +26 -0
- data/test/fixtures/stylesheets/custom.css +3 -0
- data/test/fixtures/subs-docinfo.html +2 -0
- data/test/fixtures/subs.adoc +7 -0
- data/test/fixtures/tagged-class-enclosed.rb +26 -0
- data/test/fixtures/tagged-class.rb +23 -0
- data/test/fixtures/tip.gif +0 -0
- data/test/invoker_test.rb +82 -4
- data/test/links_test.rb +312 -37
- data/test/lists_test.rb +204 -25
- data/test/manpage_test.rb +191 -4
- data/test/options_test.rb +18 -1
- data/test/paragraphs_test.rb +32 -7
- data/test/parser_test.rb +150 -30
- data/test/paths_test.rb +47 -13
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +366 -126
- data/test/sections_test.rb +203 -56
- data/test/substitutions_test.rb +339 -131
- data/test/tables_test.rb +315 -15
- data/test/test_helper.rb +400 -0
- data/test/text_test.rb +5 -5
- metadata +110 -22
@@ -13,14 +13,16 @@ class AbstractBlock < AbstractNode
|
|
13
13
|
# Public: Set the Integer level of this Section or the Section level in which this Block resides
|
14
14
|
attr_accessor :level
|
15
15
|
|
16
|
-
# Public: Set the String block title.
|
17
|
-
attr_writer :title
|
18
|
-
|
19
16
|
# Public: Get/Set the String style (block type qualifier) for this block.
|
20
17
|
attr_accessor :style
|
21
18
|
|
22
|
-
# Public:
|
23
|
-
|
19
|
+
# Public: Set the caption for this block
|
20
|
+
attr_writer :caption
|
21
|
+
|
22
|
+
# Public: Get/Set the number of this block (if section, relative to parent, otherwise absolute)
|
23
|
+
# Only assigned to section if automatic section numbering is enabled
|
24
|
+
# Only assigned to formal block (block with title) if corresponding caption attribute is present
|
25
|
+
attr_accessor :number
|
24
26
|
|
25
27
|
# Public: Gets/Sets the location in the AsciiDoc source where this block begins
|
26
28
|
attr_accessor :source_location
|
@@ -28,21 +30,18 @@ class AbstractBlock < AbstractNode
|
|
28
30
|
def initialize parent, context, opts = {}
|
29
31
|
super
|
30
32
|
@content_model = :compound
|
31
|
-
@subs = []
|
32
|
-
@default_subs = nil
|
33
33
|
@blocks = []
|
34
|
-
@
|
35
|
-
@title = nil
|
36
|
-
|
37
|
-
|
38
|
-
@level = if context == :document
|
39
|
-
0
|
34
|
+
@subs = []
|
35
|
+
@id = @title = @title_converted = @caption = @number = @style = @default_subs = @source_location = nil
|
36
|
+
if context == :document
|
37
|
+
@level = 0
|
40
38
|
elsif parent && context != :section
|
41
|
-
parent.level
|
39
|
+
@level = parent.level
|
40
|
+
else
|
41
|
+
@level = nil
|
42
42
|
end
|
43
43
|
@next_section_index = 0
|
44
44
|
@next_section_number = 1
|
45
|
-
@source_location = nil
|
46
45
|
end
|
47
46
|
|
48
47
|
def block?
|
@@ -72,12 +71,12 @@ class AbstractBlock < AbstractNode
|
|
72
71
|
end
|
73
72
|
|
74
73
|
# Alias render to convert to maintain backwards compatibility
|
75
|
-
alias
|
74
|
+
alias render convert
|
76
75
|
|
77
76
|
# Public: Get the converted result of the child blocks by converting the
|
78
77
|
# children appropriate to content model that this block supports.
|
79
78
|
def content
|
80
|
-
@blocks.map {|b| b.convert } *
|
79
|
+
@blocks.map {|b| b.convert } * LF
|
81
80
|
end
|
82
81
|
|
83
82
|
# Public: Get the source file where this block started
|
@@ -101,10 +100,11 @@ class AbstractBlock < AbstractNode
|
|
101
100
|
@subs.include? name
|
102
101
|
end
|
103
102
|
|
104
|
-
# Public: A convenience method that
|
105
|
-
#
|
103
|
+
# Public: A convenience method that checks whether the title of this block is defined.
|
104
|
+
#
|
105
|
+
# Returns a [Boolean] indicating whether this block has a title.
|
106
106
|
def title?
|
107
|
-
|
107
|
+
@title ? true : false
|
108
108
|
end
|
109
109
|
|
110
110
|
# Public: Get the String title of this Block with title substitions applied
|
@@ -119,16 +119,28 @@ class AbstractBlock < AbstractNode
|
|
119
119
|
# block.title
|
120
120
|
# => "Foo 3^ # :: Bar(1)"
|
121
121
|
#
|
122
|
-
# Returns the String title
|
122
|
+
# Returns the converted String title for this Block, or nil if the source title is falsy
|
123
123
|
def title
|
124
|
-
# prevent substitutions from being applied multiple times
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
124
|
+
# prevent substitutions from being applied to title multiple times
|
125
|
+
@title_converted ? @converted_title : (@converted_title = (@title_converted = true) && @title && (apply_title_subs @title))
|
126
|
+
end
|
127
|
+
|
128
|
+
# Public: Set the String block title.
|
129
|
+
#
|
130
|
+
# Returns the new String title assigned to this Block
|
131
|
+
def title= val
|
132
|
+
@title, @title_converted = val, nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# Gets the caption for this block.
|
136
|
+
#
|
137
|
+
# This method routes the deprecated use of the caption method on an
|
138
|
+
# admonition block to the textlabel attribute.
|
139
|
+
#
|
140
|
+
# Returns the [String] caption for this block (or the value of the textlabel
|
141
|
+
# attribute if this is an admonition block).
|
142
|
+
def caption
|
143
|
+
@context == :admonition ? @attributes['textlabel'] : @caption
|
132
144
|
end
|
133
145
|
|
134
146
|
# Public: Convenience method that returns the interpreted title of the Block
|
@@ -139,12 +151,70 @@ class AbstractBlock < AbstractNode
|
|
139
151
|
# two values. If the Block does not have a caption, the interpreted title is
|
140
152
|
# returned.
|
141
153
|
#
|
142
|
-
# Returns the String title prefixed with the caption, or just the
|
143
|
-
# caption is set
|
154
|
+
# Returns the converted String title prefixed with the caption, or just the
|
155
|
+
# converted String title if no caption is set
|
144
156
|
def captioned_title
|
145
157
|
%(#{@caption}#{title})
|
146
158
|
end
|
147
159
|
|
160
|
+
# Public: Returns the converted alt text for this block image.
|
161
|
+
#
|
162
|
+
# Returns the [String] value of the alt attribute with XML special character
|
163
|
+
# and replacement substitutions applied.
|
164
|
+
def alt
|
165
|
+
if (text = @attributes['alt'])
|
166
|
+
if text == @attributes['default-alt']
|
167
|
+
sub_specialchars text
|
168
|
+
else
|
169
|
+
text = sub_specialchars text
|
170
|
+
(ReplaceableTextRx.match? text) ? (sub_replacements text) : text
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Public: Generate cross reference text (xreftext) that can be used to refer
|
176
|
+
# to this block.
|
177
|
+
#
|
178
|
+
# Use the explicit reftext for this block, if specified, retrieved from the
|
179
|
+
# {#reftext} method. Otherwise, if this is a section or captioned block (a
|
180
|
+
# block with both a title and caption), generate the xreftext according to
|
181
|
+
# the value of the xrefstyle argument (e.g., full, short). This logic may
|
182
|
+
# leverage the {Substitutors#sub_quotes} method to apply formatting to the
|
183
|
+
# text. If this is not a captioned block, return the title, if present, or
|
184
|
+
# nil otherwise.
|
185
|
+
#
|
186
|
+
# xrefstyle - An optional String that specifies the style to use to format
|
187
|
+
# the xreftext ('full', 'short', or 'basic') (default: nil).
|
188
|
+
#
|
189
|
+
# Returns the generated [String] xreftext used to refer to this block or
|
190
|
+
# nothing if there isn't sufficient information to generate one.
|
191
|
+
def xreftext xrefstyle = nil
|
192
|
+
if (val = reftext) && !val.empty?
|
193
|
+
val
|
194
|
+
# NOTE xrefstyle only applies to blocks with a title and a caption or number
|
195
|
+
elsif xrefstyle && @title && @caption
|
196
|
+
case xrefstyle
|
197
|
+
when 'full'
|
198
|
+
quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title
|
199
|
+
if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)])
|
200
|
+
%(#{prefix} #{@number}, #{quoted_title})
|
201
|
+
else
|
202
|
+
%(#{@caption.chomp '. '}, #{quoted_title})
|
203
|
+
end
|
204
|
+
when 'short'
|
205
|
+
if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)])
|
206
|
+
%(#{prefix} #{@number})
|
207
|
+
else
|
208
|
+
@caption.chomp '. '
|
209
|
+
end
|
210
|
+
else # 'basic'
|
211
|
+
title
|
212
|
+
end
|
213
|
+
else
|
214
|
+
title
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
148
218
|
# Public: Determine whether this Block contains block content
|
149
219
|
#
|
150
220
|
# Returns A Boolean indicating whether this Block has block content
|
@@ -176,7 +246,7 @@ class AbstractBlock < AbstractNode
|
|
176
246
|
end
|
177
247
|
|
178
248
|
# NOTE append alias required for adapting to a Java API
|
179
|
-
alias
|
249
|
+
alias append <<
|
180
250
|
|
181
251
|
# Public: Get the Array of child Section objects
|
182
252
|
#
|
@@ -307,7 +377,13 @@ class AbstractBlock < AbstractNode
|
|
307
377
|
end
|
308
378
|
result
|
309
379
|
end
|
310
|
-
alias
|
380
|
+
alias query find_by
|
381
|
+
|
382
|
+
# Move to the next adjacent block in document order. If the current block is the last
|
383
|
+
# item in a list, this method will return the following sibling of the list block.
|
384
|
+
def next_adjacent_block
|
385
|
+
(sib = (p = parent).blocks[(p.blocks.find_index self) + 1]) ? sib : p.next_adjacent_block unless @context == :document
|
386
|
+
end
|
311
387
|
|
312
388
|
# Public: Remove a substitution from this block
|
313
389
|
#
|
@@ -319,39 +395,53 @@ class AbstractBlock < AbstractNode
|
|
319
395
|
nil
|
320
396
|
end
|
321
397
|
|
322
|
-
# Public: Generate
|
323
|
-
# is not already assigned.
|
398
|
+
# Public: Generate and assign caption to block if not already assigned.
|
324
399
|
#
|
325
|
-
# If the block has a title and a caption prefix is available
|
326
|
-
#
|
327
|
-
#
|
328
|
-
# the block.
|
400
|
+
# If the block has a title and a caption prefix is available for this block,
|
401
|
+
# then build a caption from this information, assign it a number and store it
|
402
|
+
# to the caption attribute on the block.
|
329
403
|
#
|
330
|
-
# If
|
331
|
-
# do nothing.
|
404
|
+
# If a caption has already been assigned to this block, do nothing.
|
332
405
|
#
|
333
|
-
#
|
334
|
-
#
|
335
|
-
# is used. (default: nil).
|
406
|
+
# The parts of a complete caption are: <prefix> <number>. <title>
|
407
|
+
# This partial caption represents the part the precedes the title.
|
336
408
|
#
|
337
|
-
#
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
caption_key = "#{key}-caption"
|
349
|
-
if (caption_title = @document.attributes[caption_key])
|
350
|
-
caption_num = @document.counter_increment("#{key}-number", self)
|
351
|
-
@caption = "#{caption_title} #{caption_num}. "
|
352
|
-
end
|
409
|
+
# value - The explicit String caption to assign to this block (default: nil).
|
410
|
+
# key - The String prefix for the caption and counter attribute names.
|
411
|
+
# If not provided, the name of the context for this block is used.
|
412
|
+
# (default: nil)
|
413
|
+
#
|
414
|
+
# Returns nothing.
|
415
|
+
def assign_caption value = nil, key = nil
|
416
|
+
unless @caption || !@title || (@caption = value || @document.attributes['caption'])
|
417
|
+
if (prefix = @document.attributes[%(#{key ||= @context}-caption)])
|
418
|
+
@caption = %(#{prefix} #{@number = @document.increment_and_store_counter "#{key}-number", self}. )
|
419
|
+
nil
|
353
420
|
end
|
354
421
|
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Internal: Assign the next index (0-based) and number (1-based) to the section
|
425
|
+
#
|
426
|
+
# Assign to the specified section the next index and, if the section is
|
427
|
+
# numbered, number within this block (its parent).
|
428
|
+
#
|
429
|
+
# Returns nothing
|
430
|
+
def enumerate_section section
|
431
|
+
@next_section_index = (section.index = @next_section_index) + 1
|
432
|
+
if (sectname = section.sectname) == 'appendix'
|
433
|
+
section.number = @document.counter 'appendix-number', 'A'
|
434
|
+
if (caption = @document.attributes['appendix-caption'])
|
435
|
+
section.caption = %(#{caption} #{section.number}: )
|
436
|
+
else
|
437
|
+
section.caption = %(#{section.number}. )
|
438
|
+
end
|
439
|
+
# NOTE currently chapters in a book doctype are sequential even for multi-part books (see #979)
|
440
|
+
elsif sectname == 'chapter'
|
441
|
+
section.number = @document.counter 'chapter-number', 1
|
442
|
+
else
|
443
|
+
@next_section_number = (section.number = @next_section_number) + 1
|
444
|
+
end if section.numbered
|
355
445
|
nil
|
356
446
|
end
|
357
447
|
|
@@ -366,35 +456,6 @@ class AbstractBlock < AbstractNode
|
|
366
456
|
ORDERED_LIST_KEYWORDS[list_type || @style]
|
367
457
|
end
|
368
458
|
|
369
|
-
# Internal: Assign the next index (0-based) to this section
|
370
|
-
#
|
371
|
-
# Assign the next index of this section within the parent
|
372
|
-
# Block (in document order)
|
373
|
-
#
|
374
|
-
# Returns nothing
|
375
|
-
def assign_index(section)
|
376
|
-
section.index = @next_section_index
|
377
|
-
@next_section_index += 1
|
378
|
-
|
379
|
-
if section.sectname == 'appendix'
|
380
|
-
appendix_number = @document.counter 'appendix-number', 'A'
|
381
|
-
section.number = appendix_number if section.numbered
|
382
|
-
if (caption = @document.attr 'appendix-caption', '').empty?
|
383
|
-
section.caption = %(#{appendix_number}. )
|
384
|
-
else
|
385
|
-
section.caption = %(#{caption} #{appendix_number}: )
|
386
|
-
end
|
387
|
-
elsif section.numbered
|
388
|
-
# chapters in a book doctype should be sequential even when divided into parts
|
389
|
-
if (section.level == 1 || (section.level == 0 && section.special)) && @document.doctype == 'book'
|
390
|
-
section.number = @document.counter('chapter-number', 1)
|
391
|
-
else
|
392
|
-
section.number = @next_section_number
|
393
|
-
@next_section_number += 1
|
394
|
-
end
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
459
|
# Internal: Reassign the section indexes
|
399
460
|
#
|
400
461
|
# Walk the descendents of the current Document or Section
|
@@ -403,17 +464,17 @@ class AbstractBlock < AbstractNode
|
|
403
464
|
#
|
404
465
|
# IMPORTANT You must invoke this method on a node after removing
|
405
466
|
# child sections or else the internal counters will be off.
|
406
|
-
#
|
467
|
+
#
|
407
468
|
# Returns nothing
|
408
469
|
def reindex_sections
|
409
470
|
@next_section_index = 0
|
410
|
-
@next_section_number =
|
411
|
-
@blocks.each
|
471
|
+
@next_section_number = 1
|
472
|
+
@blocks.each do |block|
|
412
473
|
if block.context == :section
|
413
|
-
|
474
|
+
enumerate_section block
|
414
475
|
block.reindex_sections
|
415
476
|
end
|
416
|
-
|
477
|
+
end
|
417
478
|
end
|
418
479
|
end
|
419
480
|
end
|
@@ -26,20 +26,17 @@ class AbstractNode
|
|
26
26
|
attr_reader :attributes
|
27
27
|
|
28
28
|
def initialize parent, context, opts = {}
|
29
|
-
# document is a special case, should refer to itself
|
30
29
|
if context == :document
|
31
|
-
|
30
|
+
# document is a special case, should refer to itself
|
31
|
+
@document, @parent = self, nil
|
32
32
|
else
|
33
33
|
if parent
|
34
|
-
@parent = parent
|
35
|
-
@document = parent.document
|
34
|
+
@document, @parent = parent.document, parent
|
36
35
|
else
|
37
|
-
@parent = nil
|
38
|
-
@document = nil
|
36
|
+
@document = @parent = nil
|
39
37
|
end
|
40
38
|
end
|
41
|
-
@context = context
|
42
|
-
@node_name = context.to_s
|
39
|
+
@node_name = (@context = context).to_s
|
43
40
|
# QUESTION are we correct in duplicating the attributes (seems to be just as fast)
|
44
41
|
@attributes = (opts.key? :attributes) ? opts[:attributes].dup : {}
|
45
42
|
@passthroughs = {}
|
@@ -51,8 +48,7 @@ class AbstractNode
|
|
51
48
|
#
|
52
49
|
# Returns nothing
|
53
50
|
def parent=(parent)
|
54
|
-
@parent = parent
|
55
|
-
@document = parent.document
|
51
|
+
@parent, @document = parent, parent.document
|
56
52
|
nil
|
57
53
|
end
|
58
54
|
|
@@ -82,21 +78,17 @@ class AbstractNode
|
|
82
78
|
# Document node and return the value of the attribute if found. Otherwise,
|
83
79
|
# return the default value, which defaults to nil.
|
84
80
|
#
|
85
|
-
# name
|
86
|
-
#
|
87
|
-
# inherit
|
88
|
-
#
|
81
|
+
# name - the String or Symbol name of the attribute to lookup
|
82
|
+
# default_val - the Object value to return if the attribute is not found (default: nil)
|
83
|
+
# inherit - a Boolean indicating whether to check for the attribute on the
|
84
|
+
# AsciiDoctor::Document if not found on this node (default: false)
|
89
85
|
#
|
90
86
|
# return the value of the attribute or the default value if the attribute
|
91
87
|
# is not found in the attributes of this node or the document node
|
92
|
-
def attr
|
93
|
-
name = name.to_s
|
94
|
-
|
95
|
-
|
96
|
-
@attributes[name] || @document.attributes[name] || default_value
|
97
|
-
else
|
98
|
-
@attributes[name] || default_value
|
99
|
-
end
|
88
|
+
def attr name, default_val = nil, inherit = true
|
89
|
+
name = name.to_s
|
90
|
+
# NOTE if @parent is set, it means @document is also set
|
91
|
+
@attributes[name] || (inherit && @parent ? @document.attributes[name] || default_val : default_val)
|
100
92
|
end
|
101
93
|
|
102
94
|
# Public: Check if the attribute is defined, optionally performing a
|
@@ -108,35 +100,33 @@ class AbstractNode
|
|
108
100
|
# comparison value is specified (not nil), return whether the two values match.
|
109
101
|
# Otherwise, return whether the attribute was found.
|
110
102
|
#
|
111
|
-
# name
|
112
|
-
#
|
113
|
-
# inherit
|
114
|
-
#
|
103
|
+
# name - the String or Symbol name of the attribute to lookup
|
104
|
+
# expect_val - the expected Object value of the attribute (default: nil)
|
105
|
+
# inherit - a Boolean indicating whether to check for the attribute on the
|
106
|
+
# AsciiDoctor::Document if not found on this node (default: false)
|
115
107
|
#
|
116
108
|
# return a Boolean indicating whether the attribute exists and, if a
|
117
109
|
# comparison value is specified, whether the value of the attribute matches
|
118
110
|
# the comparison value
|
119
|
-
def attr?
|
120
|
-
name = name.to_s
|
121
|
-
|
122
|
-
if
|
123
|
-
@attributes.
|
124
|
-
elsif inherit
|
125
|
-
expect == (@attributes[name] || @document.attributes[name])
|
111
|
+
def attr? name, expect_val = nil, inherit = true
|
112
|
+
name = name.to_s
|
113
|
+
# NOTE if @parent is set, it means @document is also set
|
114
|
+
if expect_val.nil?
|
115
|
+
(@attributes.key? name) || (inherit && @parent && (@document.attributes.key? name))
|
126
116
|
else
|
127
|
-
|
117
|
+
expect_val == (@attributes[name] || (inherit && @parent ? @document.attributes[name] : nil))
|
128
118
|
end
|
129
119
|
end
|
130
120
|
|
131
121
|
# Public: Assign the value to the attribute name for the current node.
|
132
122
|
#
|
133
123
|
# name - The String attribute name to assign
|
134
|
-
# value - The Object value to assign to the attribute
|
124
|
+
# value - The Object value to assign to the attribute (default: '')
|
135
125
|
# overwrite - A Boolean indicating whether to assign the attribute
|
136
126
|
# if currently present in the attributes Hash (default: true)
|
137
127
|
#
|
138
128
|
# Returns a [Boolean] indicating whether the assignment was performed
|
139
|
-
def set_attr name, value, overwrite = true
|
129
|
+
def set_attr name, value = '', overwrite = true
|
140
130
|
if overwrite == false && (@attributes.key? name)
|
141
131
|
false
|
142
132
|
else
|
@@ -145,14 +135,23 @@ class AbstractNode
|
|
145
135
|
end
|
146
136
|
end
|
147
137
|
|
138
|
+
# Public: Remove the attribute from the current node.
|
139
|
+
#
|
140
|
+
# name - The String attribute name to remove
|
141
|
+
#
|
142
|
+
# Returns the previous [String] value, or nil if the attribute was not present.
|
143
|
+
def remove_attr name
|
144
|
+
@attributes.delete name
|
145
|
+
end
|
146
|
+
|
148
147
|
# TODO document me
|
149
148
|
def set_option(name)
|
150
|
-
if @attributes.
|
151
|
-
@attributes['options'] =
|
149
|
+
if @attributes.key? 'options'
|
150
|
+
@attributes['options'] = %(#{@attributes['options']},#{name})
|
152
151
|
else
|
153
152
|
@attributes['options'] = name
|
154
153
|
end
|
155
|
-
@attributes[
|
154
|
+
@attributes[%(#{name}-option)] = ''
|
156
155
|
end
|
157
156
|
|
158
157
|
# Public: A convenience method to check if the specified option attribute is
|
@@ -165,7 +164,7 @@ class AbstractNode
|
|
165
164
|
#
|
166
165
|
# return a Boolean indicating whether the option has been specified
|
167
166
|
def option?(name)
|
168
|
-
@attributes.
|
167
|
+
@attributes.key? %(#{name}-option)
|
169
168
|
end
|
170
169
|
|
171
170
|
# Public: Update the attributes of this node with the new values in
|
@@ -189,11 +188,11 @@ class AbstractNode
|
|
189
188
|
end
|
190
189
|
|
191
190
|
# Public: A convenience method that checks if the role attribute is specified
|
192
|
-
def role?
|
193
|
-
if
|
194
|
-
|
191
|
+
def role? expect_val = nil
|
192
|
+
if expect_val
|
193
|
+
expect_val == (@attributes['role'] || @document.attributes['role'])
|
195
194
|
else
|
196
|
-
@attributes.
|
195
|
+
@attributes.key?('role') || @document.attributes.key?('role')
|
197
196
|
end
|
198
197
|
end
|
199
198
|
|
@@ -205,45 +204,59 @@ class AbstractNode
|
|
205
204
|
# Public: A convenience method that checks if the specified role is present
|
206
205
|
# in the list of roles on this node
|
207
206
|
def has_role?(name)
|
208
|
-
|
209
|
-
|
210
|
-
else
|
211
|
-
false
|
212
|
-
end
|
207
|
+
# NOTE center + include? is faster than split + include?
|
208
|
+
(val = @attributes['role'] || @document.attributes['role']).nil_or_empty? ? false : %( #{val} ).include?(%( #{name} ))
|
213
209
|
end
|
214
210
|
|
215
211
|
# Public: A convenience method that returns the role names as an Array
|
212
|
+
#
|
213
|
+
# Returns the role names as an Array or an empty Array if the role attribute is absent.
|
216
214
|
def roles
|
217
|
-
|
218
|
-
val.split(' ')
|
219
|
-
else
|
220
|
-
[]
|
221
|
-
end
|
215
|
+
(val = @attributes['role'] || @document.attributes['role']).nil_or_empty? ? [] : val.split
|
222
216
|
end
|
223
217
|
|
224
218
|
# Public: A convenience method that adds the given role directly to this node
|
219
|
+
#
|
220
|
+
# Returns a Boolean indicating whether the role was added.
|
225
221
|
def add_role(name)
|
226
|
-
|
227
|
-
@attributes['role'] =
|
222
|
+
if (val = @attributes['role']).nil_or_empty?
|
223
|
+
@attributes['role'] = name
|
224
|
+
true
|
225
|
+
# NOTE center + include? is faster than split + include?
|
226
|
+
elsif %( #{val} ).include?(%( #{name} ))
|
227
|
+
false
|
228
|
+
else
|
229
|
+
@attributes['role'] = %(#{val} #{name})
|
230
|
+
true
|
228
231
|
end
|
229
232
|
end
|
230
233
|
|
231
234
|
# Public: A convenience method that removes the given role directly from this node
|
235
|
+
#
|
236
|
+
# Returns a Boolean indicating whether the role was removed.
|
232
237
|
def remove_role(name)
|
233
|
-
if (
|
234
|
-
|
235
|
-
|
238
|
+
if (val = @attributes['role']).nil_or_empty?
|
239
|
+
false
|
240
|
+
elsif (val = val.split).delete name
|
241
|
+
if val.empty?
|
242
|
+
@attributes.delete('role')
|
243
|
+
else
|
244
|
+
@attributes['role'] = val * ' '
|
245
|
+
end
|
246
|
+
true
|
247
|
+
else
|
248
|
+
false
|
236
249
|
end
|
237
250
|
end
|
238
251
|
|
239
|
-
# Public: A convenience method that checks if the reftext attribute is
|
252
|
+
# Public: A convenience method that checks if the reftext attribute is defined.
|
240
253
|
def reftext?
|
241
|
-
@attributes.
|
254
|
+
@attributes.key? 'reftext'
|
242
255
|
end
|
243
256
|
|
244
|
-
# Public: A convenience method that returns the value of the reftext attribute
|
257
|
+
# Public: A convenience method that returns the value of the reftext attribute with substitutions applied.
|
245
258
|
def reftext
|
246
|
-
@attributes['reftext']
|
259
|
+
(val = @attributes['reftext']) ? (apply_reftext_subs val) : nil
|
247
260
|
end
|
248
261
|
|
249
262
|
# Public: Construct a reference or data URI to an icon image for the
|
@@ -315,12 +328,12 @@ class AbstractNode
|
|
315
328
|
#
|
316
329
|
# Returns A String reference or data URI for the target image
|
317
330
|
def image_uri(target_image, asset_dir_key = 'imagesdir')
|
318
|
-
if (doc = @document).safe < SafeMode::SECURE && doc.attr?
|
319
|
-
if (Helpers.uriish? target_image) ||
|
320
|
-
(asset_dir_key && (images_base = doc.attr
|
321
|
-
(target_image = normalize_web_path
|
322
|
-
if doc.attr?
|
323
|
-
generate_data_uri_from_uri target_image, doc.attr?
|
331
|
+
if (doc = @document).safe < SafeMode::SECURE && (doc.attr? 'data-uri')
|
332
|
+
if ((Helpers.uriish? target_image) && (target_image = uri_encode_spaces target_image)) ||
|
333
|
+
(asset_dir_key && (images_base = doc.attr asset_dir_key) && (Helpers.uriish? images_base) &&
|
334
|
+
(target_image = normalize_web_path target_image, images_base, false))
|
335
|
+
if doc.attr? 'allow-uri-read'
|
336
|
+
generate_data_uri_from_uri target_image, (doc.attr? 'cache-uri')
|
324
337
|
else
|
325
338
|
target_image
|
326
339
|
end
|
@@ -328,7 +341,7 @@ class AbstractNode
|
|
328
341
|
generate_data_uri target_image, asset_dir_key
|
329
342
|
end
|
330
343
|
else
|
331
|
-
normalize_web_path target_image, (asset_dir_key ? doc.attr
|
344
|
+
normalize_web_path target_image, (asset_dir_key ? (doc.attr asset_dir_key) : nil)
|
332
345
|
end
|
333
346
|
end
|
334
347
|
|
@@ -356,20 +369,13 @@ class AbstractNode
|
|
356
369
|
|
357
370
|
unless ::File.readable? image_path
|
358
371
|
warn %(asciidoctor: WARNING: image to embed not found or not readable: #{image_path})
|
359
|
-
|
360
|
-
return "data:#{mimetype}:base64,"
|
372
|
+
return %(data:#{mimetype};base64,)
|
361
373
|
# uncomment to return 1 pixel white dot instead
|
362
374
|
#return 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
|
363
375
|
end
|
364
376
|
|
365
|
-
bindata = nil
|
366
|
-
if ::IO.respond_to? :binread
|
367
|
-
bindata = ::IO.binread(image_path)
|
368
|
-
else
|
369
|
-
bindata = ::File.open(image_path, 'rb') {|file| file.read }
|
370
|
-
end
|
371
377
|
# NOTE base64 is autoloaded by reference to ::Base64
|
372
|
-
%(data:#{mimetype};base64,#{::Base64.encode64(
|
378
|
+
%(data:#{mimetype};base64,#{::Base64.encode64(::IO.binread image_path).delete LF})
|
373
379
|
end
|
374
380
|
|
375
381
|
# Public: Read the image data from the specified URI and generate a data URI
|
@@ -396,12 +402,12 @@ class AbstractNode
|
|
396
402
|
|
397
403
|
begin
|
398
404
|
mimetype = nil
|
399
|
-
bindata = open(image_uri, 'rb') {|
|
400
|
-
mimetype =
|
401
|
-
|
405
|
+
bindata = open(image_uri, 'rb') {|fd|
|
406
|
+
mimetype = fd.content_type
|
407
|
+
fd.read
|
402
408
|
}
|
403
409
|
# NOTE base64 is autoloaded by reference to ::Base64
|
404
|
-
%(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete
|
410
|
+
%(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete LF})
|
405
411
|
rescue
|
406
412
|
warn %(asciidoctor: WARNING: could not retrieve image data from URI: #{image_uri})
|
407
413
|
image_uri
|
@@ -436,7 +442,7 @@ class AbstractNode
|
|
436
442
|
Helpers.require_library 'open-uri/cached', 'open-uri-cached' if doc.attr? 'cache-uri'
|
437
443
|
begin
|
438
444
|
data = ::OpenURI.open_uri(target) {|fd| fd.read }
|
439
|
-
data = (Helpers.normalize_lines_from_string data) *
|
445
|
+
data = (Helpers.normalize_lines_from_string data) * LF if opts[:normalize]
|
440
446
|
rescue
|
441
447
|
warn %(asciidoctor: WARNING: could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
|
442
448
|
data = nil
|
@@ -447,7 +453,7 @@ class AbstractNode
|
|
447
453
|
end
|
448
454
|
else
|
449
455
|
target = normalize_system_path target, opts[:start], nil, :target_name => (opts[:label] || 'asset')
|
450
|
-
data = read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true)
|
456
|
+
data = read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true), :label => opts[:label]
|
451
457
|
end
|
452
458
|
data
|
453
459
|
end
|
@@ -465,25 +471,24 @@ class AbstractNode
|
|
465
471
|
#
|
466
472
|
# Returns the [String] content of the file at the specified path, or nil
|
467
473
|
# if the file does not exist.
|
468
|
-
def read_asset
|
474
|
+
def read_asset path, opts = {}
|
469
475
|
# remap opts for backwards compatibility
|
470
476
|
opts = { :warn_on_failure => (opts != false) } unless ::Hash === opts
|
471
477
|
if ::File.readable? path
|
472
478
|
if opts[:normalize]
|
473
|
-
Helpers.normalize_lines_from_string(::IO.read
|
479
|
+
Helpers.normalize_lines_from_string(::IO.read path) * LF
|
474
480
|
else
|
475
481
|
# QUESTION should we chomp or rstrip content?
|
476
|
-
::IO.read
|
482
|
+
::IO.read path
|
477
483
|
end
|
478
|
-
|
479
|
-
warn %(asciidoctor: WARNING: file does not exist or cannot be read: #{path})
|
480
|
-
nil
|
484
|
+
elsif opts[:warn_on_failure]
|
485
|
+
warn %(asciidoctor: WARNING: #{(attr 'docfile') || '<stdin>'}: #{opts[:label] || 'file'} does not exist or cannot be read: #{path})
|
481
486
|
end
|
482
487
|
end
|
483
488
|
|
484
|
-
# Public: Normalize the web
|
489
|
+
# Public: Normalize the web path using the PathResolver.
|
485
490
|
#
|
486
|
-
# See {PathResolver#web_path} for details.
|
491
|
+
# See {PathResolver#web_path} for details about path resolution and encoding.
|
487
492
|
#
|
488
493
|
# target - the String target path
|
489
494
|
# start - the String start (i.e, parent) path (optional, default: nil)
|
@@ -492,12 +497,21 @@ class AbstractNode
|
|
492
497
|
# Returns the resolved [String] path
|
493
498
|
def normalize_web_path(target, start = nil, preserve_uri_target = true)
|
494
499
|
if preserve_uri_target && (Helpers.uriish? target)
|
495
|
-
target
|
500
|
+
uri_encode_spaces target
|
496
501
|
else
|
497
502
|
(@path_resolver ||= PathResolver.new).web_path target, start
|
498
503
|
end
|
499
504
|
end
|
500
505
|
|
506
|
+
# Internal: URI encode spaces in a String
|
507
|
+
#
|
508
|
+
# str - the String to encode
|
509
|
+
#
|
510
|
+
# Returns the String with all spaces replaced with %20.
|
511
|
+
def uri_encode_spaces str
|
512
|
+
(str.include? ' ') ? (str.gsub ' ', '%20') : str
|
513
|
+
end
|
514
|
+
|
501
515
|
# Public: Resolve and normalize a secure path from the target and start paths
|
502
516
|
# using the PathResolver.
|
503
517
|
#
|