asciidoctor 0.1.2 → 0.1.3

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.

Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -0
  3. data/Guardfile +18 -0
  4. data/LICENSE +1 -1
  5. data/README.adoc +65 -21
  6. data/Rakefile +10 -0
  7. data/asciidoctor.gemspec +17 -35
  8. data/compat/asciidoc.conf +130 -13
  9. data/lib/asciidoctor.rb +107 -87
  10. data/lib/asciidoctor/abstract_block.rb +6 -2
  11. data/lib/asciidoctor/abstract_node.rb +21 -13
  12. data/lib/asciidoctor/attribute_list.rb +2 -5
  13. data/{stylesheets/asciidoctor.css → lib/asciidoctor/backends/_stylesheets.rb} +96 -46
  14. data/lib/asciidoctor/backends/base_template.rb +9 -4
  15. data/lib/asciidoctor/backends/docbook45.rb +246 -138
  16. data/lib/asciidoctor/backends/html5.rb +580 -381
  17. data/lib/asciidoctor/block.rb +2 -50
  18. data/lib/asciidoctor/cli/options.rb +9 -8
  19. data/lib/asciidoctor/document.rb +35 -45
  20. data/lib/asciidoctor/helpers.rb +10 -0
  21. data/lib/asciidoctor/lexer.rb +456 -148
  22. data/lib/asciidoctor/list_item.rb +0 -21
  23. data/lib/asciidoctor/path_resolver.rb +18 -12
  24. data/lib/asciidoctor/reader.rb +71 -26
  25. data/lib/asciidoctor/renderer.rb +2 -19
  26. data/lib/asciidoctor/section.rb +0 -1
  27. data/lib/asciidoctor/substituters.rb +150 -36
  28. data/lib/asciidoctor/table.rb +30 -24
  29. data/lib/asciidoctor/version.rb +1 -1
  30. data/man/asciidoctor.1 +22 -16
  31. data/man/asciidoctor.ad +24 -16
  32. data/test/attributes_test.rb +50 -0
  33. data/test/blocks_test.rb +660 -9
  34. data/test/document_test.rb +191 -14
  35. data/test/fixtures/encoding.asciidoc +8 -0
  36. data/test/invoker_test.rb +47 -0
  37. data/test/lexer_test.rb +172 -0
  38. data/test/links_test.rb +28 -0
  39. data/test/lists_test.rb +172 -13
  40. data/test/options_test.rb +29 -2
  41. data/test/paragraphs_test.rb +105 -47
  42. data/test/paths_test.rb +3 -3
  43. data/test/reader_test.rb +46 -0
  44. data/test/sections_test.rb +365 -12
  45. data/test/substitutions_test.rb +127 -11
  46. data/test/tables_test.rb +81 -14
  47. data/test/test_helper.rb +18 -7
  48. data/test/text_test.rb +17 -5
  49. metadata +9 -36
data/lib/asciidoctor.rb CHANGED
@@ -1,9 +1,10 @@
1
- require 'rubygems' unless RUBY_VERSION >= '1.9'
1
+ if RUBY_VERSION < '1.9'
2
+ require 'rubygems'
3
+ end
2
4
  require 'strscan'
3
5
  require 'set'
4
6
 
5
7
  $:.unshift(File.dirname(__FILE__))
6
- #$:.unshift(File.join(File.dirname(__FILE__), '..', 'vendor'))
7
8
 
8
9
  # Public: Methods for parsing Asciidoc input files and rendering documents
9
10
  # using eRuby templates.
@@ -91,6 +92,12 @@ module Asciidoctor
91
92
  # The root path of the Asciidoctor gem
92
93
  ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..'))
93
94
 
95
+ # Flag to indicate whether encoding of external strings needs to be forced to UTF-8
96
+ # _All_ input data must be force encoded to UTF-8 if Encoding.default_external is *not* UTF-8
97
+ # Address failures performing string operations that are reported as "invalid byte sequence in US-ASCII"
98
+ # Ruby 1.8 doesn't seem to experience this problem (perhaps because it isn't validating the encodings)
99
+ FORCE_ENCODING = RUBY_VERSION > '1.9' && Encoding.default_external != Encoding::UTF_8
100
+
94
101
  # The default document type
95
102
  # Can influence markup generated by render templates
96
103
  DEFAULT_DOCTYPE = 'article'
