asciidoctor 0.1.4 → 1.5.0
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 +209 -25
- data/{LICENSE → LICENSE.adoc} +4 -3
- data/README.adoc +392 -395
- data/Rakefile +94 -137
- data/benchmark/benchmark.rb +127 -0
- data/benchmark/sample-data/mdbasics.adoc +334 -0
- data/bin/asciidoctor +5 -8
- data/bin/asciidoctor-safe +4 -8
- data/compat/asciidoc.conf +78 -11
- data/compat/font-awesome-3-compat.css +397 -0
- data/data/stylesheets/asciidoctor-default.css +399 -0
- data/data/stylesheets/coderay-asciidoctor.css +89 -0
- data/features/open_block.feature +92 -0
- data/features/pass_block.feature +66 -0
- data/features/step_definitions.rb +42 -0
- data/features/text_formatting.feature +55 -0
- data/features/xref.feature +116 -0
- data/lib/asciidoctor.rb +1155 -605
- data/lib/asciidoctor/abstract_block.rb +157 -71
- data/lib/asciidoctor/abstract_node.rb +150 -93
- data/lib/asciidoctor/attribute_list.rb +85 -90
- data/lib/asciidoctor/block.rb +51 -24
- data/lib/asciidoctor/callouts.rb +4 -7
- data/lib/asciidoctor/cli.rb +3 -0
- data/lib/asciidoctor/cli/invoker.rb +86 -76
- data/lib/asciidoctor/cli/options.rb +111 -61
- data/lib/asciidoctor/converter.rb +232 -0
- data/lib/asciidoctor/converter/base.rb +58 -0
- data/lib/asciidoctor/converter/composite.rb +66 -0
- data/lib/asciidoctor/converter/docbook45.rb +94 -0
- data/lib/asciidoctor/converter/docbook5.rb +684 -0
- data/lib/asciidoctor/converter/factory.rb +225 -0
- data/lib/asciidoctor/converter/html5.rb +1081 -0
- data/lib/asciidoctor/converter/template.rb +296 -0
- data/lib/asciidoctor/core_ext.rb +7 -0
- data/lib/asciidoctor/core_ext/object/nil_or_empty.rb +23 -0
- data/lib/asciidoctor/core_ext/string/chr.rb +6 -0
- data/lib/asciidoctor/core_ext/symbol/length.rb +6 -0
- data/lib/asciidoctor/document.rb +590 -304
- data/lib/asciidoctor/extensions.rb +1100 -308
- data/lib/asciidoctor/helpers.rb +109 -46
- data/lib/asciidoctor/inline.rb +16 -9
- data/lib/asciidoctor/list.rb +23 -15
- data/lib/asciidoctor/opal_ext.rb +4 -0
- data/lib/asciidoctor/opal_ext/comparable.rb +38 -0
- data/lib/asciidoctor/opal_ext/dir.rb +13 -0
- data/lib/asciidoctor/opal_ext/error.rb +2 -0
- data/lib/asciidoctor/opal_ext/file.rb +125 -0
- data/lib/asciidoctor/{lexer.rb → parser.rb} +646 -455
- data/lib/asciidoctor/path_resolver.rb +141 -77
- data/lib/asciidoctor/reader.rb +257 -187
- data/lib/asciidoctor/section.rb +12 -16
- data/lib/asciidoctor/stylesheets.rb +91 -0
- data/lib/asciidoctor/substitutors.rb +1548 -0
- data/lib/asciidoctor/table.rb +73 -57
- data/lib/asciidoctor/timings.rb +39 -0
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +22 -14
- data/man/asciidoctor.adoc +18 -10
- data/test/attributes_test.rb +314 -14
- data/test/blocks_test.rb +763 -118
- data/test/converter_test.rb +352 -0
- data/test/document_test.rb +518 -199
- data/test/extensions_test.rb +273 -103
- data/test/fixtures/asciidoc_index.txt +27 -13
- data/test/fixtures/basic-docinfo.xml +1 -1
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/docinfo.xml +1 -1
- data/test/fixtures/include-file.asciidoc +2 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/invoker_test.rb +173 -61
- data/test/links_test.rb +97 -21
- data/test/lists_test.rb +181 -22
- data/test/options_test.rb +86 -2
- data/test/paragraphs_test.rb +47 -5
- data/test/{lexer_test.rb → parser_test.rb} +128 -57
- data/test/paths_test.rb +36 -1
- data/test/preamble_test.rb +25 -17
- data/test/reader_test.rb +404 -249
- data/test/sections_test.rb +623 -58
- data/test/substitutions_test.rb +609 -132
- data/test/tables_test.rb +198 -24
- data/test/test_helper.rb +101 -31
- data/test/text_test.rb +88 -31
- metadata +160 -64
- data/Gemfile +0 -12
- data/Guardfile +0 -18
- data/asciidoctor.gemspec +0 -143
- data/lib/asciidoctor/backends/_stylesheets.rb +0 -466
- data/lib/asciidoctor/backends/base_template.rb +0 -114
- data/lib/asciidoctor/backends/docbook45.rb +0 -774
- data/lib/asciidoctor/backends/docbook5.rb +0 -103
- data/lib/asciidoctor/backends/html5.rb +0 -1214
- data/lib/asciidoctor/renderer.rb +0 -259
- data/lib/asciidoctor/substituters.rb +0 -1083
- data/test/fixtures/asciidoc.txt +0 -105
- data/test/fixtures/ascshort.txt +0 -32
- data/test/fixtures/list_elements.asciidoc +0 -10
- data/test/renderer_test.rb +0 -162
@@ -0,0 +1,225 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
module Converter
|
3
|
+
# A factory for instantiating converters that are used to convert a
|
4
|
+
# {Document} (i.e., a parsed AsciiDoc tree structure) or {AbstractNode} to
|
5
|
+
# a backend format such as HTML or DocBook. {Factory Converter::Factory} is
|
6
|
+
# the primary entry point for creating, registering and accessing
|
7
|
+
# converters.
|
8
|
+
#
|
9
|
+
# {Converter} objects are instantiated by passing a String backend name
|
10
|
+
# and, optionally, an options Hash to the {Factory#create} method. The
|
11
|
+
# backend can be thought of as an intent to convert a document to a
|
12
|
+
# specified format. For example:
|
13
|
+
#
|
14
|
+
# converter = Asciidoctor::Converter::Factory.create 'html5', :htmlsyntax => 'xml'
|
15
|
+
#
|
16
|
+
# Converter objects are thread safe. They only survive the lifetime of a single conversion.
|
17
|
+
#
|
18
|
+
# A singleton instance of {Factory Converter::Factory} can be accessed
|
19
|
+
# using the {Factory.default} method. This instance maintains the global
|
20
|
+
# registry of statically registered converters. The registery includes
|
21
|
+
# built-in converters for {Html5Converter HTML 5}, {DocBook5Converter
|
22
|
+
# DocBook 5} and {DocBook45Converter DocBook 4.5}, as well as any custom
|
23
|
+
# converters that have been discovered or explicitly registered.
|
24
|
+
#
|
25
|
+
# If the {https://rubygems.org/gems/thread_safe thread_safe} gem is
|
26
|
+
# installed, access to the default factory is guaranteed to be thread safe.
|
27
|
+
# Otherwise, a warning is issued to the user.
|
28
|
+
class Factory
|
29
|
+
@__default__ = nil
|
30
|
+
class << self
|
31
|
+
|
32
|
+
# Public: Retrieves a singleton instance of {Factory Converter::Factory}.
|
33
|
+
#
|
34
|
+
# If the thread_safe gem is installed, the registry of converters is
|
35
|
+
# initialized as a ThreadSafe::Cache. Otherwise, a warning is issued and
|
36
|
+
# the registry of converters is initialized using a normal Hash.
|
37
|
+
#
|
38
|
+
# initialize_singleton - A Boolean to indicate whether the singleton should
|
39
|
+
# be initialize if it has not already been created.
|
40
|
+
# If false, and a singleton has not been previously
|
41
|
+
# initialized, a fresh instance is returned.
|
42
|
+
#
|
43
|
+
# Returns the default [Factory] singleton instance
|
44
|
+
def default initialize_singleton = true
|
45
|
+
return @__default__ || new unless initialize_singleton
|
46
|
+
# FIXME this assignment is not thread_safe, may need to use a ::Threadsafe helper here
|
47
|
+
@__default__ ||= begin
|
48
|
+
require 'thread_safe'.to_s unless defined? ::ThreadSafe
|
49
|
+
new ::ThreadSafe::Cache.new
|
50
|
+
rescue ::LoadError
|
51
|
+
warn 'asciidoctor: WARNING: gem \'thread_safe\' is not installed. This gem recommended when registering custom converters.'
|
52
|
+
new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: Register a custom converter in the global converter factory to
|
57
|
+
# handle conversion to the specified backends. If the backend value is an
|
58
|
+
# asterisk, the converter is used to handle any backend that does not have
|
59
|
+
# an explicit converter.
|
60
|
+
#
|
61
|
+
# converter - The Converter class to register
|
62
|
+
# backends - A String Array of backend names that this converter should
|
63
|
+
# be registered to handle (optional, default: ['*'])
|
64
|
+
#
|
65
|
+
# Returns nothing
|
66
|
+
def register converter, backends = ['*']
|
67
|
+
default.register converter, backends
|
68
|
+
end
|
69
|
+
|
70
|
+
# Public: Lookup the custom converter for the specified backend in the
|
71
|
+
# global factory.
|
72
|
+
#
|
73
|
+
# This method does not resolve the built-in converters.
|
74
|
+
#
|
75
|
+
# backend - The String backend name
|
76
|
+
#
|
77
|
+
# Returns the [Converter] class registered to convert the specified backend
|
78
|
+
# or nil if no match is found
|
79
|
+
def resolve backend
|
80
|
+
default.resolve backend
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: Lookup the converter for the specified backend in the global
|
84
|
+
# factory and instantiate it, forwarding the Hash of options to the
|
85
|
+
# constructor of the converter class.
|
86
|
+
#
|
87
|
+
# If the custom converter is not found, an attempt will be made to find
|
88
|
+
# and instantiate a built-in converter.
|
89
|
+
#
|
90
|
+
#
|
91
|
+
# backend - The String backend name
|
92
|
+
# opts - A Hash of options to pass to the converter
|
93
|
+
#
|
94
|
+
# Returns an instance of [Converter] for converting the specified backend or
|
95
|
+
# nil if no match is found.
|
96
|
+
def create backend, opts = {}
|
97
|
+
default.create backend, opts
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public: Retrieve the global Hash of custom Converter classes keyed by backend.
|
101
|
+
#
|
102
|
+
# Returns the the global [Hash] of custom Converter classes
|
103
|
+
def converters
|
104
|
+
default.converters
|
105
|
+
end
|
106
|
+
|
107
|
+
# Public: Unregister all Converter classes in the global factory.
|
108
|
+
#
|
109
|
+
# Returns nothing
|
110
|
+
def unregister_all
|
111
|
+
default.unregister_all
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Public: Get the Hash of Converter classes keyed by backend name
|
116
|
+
attr_reader :converters
|
117
|
+
|
118
|
+
def initialize converters = nil
|
119
|
+
@converters = converters || {}
|
120
|
+
@star_converter = nil
|
121
|
+
end
|
122
|
+
|
123
|
+
# Public: Register a custom converter with this factory to handle conversion
|
124
|
+
# to the specified backends. If the backend value is an asterisk, the
|
125
|
+
# converter is used to handle any backend that does not have an explicit
|
126
|
+
# converter.
|
127
|
+
#
|
128
|
+
# converter - The Converter class to register
|
129
|
+
# backends - A String Array of backend names that this converter should
|
130
|
+
# be registered to handle (optional, default: ['*'])
|
131
|
+
#
|
132
|
+
# Returns nothing
|
133
|
+
def register converter, backends = ['*']
|
134
|
+
backends.each do |backend|
|
135
|
+
@converters[backend] = converter
|
136
|
+
if backend == '*'
|
137
|
+
@star_converter = converter
|
138
|
+
end
|
139
|
+
end
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
|
143
|
+
# Public: Lookup the custom converter registered with this factory to handle
|
144
|
+
# the specified backend.
|
145
|
+
#
|
146
|
+
# backend - The String backend name
|
147
|
+
#
|
148
|
+
# Returns the [Converter] class registered to convert the specified backend
|
149
|
+
# or nil if no match is found
|
150
|
+
def resolve backend
|
151
|
+
@converters && (@converters[backend] || @star_converter)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Public: Unregister all Converter classes that are registered with this
|
155
|
+
# factory.
|
156
|
+
#
|
157
|
+
# Returns nothing
|
158
|
+
def unregister_all
|
159
|
+
@converters.clear
|
160
|
+
@star_converter = nil
|
161
|
+
end
|
162
|
+
|
163
|
+
# Public: Create a new Converter object that can be used to convert the
|
164
|
+
# {AbstractNode} (typically a {Document}) to the specified String backend.
|
165
|
+
# This method accepts an optional Hash of options that are passed to the
|
166
|
+
# converter's constructor.
|
167
|
+
#
|
168
|
+
# If a custom Converter is found to convert the specified backend, it is
|
169
|
+
# instantiated (if necessary) and returned immediately. If a custom
|
170
|
+
# Converter is not found, an attempt is made to resolve a built-in
|
171
|
+
# converter. If the `:template_dirs` key is found in the Hash passed as the
|
172
|
+
# second argument, a {CompositeConverter} is created that delegates to a
|
173
|
+
# {TemplateConverter} and, if resolved, the built-in converter. If the
|
174
|
+
# `:template_dirs` key is not found, the built-in converter is returned
|
175
|
+
# or nil if no converter is resolved.
|
176
|
+
#
|
177
|
+
# backend - the String backend name
|
178
|
+
# opts - an optional Hash of options that get passed on to the converter's
|
179
|
+
# constructor. If the :template_dirs key is found in the options
|
180
|
+
# Hash, this method returns a {CompositeConverter} that delegates
|
181
|
+
# to a {TemplateConverter}. (optional, default: {})
|
182
|
+
#
|
183
|
+
# Returns the [Converter] object
|
184
|
+
def create backend, opts = {}
|
185
|
+
if (converter = resolve backend)
|
186
|
+
if converter.is_a? ::Class
|
187
|
+
return converter.new backend, opts
|
188
|
+
else
|
189
|
+
return converter
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
base_converter = case backend
|
194
|
+
when 'html5'
|
195
|
+
unless defined? ::Asciidoctor::Converter::Html5Converter
|
196
|
+
require 'asciidoctor/converter/html5'.to_s
|
197
|
+
end
|
198
|
+
Html5Converter.new backend, opts
|
199
|
+
when 'docbook5'
|
200
|
+
unless defined? ::Asciidoctor::Converter::DocBook5Converter
|
201
|
+
require 'asciidoctor/converter/docbook5'.to_s
|
202
|
+
end
|
203
|
+
DocBook5Converter.new backend, opts
|
204
|
+
when 'docbook45'
|
205
|
+
unless defined? ::Asciidoctor::Converter::DocBook45Converter
|
206
|
+
require 'asciidoctor/converter/docbook45'.to_s
|
207
|
+
end
|
208
|
+
DocBook45Converter.new backend, opts
|
209
|
+
end
|
210
|
+
|
211
|
+
return base_converter unless opts.key? :template_dirs
|
212
|
+
|
213
|
+
unless defined? ::Asciidoctor::Converter::TemplateConverter
|
214
|
+
require 'asciidoctor/converter/template'.to_s
|
215
|
+
end
|
216
|
+
unless defined? ::Asciidoctor::Converter::CompositeConverter
|
217
|
+
require 'asciidoctor/converter/composite'.to_s
|
218
|
+
end
|
219
|
+
template_converter = TemplateConverter.new backend, opts[:template_dirs], opts
|
220
|
+
# QUESTION should we omit the composite converter if built_in_converter is nil?
|
221
|
+
CompositeConverter.new backend, template_converter, base_converter
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,1081 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
# A built-in {Converter} implementation that generates HTML 5 output
|
3
|
+
# consistent with the html5 backend from AsciiDoc Python.
|
4
|
+
class Converter::Html5Converter < Converter::BuiltIn
|
5
|
+
QUOTE_TAGS = {
|
6
|
+
:emphasis => ['<em>', '</em>', true],
|
7
|
+
:strong => ['<strong>', '</strong>', true],
|
8
|
+
:monospaced => ['<code>', '</code>', true],
|
9
|
+
:superscript => ['<sup>', '</sup>', true],
|
10
|
+
:subscript => ['<sub>', '</sub>', true],
|
11
|
+
:double => ['“', '”', false],
|
12
|
+
:single => ['‘', '’', false],
|
13
|
+
:mark => ['<mark>', '</mark>', true],
|
14
|
+
:asciimath => ['\\$', '\\$', false],
|
15
|
+
:latexmath => ['\\(', '\\)', false]
|
16
|
+
# Opal can't resolve these constants when referenced here
|
17
|
+
#:asciimath => INLINE_MATH_DELIMITERS[:asciimath] + [false],
|
18
|
+
#:latexmath => INLINE_MATH_DELIMITERS[:latexmath] + [false]
|
19
|
+
}
|
20
|
+
QUOTE_TAGS.default = [nil, nil, nil]
|
21
|
+
|
22
|
+
def initialize backend, opts = {}
|
23
|
+
@xml_mode = opts[:htmlsyntax] == 'xml'
|
24
|
+
@void_element_slash = @xml_mode ? '/' : nil
|
25
|
+
@stylesheets = Stylesheets.instance
|
26
|
+
end
|
27
|
+
|
28
|
+
def document node
|
29
|
+
result = []
|
30
|
+
slash = @void_element_slash
|
31
|
+
br = %(<br#{slash}>)
|
32
|
+
asset_uri_scheme = (node.attr 'asset-uri-scheme', 'https')
|
33
|
+
asset_uri_scheme = %(#{asset_uri_scheme}:) unless asset_uri_scheme.empty?
|
34
|
+
cdn_base = %(#{asset_uri_scheme}//cdnjs.cloudflare.com/ajax/libs)
|
35
|
+
linkcss = node.safe >= SafeMode::SECURE || (node.attr? 'linkcss')
|
36
|
+
result << '<!DOCTYPE html>'
|
37
|
+
lang_attribute = (node.attr? 'nolang') ? nil : %( lang="#{node.attr 'lang', 'en'}")
|
38
|
+
result << %(<html#{@xml_mode ? ' xmlns="http://www.w3.org/1999/xhtml"' : nil}#{lang_attribute}>)
|
39
|
+
result << %(<head>
|
40
|
+
<meta charset="#{node.attr 'encoding', 'UTF-8'}"#{slash}>
|
41
|
+
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"#{slash}><![endif]-->
|
42
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"#{slash}>
|
43
|
+
<meta name="generator" content="Asciidoctor #{node.attr 'asciidoctor-version'}"#{slash}>)
|
44
|
+
|
45
|
+
result << %(<meta name="application-name" content="#{node.attr 'app-name'}"#{slash}>) if node.attr? 'app-name'
|
46
|
+
result << %(<meta name="description" content="#{node.attr 'description'}"#{slash}>) if node.attr? 'description'
|
47
|
+
result << %(<meta name="keywords" content="#{node.attr 'keywords'}"#{slash}>) if node.attr? 'keywords'
|
48
|
+
result << %(<meta name="author" content="#{node.attr 'authors'}"#{slash}>) if node.attr? 'authors'
|
49
|
+
result << %(<meta name="copyright" content="#{node.attr 'copyright'}"#{slash}>) if node.attr? 'copyright'
|
50
|
+
|
51
|
+
result << %(<title>#{node.doctitle :sanitize => true, :use_fallback => true}</title>)
|
52
|
+
if DEFAULT_STYLESHEET_KEYS.include?(node.attr 'stylesheet')
|
53
|
+
if (webfonts = node.attr 'webfonts')
|
54
|
+
result << %(<link rel="stylesheet" href="#{asset_uri_scheme}//fonts.googleapis.com/css?family=#{webfonts.empty? ? 'Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400' : webfonts}"#{slash}>)
|
55
|
+
end
|
56
|
+
if linkcss
|
57
|
+
result << %(<link rel="stylesheet" href="#{node.normalize_web_path DEFAULT_STYLESHEET_NAME, (node.attr 'stylesdir', '')}"#{slash}>)
|
58
|
+
else
|
59
|
+
result << @stylesheets.embed_primary_stylesheet
|
60
|
+
end
|
61
|
+
elsif node.attr? 'stylesheet'
|
62
|
+
if linkcss
|
63
|
+
result << %(<link rel="stylesheet" href="#{node.normalize_web_path((node.attr 'stylesheet'), (node.attr 'stylesdir', ''))}"#{slash}>)
|
64
|
+
else
|
65
|
+
result << %(<style>
|
66
|
+
#{node.read_asset node.normalize_system_path((node.attr 'stylesheet'), (node.attr 'stylesdir', '')), true}
|
67
|
+
</style>)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if node.attr? 'icons', 'font'
|
72
|
+
if node.attr? 'iconfont-remote'
|
73
|
+
result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/4.1.0/css/font-awesome.min.css]}"#{slash}>)
|
74
|
+
else
|
75
|
+
iconfont_stylesheet = %(#{node.attr 'iconfont-name', 'font-awesome'}.css)
|
76
|
+
result << %(<link rel="stylesheet" href="#{node.normalize_web_path iconfont_stylesheet, (node.attr 'stylesdir', '')}"#{slash}>)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
case node.attr 'source-highlighter'
|
81
|
+
when 'coderay'
|
82
|
+
if (node.attr 'coderay-css', 'class') == 'class'
|
83
|
+
if linkcss
|
84
|
+
result << %(<link rel="stylesheet" href="#{node.normalize_web_path @stylesheets.coderay_stylesheet_name, (node.attr 'stylesdir', '')}"#{slash}>)
|
85
|
+
else
|
86
|
+
result << @stylesheets.embed_coderay_stylesheet
|
87
|
+
end
|
88
|
+
end
|
89
|
+
when 'pygments'
|
90
|
+
if (node.attr 'pygments-css', 'class') == 'class'
|
91
|
+
pygments_style = node.attr 'pygments-style'
|
92
|
+
if linkcss
|
93
|
+
result << %(<link rel="stylesheet" href="#{node.normalize_web_path @stylesheets.pygments_stylesheet_name(pygments_style), (node.attr 'stylesdir', '')}"#{slash}>)
|
94
|
+
else
|
95
|
+
result << (@stylesheets.embed_pygments_stylesheet pygments_style)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
when 'highlightjs', 'highlight.js'
|
99
|
+
highlightjs_path = node.attr 'highlightjsdir', %(#{cdn_base}/highlight.js/8.1)
|
100
|
+
result << %(<link rel="stylesheet" href="#{highlightjs_path}/styles/#{node.attr 'highlightjs-theme', 'github'}.min.css"#{slash}>
|
101
|
+
<script src="#{highlightjs_path}/highlight.min.js"></script>
|
102
|
+
<script>hljs.initHighlightingOnLoad()</script>)
|
103
|
+
when 'prettify'
|
104
|
+
prettify_path = node.attr 'prettifydir', %(#{cdn_base}/prettify/r298)
|
105
|
+
result << %(<link rel="stylesheet" href="#{prettify_path}/#{node.attr 'prettify-theme', 'prettify'}.min.css"#{slash}>
|
106
|
+
<script src="#{prettify_path}/prettify.min.js"></script>
|
107
|
+
<script>document.addEventListener('DOMContentLoaded', prettyPrint)</script>)
|
108
|
+
end
|
109
|
+
|
110
|
+
if node.attr? 'stem'
|
111
|
+
result << %(<script type="text/x-mathjax-config">
|
112
|
+
MathJax.Hub.Config({
|
113
|
+
tex2jax: {
|
114
|
+
inlineMath: [#{INLINE_MATH_DELIMITERS[:latexmath]}],
|
115
|
+
displayMath: [#{BLOCK_MATH_DELIMITERS[:latexmath]}],
|
116
|
+
ignoreClass: "nostem|nolatexmath"
|
117
|
+
},
|
118
|
+
asciimath2jax: {
|
119
|
+
delimiters: [#{BLOCK_MATH_DELIMITERS[:asciimath]}],
|
120
|
+
ignoreClass: "nostem|noasciimath"
|
121
|
+
}
|
122
|
+
});
|
123
|
+
</script>
|
124
|
+
<script src="#{cdn_base}/mathjax/2.4.0/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>)
|
125
|
+
end
|
126
|
+
|
127
|
+
unless (docinfo_content = node.docinfo).empty?
|
128
|
+
result << docinfo_content
|
129
|
+
end
|
130
|
+
|
131
|
+
result << '</head>'
|
132
|
+
body_attrs = []
|
133
|
+
if node.id
|
134
|
+
body_attrs << %(id="#{node.id}")
|
135
|
+
end
|
136
|
+
if (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
|
137
|
+
body_attrs << %(class="#{node.doctype} #{node.attr 'toc-class'} toc-#{node.attr 'toc-position', 'header'}")
|
138
|
+
else
|
139
|
+
body_attrs << %(class="#{node.doctype}")
|
140
|
+
end
|
141
|
+
if node.attr? 'max-width'
|
142
|
+
body_attrs << %(style="max-width: #{node.attr 'max-width'};")
|
143
|
+
end
|
144
|
+
result << %(<body #{body_attrs * ' '}>)
|
145
|
+
|
146
|
+
unless node.noheader
|
147
|
+
result << '<div id="header">'
|
148
|
+
if node.doctype == 'manpage'
|
149
|
+
result << %(<h1>#{node.doctitle} Manual Page</h1>)
|
150
|
+
if (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
|
151
|
+
result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
|
152
|
+
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
153
|
+
#{outline node}
|
154
|
+
</div>)
|
155
|
+
end
|
156
|
+
result << %(<h2>#{node.attr 'manname-title'}</h2>
|
157
|
+
<div class="sectionbody">
|
158
|
+
<p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
|
159
|
+
</div>)
|
160
|
+
else
|
161
|
+
if node.has_header?
|
162
|
+
result << %(<h1>#{node.header.title}</h1>) unless node.notitle
|
163
|
+
details = []
|
164
|
+
if node.attr? 'author'
|
165
|
+
details << %(<span id="author" class="author">#{node.attr 'author'}</span>#{br})
|
166
|
+
if node.attr? 'email'
|
167
|
+
details << %(<span id="email" class="email">#{node.sub_macros(node.attr 'email')}</span>#{br})
|
168
|
+
end
|
169
|
+
if (authorcount = (node.attr 'authorcount').to_i) > 1
|
170
|
+
(2..authorcount).each do |idx|
|
171
|
+
details << %(<span id="author#{idx}" class="author">#{node.attr "author_#{idx}"}</span>#{br})
|
172
|
+
if node.attr? %(email_#{idx})
|
173
|
+
details << %(<span id="email#{idx}" class="email">#{node.sub_macros(node.attr "email_#{idx}")}</span>#{br})
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
if node.attr? 'revnumber'
|
179
|
+
details << %(<span id="revnumber">#{((node.attr 'version-label') || '').downcase} #{node.attr 'revnumber'}#{(node.attr? 'revdate') ? ',' : ''}</span>)
|
180
|
+
end
|
181
|
+
if node.attr? 'revdate'
|
182
|
+
details << %(<span id="revdate">#{node.attr 'revdate'}</span>)
|
183
|
+
end
|
184
|
+
if node.attr? 'revremark'
|
185
|
+
details << %(#{br}<span id="revremark">#{node.attr 'revremark'}</span>)
|
186
|
+
end
|
187
|
+
unless details.empty?
|
188
|
+
result << '<div class="details">'
|
189
|
+
result.concat details
|
190
|
+
result << '</div>'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
if (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
|
195
|
+
result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
|
196
|
+
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
197
|
+
#{outline node}
|
198
|
+
</div>)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
result << '</div>'
|
202
|
+
end
|
203
|
+
|
204
|
+
result << %(<div id="content">
|
205
|
+
#{node.content}
|
206
|
+
</div>)
|
207
|
+
|
208
|
+
if node.footnotes? && !(node.attr? 'nofootnotes')
|
209
|
+
result << %(<div id="footnotes">
|
210
|
+
<hr#{slash}>)
|
211
|
+
node.footnotes.each do |footnote|
|
212
|
+
result << %(<div class="footnote" id="_footnote_#{footnote.index}">
|
213
|
+
<a href="#_footnoteref_#{footnote.index}">#{footnote.index}</a>. #{footnote.text}
|
214
|
+
</div>)
|
215
|
+
end
|
216
|
+
result << '</div>'
|
217
|
+
end
|
218
|
+
unless node.nofooter
|
219
|
+
result << '<div id="footer">'
|
220
|
+
result << '<div id="footer-text">'
|
221
|
+
if node.attr? 'revnumber'
|
222
|
+
result << %(#{node.attr 'version-label'} #{node.attr 'revnumber'}#{br})
|
223
|
+
end
|
224
|
+
if node.attr? 'last-update-label'
|
225
|
+
result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'})
|
226
|
+
end
|
227
|
+
result << '</div>'
|
228
|
+
unless (docinfo_content = node.docinfo :footer).empty?
|
229
|
+
result << docinfo_content
|
230
|
+
end
|
231
|
+
result << '</div>'
|
232
|
+
end
|
233
|
+
|
234
|
+
result << '</body>'
|
235
|
+
result << '</html>'
|
236
|
+
result * EOL
|
237
|
+
end
|
238
|
+
|
239
|
+
def embedded node
|
240
|
+
result = []
|
241
|
+
if !node.notitle && node.has_header?
|
242
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
243
|
+
result << %(<h1#{id_attr}>#{node.header.title}</h1>)
|
244
|
+
end
|
245
|
+
|
246
|
+
result << node.content
|
247
|
+
|
248
|
+
if node.footnotes? && !(node.attr? 'nofootnotes')
|
249
|
+
result << %(<div id="footnotes">
|
250
|
+
<hr#{@void_element_slash}>)
|
251
|
+
node.footnotes.each do |footnote|
|
252
|
+
result << %(<div class="footnote" id="_footnote_#{footnote.index}">
|
253
|
+
<a href="#_footnoteref_#{footnote.index}">#{footnote.index}</a> #{footnote.text}
|
254
|
+
</div>)
|
255
|
+
end
|
256
|
+
|
257
|
+
result << '</div>'
|
258
|
+
end
|
259
|
+
|
260
|
+
result * EOL
|
261
|
+
end
|
262
|
+
|
263
|
+
def outline node, opts = {}
|
264
|
+
return if (sections = node.sections).empty?
|
265
|
+
sectnumlevels = opts[:sectnumlevels] || (node.document.attr 'sectnumlevels', 3).to_i
|
266
|
+
toclevels = opts[:toclevels] || (node.document.attr 'toclevels', 2).to_i
|
267
|
+
result = []
|
268
|
+
# FIXME the level for special sections should be set correctly in the model
|
269
|
+
# slevel will only be 0 if we have a book doctype with parts
|
270
|
+
slevel = (first_section = sections[0]).level
|
271
|
+
slevel = 1 if slevel == 0 && first_section.special
|
272
|
+
result << %(<ul class="sectlevel#{slevel}">)
|
273
|
+
sections.each do |section|
|
274
|
+
section_num = (section.numbered && !section.caption && section.level <= sectnumlevels) ? %(#{section.sectnum} ) : nil
|
275
|
+
if section.level < toclevels && (child_toc_level = outline section, :toclevels => toclevels, :secnumlevels => sectnumlevels)
|
276
|
+
result << %(<li><a href="##{section.id}">#{section_num}#{section.captioned_title}</a>)
|
277
|
+
result << child_toc_level
|
278
|
+
result << '</li>'
|
279
|
+
else
|
280
|
+
result << %(<li><a href="##{section.id}">#{section_num}#{section.captioned_title}</a></li>)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
result << '</ul>'
|
284
|
+
result * EOL
|
285
|
+
end
|
286
|
+
|
287
|
+
def section node
|
288
|
+
slevel = node.level
|
289
|
+
# QUESTION should the check for slevel be done in section?
|
290
|
+
slevel = 1 if slevel == 0 && node.special
|
291
|
+
htag = %(h#{slevel + 1})
|
292
|
+
id_attr = anchor = link_start = link_end = nil
|
293
|
+
if node.id
|
294
|
+
id_attr = %( id="#{node.id}")
|
295
|
+
if node.document.attr? 'sectanchors'
|
296
|
+
anchor = %(<a class="anchor" href="##{node.id}"></a>)
|
297
|
+
# possible idea - anchor icons GitHub-style
|
298
|
+
#if node.document.attr? 'icons', 'font'
|
299
|
+
# anchor = %(<a class="anchor" href="##{node.id}"><i class="fa fa-anchor"></i></a>)
|
300
|
+
#else
|
301
|
+
elsif node.document.attr? 'sectlinks'
|
302
|
+
link_start = %(<a class="link" href="##{node.id}">)
|
303
|
+
link_end = '</a>'
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
if slevel == 0
|
308
|
+
%(<h1#{id_attr} class="sect0">#{anchor}#{link_start}#{node.title}#{link_end}</h1>
|
309
|
+
#{node.content})
|
310
|
+
else
|
311
|
+
class_attr = (role = node.role) ? %( class="sect#{slevel} #{role}") : %( class="sect#{slevel}")
|
312
|
+
sectnum = if node.numbered && !node.caption && slevel <= (node.document.attr 'sectnumlevels', 3).to_i
|
313
|
+
%(#{node.sectnum} )
|
314
|
+
end
|
315
|
+
%(<div#{class_attr}>
|
316
|
+
<#{htag}#{id_attr}>#{anchor}#{link_start}#{sectnum}#{node.captioned_title}#{link_end}</#{htag}>
|
317
|
+
#{slevel == 1 ? %[<div class="sectionbody">\n#{node.content}\n</div>] : node.content}
|
318
|
+
</div>)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def admonition node
|
323
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
324
|
+
name = node.attr 'name'
|
325
|
+
title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : nil
|
326
|
+
caption = if node.document.attr? 'icons'
|
327
|
+
if node.document.attr? 'icons', 'font'
|
328
|
+
%(<i class="fa icon-#{name}" title="#{node.caption}"></i>)
|
329
|
+
else
|
330
|
+
%(<img src="#{node.icon_uri name}" alt="#{node.caption}"#{@void_element_slash}>)
|
331
|
+
end
|
332
|
+
else
|
333
|
+
%(<div class="title">#{node.caption}</div>)
|
334
|
+
end
|
335
|
+
%(<div#{id_attr} class="admonitionblock #{name}#{(role = node.role) && " #{role}"}">
|
336
|
+
<table>
|
337
|
+
<tr>
|
338
|
+
<td class="icon">
|
339
|
+
#{caption}
|
340
|
+
</td>
|
341
|
+
<td class="content">
|
342
|
+
#{title_element}#{node.content}
|
343
|
+
</td>
|
344
|
+
</tr>
|
345
|
+
</table>
|
346
|
+
</div>)
|
347
|
+
end
|
348
|
+
|
349
|
+
def audio node
|
350
|
+
xml = node.document.attr? 'htmlsyntax', 'xml'
|
351
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
352
|
+
classes = ['audioblock', node.style, node.role].compact
|
353
|
+
class_attribute = %( class="#{classes * ' '}")
|
354
|
+
title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) : nil
|
355
|
+
%(<div#{id_attribute}#{class_attribute}>
|
356
|
+
#{title_element}<div class="content">
|
357
|
+
<audio src="#{node.media_uri(node.attr 'target')}"#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : nil}#{(node.option? 'nocontrols') ? nil : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : nil}>
|
358
|
+
Your browser does not support the audio tag.
|
359
|
+
</audio>
|
360
|
+
</div>
|
361
|
+
</div>)
|
362
|
+
end
|
363
|
+
|
364
|
+
def colist node
|
365
|
+
result = []
|
366
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
367
|
+
classes = ['colist', node.style, node.role].compact
|
368
|
+
class_attribute = %( class="#{classes * ' '}")
|
369
|
+
|
370
|
+
result << %(<div#{id_attribute}#{class_attribute}>)
|
371
|
+
result << %(<div class="title">#{node.title}</div>) if node.title?
|
372
|
+
|
373
|
+
if node.document.attr? 'icons'
|
374
|
+
result << '<table>'
|
375
|
+
|
376
|
+
font_icons = node.document.attr? 'icons', 'font'
|
377
|
+
node.items.each_with_index do |item, i|
|
378
|
+
num = i + 1
|
379
|
+
num_element = if font_icons
|
380
|
+
%(<i class="conum" data-value="#{num}"></i><b>#{num}</b>)
|
381
|
+
else
|
382
|
+
%(<img src="#{node.icon_uri "callouts/#{num}"}" alt="#{num}"#{@void_element_slash}>)
|
383
|
+
end
|
384
|
+
result << %(<tr>
|
385
|
+
<td>#{num_element}</td>
|
386
|
+
<td>#{item.text}</td>
|
387
|
+
</tr>)
|
388
|
+
end
|
389
|
+
|
390
|
+
result << '</table>'
|
391
|
+
else
|
392
|
+
result << '<ol>'
|
393
|
+
node.items.each do |item|
|
394
|
+
result << %(<li>
|
395
|
+
<p>#{item.text}</p>
|
396
|
+
</li>)
|
397
|
+
end
|
398
|
+
result << '</ol>'
|
399
|
+
end
|
400
|
+
|
401
|
+
result << '</div>'
|
402
|
+
result * EOL
|
403
|
+
end
|
404
|
+
|
405
|
+
def dlist node
|
406
|
+
result = []
|
407
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
408
|
+
|
409
|
+
classes = case node.style
|
410
|
+
when 'qanda'
|
411
|
+
['qlist', 'qanda', node.role]
|
412
|
+
when 'horizontal'
|
413
|
+
['hdlist', node.role]
|
414
|
+
else
|
415
|
+
['dlist', node.style, node.role]
|
416
|
+
end.compact
|
417
|
+
|
418
|
+
class_attribute = %( class="#{classes * ' '}")
|
419
|
+
|
420
|
+
result << %(<div#{id_attribute}#{class_attribute}>)
|
421
|
+
result << %(<div class="title">#{node.title}</div>) if node.title?
|
422
|
+
case node.style
|
423
|
+
when 'qanda'
|
424
|
+
result << '<ol>'
|
425
|
+
node.items.each do |terms, dd|
|
426
|
+
result << '<li>'
|
427
|
+
[*terms].each do |dt|
|
428
|
+
result << %(<p><em>#{dt.text}</em></p>)
|
429
|
+
end
|
430
|
+
if dd
|
431
|
+
result << %(<p>#{dd.text}</p>) if dd.text?
|
432
|
+
result << dd.content if dd.blocks?
|
433
|
+
end
|
434
|
+
result << '</li>'
|
435
|
+
end
|
436
|
+
result << '</ol>'
|
437
|
+
when 'horizontal'
|
438
|
+
slash = @void_element_slash
|
439
|
+
result << '<table>'
|
440
|
+
if (node.attr? 'labelwidth') || (node.attr? 'itemwidth')
|
441
|
+
result << '<colgroup>'
|
442
|
+
col_style_attribute = (node.attr? 'labelwidth') ? %( style="width: #{(node.attr 'labelwidth').chomp '%'}%;") : nil
|
443
|
+
result << %(<col#{col_style_attribute}#{slash}>)
|
444
|
+
col_style_attribute = (node.attr? 'itemwidth') ? %( style="width: #{(node.attr 'itemwidth').chomp '%'}%;") : nil
|
445
|
+
result << %(<col#{col_style_attribute}#{slash}>)
|
446
|
+
result << '</colgroup>'
|
447
|
+
end
|
448
|
+
node.items.each do |terms, dd|
|
449
|
+
result << '<tr>'
|
450
|
+
result << %(<td class="hdlist1#{(node.option? 'strong') ? ' strong' : nil}">)
|
451
|
+
terms_array = [*terms]
|
452
|
+
last_term = terms_array[-1]
|
453
|
+
terms_array.each do |dt|
|
454
|
+
result << dt.text
|
455
|
+
result << %(<br#{slash}>) if dt != last_term
|
456
|
+
end
|
457
|
+
result << '</td>'
|
458
|
+
result << '<td class="hdlist2">'
|
459
|
+
if dd
|
460
|
+
result << %(<p>#{dd.text}</p>) if dd.text?
|
461
|
+
result << dd.content if dd.blocks?
|
462
|
+
end
|
463
|
+
result << '</td>'
|
464
|
+
result << '</tr>'
|
465
|
+
end
|
466
|
+
result << '</table>'
|
467
|
+
else
|
468
|
+
result << '<dl>'
|
469
|
+
dt_style_attribute = node.style ? nil : ' class="hdlist1"'
|
470
|
+
node.items.each do |terms, dd|
|
471
|
+
[*terms].each do |dt|
|
472
|
+
result << %(<dt#{dt_style_attribute}>#{dt.text}</dt>)
|
473
|
+
end
|
474
|
+
if dd
|
475
|
+
result << '<dd>'
|
476
|
+
result << %(<p>#{dd.text}</p>) if dd.text?
|
477
|
+
result << dd.content if dd.blocks?
|
478
|
+
result << '</dd>'
|
479
|
+
end
|
480
|
+
end
|
481
|
+
result << '</dl>'
|
482
|
+
end
|
483
|
+
|
484
|
+
result << '</div>'
|
485
|
+
result * EOL
|
486
|
+
end
|
487
|
+
|
488
|
+
def example node
|
489
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
490
|
+
title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) : nil
|
491
|
+
|
492
|
+
%(<div#{id_attribute} class="#{(role = node.role) ? ['exampleblock', role] * ' ' : 'exampleblock'}">
|
493
|
+
#{title_element}<div class="content">
|
494
|
+
#{node.content}
|
495
|
+
</div>
|
496
|
+
</div>)
|
497
|
+
end
|
498
|
+
|
499
|
+
def floating_title node
|
500
|
+
tag_name = %(h#{node.level + 1})
|
501
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
502
|
+
classes = [node.style, node.role].compact
|
503
|
+
%(<#{tag_name}#{id_attribute} class="#{classes * ' '}">#{node.title}</#{tag_name}>)
|
504
|
+
end
|
505
|
+
|
506
|
+
def image node
|
507
|
+
align = (node.attr? 'align') ? (node.attr 'align') : nil
|
508
|
+
float = (node.attr? 'float') ? (node.attr 'float') : nil
|
509
|
+
style_attribute = if align || float
|
510
|
+
styles = [align ? %(text-align: #{align}) : nil, float ? %(float: #{float}) : nil].compact
|
511
|
+
%( style="#{styles * ';'}")
|
512
|
+
end
|
513
|
+
|
514
|
+
width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : nil
|
515
|
+
height_attribute = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : nil
|
516
|
+
|
517
|
+
img_element = %(<img src="#{node.image_uri node.attr('target')}" alt="#{node.attr 'alt'}"#{width_attribute}#{height_attribute}#{@void_element_slash}>)
|
518
|
+
if (link = node.attr 'link')
|
519
|
+
img_element = %(<a class="image" href="#{link}">#{img_element}</a>)
|
520
|
+
end
|
521
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
522
|
+
classes = ['imageblock', node.style, node.role].compact
|
523
|
+
class_attribute = %( class="#{classes * ' '}")
|
524
|
+
title_element = node.title? ? %(\n<div class="title">#{node.captioned_title}</div>) : nil
|
525
|
+
|
526
|
+
%(<div#{id_attribute}#{class_attribute}#{style_attribute}>
|
527
|
+
<div class="content">
|
528
|
+
#{img_element}
|
529
|
+
</div>#{title_element}
|
530
|
+
</div>)
|
531
|
+
end
|
532
|
+
|
533
|
+
def listing node
|
534
|
+
nowrap = !(node.document.attr? 'prewrap') || (node.option? 'nowrap')
|
535
|
+
if node.style == 'source'
|
536
|
+
if (language = node.attr 'language', nil, false)
|
537
|
+
code_attrs = %( data-lang="#{language}")
|
538
|
+
else
|
539
|
+
code_attrs = nil
|
540
|
+
end
|
541
|
+
case node.document.attr 'source-highlighter'
|
542
|
+
when 'coderay'
|
543
|
+
pre_class = %( class="CodeRay highlight#{nowrap ? ' nowrap' : nil}")
|
544
|
+
when 'pygments'
|
545
|
+
pre_class = %( class="pygments highlight#{nowrap ? ' nowrap' : nil}")
|
546
|
+
when 'highlightjs', 'highlight.js'
|
547
|
+
pre_class = %( class="highlightjs highlight#{nowrap ? ' nowrap' : nil}")
|
548
|
+
code_attrs = %( class="language-#{language}"#{code_attrs}) if language
|
549
|
+
when 'prettify'
|
550
|
+
pre_class = %( class="prettyprint highlight#{nowrap ? ' nowrap' : nil}#{(node.attr? 'linenums') ? ' linenums' : nil}")
|
551
|
+
code_attrs = %( class="language-#{language}"#{code_attrs}) if language
|
552
|
+
when 'html-pipeline'
|
553
|
+
pre_class = language ? %( lang="#{language}") : nil
|
554
|
+
code_attrs = nil
|
555
|
+
else
|
556
|
+
pre_class = %( class="highlight#{nowrap ? ' nowrap' : nil}")
|
557
|
+
code_attrs = %( class="language-#{language}"#{code_attrs}) if language
|
558
|
+
end
|
559
|
+
pre_start = %(<pre#{pre_class}><code#{code_attrs}>)
|
560
|
+
pre_end = '</code></pre>'
|
561
|
+
else
|
562
|
+
pre_start = %(<pre#{nowrap ? ' class="nowrap"' : nil}>)
|
563
|
+
pre_end = '</pre>'
|
564
|
+
end
|
565
|
+
|
566
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
567
|
+
title_element = node.title? ? %(<div class="title">#{node.captioned_title}</div>\n) : nil
|
568
|
+
%(<div#{id_attribute} class="listingblock#{(role = node.role) && " #{role}"}">
|
569
|
+
#{title_element}<div class="content">
|
570
|
+
#{pre_start}#{node.content}#{pre_end}
|
571
|
+
</div>
|
572
|
+
</div>)
|
573
|
+
end
|
574
|
+
|
575
|
+
def literal node
|
576
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
577
|
+
title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : nil
|
578
|
+
nowrap = !(node.document.attr? 'prewrap') || (node.option? 'nowrap')
|
579
|
+
%(<div#{id_attribute} class="literalblock#{(role = node.role) && " #{role}"}">
|
580
|
+
#{title_element}<div class="content">
|
581
|
+
<pre#{nowrap ? ' class="nowrap"' : nil}>#{node.content}</pre>
|
582
|
+
</div>
|
583
|
+
</div>)
|
584
|
+
end
|
585
|
+
|
586
|
+
def stem node
|
587
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
588
|
+
title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : nil
|
589
|
+
open, close = BLOCK_MATH_DELIMITERS[node.style.to_sym]
|
590
|
+
|
591
|
+
unless ((equation = node.content).start_with? open) && (equation.end_with? close)
|
592
|
+
equation = %(#{open}#{equation}#{close})
|
593
|
+
end
|
594
|
+
|
595
|
+
%(<div#{id_attribute} class="#{(role = node.role) ? ['stemblock', role] * ' ' : 'stemblock'}">
|
596
|
+
#{title_element}<div class="content">
|
597
|
+
#{equation}
|
598
|
+
</div>
|
599
|
+
</div>)
|
600
|
+
end
|
601
|
+
|
602
|
+
def olist node
|
603
|
+
result = []
|
604
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
605
|
+
classes = ['olist', node.style, node.role].compact
|
606
|
+
class_attribute = %( class="#{classes * ' '}")
|
607
|
+
|
608
|
+
result << %(<div#{id_attribute}#{class_attribute}>)
|
609
|
+
result << %(<div class="title">#{node.title}</div>) if node.title?
|
610
|
+
|
611
|
+
type_attribute = (keyword = node.list_marker_keyword) ? %( type="#{keyword}") : nil
|
612
|
+
start_attribute = (node.attr? 'start') ? %( start="#{node.attr 'start'}") : nil
|
613
|
+
result << %(<ol class="#{node.style}"#{type_attribute}#{start_attribute}>)
|
614
|
+
|
615
|
+
node.items.each do |item|
|
616
|
+
result << '<li>'
|
617
|
+
result << %(<p>#{item.text}</p>)
|
618
|
+
result << item.content if item.blocks?
|
619
|
+
result << '</li>'
|
620
|
+
end
|
621
|
+
|
622
|
+
result << '</ol>'
|
623
|
+
result << '</div>'
|
624
|
+
result * EOL
|
625
|
+
end
|
626
|
+
|
627
|
+
def open node
|
628
|
+
if (style = node.style) == 'abstract'
|
629
|
+
if node.parent == node.document && node.document.doctype == 'book'
|
630
|
+
warn 'asciidoctor: WARNING: abstract block cannot be used in a document without a title when doctype is book. Excluding block content.'
|
631
|
+
''
|
632
|
+
else
|
633
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
634
|
+
title_el = node.title? ? %(<div class="title">#{node.title}</div>) : nil
|
635
|
+
%(<div#{id_attr} class="quoteblock abstract#{(role = node.role) && " #{role}"}">
|
636
|
+
#{title_el}<blockquote>
|
637
|
+
#{node.content}
|
638
|
+
</blockquote>
|
639
|
+
</div>)
|
640
|
+
end
|
641
|
+
elsif style == 'partintro' && (node.level != 0 || node.parent.context != :section || node.document.doctype != 'book')
|
642
|
+
warn 'asciidoctor: ERROR: partintro block can only be used when doctype is book and it\'s a child of a book part. Excluding block content.'
|
643
|
+
''
|
644
|
+
else
|
645
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
646
|
+
title_el = node.title? ? %(<div class="title">#{node.title}</div>) : nil
|
647
|
+
%(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role) && " #{role}"}">
|
648
|
+
#{title_el}<div class="content">
|
649
|
+
#{node.content}
|
650
|
+
</div>
|
651
|
+
</div>)
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def page_break node
|
656
|
+
'<div style="page-break-after: always;"></div>'
|
657
|
+
end
|
658
|
+
|
659
|
+
def paragraph node
|
660
|
+
attributes = if node.id
|
661
|
+
if node.role
|
662
|
+
%( id="#{node.id}" class="paragraph #{node.role}")
|
663
|
+
else
|
664
|
+
%( id="#{node.id}" class="paragraph")
|
665
|
+
end
|
666
|
+
elsif node.role
|
667
|
+
%( class="paragraph #{node.role}")
|
668
|
+
else
|
669
|
+
' class="paragraph"'
|
670
|
+
end
|
671
|
+
|
672
|
+
if node.title?
|
673
|
+
%(<div#{attributes}>
|
674
|
+
<div class="title">#{node.title}</div>
|
675
|
+
<p>#{node.content}</p>
|
676
|
+
</div>)
|
677
|
+
else
|
678
|
+
%(<div#{attributes}>
|
679
|
+
<p>#{node.content}</p>
|
680
|
+
</div>)
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
def preamble node
|
685
|
+
toc = if (node.attr? 'toc') && (node.attr? 'toc-placement', 'preamble')
|
686
|
+
%(\n<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
|
687
|
+
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
688
|
+
#{outline node.document}
|
689
|
+
</div>)
|
690
|
+
end
|
691
|
+
|
692
|
+
%(<div id="preamble">
|
693
|
+
<div class="sectionbody">
|
694
|
+
#{node.content}
|
695
|
+
</div>#{toc}
|
696
|
+
</div>)
|
697
|
+
end
|
698
|
+
|
699
|
+
def quote node
|
700
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
701
|
+
classes = ['quoteblock', node.role].compact
|
702
|
+
class_attribute = %( class="#{classes * ' '}")
|
703
|
+
title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) : nil
|
704
|
+
attribution = (node.attr? 'attribution') ? (node.attr 'attribution') : nil
|
705
|
+
citetitle = (node.attr? 'citetitle') ? (node.attr 'citetitle') : nil
|
706
|
+
if attribution || citetitle
|
707
|
+
cite_element = citetitle ? %(<cite>#{citetitle}</cite>) : nil
|
708
|
+
attribution_text = attribution ? %(— #{attribution}#{citetitle ? "<br#{@void_element_slash}>\n" : nil}) : nil
|
709
|
+
attribution_element = %(\n<div class="attribution">\n#{attribution_text}#{cite_element}\n</div>)
|
710
|
+
else
|
711
|
+
attribution_element = nil
|
712
|
+
end
|
713
|
+
|
714
|
+
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
715
|
+
<blockquote>
|
716
|
+
#{node.content}
|
717
|
+
</blockquote>#{attribution_element}
|
718
|
+
</div>)
|
719
|
+
end
|
720
|
+
|
721
|
+
def thematic_break node
|
722
|
+
%(<hr#{@void_element_slash}>)
|
723
|
+
end
|
724
|
+
|
725
|
+
def sidebar node
|
726
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
727
|
+
title_element = node.title? ? %(<div class="title">#{node.title}</div>\n) : nil
|
728
|
+
%(<div#{id_attribute} class="#{(role = node.role) ? ['sidebarblock', role] * ' ' : 'sidebarblock'}">
|
729
|
+
<div class="content">
|
730
|
+
#{title_element}#{node.content}
|
731
|
+
</div>
|
732
|
+
</div>)
|
733
|
+
end
|
734
|
+
|
735
|
+
def table node
|
736
|
+
result = []
|
737
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
738
|
+
classes = ['tableblock', %(frame-#{node.attr 'frame', 'all'}), %(grid-#{node.attr 'grid', 'all'})]
|
739
|
+
styles = []
|
740
|
+
unless node.option? 'autowidth'
|
741
|
+
if (tablepcwidth = node.attr 'tablepcwidth') == 100
|
742
|
+
classes << 'spread'
|
743
|
+
else
|
744
|
+
styles << %(width: #{tablepcwidth}%;)
|
745
|
+
end
|
746
|
+
end
|
747
|
+
if (role = node.role)
|
748
|
+
classes << role
|
749
|
+
end
|
750
|
+
class_attribute = %( class="#{classes * ' '}")
|
751
|
+
styles << %(float: #{node.attr 'float'};) if node.attr? 'float'
|
752
|
+
style_attribute = styles.empty? ? nil : %( style="#{styles * ' '}")
|
753
|
+
|
754
|
+
result << %(<table#{id_attribute}#{class_attribute}#{style_attribute}>)
|
755
|
+
result << %(<caption class="title">#{node.captioned_title}</caption>) if node.title?
|
756
|
+
if (node.attr 'rowcount') > 0
|
757
|
+
slash = @void_element_slash
|
758
|
+
result << '<colgroup>'
|
759
|
+
if node.option? 'autowidth'
|
760
|
+
tag = %(<col#{slash}>)
|
761
|
+
node.columns.size.times do
|
762
|
+
result << tag
|
763
|
+
end
|
764
|
+
else
|
765
|
+
node.columns.each do |col|
|
766
|
+
result << %(<col style="width: #{col.attr 'colpcwidth'}%;"#{slash}>)
|
767
|
+
end
|
768
|
+
end
|
769
|
+
result << '</colgroup>'
|
770
|
+
[:head, :foot, :body].select {|tsec| !node.rows[tsec].empty? }.each do |tsec|
|
771
|
+
result << %(<t#{tsec}>)
|
772
|
+
node.rows[tsec].each do |row|
|
773
|
+
result << '<tr>'
|
774
|
+
row.each do |cell|
|
775
|
+
if tsec == :head
|
776
|
+
cell_content = cell.text
|
777
|
+
else
|
778
|
+
case cell.style
|
779
|
+
when :asciidoc
|
780
|
+
cell_content = %(<div>#{cell.content}</div>)
|
781
|
+
when :verse
|
782
|
+
cell_content = %(<div class="verse">#{cell.text}</div>)
|
783
|
+
when :literal
|
784
|
+
cell_content = %(<div class="literal"><pre>#{cell.text}</pre></div>)
|
785
|
+
else
|
786
|
+
cell_content = ''
|
787
|
+
cell.content.each do |text|
|
788
|
+
cell_content = %(#{cell_content}<p class="tableblock">#{text}</p>)
|
789
|
+
end
|
790
|
+
end
|
791
|
+
end
|
792
|
+
|
793
|
+
cell_tag_name = (tsec == :head || cell.style == :header ? 'th' : 'td')
|
794
|
+
cell_class_attribute = %( class="tableblock halign-#{cell.attr 'halign'} valign-#{cell.attr 'valign'}")
|
795
|
+
cell_colspan_attribute = cell.colspan ? %( colspan="#{cell.colspan}") : nil
|
796
|
+
cell_rowspan_attribute = cell.rowspan ? %( rowspan="#{cell.rowspan}") : nil
|
797
|
+
cell_style_attribute = (node.document.attr? 'cellbgcolor') ? %( style="background-color: #{node.document.attr 'cellbgcolor'};") : nil
|
798
|
+
result << %(<#{cell_tag_name}#{cell_class_attribute}#{cell_colspan_attribute}#{cell_rowspan_attribute}#{cell_style_attribute}>#{cell_content}</#{cell_tag_name}>)
|
799
|
+
end
|
800
|
+
result << '</tr>'
|
801
|
+
end
|
802
|
+
result << %(</t#{tsec}>)
|
803
|
+
end
|
804
|
+
end
|
805
|
+
result << '</table>'
|
806
|
+
result * EOL
|
807
|
+
end
|
808
|
+
|
809
|
+
def toc node
|
810
|
+
return '<!-- toc disabled -->' unless (doc = node.document).attr?('toc-placement', 'macro') && doc.attr?('toc')
|
811
|
+
|
812
|
+
if node.id
|
813
|
+
id_attr = %( id="#{node.id}")
|
814
|
+
title_id_attr = %( id="#{node.id}title")
|
815
|
+
else
|
816
|
+
id_attr = ' id="toc"'
|
817
|
+
title_id_attr = ' id="toctitle"'
|
818
|
+
end
|
819
|
+
title = node.title? ? node.title : (doc.attr 'toc-title')
|
820
|
+
levels = (node.attr? 'levels') ? (node.attr 'levels').to_i : nil
|
821
|
+
role = node.role? ? node.role : (doc.attr 'toc-class', 'toc')
|
822
|
+
|
823
|
+
%(<div#{id_attr} class="#{role}">
|
824
|
+
<div#{title_id_attr} class="title">#{title}</div>
|
825
|
+
#{outline doc, :toclevels => levels}
|
826
|
+
</div>)
|
827
|
+
end
|
828
|
+
|
829
|
+
def ulist node
|
830
|
+
result = []
|
831
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
832
|
+
div_classes = ['ulist', node.style, node.role].compact
|
833
|
+
marker_checked = nil
|
834
|
+
marker_unchecked = nil
|
835
|
+
if (checklist = node.option? 'checklist')
|
836
|
+
div_classes.insert 1, 'checklist'
|
837
|
+
ul_class_attribute = ' class="checklist"'
|
838
|
+
if node.option? 'interactive'
|
839
|
+
if node.document.attr? 'htmlsyntax', 'xml'
|
840
|
+
marker_checked = '<input type="checkbox" data-item-complete="1" checked="checked"/> '
|
841
|
+
marker_unchecked = '<input type="checkbox" data-item-complete="0"/> '
|
842
|
+
else
|
843
|
+
marker_checked = '<input type="checkbox" data-item-complete="1" checked> '
|
844
|
+
marker_unchecked = '<input type="checkbox" data-item-complete="0"> '
|
845
|
+
end
|
846
|
+
else
|
847
|
+
if node.document.attr? 'icons', 'font'
|
848
|
+
marker_checked = '<i class="fa fa-check-square-o"></i> '
|
849
|
+
marker_unchecked = '<i class="fa fa-square-o"></i> '
|
850
|
+
else
|
851
|
+
marker_checked = '✓ '
|
852
|
+
marker_unchecked = '❏ '
|
853
|
+
end
|
854
|
+
end
|
855
|
+
else
|
856
|
+
ul_class_attribute = node.style ? %( class="#{node.style}") : nil
|
857
|
+
end
|
858
|
+
result << %(<div#{id_attribute} class="#{div_classes * ' '}">)
|
859
|
+
result << %(<div class="title">#{node.title}</div>) if node.title?
|
860
|
+
result << %(<ul#{ul_class_attribute}>)
|
861
|
+
|
862
|
+
node.items.each do |item|
|
863
|
+
result << '<li>'
|
864
|
+
if checklist && (item.attr? 'checkbox')
|
865
|
+
result << %(<p>#{(item.attr? 'checked') ? marker_checked : marker_unchecked}#{item.text}</p>)
|
866
|
+
else
|
867
|
+
result << %(<p>#{item.text}</p>)
|
868
|
+
end
|
869
|
+
result << item.content if item.blocks?
|
870
|
+
result << '</li>'
|
871
|
+
end
|
872
|
+
|
873
|
+
result << '</ul>'
|
874
|
+
result << '</div>'
|
875
|
+
result * EOL
|
876
|
+
end
|
877
|
+
|
878
|
+
def verse node
|
879
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
880
|
+
classes = ['verseblock', node.role].compact
|
881
|
+
class_attribute = %( class="#{classes * ' '}")
|
882
|
+
title_element = node.title? ? %(\n<div class="title">#{node.title}</div>) : nil
|
883
|
+
attribution = (node.attr? 'attribution') ? (node.attr 'attribution') : nil
|
884
|
+
citetitle = (node.attr? 'citetitle') ? (node.attr 'citetitle') : nil
|
885
|
+
if attribution || citetitle
|
886
|
+
cite_element = citetitle ? %(<cite>#{citetitle}</cite>) : nil
|
887
|
+
attribution_text = attribution ? %(— #{attribution}#{citetitle ? "<br#{@void_element_slash}>\n" : nil}) : nil
|
888
|
+
attribution_element = %(\n<div class="attribution">\n#{attribution_text}#{cite_element}\n</div>)
|
889
|
+
else
|
890
|
+
attribution_element = nil
|
891
|
+
end
|
892
|
+
|
893
|
+
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
894
|
+
<pre class="content">#{node.content}</pre>#{attribution_element}
|
895
|
+
</div>)
|
896
|
+
end
|
897
|
+
|
898
|
+
def video node
|
899
|
+
xml = node.document.attr? 'htmlsyntax', 'xml'
|
900
|
+
id_attribute = node.id ? %( id="#{node.id}") : nil
|
901
|
+
classes = ['videoblock', node.style, node.role].compact
|
902
|
+
class_attribute = %( class="#{classes * ' '}")
|
903
|
+
title_element = node.title? ? %(\n<div class="title">#{node.captioned_title}</div>) : nil
|
904
|
+
width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : nil
|
905
|
+
height_attribute = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : nil
|
906
|
+
case node.attr 'poster'
|
907
|
+
when 'vimeo'
|
908
|
+
start_anchor = (node.attr? 'start') ? "#at=#{node.attr 'start'}" : nil
|
909
|
+
delimiter = '?'
|
910
|
+
autoplay_param = (node.option? 'autoplay') ? "#{delimiter}autoplay=1" : nil
|
911
|
+
delimiter = '&' if autoplay_param
|
912
|
+
loop_param = (node.option? 'loop') ? "#{delimiter}loop=1" : nil
|
913
|
+
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
914
|
+
<div class="content">
|
915
|
+
<iframe#{width_attribute}#{height_attribute} src="//player.vimeo.com/video/#{node.attr 'target'}#{start_anchor}#{autoplay_param}#{loop_param}" frameborder="0"#{append_boolean_attribute 'webkitAllowFullScreen', xml}#{append_boolean_attribute 'mozallowfullscreen', xml}#{append_boolean_attribute 'allowFullScreen', xml}></iframe>
|
916
|
+
</div>
|
917
|
+
</div>)
|
918
|
+
when 'youtube'
|
919
|
+
start_param = (node.attr? 'start') ? "&start=#{node.attr 'start'}" : nil
|
920
|
+
end_param = (node.attr? 'end') ? "&end=#{node.attr 'end'}" : nil
|
921
|
+
autoplay_param = (node.option? 'autoplay') ? '&autoplay=1' : nil
|
922
|
+
loop_param = (node.option? 'loop') ? '&loop=1' : nil
|
923
|
+
controls_param = (node.option? 'nocontrols') ? '&controls=0' : nil
|
924
|
+
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
925
|
+
<div class="content">
|
926
|
+
<iframe#{width_attribute}#{height_attribute} src="//www.youtube.com/embed/#{node.attr 'target'}?rel=0#{start_param}#{end_param}#{autoplay_param}#{loop_param}#{controls_param}" frameborder="0"#{(node.option? 'nofullscreen') ? nil : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
|
927
|
+
</div>
|
928
|
+
</div>)
|
929
|
+
else
|
930
|
+
poster_attribute = %(#{poster = node.attr 'poster'}).empty? ? nil : %( poster="#{node.media_uri poster}")
|
931
|
+
time_anchor = ((node.attr? 'start') || (node.attr? 'end')) ? %(#t=#{node.attr 'start'}#{(node.attr? 'end') ? ',' : nil}#{node.attr 'end'}) : nil
|
932
|
+
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
933
|
+
<div class="content">
|
934
|
+
<video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : nil}#{(node.option? 'nocontrols') ? nil : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : nil}>
|
935
|
+
Your browser does not support the video tag.
|
936
|
+
</video>
|
937
|
+
</div>
|
938
|
+
</div>)
|
939
|
+
end
|
940
|
+
end
|
941
|
+
|
942
|
+
def inline_anchor node
|
943
|
+
target = node.target
|
944
|
+
case node.type
|
945
|
+
when :xref
|
946
|
+
refid = (node.attr 'refid') || target
|
947
|
+
# NOTE we lookup text in converter because DocBook doesn't need this logic
|
948
|
+
text = node.text || (node.document.references[:ids][refid] || %([#{refid}]))
|
949
|
+
%(<a href="#{target}">#{text}</a>)
|
950
|
+
when :ref
|
951
|
+
%(<a id="#{target}"></a>)
|
952
|
+
when :link
|
953
|
+
attrs = []
|
954
|
+
attrs << %( id="#{node.id}") if node.id
|
955
|
+
if (role = node.role)
|
956
|
+
attrs << %( class="#{role}")
|
957
|
+
end
|
958
|
+
attrs << %( title="#{node.attr 'title'}") if node.attr? 'title'
|
959
|
+
attrs << %( target="#{node.attr 'window'}") if node.attr? 'window'
|
960
|
+
%(<a href="#{target}"#{attrs.join}>#{node.text}</a>)
|
961
|
+
when :bibref
|
962
|
+
%(<a id="#{target}"></a>[#{target}])
|
963
|
+
else
|
964
|
+
warn %(asciidoctor: WARNING: unknown anchor type: #{node.type.inspect})
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
def inline_break node
|
969
|
+
%(#{node.text}<br#{@void_element_slash}>)
|
970
|
+
end
|
971
|
+
|
972
|
+
def inline_button node
|
973
|
+
%(<b class="button">#{node.text}</b>)
|
974
|
+
end
|
975
|
+
|
976
|
+
def inline_callout node
|
977
|
+
if node.document.attr? 'icons', 'font'
|
978
|
+
%(<i class="conum" data-value="#{node.text}"></i><b>(#{node.text})</b>)
|
979
|
+
elsif node.document.attr? 'icons'
|
980
|
+
src = node.icon_uri("callouts/#{node.text}")
|
981
|
+
%(<img src="#{src}" alt="#{node.text}"#{@void_element_slash}>)
|
982
|
+
else
|
983
|
+
%(<b class="conum">(#{node.text})</b>)
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
def inline_footnote node
|
988
|
+
if (index = node.attr 'index')
|
989
|
+
if node.type == :xref
|
990
|
+
%(<span class="footnoteref">[<a class="footnote" href="#_footnote_#{index}" title="View footnote.">#{index}</a>]</span>)
|
991
|
+
else
|
992
|
+
id_attr = node.id ? %( id="_footnote_#{node.id}") : nil
|
993
|
+
%(<span class="footnote"#{id_attr}>[<a id="_footnoteref_#{index}" class="footnote" href="#_footnote_#{index}" title="View footnote.">#{index}</a>]</span>)
|
994
|
+
end
|
995
|
+
elsif node.type == :xref
|
996
|
+
%(<span class="footnoteref red" title="Unresolved footnote reference.">[#{node.text}]</span>)
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
1000
|
+
def inline_image node
|
1001
|
+
if (type = node.type) == 'icon' && (node.document.attr? 'icons', 'font')
|
1002
|
+
style_class = %(fa fa-#{node.target})
|
1003
|
+
if node.attr? 'size'
|
1004
|
+
style_class = %(#{style_class} fa-#{node.attr 'size'})
|
1005
|
+
end
|
1006
|
+
if node.attr? 'rotate'
|
1007
|
+
style_class = %(#{style_class} fa-rotate-#{node.attr 'rotate'})
|
1008
|
+
end
|
1009
|
+
if node.attr? 'flip'
|
1010
|
+
style_class = %(#{style_class} fa-flip-#{node.attr 'flip'})
|
1011
|
+
end
|
1012
|
+
title_attribute = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : nil
|
1013
|
+
img = %(<i class="#{style_class}"#{title_attribute}></i>)
|
1014
|
+
elsif type == 'icon' && !(node.document.attr? 'icons')
|
1015
|
+
img = %([#{node.attr 'alt'}])
|
1016
|
+
else
|
1017
|
+
resolved_target = (type == 'icon') ? (node.icon_uri node.target) : (node.image_uri node.target)
|
1018
|
+
|
1019
|
+
attrs = ['alt', 'width', 'height', 'title'].map {|name|
|
1020
|
+
(node.attr? name) ? %( #{name}="#{node.attr name}") : nil
|
1021
|
+
}.join
|
1022
|
+
|
1023
|
+
img = %(<img src="#{resolved_target}"#{attrs}#{@void_element_slash}>)
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
if node.attr? 'link'
|
1027
|
+
window_attr = (node.attr? 'window') ? %( target="#{node.attr 'window'}") : nil
|
1028
|
+
img = %(<a class="image" href="#{node.attr 'link'}"#{window_attr}>#{img}</a>)
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
style_classes = (role = node.role) ? %(#{type} #{role}) : type
|
1032
|
+
style_attr = (node.attr? 'float') ? %( style="float: #{node.attr 'float'}") : nil
|
1033
|
+
|
1034
|
+
%(<span class="#{style_classes}"#{style_attr}>#{img}</span>)
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
def inline_indexterm node
|
1038
|
+
node.type == :visible ? node.text : ''
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def inline_kbd node
|
1042
|
+
if (keys = node.attr 'keys').size == 1
|
1043
|
+
%(<kbd>#{keys[0]}</kbd>)
|
1044
|
+
else
|
1045
|
+
key_combo = keys.map {|key| %(<kbd>#{key}</kbd>+) }.join.chop
|
1046
|
+
%(<span class="keyseq">#{key_combo}</span>)
|
1047
|
+
end
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
def inline_menu node
|
1051
|
+
menu = node.attr 'menu'
|
1052
|
+
if !(submenus = node.attr 'submenus').empty?
|
1053
|
+
submenu_path = submenus.map {|submenu| %(<span class="submenu">#{submenu}</span> ▸ ) }.join.chop
|
1054
|
+
%(<span class="menuseq"><span class="menu">#{menu}</span> ▸ #{submenu_path} <span class="menuitem">#{node.attr 'menuitem'}</span></span>)
|
1055
|
+
elsif (menuitem = node.attr 'menuitem')
|
1056
|
+
%(<span class="menuseq"><span class="menu">#{menu}</span> ▸ <span class="menuitem">#{menuitem}</span></span>)
|
1057
|
+
else
|
1058
|
+
%(<span class="menu">#{menu}</span>)
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
def inline_quoted node
|
1063
|
+
open, close, is_tag = QUOTE_TAGS[node.type]
|
1064
|
+
if (role = node.role)
|
1065
|
+
if is_tag
|
1066
|
+
quoted_text = %(#{open.chop} class="#{role}">#{node.text}#{close})
|
1067
|
+
else
|
1068
|
+
quoted_text = %(<span class="#{role}">#{open}#{node.text}#{close}</span>)
|
1069
|
+
end
|
1070
|
+
else
|
1071
|
+
quoted_text = %(#{open}#{node.text}#{close})
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
node.id ? %(<a id="#{node.id}"></a>#{quoted_text}) : quoted_text
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def append_boolean_attribute name, xml
|
1078
|
+
xml ? %( #{name}="#{name}") : %( #{name})
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
end
|