asciidoctor 0.1.3 → 0.1.4
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 +387 -0
- data/README.adoc +358 -348
- data/asciidoctor.gemspec +30 -9
- data/bin/asciidoctor +3 -0
- data/bin/asciidoctor-safe +3 -0
- data/compat/asciidoc.conf +76 -4
- data/lib/asciidoctor.rb +174 -79
- data/lib/asciidoctor/abstract_block.rb +131 -101
- data/lib/asciidoctor/abstract_node.rb +108 -26
- data/lib/asciidoctor/attribute_list.rb +1 -1
- data/lib/asciidoctor/backends/_stylesheets.rb +204 -62
- data/lib/asciidoctor/backends/base_template.rb +11 -22
- data/lib/asciidoctor/backends/docbook45.rb +158 -163
- data/lib/asciidoctor/backends/docbook5.rb +103 -0
- data/lib/asciidoctor/backends/html5.rb +662 -445
- data/lib/asciidoctor/block.rb +54 -44
- data/lib/asciidoctor/cli/invoker.rb +41 -20
- data/lib/asciidoctor/cli/options.rb +66 -20
- data/lib/asciidoctor/debug.rb +1 -1
- data/lib/asciidoctor/document.rb +265 -100
- data/lib/asciidoctor/extensions.rb +443 -0
- data/lib/asciidoctor/helpers.rb +38 -6
- data/lib/asciidoctor/inline.rb +5 -5
- data/lib/asciidoctor/lexer.rb +532 -250
- data/lib/asciidoctor/{list_item.rb → list.rb} +33 -13
- data/lib/asciidoctor/path_resolver.rb +28 -2
- data/lib/asciidoctor/reader.rb +814 -455
- data/lib/asciidoctor/renderer.rb +128 -42
- data/lib/asciidoctor/section.rb +55 -41
- data/lib/asciidoctor/substituters.rb +380 -107
- data/lib/asciidoctor/table.rb +40 -30
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +32 -96
- data/man/{asciidoctor.ad → asciidoctor.adoc} +57 -48
- data/test/attributes_test.rb +200 -27
- data/test/blocks_test.rb +361 -22
- data/test/document_test.rb +496 -81
- data/test/extensions_test.rb +448 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.xml +3 -3
- data/test/fixtures/basic.asciidoc +1 -0
- data/test/fixtures/child-include.adoc +5 -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/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.xml +1 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/invoker_test.rb +82 -8
- data/test/lexer_test.rb +21 -3
- data/test/links_test.rb +34 -2
- data/test/lists_test.rb +304 -7
- data/test/options_test.rb +19 -3
- data/test/paragraphs_test.rb +13 -0
- data/test/paths_test.rb +22 -0
- data/test/preamble_test.rb +20 -0
- data/test/reader_test.rb +1096 -644
- data/test/renderer_test.rb +152 -12
- data/test/sections_test.rb +417 -76
- data/test/substitutions_test.rb +339 -138
- data/test/tables_test.rb +109 -4
- data/test/test_helper.rb +79 -13
- data/test/text_test.rb +111 -11
- metadata +54 -18
data/lib/asciidoctor/block.rb
CHANGED
@@ -3,74 +3,84 @@ module Asciidoctor
|
|
3
3
|
#
|
4
4
|
# Examples
|
5
5
|
#
|
6
|
-
# block = Asciidoctor::Block.new(
|
6
|
+
# block = Asciidoctor::Block.new(parent, :paragraph, :source => '_This_ is a <test>')
|
7
7
|
# block.content
|
8
|
-
# =>
|
8
|
+
# => "<em>This</em> is a <test>"
|
9
9
|
class Block < AbstractBlock
|
10
10
|
|
11
11
|
# Public: Create alias for context to be consistent w/ AsciiDoc
|
12
12
|
alias :blockname :context
|
13
13
|
|
14
|
-
# Public: Get/Set the original Array content for this
|
15
|
-
attr_accessor :
|
14
|
+
# Public: Get/Set the original Array content for this block, if applicable
|
15
|
+
attr_accessor :lines
|
16
16
|
|
17
17
|
# Public: Initialize an Asciidoctor::Block object.
|
18
18
|
#
|
19
|
-
# parent
|
20
|
-
# context
|
21
|
-
#
|
22
|
-
|
23
|
-
|
19
|
+
# parent - The parent AbstractBlock with a compound content model to which this Block will be appended.
|
20
|
+
# context - The Symbol context name for the type of content (e.g., :paragraph).
|
21
|
+
# opts - a Hash of options to customize block initialization: (default: {})
|
22
|
+
# * :content_model indicates whether blocks can be nested in this Block (:compound), otherwise
|
23
|
+
# how the lines should be processed (:simple, :verbatim, :raw, :empty). (default: :simple)
|
24
|
+
# * :attributes a Hash of attributes (key/value pairs) to assign to this Block. (default: {})
|
25
|
+
# * :source a String or Array of raw source for this Block. (default: nil)
|
26
|
+
#--
|
27
|
+
# QUESTION should we store source_data as lines for blocks that have compound content models?
|
28
|
+
def initialize(parent, context, opts = {})
|
24
29
|
super(parent, context)
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
@content_model = opts.fetch(:content_model, nil) || :simple
|
31
|
+
@attributes = opts.fetch(:attributes, nil) || {}
|
32
|
+
@subs = opts[:subs] if opts.has_key? :subs
|
33
|
+
raw_source = opts.fetch(:source, nil) || nil
|
34
|
+
if raw_source.nil?
|
35
|
+
@lines = []
|
36
|
+
elsif raw_source.class == String
|
37
|
+
# FIXME make line normalization a utility method since it's used multiple times in code base!!
|
38
|
+
if ::Asciidoctor::FORCE_ENCODING
|
39
|
+
@lines = raw_source.lines.map {|line| "#{line.rstrip.force_encoding(::Encoding::UTF_8)}\n" }
|
40
|
+
else
|
41
|
+
@lines = raw_source.lines.map {|line| "#{line.rstrip}\n" }
|
42
|
+
end
|
43
|
+
if (last = @lines.pop)
|
44
|
+
@lines.push last.chomp
|
45
|
+
end
|
46
|
+
else
|
47
|
+
@lines = raw_source.dup
|
48
|
+
end
|
37
49
|
end
|
38
50
|
|
39
|
-
# Public: Get an
|
40
|
-
#
|
51
|
+
# Public: Get an rendered version of the block content, performing
|
52
|
+
# any substitutions on the content.
|
41
53
|
#
|
42
54
|
# Examples
|
43
55
|
#
|
44
56
|
# doc = Asciidoctor::Document.new
|
45
57
|
# block = Asciidoctor::Block.new(doc, :paragraph,
|
46
|
-
#
|
58
|
+
# :source => '_This_ is what happens when you <meet> a stranger in the <alps>!')
|
47
59
|
# block.content
|
48
|
-
# =>
|
60
|
+
# => "<em>This</em> is what happens when you <meet> a stranger in the <alps>!"
|
49
61
|
def content
|
50
|
-
case @
|
51
|
-
when :
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
when :ulist, :olist, :dlist, :colist
|
56
|
-
@buffer
|
57
|
-
when :listing, :literal
|
58
|
-
apply_literal_subs(@buffer)
|
59
|
-
when :pass
|
60
|
-
apply_passthrough_subs(@buffer)
|
61
|
-
when :admonition, :example, :sidebar, :quote, :verse, :open
|
62
|
-
if !@buffer.nil?
|
63
|
-
apply_para_subs(@buffer)
|
64
|
-
else
|
65
|
-
@blocks.map {|b| b.render }.join
|
66
|
-
end
|
62
|
+
case @content_model
|
63
|
+
when :compound
|
64
|
+
super
|
65
|
+
when :simple, :verbatim, :raw
|
66
|
+
apply_subs @lines.join, @subs
|
67
67
|
else
|
68
|
-
|
68
|
+
warn "Unknown content model '#@content_model' for block: #{to_s}" unless @content_model == :empty
|
69
|
+
nil
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
73
|
+
# Public: Returns the preprocessed source of this block
|
74
|
+
#
|
75
|
+
# Returns the a String containing the lines joined together or nil if there
|
76
|
+
# are no lines
|
77
|
+
def source
|
78
|
+
@lines.join
|
79
|
+
end
|
80
|
+
|
72
81
|
def to_s
|
73
|
-
|
82
|
+
content_summary = @content_model == :compound ? %(# of blocks = #{@blocks.size}) : %(# of lines = #{@lines.size})
|
83
|
+
%(Block[@context: :#@context, @content_model: :#@content_model, #{content_summary}])
|
74
84
|
end
|
75
85
|
end
|
76
86
|
end
|
@@ -3,11 +3,11 @@ module Asciidoctor
|
|
3
3
|
# Public Invocation class for starting Asciidoctor via CLI
|
4
4
|
class Invoker
|
5
5
|
attr_reader :options
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :documents
|
7
7
|
attr_reader :code
|
8
8
|
|
9
9
|
def initialize(*options)
|
10
|
-
@
|
10
|
+
@documents = []
|
11
11
|
@out = nil
|
12
12
|
@err = nil
|
13
13
|
@code = 0
|
@@ -31,13 +31,14 @@ module Asciidoctor
|
|
31
31
|
|
32
32
|
begin
|
33
33
|
opts = {}
|
34
|
-
|
35
|
-
|
34
|
+
profile = false
|
35
|
+
infiles = []
|
36
36
|
outfile = nil
|
37
|
+
tofile = nil
|
37
38
|
@options.map {|k, v|
|
38
39
|
case k
|
39
|
-
when :
|
40
|
-
|
40
|
+
when :input_files
|
41
|
+
infiles = v
|
41
42
|
when :output_file
|
42
43
|
outfile = v
|
43
44
|
when :destination_dir
|
@@ -46,7 +47,7 @@ module Asciidoctor
|
|
46
47
|
when :attributes
|
47
48
|
opts[:attributes] = v.dup
|
48
49
|
when :verbose
|
49
|
-
|
50
|
+
profile = true if v
|
50
51
|
when :trace
|
51
52
|
# currently, nothing
|
52
53
|
else
|
@@ -54,28 +55,44 @@ module Asciidoctor
|
|
54
55
|
end
|
55
56
|
}
|
56
57
|
|
57
|
-
if
|
58
|
-
|
59
|
-
|
58
|
+
if infiles.size == 1 && infiles.first == '-'
|
59
|
+
# allows use of block to supply stdin, particularly useful for tests
|
60
|
+
inputs = [block_given? ? yield : STDIN]
|
60
61
|
else
|
61
|
-
|
62
|
+
inputs = infiles.map {|infile| File.new infile}
|
62
63
|
end
|
63
64
|
|
64
|
-
|
65
|
-
|
65
|
+
# NOTE: if infile is stdin, default to outfile as stout
|
66
|
+
if outfile == '-' || (infiles.size == 1 && infiles.first == '-' && outfile.to_s.empty?)
|
67
|
+
tofile = (@out || $stdout)
|
66
68
|
elsif !outfile.nil?
|
67
|
-
|
69
|
+
tofile = outfile
|
70
|
+
opts[:mkdirs] = true
|
68
71
|
else
|
72
|
+
tofile = nil
|
73
|
+
# automatically calculate outfile based on infile
|
69
74
|
opts[:in_place] = true unless opts.has_key? :to_dir
|
75
|
+
opts[:mkdirs] = true
|
70
76
|
end
|
71
77
|
|
72
|
-
|
78
|
+
original_opts = opts
|
79
|
+
inputs.each do |input|
|
80
|
+
|
81
|
+
opts = Helpers.clone_options(original_opts) if inputs.size > 1
|
82
|
+
opts[:to_file] = tofile unless tofile.nil?
|
83
|
+
opts[:monitor] = {} if profile
|
73
84
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
85
|
+
@documents ||= []
|
86
|
+
@documents.push Asciidoctor.render(input, opts)
|
87
|
+
|
88
|
+
if profile
|
89
|
+
monitor = opts[:monitor]
|
90
|
+
err = (@err || $stderr)
|
91
|
+
err.puts "Input file: #{input.respond_to?(:path) ? input.path : '-'}"
|
92
|
+
err.puts " Time to read and parse source: #{'%05.5f' % monitor[:parse]}"
|
93
|
+
err.puts " Time to render document: #{monitor.has_key?(:render) ? '%05.5f' % monitor[:render] : 'n/a'}"
|
94
|
+
err.puts " Total time to read, parse and render: #{'%05.5f' % (monitor[:load_render] || monitor[:parse])}"
|
95
|
+
end
|
79
96
|
end
|
80
97
|
rescue Exception => e
|
81
98
|
raise e if @options[:trace] || SystemExit === e
|
@@ -87,6 +104,10 @@ module Asciidoctor
|
|
87
104
|
end
|
88
105
|
end
|
89
106
|
|
107
|
+
def document
|
108
|
+
@documents.size > 0 ? @documents.first : nil
|
109
|
+
end
|
110
|
+
|
90
111
|
def redirect_streams(out, err = nil)
|
91
112
|
@out = out
|
92
113
|
@err = err
|
@@ -8,11 +8,12 @@ module Asciidoctor
|
|
8
8
|
|
9
9
|
def initialize(options = {})
|
10
10
|
self[:attributes] = options[:attributes] || {}
|
11
|
-
self[:
|
11
|
+
self[:input_files] = options[:input_files] || nil
|
12
12
|
self[:output_file] = options[:output_file] || nil
|
13
13
|
self[:safe] = options[:safe] || SafeMode::UNSAFE
|
14
14
|
self[:header_footer] = options[:header_footer] || true
|
15
|
-
self[:
|
15
|
+
self[:template_dirs] = options[:template_dirs] || nil
|
16
|
+
self[:template_engine] = options[:template_engine] || nil
|
16
17
|
if options[:doctype]
|
17
18
|
self[:attributes]['doctype'] = options[:doctype]
|
18
19
|
end
|
@@ -34,8 +35,8 @@ module Asciidoctor
|
|
34
35
|
def parse!(args)
|
35
36
|
opts_parser = OptionParser.new do |opts|
|
36
37
|
opts.banner = <<-EOS
|
37
|
-
Usage: asciidoctor [OPTION]...
|
38
|
-
Translate the AsciiDoc source FILE into the backend output format (e.g., HTML 5, DocBook 4.5, etc.)
|
38
|
+
Usage: asciidoctor [OPTION]... FILE...
|
39
|
+
Translate the AsciiDoc source FILE or FILE(s) into the backend output format (e.g., HTML 5, DocBook 4.5, etc.)
|
39
40
|
By default, the output is written to a file with the basename of the source file and the appropriate extension.
|
40
41
|
Example: asciidoctor -b html5 source.asciidoc
|
41
42
|
|
@@ -47,8 +48,8 @@ Example: asciidoctor -b html5 source.asciidoc
|
|
47
48
|
opts.on('-b', '--backend BACKEND', 'set output format backend (default: html5)') do |backend|
|
48
49
|
self[:attributes]['backend'] = backend
|
49
50
|
end
|
50
|
-
opts.on('-d', '--doctype DOCTYPE', ['article', 'book', 'inline'],
|
51
|
-
'document type to use when rendering output: [article, book, inline] (default: article)') do |doc_type|
|
51
|
+
opts.on('-d', '--doctype DOCTYPE', ['article', 'book', 'manpage', 'inline'],
|
52
|
+
'document type to use when rendering output: [article, book, manpage, inline] (default: article)') do |doc_type|
|
52
53
|
self[:attributes]['doctype'] = doc_type
|
53
54
|
end
|
54
55
|
opts.on('-o', '--out-file FILE', 'output file (default: based on input file path); use - to output to STDOUT') do |output_file|
|
@@ -84,11 +85,25 @@ Example: asciidoctor -b html5 source.asciidoc
|
|
84
85
|
'defined in the source document') do |attribs|
|
85
86
|
attribs.each do |attrib|
|
86
87
|
key, val = attrib.split '=', 2
|
88
|
+
# move leading ! to end for internal processing
|
89
|
+
#if val.nil? && key.start_with?('!')
|
90
|
+
# key = "#{key[1..-1]}!"
|
91
|
+
#end
|
87
92
|
self[:attributes][key] = val || ''
|
88
93
|
end
|
89
94
|
end
|
90
|
-
opts.on('-T', '--template-dir DIR', 'directory containing custom render templates
|
91
|
-
|
95
|
+
opts.on('-T', '--template-dir DIR', 'a directory containing custom render templates that override the built-in set (requires tilt gem)',
|
96
|
+
'may be specified multiple times') do |template_dir|
|
97
|
+
if self[:template_dirs].nil?
|
98
|
+
self[:template_dirs] = [template_dir]
|
99
|
+
elsif self[:template_dirs].is_a? Array
|
100
|
+
self[:template_dirs].push template_dir
|
101
|
+
else
|
102
|
+
self[:template_dirs] = [self[:template_dirs], template_dir]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
opts.on('-E', '--template-engine NAME', 'template engine to use for the custom render templates (loads gem on demand)') do |template_engine|
|
106
|
+
self[:template_engine] = template_engine
|
92
107
|
end
|
93
108
|
opts.on('-B', '--base-dir DIR', 'base directory containing the document and resources (default: directory of source file)') do |base_dir|
|
94
109
|
self[:base_dir] = base_dir
|
@@ -113,23 +128,54 @@ Example: asciidoctor -b html5 source.asciidoc
|
|
113
128
|
end
|
114
129
|
|
115
130
|
begin
|
131
|
+
infiles = []
|
132
|
+
opts_parser.parse! args
|
133
|
+
|
134
|
+
if args.empty?
|
135
|
+
$stderr.puts opts_parser
|
136
|
+
return 1
|
137
|
+
end
|
138
|
+
|
116
139
|
# shave off the file to process so that options errors appear correctly
|
117
|
-
if args.
|
118
|
-
|
140
|
+
if args.size == 1 && args.first == '-'
|
141
|
+
infiles.push args.pop
|
142
|
+
elsif
|
143
|
+
args.each do |file|
|
144
|
+
if (file == '-' || file.start_with?('-'))
|
145
|
+
# warn, but don't panic; we may have enough to proceed, so we won't force a failure
|
146
|
+
$stderr.puts "asciidoctor: WARNING: extra arguments detected (unparsed arguments: #{args.map{|a| "'#{a}'"} * ', '}) or incorrect usage of stdin"
|
147
|
+
else
|
148
|
+
# TODO this glob may not be necessary as the shell should have already performed expansion
|
149
|
+
matches = Dir.glob file
|
150
|
+
|
151
|
+
if matches.empty?
|
152
|
+
$stderr.puts "asciidoctor: FAILED: input file #{file} missing or cannot be read"
|
153
|
+
return 1
|
154
|
+
end
|
155
|
+
|
156
|
+
infiles.concat matches
|
157
|
+
end
|
158
|
+
end
|
119
159
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
160
|
+
|
161
|
+
infiles.each do |file|
|
162
|
+
unless file == '-' || File.readable?(file)
|
163
|
+
$stderr.puts "asciidoctor: FAILED: input file #{file} missing or cannot be read"
|
164
|
+
return 1
|
165
|
+
end
|
124
166
|
end
|
125
167
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
168
|
+
self[:input_files] = infiles
|
169
|
+
|
170
|
+
if !self[:template_dirs].nil?
|
171
|
+
begin
|
172
|
+
require 'tilt'
|
173
|
+
rescue LoadError
|
174
|
+
$stderr.puts 'asciidoctor: FAILED: tilt could not be loaded; to use a custom backend, you must have the tilt gem installed (gem install tilt)'
|
175
|
+
return 1
|
176
|
+
end
|
132
177
|
end
|
178
|
+
|
133
179
|
rescue OptionParser::MissingArgument
|
134
180
|
$stderr.puts "asciidoctor: option #{$!.message}"
|
135
181
|
$stdout.puts opts_parser
|
data/lib/asciidoctor/debug.rb
CHANGED
data/lib/asciidoctor/document.rb
CHANGED
@@ -25,9 +25,12 @@ class Document < AbstractBlock
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def save_to(block_attributes)
|
28
|
-
block_attributes[:attribute_entries] ||= []
|
29
|
-
block_attributes[:attribute_entries] << self
|
28
|
+
(block_attributes[:attribute_entries] ||= []) << self
|
30
29
|
end
|
30
|
+
|
31
|
+
#def save_to_next_block(document)
|
32
|
+
# (document.attributes[:pending_attribute_entries] ||= []) << self
|
33
|
+
#end
|
31
34
|
end
|
32
35
|
|
33
36
|
# Public A read-only integer value indicating the level of security that
|
@@ -84,34 +87,55 @@ class Document < AbstractBlock
|
|
84
87
|
# Public: A reference to the parent document of this nested document.
|
85
88
|
attr_reader :parent_document
|
86
89
|
|
90
|
+
# Public: The extensions registry
|
91
|
+
attr_reader :extensions
|
92
|
+
|
87
93
|
# Public: Initialize an Asciidoc object.
|
88
94
|
#
|
89
95
|
# data - The Array of Strings holding the Asciidoc source document. (default: [])
|
90
96
|
# options - A Hash of options to control processing, such as setting the safe mode (:safe),
|
91
97
|
# suppressing the header/footer (:header_footer) and attribute overrides (:attributes)
|
92
98
|
# (default: {})
|
93
|
-
# block - A block that can be used to retrieve external Asciidoc
|
94
|
-
# data to include in this document.
|
95
99
|
#
|
96
100
|
# Examples
|
97
101
|
#
|
98
102
|
# data = File.readlines(filename)
|
99
103
|
# doc = Asciidoctor::Document.new(data)
|
100
104
|
# puts doc.render
|
101
|
-
def initialize(data = [], options = {}
|
105
|
+
def initialize(data = [], options = {})
|
102
106
|
super(self, :document)
|
103
|
-
@renderer = nil
|
104
107
|
|
105
108
|
if options[:parent]
|
106
109
|
@parent_document = options.delete(:parent)
|
107
|
-
# should we dup attributes here?
|
108
|
-
options[:attributes] = @parent_document.attributes
|
109
110
|
options[:base_dir] ||= @parent_document.base_dir
|
111
|
+
# QUESTION should we support setting attribute in parent document from nested document?
|
112
|
+
# NOTE we must dup or else all the assignments to the overrides clobbers the real attributes
|
113
|
+
@attribute_overrides = @parent_document.attributes.dup
|
110
114
|
@safe = @parent_document.safe
|
111
115
|
@renderer = @parent_document.renderer
|
116
|
+
initialize_extensions = false
|
117
|
+
@extensions = @parent_document.extensions
|
112
118
|
else
|
113
119
|
@parent_document = nil
|
120
|
+
# copy attributes map and normalize keys
|
121
|
+
# attribute overrides are attributes that can only be set from the commandline
|
122
|
+
# a direct assignment effectively makes the attribute a constant
|
123
|
+
# a nil value or name with leading or trailing ! will result in the attribute being unassigned
|
124
|
+
@attribute_overrides = (options[:attributes] || {}).inject({}) do |collector,(key,value)|
|
125
|
+
if key.start_with?('!')
|
126
|
+
key = key[1..-1]
|
127
|
+
value = nil
|
128
|
+
elsif key.end_with?('!')
|
129
|
+
key = key[0..-2]
|
130
|
+
value = nil
|
131
|
+
end
|
132
|
+
collector[key.downcase] = value
|
133
|
+
collector
|
134
|
+
end
|
114
135
|
@safe = nil
|
136
|
+
@renderer = nil
|
137
|
+
initialize_extensions = Asciidoctor.const_defined?('Extensions')
|
138
|
+
@extensions = nil # initialize furthur down
|
115
139
|
end
|
116
140
|
|
117
141
|
@header = nil
|
@@ -120,22 +144,26 @@ class Document < AbstractBlock
|
|
120
144
|
:footnotes => [],
|
121
145
|
:links => [],
|
122
146
|
:images => [],
|
123
|
-
:indexterms => []
|
147
|
+
:indexterms => [],
|
148
|
+
:includes => Set.new,
|
124
149
|
}
|
125
150
|
@counters = {}
|
126
151
|
@callouts = Callouts.new
|
152
|
+
@attributes_modified = Set.new
|
127
153
|
@options = options
|
128
|
-
|
129
|
-
|
130
|
-
@safe =
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
154
|
+
unless @parent_document
|
155
|
+
# safely resolve the safe mode from const, int or string
|
156
|
+
if @safe.nil? && !(safe_mode = @options[:safe])
|
157
|
+
@safe = SafeMode::SECURE
|
158
|
+
elsif safe_mode.is_a?(Fixnum)
|
159
|
+
# be permissive in case API user wants to define new levels
|
160
|
+
@safe = safe_mode
|
161
|
+
else
|
162
|
+
begin
|
163
|
+
@safe = SafeMode.const_get(safe_mode.to_s.upcase).to_i
|
164
|
+
rescue
|
165
|
+
@safe = SafeMode::SECURE.to_i
|
166
|
+
end
|
139
167
|
end
|
140
168
|
end
|
141
169
|
@options[:header_footer] = @options.fetch(:header_footer, false)
|
@@ -145,7 +173,10 @@ class Document < AbstractBlock
|
|
145
173
|
@attributes['notitle'] = '' unless @options[:header_footer]
|
146
174
|
@attributes['toc-placement'] = 'auto'
|
147
175
|
@attributes['stylesheet'] = ''
|
148
|
-
@attributes['
|
176
|
+
@attributes['copycss'] = '' if @options[:header_footer]
|
177
|
+
@attributes['prewrap'] = ''
|
178
|
+
@attributes['attribute-undefined'] = COMPLIANCE[:attribute_undefined]
|
179
|
+
@attributes['attribute-missing'] = COMPLIANCE[:attribute_missing]
|
149
180
|
|
150
181
|
# language strings
|
151
182
|
# TODO load these based on language settings
|
@@ -160,11 +191,10 @@ class Document < AbstractBlock
|
|
160
191
|
#@attributes['listing-caption'] = 'Listing'
|
161
192
|
@attributes['table-caption'] = 'Table'
|
162
193
|
@attributes['toc-title'] = 'Table of Contents'
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
@attribute_overrides = options[:attributes] || {}
|
194
|
+
@attributes['manname-title'] = 'NAME'
|
195
|
+
@attributes['untitled-label'] = 'Untitled'
|
196
|
+
@attributes['version-label'] = 'Version'
|
197
|
+
@attributes['last-update-label'] = 'Last updated'
|
168
198
|
|
169
199
|
@attribute_overrides['asciidoctor'] = ''
|
170
200
|
@attribute_overrides['asciidoctor-version'] = VERSION
|
@@ -177,22 +207,27 @@ class Document < AbstractBlock
|
|
177
207
|
# sync the embedded attribute w/ the value of options...do not allow override
|
178
208
|
@attribute_overrides['embedded'] = @options[:header_footer] ? nil : ''
|
179
209
|
|
180
|
-
# the only way to set the include-depth attribute is via the document options
|
181
|
-
#
|
182
|
-
@attribute_overrides['include-depth'] ||=
|
210
|
+
# the only way to set the max-include-depth attribute is via the document options
|
211
|
+
# 64 is the AsciiDoc default
|
212
|
+
@attribute_overrides['max-include-depth'] ||= 64
|
213
|
+
|
214
|
+
# the only way to enable uri reads is via the document options, disabled by default
|
215
|
+
unless !@attribute_overrides['allow-uri-read'].nil?
|
216
|
+
@attribute_overrides['allow-uri-read'] = nil
|
217
|
+
end
|
183
218
|
|
184
219
|
# if the base_dir option is specified, it overrides docdir as the root for relative paths
|
185
220
|
# otherwise, the base_dir is the directory of the source file (docdir) or the current
|
186
221
|
# directory of the input is a string
|
187
|
-
if options[:base_dir].nil?
|
222
|
+
if @options[:base_dir].nil?
|
188
223
|
if @attribute_overrides['docdir']
|
189
224
|
@base_dir = @attribute_overrides['docdir'] = File.expand_path(@attribute_overrides['docdir'])
|
190
225
|
else
|
191
|
-
#
|
226
|
+
#warn 'asciidoctor: WARNING: setting base_dir is recommended when working with string documents' unless nested?
|
192
227
|
@base_dir = @attribute_overrides['docdir'] = File.expand_path(Dir.pwd)
|
193
228
|
end
|
194
229
|
else
|
195
|
-
@base_dir = @attribute_overrides['docdir'] = File.expand_path(options[:base_dir])
|
230
|
+
@base_dir = @attribute_overrides['docdir'] = File.expand_path(@options[:base_dir])
|
196
231
|
end
|
197
232
|
|
198
233
|
# allow common attributes backend and doctype to be set using options hash
|
@@ -205,18 +240,19 @@ class Document < AbstractBlock
|
|
205
240
|
end
|
206
241
|
|
207
242
|
if @safe >= SafeMode::SERVER
|
208
|
-
# restrict document from setting
|
243
|
+
# restrict document from setting copycss, source-highlighter and backend
|
209
244
|
@attribute_overrides['copycss'] ||= nil
|
210
245
|
@attribute_overrides['source-highlighter'] ||= nil
|
211
246
|
@attribute_overrides['backend'] ||= DEFAULT_BACKEND
|
212
247
|
# restrict document from seeing the docdir and trim docfile to relative path
|
213
|
-
if @attribute_overrides.has_key?('docfile')
|
248
|
+
if !@parent_document && @attribute_overrides.has_key?('docfile')
|
214
249
|
@attribute_overrides['docfile'] = @attribute_overrides['docfile'][(@attribute_overrides['docdir'].length + 1)..-1]
|
215
250
|
end
|
216
251
|
@attribute_overrides['docdir'] = ''
|
217
252
|
if @safe >= SafeMode::SECURE
|
218
|
-
# assign linkcss (preventing css embedding) unless disabled from the commandline
|
219
|
-
|
253
|
+
# assign linkcss (preventing css embedding) unless explicitly disabled from the commandline or API
|
254
|
+
# effectively the same has "has key 'linkcss' and value == nil"
|
255
|
+
unless @attribute_overrides.fetch('linkcss', '').nil?
|
220
256
|
@attribute_overrides['linkcss'] = ''
|
221
257
|
end
|
222
258
|
# restrict document from enabling icons
|
@@ -229,15 +265,20 @@ class Document < AbstractBlock
|
|
229
265
|
# a nil value undefines the attribute
|
230
266
|
if val.nil?
|
231
267
|
@attributes.delete(key)
|
232
|
-
# a negative key undefines the attribute
|
233
|
-
|
234
|
-
|
268
|
+
# a negative key (trailing !) undefines the attribute
|
269
|
+
# NOTE already normalize above as key with nil value
|
270
|
+
#elsif key.end_with? '!'
|
271
|
+
# @attributes.delete(key[0..-2])
|
272
|
+
# a negative key (leading !) undefines the attribute
|
273
|
+
# NOTE already normalize above as key with nil value
|
274
|
+
#elsif key.start_with? '!'
|
275
|
+
# @attributes.delete(key[1..-1])
|
235
276
|
# otherwise it's an attribute assignment
|
236
277
|
else
|
237
278
|
# a value ending in @ indicates this attribute does not override
|
238
279
|
# an attribute with the same key in the document souce
|
239
280
|
if val.is_a?(String) && val.end_with?('@')
|
240
|
-
val.chop
|
281
|
+
val = val.chop
|
241
282
|
verdict = true
|
242
283
|
end
|
243
284
|
@attributes[key] = val
|
@@ -245,43 +286,55 @@ class Document < AbstractBlock
|
|
245
286
|
verdict
|
246
287
|
}
|
247
288
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
@
|
289
|
+
if !@parent_document
|
290
|
+
# setup default backend and doctype
|
291
|
+
@attributes['backend'] ||= DEFAULT_BACKEND
|
292
|
+
@attributes['doctype'] ||= DEFAULT_DOCTYPE
|
293
|
+
update_backend_attributes
|
294
|
+
|
295
|
+
#@attributes['indir'] = @attributes['docdir']
|
296
|
+
#@attributes['infile'] = @attributes['docfile']
|
297
|
+
|
298
|
+
# dynamic intrinstic attribute values
|
299
|
+
now = Time.new
|
300
|
+
@attributes['localdate'] ||= now.strftime('%Y-%m-%d')
|
301
|
+
@attributes['localtime'] ||= now.strftime('%H:%M:%S %Z')
|
302
|
+
@attributes['localdatetime'] ||= [@attributes['localdate'], @attributes['localtime']] * ' '
|
303
|
+
|
304
|
+
# docdate, doctime and docdatetime should default to
|
305
|
+
# localdate, localtime and localdatetime if not otherwise set
|
306
|
+
@attributes['docdate'] ||= @attributes['localdate']
|
307
|
+
@attributes['doctime'] ||= @attributes['localtime']
|
308
|
+
@attributes['docdatetime'] ||= @attributes['localdatetime']
|
309
|
+
|
310
|
+
# fallback directories
|
311
|
+
@attributes['stylesdir'] ||= '.'
|
312
|
+
@attributes['iconsdir'] ||= File.join(@attributes.fetch('imagesdir', './images'), 'icons')
|
313
|
+
|
314
|
+
@extensions = initialize_extensions ? Extensions::Registry.new(self) : nil
|
315
|
+
@reader = PreprocessorReader.new self, data, Asciidoctor::Reader::Cursor.new(@attributes['docfile'], @base_dir)
|
316
|
+
|
317
|
+
if @extensions && @extensions.preprocessors?
|
318
|
+
@extensions.load_preprocessors(self).each do |processor|
|
319
|
+
@reader = processor.process(@reader, @reader.lines) || @reader
|
320
|
+
end
|
321
|
+
end
|
261
322
|
else
|
262
|
-
|
323
|
+
# don't need to do the extra processing within our own document
|
324
|
+
# FIXME line info isn't reported correctly within include files in nested document
|
325
|
+
@reader = Reader.new data, options[:cursor]
|
263
326
|
end
|
264
327
|
|
265
|
-
# dynamic intrinstic attribute values
|
266
|
-
now = Time.new
|
267
|
-
@attributes['localdate'] ||= now.strftime('%Y-%m-%d')
|
268
|
-
@attributes['localtime'] ||= now.strftime('%H:%M:%S %Z')
|
269
|
-
@attributes['localdatetime'] ||= [@attributes['localdate'], @attributes['localtime']] * ' '
|
270
|
-
|
271
|
-
# docdate, doctime and docdatetime should default to
|
272
|
-
# localdate, localtime and localdatetime if not otherwise set
|
273
|
-
@attributes['docdate'] ||= @attributes['localdate']
|
274
|
-
@attributes['doctime'] ||= @attributes['localtime']
|
275
|
-
@attributes['docdatetime'] ||= @attributes['localdatetime']
|
276
|
-
|
277
|
-
# fallback directories
|
278
|
-
@attributes['stylesdir'] ||= '.'
|
279
|
-
@attributes['iconsdir'] ||= File.join(@attributes.fetch('imagesdir', './images'), 'icons')
|
280
|
-
|
281
328
|
# Now parse the lines in the reader into blocks
|
282
329
|
Lexer.parse(@reader, self, :header_only => @options.fetch(:parse_header_only, false))
|
283
330
|
|
284
331
|
@callouts.rewind
|
332
|
+
|
333
|
+
if !@parent_document && @extensions && @extensions.treeprocessors?
|
334
|
+
@extensions.load_treeprocessors(self).each do |processor|
|
335
|
+
processor.process
|
336
|
+
end
|
337
|
+
end
|
285
338
|
end
|
286
339
|
|
287
340
|
# Public: Get the named counter and take the next number in the sequence.
|
@@ -363,7 +416,7 @@ class Document < AbstractBlock
|
|
363
416
|
end
|
364
417
|
|
365
418
|
def nested?
|
366
|
-
|
419
|
+
@parent_document ? true : false
|
367
420
|
end
|
368
421
|
|
369
422
|
def embedded?
|
@@ -371,14 +424,18 @@ class Document < AbstractBlock
|
|
371
424
|
@attributes.has_key? 'embedded'
|
372
425
|
end
|
373
426
|
|
427
|
+
def extensions?
|
428
|
+
@extensions ? true : false
|
429
|
+
end
|
430
|
+
|
374
431
|
# Make the raw source for the Document available.
|
375
432
|
def source
|
376
|
-
@reader.source
|
433
|
+
@reader.source if @reader
|
377
434
|
end
|
378
435
|
|
379
436
|
# Make the raw source lines for the Document available.
|
380
437
|
def source_lines
|
381
|
-
@reader.
|
438
|
+
@reader.source_lines if @reader
|
382
439
|
end
|
383
440
|
|
384
441
|
def doctype
|
@@ -389,24 +446,34 @@ class Document < AbstractBlock
|
|
389
446
|
@attributes['backend']
|
390
447
|
end
|
391
448
|
|
449
|
+
def basebackend? base
|
450
|
+
@attributes['basebackend'] == base
|
451
|
+
end
|
452
|
+
|
392
453
|
# The title explicitly defined in the document attributes
|
393
454
|
def title
|
394
455
|
@attributes['title']
|
395
456
|
end
|
396
457
|
|
397
458
|
def title=(title)
|
398
|
-
@header ||= Section.new
|
459
|
+
@header ||= Section.new(self, 0)
|
399
460
|
@header.title = title
|
400
461
|
end
|
401
462
|
|
402
463
|
# We need to be able to return some semblance of a title
|
403
|
-
def doctitle
|
404
|
-
if !(
|
405
|
-
title
|
464
|
+
def doctitle(opts = {})
|
465
|
+
if !(val = @attributes.fetch('title', '')).empty?
|
466
|
+
val = title
|
406
467
|
elsif !(sect = first_section).nil? && sect.title?
|
407
|
-
sect.title
|
468
|
+
val = sect.title
|
408
469
|
else
|
409
|
-
nil
|
470
|
+
return nil
|
471
|
+
end
|
472
|
+
|
473
|
+
if opts[:sanitize] && val.include?('<')
|
474
|
+
val.gsub(/<[^>]+>/, '').tr_s(' ', ' ').strip
|
475
|
+
else
|
476
|
+
val
|
410
477
|
end
|
411
478
|
end
|
412
479
|
alias :name :doctitle
|
@@ -426,7 +493,7 @@ class Document < AbstractBlock
|
|
426
493
|
end
|
427
494
|
|
428
495
|
def notitle
|
429
|
-
@attributes.has_key?
|
496
|
+
!@attributes.has_key?('showtitle') && @attributes.has_key?('notitle')
|
430
497
|
end
|
431
498
|
|
432
499
|
def noheader
|
@@ -439,27 +506,94 @@ class Document < AbstractBlock
|
|
439
506
|
end
|
440
507
|
|
441
508
|
def has_header?
|
442
|
-
|
509
|
+
@header ? true : false
|
510
|
+
end
|
511
|
+
|
512
|
+
# Public: Append a content Block to this Document.
|
513
|
+
#
|
514
|
+
# If the child block is a Section, assign an index to it.
|
515
|
+
#
|
516
|
+
# block - The child Block to append to this parent Block
|
517
|
+
#
|
518
|
+
# Returns nothing.
|
519
|
+
def <<(block)
|
520
|
+
super
|
521
|
+
if block.context == :section
|
522
|
+
assign_index block
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
# Internal: called after the header has been parsed and before the content
|
527
|
+
# will be parsed.
|
528
|
+
#--
|
529
|
+
# QUESTION should we invoke the Treeprocessors here, passing in a phase?
|
530
|
+
# QUESTION is finalize_header the right name?
|
531
|
+
def finalize_header unrooted_attributes, header_valid = true
|
532
|
+
clear_playback_attributes unrooted_attributes
|
533
|
+
save_attributes
|
534
|
+
unrooted_attributes['invalid-header'] = true unless header_valid
|
535
|
+
unrooted_attributes
|
443
536
|
end
|
444
537
|
|
445
538
|
# Internal: Branch the attributes so that the original state can be restored
|
446
539
|
# at a future time.
|
447
540
|
def save_attributes
|
541
|
+
# enable toc and numbered by default in DocBook backend
|
542
|
+
# NOTE the attributes_modified should go away once we have a proper attribute storage & tracking facility
|
543
|
+
if @attributes['basebackend'] == 'docbook'
|
544
|
+
@attributes['toc'] = '' unless attribute_locked?('toc') || @attributes_modified.include?('toc')
|
545
|
+
@attributes['numbered'] = '' unless attribute_locked?('numbered') || @attributes_modified.include?('numbered')
|
546
|
+
end
|
547
|
+
|
448
548
|
unless @attributes.has_key?('doctitle') || (val = doctitle).nil?
|
449
549
|
@attributes['doctitle'] = val
|
450
550
|
end
|
451
551
|
|
452
552
|
# css-signature cannot be updated after header attributes are processed
|
453
|
-
if
|
553
|
+
if !@id && @attributes.has_key?('css-signature')
|
454
554
|
@id = @attributes['css-signature']
|
455
555
|
end
|
456
556
|
|
457
|
-
|
557
|
+
toc_val = @attributes['toc']
|
558
|
+
toc2_val = @attributes['toc2']
|
559
|
+
toc_position_val = @attributes['toc-position']
|
560
|
+
|
561
|
+
if (!toc_val.nil? && (toc_val != '' || toc_position_val.to_s != '')) || !toc2_val.nil?
|
562
|
+
default_toc_position = 'left'
|
563
|
+
default_toc_class = 'toc2'
|
564
|
+
position = [toc_position_val, toc2_val, toc_val].find {|pos| pos.to_s != ''}
|
565
|
+
position = default_toc_position if !position && !toc2_val.nil?
|
458
566
|
@attributes['toc'] = ''
|
459
|
-
|
567
|
+
case position
|
568
|
+
when 'left', '<', '<'
|
569
|
+
@attributes['toc-position'] = 'left'
|
570
|
+
when 'right', '>', '>'
|
571
|
+
@attributes['toc-position'] = 'right'
|
572
|
+
when 'top', '^'
|
573
|
+
@attributes['toc-position'] = 'top'
|
574
|
+
when 'bottom', 'v'
|
575
|
+
@attributes['toc-position'] = 'bottom'
|
576
|
+
when 'center'
|
577
|
+
@attributes.delete('toc2')
|
578
|
+
default_toc_class = nil
|
579
|
+
default_toc_position = 'center'
|
580
|
+
end
|
581
|
+
@attributes['toc-class'] ||= default_toc_class if default_toc_class
|
582
|
+
@attributes['toc-position'] ||= default_toc_position if default_toc_position
|
460
583
|
end
|
461
584
|
|
462
585
|
@original_attributes = @attributes.dup
|
586
|
+
|
587
|
+
# unfreeze "flexible" attributes
|
588
|
+
unless nested?
|
589
|
+
FLEXIBLE_ATTRIBUTES.each do |name|
|
590
|
+
# turning a flexible attribute off should be permanent
|
591
|
+
# (we may need more config if that's not always the case)
|
592
|
+
if @attribute_overrides.has_key?(name) && !@attribute_overrides[name].nil?
|
593
|
+
@attribute_overrides.delete(name)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
463
597
|
end
|
464
598
|
|
465
599
|
# Internal: Restore the attributes to the previously saved state
|
@@ -501,6 +635,7 @@ class Document < AbstractBlock
|
|
501
635
|
false
|
502
636
|
else
|
503
637
|
@attributes[name] = apply_attribute_value_subs(value)
|
638
|
+
@attributes_modified << name
|
504
639
|
if name == 'backend'
|
505
640
|
update_backend_attributes()
|
506
641
|
end
|
@@ -520,6 +655,7 @@ class Document < AbstractBlock
|
|
520
655
|
false
|
521
656
|
else
|
522
657
|
@attributes.delete(name)
|
658
|
+
@attributes_modified << name
|
523
659
|
true
|
524
660
|
end
|
525
661
|
end
|
@@ -530,7 +666,7 @@ class Document < AbstractBlock
|
|
530
666
|
#
|
531
667
|
# Returns true if the attribute is locked, false otherwise
|
532
668
|
def attribute_locked?(name)
|
533
|
-
@attribute_overrides.has_key?(name)
|
669
|
+
@attribute_overrides.has_key?(name)
|
534
670
|
end
|
535
671
|
|
536
672
|
# Internal: Apply substitutions to the attribute value
|
@@ -546,12 +682,9 @@ class Document < AbstractBlock
|
|
546
682
|
if value.match(REGEXP[:pass_macro_basic])
|
547
683
|
# copy match for Ruby 1.8.7 compat
|
548
684
|
m = $~
|
549
|
-
subs = []
|
550
685
|
if !m[1].empty?
|
551
|
-
subs =
|
552
|
-
|
553
|
-
if !subs.empty?
|
554
|
-
apply_subs(m[2], subs)
|
686
|
+
subs = resolve_pass_subs m[1]
|
687
|
+
subs.empty? ? m[2] : apply_subs(m[2], subs)
|
555
688
|
else
|
556
689
|
m[2]
|
557
690
|
end
|
@@ -566,7 +699,7 @@ class Document < AbstractBlock
|
|
566
699
|
if BACKEND_ALIASES.has_key? backend
|
567
700
|
backend = @attributes['backend'] = BACKEND_ALIASES[backend]
|
568
701
|
end
|
569
|
-
basebackend = backend.sub(
|
702
|
+
basebackend = backend.sub(REGEXP[:trailing_digit], '')
|
570
703
|
page_width = DEFAULT_PAGE_WIDTHS[basebackend]
|
571
704
|
if page_width
|
572
705
|
@attributes['pagewidth'] = page_width
|
@@ -593,9 +726,12 @@ class Document < AbstractBlock
|
|
593
726
|
|
594
727
|
# Load up relevant Document @options
|
595
728
|
if @options.has_key? :template_dir
|
596
|
-
render_options[:
|
729
|
+
render_options[:template_dirs] = [@options[:template_dir]]
|
730
|
+
elsif @options.has_key? :template_dirs
|
731
|
+
render_options[:template_dirs] = @options[:template_dirs]
|
597
732
|
end
|
598
733
|
|
734
|
+
render_options[:template_cache] = @options.fetch(:template_cache, true)
|
599
735
|
render_options[:backend] = @attributes.fetch('backend', 'html5')
|
600
736
|
render_options[:template_engine] = @options[:template_engine]
|
601
737
|
render_options[:eruby] = @options.fetch(:eruby, 'erb')
|
@@ -614,23 +750,42 @@ class Document < AbstractBlock
|
|
614
750
|
def render(opts = {})
|
615
751
|
restore_attributes
|
616
752
|
r = renderer(opts)
|
753
|
+
|
754
|
+
# QUESTION should we add Preserializeprocessors? is it the right name?
|
755
|
+
#if !@parent_document && @extensions && @extensions.preserializeprocessors?
|
756
|
+
# @extensions.load_preserializeprocessors(self).each do |processor|
|
757
|
+
# processor.process r
|
758
|
+
# end
|
759
|
+
#end
|
760
|
+
|
617
761
|
if doctype == 'inline'
|
618
762
|
# QUESTION should we warn if @blocks.size > 0 and the first block is not a paragraph?
|
619
|
-
if
|
620
|
-
block.content
|
763
|
+
if !(block = @blocks.first).nil? && block.content_model != :compound
|
764
|
+
output = block.content
|
621
765
|
else
|
622
|
-
''
|
766
|
+
output = ''
|
623
767
|
end
|
624
768
|
else
|
625
|
-
@options.merge(opts)[:header_footer] ? r.render('document', self).strip : r.render('embedded', self)
|
769
|
+
output = @options.merge(opts)[:header_footer] ? r.render('document', self).strip : r.render('embedded', self)
|
626
770
|
end
|
771
|
+
|
772
|
+
if !@parent_document && @extensions
|
773
|
+
if @extensions.postprocessors?
|
774
|
+
@extensions.load_postprocessors(self).each do |processor|
|
775
|
+
output = processor.process output
|
776
|
+
end
|
777
|
+
end
|
778
|
+
@extensions.reset
|
779
|
+
end
|
780
|
+
|
781
|
+
output
|
627
782
|
end
|
628
783
|
|
629
784
|
def content
|
630
785
|
# per AsciiDoc-spec, remove the title before rendering the body,
|
631
786
|
# regardless of whether the header is rendered)
|
632
787
|
@attributes.delete('title')
|
633
|
-
|
788
|
+
super
|
634
789
|
end
|
635
790
|
|
636
791
|
# Public: Read the docinfo file(s) for inclusion in the
|
@@ -640,14 +795,21 @@ class Document < AbstractBlock
|
|
640
795
|
# attribute is set, read the doc-name.docinfo.ext file. If the docinfo2
|
641
796
|
# attribute is set, read both files in that order.
|
642
797
|
#
|
798
|
+
# pos - The Symbol position of the docinfo, either :header or :footer. (default: :header)
|
643
799
|
# ext - The extension of the docinfo file(s). If not set, the extension
|
644
800
|
# will be determined based on the basebackend. (default: nil)
|
645
801
|
#
|
646
802
|
# returns The contents of the docinfo file(s)
|
647
|
-
def docinfo(ext = nil)
|
803
|
+
def docinfo(pos = :header, ext = nil)
|
648
804
|
if safe >= SafeMode::SECURE
|
649
805
|
''
|
650
806
|
else
|
807
|
+
case pos
|
808
|
+
when :footer
|
809
|
+
qualifier = '-footer'
|
810
|
+
else
|
811
|
+
qualifier = nil
|
812
|
+
end
|
651
813
|
ext = @attributes['outfilesuffix'] if ext.nil?
|
652
814
|
|
653
815
|
content = nil
|
@@ -655,21 +817,24 @@ class Document < AbstractBlock
|
|
655
817
|
docinfo = @attributes.has_key?('docinfo')
|
656
818
|
docinfo1 = @attributes.has_key?('docinfo1')
|
657
819
|
docinfo2 = @attributes.has_key?('docinfo2')
|
658
|
-
docinfo_filename = "docinfo#{ext}"
|
820
|
+
docinfo_filename = "docinfo#{qualifier}#{ext}"
|
659
821
|
if docinfo1 || docinfo2
|
660
822
|
docinfo_path = normalize_system_path(docinfo_filename)
|
661
823
|
content = read_asset(docinfo_path)
|
824
|
+
content = sub_attributes(content.lines.entries).join unless content.nil?
|
662
825
|
end
|
663
826
|
|
664
827
|
if (docinfo || docinfo2) && @attributes.has_key?('docname')
|
665
828
|
docinfo_path = normalize_system_path("#{@attributes['docname']}-#{docinfo_filename}")
|
666
829
|
content2 = read_asset(docinfo_path)
|
667
830
|
unless content2.nil?
|
831
|
+
content2 = sub_attributes(content2.lines.entries).join
|
668
832
|
content = content.nil? ? content2 : "#{content}\n#{content2}"
|
669
833
|
end
|
670
834
|
end
|
671
835
|
|
672
|
-
|
836
|
+
# to_s forces nil to empty string
|
837
|
+
content.to_s
|
673
838
|
end
|
674
839
|
end
|
675
840
|
|