@@ -98,11 +105,9 @@ module Asciidoctor
98
105
  # The backend determines the format of the rendered output, default to html5
99
106
  DEFAULT_BACKEND = 'html5'
100
107
 
101
- DEFAULT_STYLESHEET_PATH = File.join(ROOT_PATH, 'stylesheets', 'asciidoctor.css')
102
-
103
108
  DEFAULT_STYLESHEET_KEYS = ['', 'DEFAULT'].to_set
104
109
 
105
- DEFAULT_STYLESHEET_NAME = File.basename(DEFAULT_STYLESHEET_PATH)
110
+ DEFAULT_STYLESHEET_NAME = 'asciidoctor.css'
106
111
 
107
112
  # Pointers to the preferred version for a given backend.
108
113
  BACKEND_ALIASES = {
@@ -133,21 +138,22 @@ module Asciidoctor
133
138
 
134
139
  ADMONITION_STYLES = ['NOTE', 'TIP', 'IMPORTANT', 'WARNING', 'CAUTION'].to_set
135
140
 
136
- # NOTE: AsciiDoc doesn't support pass style for paragraph
137
- PARAGRAPH_STYLES = ['comment', 'example', 'literal', 'listing', 'normal', 'pass', 'quote', 'sidebar', 'source', 'verse'].to_set
141
+ PARAGRAPH_STYLES = ['comment', 'example', 'literal', 'listing', 'normal', 'pass', 'quote', 'sidebar', 'source', 'verse', 'abstract', 'partintro'].to_set
138
142
 
139
143
  VERBATIM_STYLES = ['literal', 'listing', 'source', 'verse'].to_set
140
144
 
141
145
  DELIMITED_BLOCKS = {
142
- # NOTE: AsciiDoc doesn't support pass style for open block
143
- '--' => [:open, ['comment', 'example', 'literal', 'listing', 'pass', 'quote', 'sidebar', 'source', 'verse', 'admonition'].to_set],
146
+ '--' => [:open, ['comment', 'example', 'literal', 'listing', 'pass', 'quote', 'sidebar', 'source', 'verse', 'admonition', 'abstract', 'partintro'].to_set],
144
147
  '----' => [:listing, ['literal', 'source'].to_set],
145
148
  '....' => [:literal, ['listing', 'source'].to_set],
146
149
  '====' => [:example, ['admonition'].to_set],
147
150
  '****' => [:sidebar, Set.new],
148
151
  '____' => [:quote, ['verse'].to_set],
152
+ '""' => [:quote, ['verse'].to_set],
149
153
  '++++' => [:pass, Set.new],
150
154
  '|===' => [:table, Set.new],
155
+ ',===' => [:table, Set.new],
156
+ ':===' => [:table, Set.new],
151
157
  '!===' => [:table, Set.new],
152
158
  '////' => [:comment, Set.new],
153
159
  '```' => [:fenced_code, Set.new],
@@ -173,12 +179,19 @@ module Asciidoctor
173
179
  :upperroman => /[IVX]+\)/
174
180
  }
175
181
 
182
+ ORDERED_LIST_KEYWORDS = {
183
+ 'loweralpha' => 'a',
184
+ 'lowerroman' => 'i',
185
+ 'upperalpha' => 'A',
186
+ 'upperroman' => 'I'
187
+ }
188
+
176
189
  LIST_CONTINUATION = '+'
177
190
 
178
191
  LINE_BREAK = ' +'
179
192
 
180
193
  # NOTE allows for empty space in line as it could be left by the template engine
181
- BLANK_LINES_PATTERN = /^\s*\n/
194
+ BLANK_LINE_PATTERN = /^[[:blank:]]*\n/
182
195
 
183
196
  LINE_FEED_ENTITY = '&#10;' # or &#x0A;
184
197
 
@@ -200,7 +213,12 @@ module Asciidoctor
200
213
  # a block to be different lengths
201
214
  # this option requires that they be the same
202
215
  # Compliance value: false
203
- :congruent_block_delimiters => true
216
+ :congruent_block_delimiters => true,
217
+
218
+ # AsciiDoc will recognize commonly-used Markdown syntax
219
+ # to the degree it does not interfere with existing
220
+ # AsciiDoc behavior.
221
+ :markdown_syntax => true
204
222
  }
205
223
 
206
224
  # The following pattern, which appears frequently, captures the contents between square brackets,
@@ -223,10 +241,11 @@ module Asciidoctor
223
241
  # [[ref]] (anywhere inline)
