asciidoctor 0.1.2 → 0.1.3

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