224
242
  :anchor_macro => /\\?\[\[([\w":].*?)\]\]/,
225
243
 
226
- # matches any block delimiter:
227
- # open, listing, example, literal, comment, quote, sidebar, passthrough, table
228
- # NOTE position the most common blocks towards the front of the pattern
229
- :any_blk => %r{^(?:--|(?:-|\.|=|\*|_|\+|/){4,}|[\|!]={3,}|(?:`|~){3,}.*)$},
244
+ # matches any unbounded block delimiter:
245
+ # listing, literal, example, sidebar, quote, passthrough, table, fenced code
246
+ # does not include open block or air quotes
247
+ # TIP position the most common blocks towards the front of the pattern
248
+ :any_blk => %r{^(?:(?:-|\.|=|\*|_|\+|/){4,}|[\|,;!]={3,}|(?:`|~){3,}.*)$},
230
249
 
231
250
  # detect a list item of any sort
232
251
  # [[:graph:]] is a non-blank character
@@ -262,15 +281,15 @@ module Asciidoctor
262
281
  # [NOTE, caption="Good to know"]
263
282
  # Can be defined by an attribute
264
283
  # [{lead}]
265
- :blk_attr_list => /^\[(|[[:blank:]]*[\w\{,"'].*)\]$/,
284
+ :blk_attr_list => /^\[(|[[:blank:]]*[\w\{,.#"'].*)\]$/,
266
285
 
267
286
  # block attribute list or block id (bulk query)
268
- :attr_line => /^\[(|[[:blank:]]*[\w\{,"'].*|\[[^\[\]]*\])\]$/,
287
+ :attr_line => /^\[(|[[:blank:]]*[\w\{,.#"'].*|\[[^\[\]]*\])\]$/,
269
288
 
270
289
  # attribute reference
271
290
  # {foo}
272
291
  # {counter:pcount:1}
273
- :attr_ref => /(\\?)\{(\w+(?:[\-:]\w+)*)(\\?)\}/,
292
+ :attr_ref => /(\\)?\{((set|counter2?):.+?|\w+(?:[\-]\w+)*)(\\)?\}/,
274
293
 
275
294
  # The author info line the appears immediately following the document title
276
295
  # John Doe <john@anonymous.com>
@@ -297,18 +316,18 @@ module Asciidoctor
297
316
  # // (and then whatever)
298
317
  :comment => %r{^//(?:[^/]|$)},
299
318
 
300
- # one,two
301
- # one, two
302
- # one , two
303
- :csv_delimiter => /[[:blank:]]*,[[:blank:]]*/,
319
+ # one,two;three;four
320
+ :ssv_or_csv_delim => /,|;/,
321
+
322
+ # one two three
323
+ :space_delim => /([^\\])[[:blank:]]+/,
304
324
 
305
- # one;two
306
- # one; two
307
- # one ; two
308
- :semicolon_delim => /[[:blank:]]*;[[:blank:]]*/,
325
+ # Ctrl + Alt+T
326
+ # Ctrl,T
327
+ :kbd_delim => /(?:\+|,)(?=[[:blank:]]*[^\1])/,
309
328
 
310
- # one,two;three;four
311
- :scsv_csv_delim => /[[:blank:]]*[,;][[:blank:]]*/,
329
+ # one\ two\ three
330
+ :escaped_space => /\\([[:blank:]])/,
312
331
 
313
332
  # 29
314
333
  :digits => /^\d+$/,
@@ -330,14 +349,27 @@ module Asciidoctor
330
349
  '::::' => /^[[:blank:]]*((?:.*[^:])?)(::::)(?:[[:blank:]]+(.*))?$/,
331
350
  ';;' => /^[[:blank:]]*(.*)(;;)(?:[[:blank:]]+(.*))?$/
332
351
  },
333
- # ====
334
- #:example => /^={4,}$/,
335
352
 
336
353
  # footnote:[text]
337
354
  # footnoteref:[id,text]
338
355
  # footnoteref:[id]
339
356
  :footnote_macro => /\\?(footnote|footnoteref):\[((?:\\\]|[^\]])*?)\]/,
340
357
 
358
+ # kbd:[F3]
359
+ # kbd:[Ctrl+Shift+T]
360
+ # kbd:[Ctrl+\]]
361
+ # kbd:[Ctrl,T]
362
+ # btn:[Save]
363
+ :kbd_btn_macro => /\\?(?:kbd|btn):\[((?:\\\]|[^\]])+?)\]/,
364
+
365
+ # menu:File[New...]
366
+ # menu:View[Page Style > No Style]
367
+ # menu:View[Page Style, No Style]
368
+ :menu_macro => /\\?menu:(\w|\w.*?\S)\[[[:blank:]]*(.+?)?\]/,
369
+
370
+ # "File > New..."
371
+ :menu_inline_macro => /\\?"(\w[^"]*?[[:blank:]]*&gt;[[:blank:]]*[^"[:blank:]][^"]*)"/,
372
+
341
373
  # image::filename.png[Caption]
342
374
  # video::http://youtube.com/12345[Cats vs Dogs]
343
375
  :media_blk_macro => /^(image|video|audio)::(\S+?)\[((?:\\\]|[^\]])*?)\]$/,
@@ -371,7 +403,7 @@ module Asciidoctor
371
403
 
372
404
  # inline link and some inline link macro
373
405
  # FIXME revisit!
374
- :link_inline => %r{(^|link:|\s|>|&lt;|[\(\)\[\]])(\\?(?:https?|ftp)://[^\s\[<]*[^\s.,\[<])(?:\[((?:\\\]|[^\]])*?)\])?},
406
+ :link_inline => %r{(^|link:|\s|>|&lt;|[\(\)\[\]])(\\?(?:https?|ftp|irc)://[^\s\[<]*[^\s.,\[<])(?:\[((?:\\\]|[^\]])*?)\])?},
375
407
 
376
408
  # inline link macro
377
409
  # link:path[label]
@@ -381,18 +413,9 @@ module Asciidoctor
381
413
  # doc.writer@asciidoc.org
382
414
  :email_inline => /[\\>:]?\w[\w.%+-]*@[[:alnum:]][[:alnum:].-]*\.[[:alpha:]]{2,4}\b/,
383
415
 
384
- # ----
385
- #:listing => /^\-{4,}$/,
386
-
387
- # ....
388
- #:literal => /^\.{4,}$/,
389
-
390
416
  # <TAB>Foo or one-or-more-spaces-or-tabs then whatever
391
417
  :lit_par => /^([[:blank:]]+.*)$/,
392
418
 
393
- # --
394
- #:open_blk => /^\-\-$/,
395
-
396
419
  # . Foo (up to 5 consecutive dots)
397
420
  # 1. Foo (arabic, default)
398
421
  # a. Foo (loweralpha)
@@ -406,9 +429,6 @@ module Asciidoctor
406
429
  # <<< (pagebreak)
407
430
  :break_line => /^('|<){3,}$/,
408
431
 
409
- # ++++
410
- #:pass => /^\+{4,}$/,
411
-
412
432
  # inline passthrough macros
413
433
  # +++text+++
414
434
  # $$text$$
@@ -424,22 +444,13 @@ module Asciidoctor
424
444
  :pass_lit => /(^|[^`\w])(\\?`([^`\s]|[^`\s].*?\S)`)(?![`\w])/m,
425
445
 
426
446
  # placeholder for extracted passthrough text
427
- :pass_placeholder => /\x0(\d+)\x0/,
428
-
429
- # ____
430
- #:quote => /^_{4,}$/,
447
+ :pass_placeholder => /\e(\d+)\e/,
431
448
 
432
449
  # The document revision info line the appears immediately following the
433
450
  # document title author info line, if present
434
451
  # v1.0, 2013-01-01: Ring in the new year release
435
452
  :revision_info => /^(?:\D*(.*?),)?(?:\s*(?!:)(.*?))(?:\s*(?!^):\s*(.*))?$/,
436
453
 
437
- # '''
438
- #:ruler => /^'{3,}$/,
439
-
440
- # ****
441
- #:sidebar_blk => /^\*{4,}$/,
442
-
443
454
  # \' within a word
444
455
  :single_quote_esc => /(\w)\\'(\w)/,
445
456
  # an alternative if our backend generated single-quoted html/xml attributes
@@ -448,16 +459,6 @@ module Asciidoctor
448
459
  # used for sanitizing attribute names
449
460
  :illegal_attr_name_chars => /[^\w\-]/,
450
461
 
451
- # |===
452
- # |table
453
- # |===
454
- #:table => /^\|={3,}$/,
455
-
456
- # !===
457
- # !table
458
- # !===
459
- #:table_nested => /^!={3,}$/,
460
-
461
462
  # 1*h,2*,^3e
462
463
  :table_colspec => /^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+)?([a-z])?$/,
463
464
 
@@ -490,7 +491,7 @@ module Asciidoctor
490
491
  # match[1] is the delimiter, whose length determines the level
491
492
  # match[2] is the title itself
492
493
  # match[3] is an inline anchor, which becomes the section id
493
- :section_title => /^(={1,5})\s+(\S.*?)(?:\s*\[\[([^\[]+)\]\])?(?:\s+\1)?$/,
494
+ :section_title => /^((?:=|#){1,6})\s+(\S.*?)(?:\s*\[\[([^\[]+)\]\])?(?:\s+\1)?$/,
494
495
 
495
496
  # does not begin with a dot and has at least one alphanumeric character
496
497
  :section_name => /^((?=.*\w+.*)[^.].*?)$/,
@@ -664,8 +665,9 @@ module Asciidoctor
664
665
  # Document object.
665
666
  #
666
667
  # input - the AsciiDoc source as a IO, String or Array.
667
- # options - a Hash of options to control processing (default: {})
668
- # see Asciidoctor::Document#initialize for details
668
+ # options - a String, Array or Hash of options to control processing (default: {})
669
+ # String and Array values are converted into a Hash.
670
+ # See Asciidoctor::Document#initialize for details about options.
669
671
  # block - a callback block for handling include::[] directives
670
672
  #
671
673
  # returns the Asciidoctor::Document
@@ -674,10 +676,31 @@ module Asciidoctor
674
676
  start = Time.now
675
677
  end
676
678
 
679
+ attrs = (options[:attributes] ||= {})
680
+ if attrs.is_a? Hash
681
+ # all good; placed here as optimization
682
+ elsif attrs.is_a? Array
683
+ attrs = options[:attributes] = attrs.inject({}) do |accum, entry|
684
+ k, v = entry.split '=', 2
685
+ accum[k] = v || ''
686
+ accum
687
+ end
688
+ elsif attrs.is_a? String
689
+ # convert non-escaped spaces into null character, so we split on the
690
+ # correct spaces chars, and restore escaped spaces
691
+ attrs = attrs.gsub(REGEXP[:space_delim], "\\1\0").gsub(REGEXP[:escaped_space], '\1')
692
+
693
+ attrs = options[:attributes] = attrs.split("\0").inject({}) do |accum, entry|
694
+ k, v = entry.split '=', 2
695
+ accum[k] = v || ''
696
+ accum
697
+ end
698
+ else
699
+ raise ArgumentError, 'illegal type for attributes option'
700
+ end
701
+
677
702
  lines = nil
678
- if input.is_a?(File)
679
- options[:attributes] ||= {}
680
- attrs = options[:attributes]
703
+ if input.is_a? File
681
704
  lines = input.readlines
682
705
  input_mtime = input.mtime
683
706
  input_path = File.expand_path(input.path)
@@ -721,8 +744,9 @@ module Asciidoctor
721
744
  # attributes on the Document.
722
745
  #
723
746
  # input - the String AsciiDoc source filename
724
- # options - a Hash of options to control processing (default: {})
725
- # see Asciidoctor::Document#initialize for details
747
+ # options - a String, Array or Hash of options to control processing (default: {})
748
+ # String and Array values are converted into a Hash.
749
+ # See Asciidoctor::Document#initialize for details about options.
726
750
  # block - a callback block for handling include::[] directives
727
751
  #
728
752
  # returns the Asciidoctor::Document
@@ -753,8 +777,9 @@ module Asciidoctor
753
777
  # default and the rendered output is returned.
754
778
  #
755
779
  # input - the String AsciiDoc source filename
756
- # options - a Hash of options to control processing (default: {})
757
- # see Asciidoctor::Document#initialize for details
780
+ # options - a String, Array or Hash of options to control processing (default: {})
781
+ # String and Array values are converted into a Hash.
782
+ # See Asciidoctor::Document#initialize for details about options.
758
783
  # block - a callback block for handling include::[] directives
759
784
  #
760
785
  # returns the Document object if the rendered result String is written to a
@@ -785,7 +810,7 @@ module Asciidoctor
785
810
  elsif write_in_place
786
811
  to_file = File.join(File.dirname(input.path), "#{doc.attributes['docname']}#{doc.attributes['outfilesuffix']}")
787
812
  elsif !stream_output && write_to_target
788
- working_dir = options.has_key?(:base_dir) ? File.expand_path(opts[:base_dir]) : File.expand_path(Dir.pwd)
813
+ working_dir = options.has_key?(:base_dir) ? File.expand_path(options[:base_dir]) : File.expand_path(Dir.pwd)
789
814
  # QUESTION should the jail be the working_dir or doc.base_dir???
790
815
  jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
791
816
  if to_dir
@@ -841,14 +866,16 @@ module Asciidoctor
841
866
  end
842
867
 
843
868
  # NOTE document cannot control this behavior if safe >= SafeMode::SERVER
844
- if !stream_output && doc.attr?('copycss') &&
869
+ if !stream_output && doc.attr?('basebackend-html') && doc.attr?('copycss') &&
845
870
  doc.attr?('linkcss') && DEFAULT_STYLESHEET_KEYS.include?(doc.attr('stylesheet'))
846
871
  Helpers.require_library 'fileutils'
847
872
  outdir = doc.attr('outdir')
848
873
  stylesdir = doc.normalize_system_path(doc.attr('stylesdir'), outdir,
849
874
  doc.safe >= SafeMode::SAFE ? outdir : nil)
850
- FileUtils.mkdir_p stylesdir
851
- FileUtils.cp DEFAULT_STYLESHEET_PATH, stylesdir, :preserve => true
875
+ Helpers.mkdir_p stylesdir
876
+ File.open(File.join(stylesdir, DEFAULT_STYLESHEET_NAME), 'w') {|f|
877
+ f.write Asciidoctor::HTML5.default_asciidoctor_stylesheet
878
+ }
852
879
  end
853
880
  doc
854
881
  else
@@ -860,8 +887,9 @@ module Asciidoctor
860
887
  # and render it to the specified backend format
861
888
  #
862
889
  # input - the String AsciiDoc source filename
863
- # options - a Hash of options to control processing (default: {})
864
- # see Asciidoctor::Document#initialize for details
890
+ # options - a String, Array or Hash of options to control processing (default: {})
891
+ # String and Array values are converted into a Hash.
892
+ # See Asciidoctor::Document#initialize for details about options.
865
893
  # block - a callback block for handling include::[] directives
866
894
  #
867
895
  # returns the Document object if the rendered result String is written to a
@@ -870,14 +898,6 @@ module Asciidoctor
870
898
  Asciidoctor.render(File.new(filename), options, &block)
871
899
  end
872
900
 
873
- # NOTE still contemplating this method
874
- #def self.parse_document_header(input, options = {})
875
- # document = Document.new [], options
876
- # reader = Reader.new input, document, true
877
- # Lexer.parse_document_header reader, document
878
- # document
879
- #end
880
-
881
901
  # modules
882
902
  require 'asciidoctor/debug'
883
903
  require 'asciidoctor/substituters'
@@ -9,11 +9,15 @@ class AbstractBlock < AbstractNode
9
9
  # Public: Set the String block title.
10
10
  attr_writer :title
11
11
 
12
+ # Public: Get/Set the caption for this block
13
+ attr_accessor :caption
14
+
12
15
  def initialize(parent, context)
13
16
  super(parent, context)
14
17
  @blocks = []
15
18
  @id = nil
16
19
  @title = nil
20
+ @caption = nil
17
21
  if context == :document
18
22
  @level = 0
19
23
  elsif !parent.nil? && !self.is_a?(Section)
@@ -63,7 +67,7 @@ class AbstractBlock < AbstractNode
63
67
  # whether this Block *can* have block content
64
68
  # that should be the option 'sectionbody'
65
69
  def blocks?
66
- !blocks.empty?
70
+ !@blocks.empty?
67
71
  end
68
72
 
69
73
  # Public: Get the element at i in the array of blocks.
@@ -219,7 +223,7 @@ class AbstractBlock < AbstractNode
219
223
  if @document.attributes.has_key?(caption_key)
220
224
  caption_title = @document.attributes["#{key}-caption"]
221
225
  caption_num = @document.counter_increment("#{key}-number", self)
222
- @caption = @attributes['caption'] = "#{caption_title} #{caption_num}. "
226
+ @caption = "#{caption_title} #{caption_num}. "
223
227
  end
224
228
  else
225
229
  @caption = caption
@@ -43,14 +43,17 @@ class AbstractNode
43
43
  # Document node and return the value of the attribute if found. Otherwise,
44
44
  # return the default value, which defaults to nil.
45
45
  #
46
- # name - the name of the attribute to lookup as a String or Symbol
47
- # default - the value to return if the attribute is not found (default: nil)
46
+ # name - the String or Symbol name of the attribute to lookup
47
+ # default - the Object value to return if the attribute is not found (default: nil)
48
+ # inherit - a Boolean indicating whether to check for the attribute on the
49
+ # AsciiDoctor::Document if not found on this node (default: false)
48
50
  #
49
51
  # return the value of the attribute or the default value if the attribute
50
52
  # is not found in the attributes of this node or the document node
51
- def attr(name, default = nil)
53
+ def attr(name, default = nil, inherit = true)
52
54
  name = name.to_s if name.is_a?(Symbol)
53
- if self == @document
55
+ inherit = false if self == @document
56
+ if !inherit
54
57
  default.nil? ? @attributes[name] : @attributes.fetch(name, default)
55
58
  else
56
59
  default.nil? ? @attributes.fetch(name, @document.attr(name)) :
@@ -59,26 +62,29 @@ class AbstractNode
59
62
  end
60
63
 
61
64
  # Public: Check if the attribute is defined, optionally performing a
62
- # comparison of its value
65
+ # comparison of its value if expected is not nil
63
66
  #
64
67
  # Check if the attribute is defined. First look in the attributes on this
65
68
  # node. If not found, and this node is a child of the Document node, look in
66
69
  # the attributes of the Document node. If the attribute is found and a
67
- # comparison value is specified, return whether the two values match.
70
+ # comparison value is specified (not nil), return whether the two values match.
68
71
  # Otherwise, return whether the attribute was found.
69
72
  #
70
- # name - the name of the attribute to lookup as a String or Symbol
71
- # expect - the expected value of the attribute (default: nil)
73
+ # name - the String or Symbol name of the attribute to lookup
74
+ # expect - the expected Object value of the attribute (default: nil)
75
+ # inherit - a Boolean indicating whether to check for the attribute on the
76
+ # AsciiDoctor::Document if not found on this node (default: false)
72
77
  #
73
78
  # return a Boolean indicating whether the attribute exists and, if a
74
79
  # comparison value is specified, whether the value of the attribute matches
75
80
  # the comparison value
76
- def attr?(name, expect = nil)
81
+ def attr?(name, expect = nil, inherit = true)
77
82
  name = name.to_s if name.is_a?(Symbol)
83
+ inherit = false if self == @document
78
84
  if expect.nil?
79
85
  if @attributes.has_key? name
80
86
  true
81
- elsif self != @document
87
+ elsif inherit
82
88
  @document.attributes.has_key? name
83
89
  else
84
90
  false
@@ -86,7 +92,7 @@ class AbstractNode
86
92
  else
87
93
  if @attributes.has_key? name
88
94
  @attributes[name] == expect
89
- elsif self != @document && @document.attributes.has_key?(name)
95
+ elsif inherit && @document.attributes.has_key?(name)
90
96
  @document.attributes[name] == expect
91
97
  else
92
98
  false
@@ -251,7 +257,9 @@ class AbstractNode
251
257
  def generate_data_uri(target_image, asset_dir_key = nil)
252
258
  Helpers.require_library 'base64'
253
259
 
254
- mimetype = 'image/' + File.extname(target_image)[1..-1]
260
+ ext = File.extname(target_image)[1..-1]
261
+ mimetype = 'image/' + ext
262
+ mimetype = "#{mimetype}+xml" if ext == 'svg'
255
263
  if asset_dir_key
256
264
  #asset_dir_path = normalize_system_path(@document.attr(asset_dir_key), nil, nil, :target_name => asset_dir_key)
257
265
  #image_path = normalize_system_path(target_image, asset_dir_path, nil, :target_name => 'image')
@@ -287,7 +295,7 @@ class AbstractNode
287
295
  # if the file does not exist.
288
296
  def read_asset(path, warn_on_failure = false)
289
297
  if File.readable? path
290
- File.read path
298
+ File.read(path).chomp
291
299
  else
292
300
  puts "asciidoctor: WARNING: file does not exist or cannot be read: #{path}" if warn_on_failure
293
301
  nil