asciidoctor 1.5.2 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +107 -1
- data/LICENSE.adoc +1 -1
- data/README.adoc +155 -230
- data/Rakefile +2 -1
- data/bin/asciidoctor +5 -1
- data/data/stylesheets/asciidoctor-default.css +37 -29
- data/data/stylesheets/coderay-asciidoctor.css +3 -3
- data/features/text_formatting.feature +2 -0
- data/lib/asciidoctor.rb +46 -21
- data/lib/asciidoctor/abstract_block.rb +14 -8
- data/lib/asciidoctor/abstract_node.rb +77 -24
- data/lib/asciidoctor/attribute_list.rb +1 -1
- data/lib/asciidoctor/block.rb +2 -3
- data/lib/asciidoctor/cli/options.rb +14 -15
- data/lib/asciidoctor/converter/docbook45.rb +8 -8
- data/lib/asciidoctor/converter/docbook5.rb +25 -17
- data/lib/asciidoctor/converter/factory.rb +6 -1
- data/lib/asciidoctor/converter/html5.rb +159 -117
- data/lib/asciidoctor/converter/manpage.rb +671 -0
- data/lib/asciidoctor/converter/template.rb +24 -17
- data/lib/asciidoctor/document.rb +89 -47
- data/lib/asciidoctor/extensions.rb +22 -21
- data/lib/asciidoctor/helpers.rb +73 -16
- data/lib/asciidoctor/list.rb +26 -5
- data/lib/asciidoctor/parser.rb +179 -122
- data/lib/asciidoctor/path_resolver.rb +6 -10
- data/lib/asciidoctor/reader.rb +37 -34
- data/lib/asciidoctor/stylesheets.rb +16 -10
- data/lib/asciidoctor/substitutors.rb +98 -21
- data/lib/asciidoctor/table.rb +21 -17
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +155 -89
- data/man/asciidoctor.adoc +19 -11
- data/test/attributes_test.rb +86 -0
- data/test/blocks_test.rb +203 -15
- data/test/converter_test.rb +15 -2
- data/test/document_test.rb +290 -36
- data/test/extensions_test.rb +22 -3
- data/test/fixtures/circle.svg +8 -0
- data/test/fixtures/subs-docinfo.html +2 -0
- data/test/fixtures/subs.adoc +7 -0
- data/test/invoker_test.rb +25 -0
- data/test/links_test.rb +17 -0
- data/test/lists_test.rb +173 -0
- data/test/options_test.rb +2 -2
- data/test/paragraphs_test.rb +2 -2
- data/test/parser_test.rb +56 -13
- data/test/reader_test.rb +35 -3
- data/test/sections_test.rb +59 -0
- data/test/substitutions_test.rb +53 -14
- data/test/tables_test.rb +158 -2
- data/test/test_helper.rb +7 -2
- metadata +22 -11
- data/benchmark/benchmark.rb +0 -129
- data/benchmark/sample-data/mdbasics.adoc +0 -334
- data/lib/asciidoctor/opal_ext.rb +0 -26
- data/lib/asciidoctor/opal_ext/comparable.rb +0 -38
- data/lib/asciidoctor/opal_ext/dir.rb +0 -13
- data/lib/asciidoctor/opal_ext/error.rb +0 -2
- data/lib/asciidoctor/opal_ext/file.rb +0 -145
@@ -90,7 +90,7 @@ class AbstractNode
|
|
90
90
|
# return the value of the attribute or the default value if the attribute
|
91
91
|
# is not found in the attributes of this node or the document node
|
92
92
|
def attr(name, default_value = nil, inherit = true)
|
93
|
-
name = name.to_s if
|
93
|
+
name = name.to_s if ::Symbol === name
|
94
94
|
inherit = false if self == @document
|
95
95
|
if inherit
|
96
96
|
@attributes[name] || @document.attributes[name] || default_value
|
@@ -117,7 +117,7 @@ class AbstractNode
|
|
117
117
|
# comparison value is specified, whether the value of the attribute matches
|
118
118
|
# the comparison value
|
119
119
|
def attr?(name, expect = nil, inherit = true)
|
120
|
-
name = name.to_s if
|
120
|
+
name = name.to_s if ::Symbol === name
|
121
121
|
inherit = false if self == @document
|
122
122
|
if expect.nil?
|
123
123
|
@attributes.has_key?(name) || (inherit && @document.attributes.has_key?(name))
|
@@ -130,23 +130,18 @@ class AbstractNode
|
|
130
130
|
|
131
131
|
# Public: Assign the value to the attribute name for the current node.
|
132
132
|
#
|
133
|
-
# name - The String attribute name
|
134
|
-
# value - The Object value to assign to the attribute
|
133
|
+
# name - The String attribute name to assign
|
134
|
+
# value - The Object value to assign to the attribute
|
135
135
|
# overwrite - A Boolean indicating whether to assign the attribute
|
136
|
-
# if currently present in the attributes Hash
|
136
|
+
# if currently present in the attributes Hash (default: true)
|
137
137
|
#
|
138
138
|
# Returns a [Boolean] indicating whether the assignment was performed
|
139
|
-
def set_attr name, value, overwrite =
|
140
|
-
if overwrite.
|
139
|
+
def set_attr name, value, overwrite = true
|
140
|
+
if overwrite == false && (@attributes.key? name)
|
141
|
+
false
|
142
|
+
else
|
141
143
|
@attributes[name] = value
|
142
144
|
true
|
143
|
-
else
|
144
|
-
if overwrite || !(@attributes.key? name)
|
145
|
-
@attributes[name] = value
|
146
|
-
true
|
147
|
-
else
|
148
|
-
false
|
149
|
-
end
|
150
145
|
end
|
151
146
|
end
|
152
147
|
|
@@ -226,6 +221,21 @@ class AbstractNode
|
|
226
221
|
end
|
227
222
|
end
|
228
223
|
|
224
|
+
# Public: A convenience method that adds the given role directly to this node
|
225
|
+
def add_role(name)
|
226
|
+
unless (roles = (@attributes['role'] || '').split(' ')).include? name
|
227
|
+
@attributes['role'] = roles.push(name) * ' '
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Public: A convenience method that removes the given role directly from this node
|
232
|
+
def remove_role(name)
|
233
|
+
if (roles = (@attributes['role'] || '').split(' ')).include? name
|
234
|
+
roles.delete name
|
235
|
+
@attributes['role'] = roles * ' '
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
229
239
|
# Public: A convenience method that checks if the reftext attribute is specified
|
230
240
|
def reftext?
|
231
241
|
@attributes.has_key?('reftext') || @document.attributes.has_key?('reftext')
|
@@ -301,8 +311,8 @@ class AbstractNode
|
|
301
311
|
# Returns A String reference or data URI for the target image
|
302
312
|
def image_uri(target_image, asset_dir_key = 'imagesdir')
|
303
313
|
if (doc = @document).safe < SafeMode::SECURE && doc.attr?('data-uri')
|
304
|
-
if
|
305
|
-
(asset_dir_key && (images_base = doc.attr(asset_dir_key)) &&
|
314
|
+
if (Helpers.uriish? target_image) ||
|
315
|
+
(asset_dir_key && (images_base = doc.attr(asset_dir_key)) && (Helpers.uriish? images_base) &&
|
306
316
|
(target_image = normalize_web_path(target_image, images_base, false)))
|
307
317
|
if doc.attr?('allow-uri-read')
|
308
318
|
generate_data_uri_from_uri target_image, doc.attr?('cache-uri')
|
@@ -330,8 +340,9 @@ class AbstractNode
|
|
330
340
|
#
|
331
341
|
# Returns A String data URI containing the content of the target image
|
332
342
|
def generate_data_uri(target_image, asset_dir_key = nil)
|
333
|
-
ext = ::File.extname
|
334
|
-
|
343
|
+
ext = ::File.extname target_image
|
344
|
+
# QUESTION what if ext is empty?
|
345
|
+
mimetype = (ext == '.svg' ? 'image/svg+xml' : %(image/#{ext[1..-1]}))
|
335
346
|
if asset_dir_key
|
336
347
|
image_path = normalize_system_path(target_image, @document.attr(asset_dir_key), nil, :target_name => 'image')
|
337
348
|
else
|
@@ -352,6 +363,7 @@ class AbstractNode
|
|
352
363
|
else
|
353
364
|
bindata = ::File.open(image_path, 'rb') {|file| file.read }
|
354
365
|
end
|
366
|
+
# NOTE base64 is autoloaded by reference to ::Base64
|
355
367
|
%(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete EOL})
|
356
368
|
end
|
357
369
|
|
@@ -368,7 +380,6 @@ class AbstractNode
|
|
368
380
|
# Returns A data URI string built from Base64 encoded data read from the URI
|
369
381
|
# and the mime type specified in the Content Type header.
|
370
382
|
def generate_data_uri_from_uri image_uri, cache_uri = false
|
371
|
-
Helpers.require_library 'base64'
|
372
383
|
if cache_uri
|
373
384
|
# caching requires the open-uri-cached gem to be installed
|
374
385
|
# processing will be automatically aborted if these libraries can't be opened
|
@@ -384,7 +395,8 @@ class AbstractNode
|
|
384
395
|
mimetype = file.content_type
|
385
396
|
file.read
|
386
397
|
}
|
387
|
-
|
398
|
+
# NOTE base64 is autoloaded by reference to ::Base64
|
399
|
+
%(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete EOL})
|
388
400
|
rescue
|
389
401
|
warn %(asciidoctor: WARNING: could not retrieve image data from URI: #{image_uri})
|
390
402
|
image_uri
|
@@ -395,6 +407,46 @@ class AbstractNode
|
|
395
407
|
end
|
396
408
|
end
|
397
409
|
|
410
|
+
# Public: Resolve the URI or system path to the specified target, then read and return its contents
|
411
|
+
#
|
412
|
+
# The URI or system path of the target is first resolved. If the resolved path is a URI, read the
|
413
|
+
# contents from the URI if the allow-uri-read attribute is set, enabling caching if the cache-uri
|
414
|
+
# attribute is also set. If the resolved path is not a URI, read the contents of the file from the
|
415
|
+
# file system. If the normalize option is set, the data will be normalized.
|
416
|
+
#
|
417
|
+
# target - The URI or local path from which to read the data.
|
418
|
+
# opts - a Hash of options to control processing (default: {})
|
419
|
+
# * :label the String label of the target to use in warning messages (default: 'asset')
|
420
|
+
# * :normalize a Boolean that indicates whether the data should be normalized (default: false)
|
421
|
+
# * :start the String relative base path to use when resolving the target (default: nil)
|
422
|
+
# * :warn_on_failure a Boolean that indicates whether warnings are issued if the target cannot be read (default: true)
|
423
|
+
# Returns the contents of the resolved target or nil if the resolved target cannot be read
|
424
|
+
# --
|
425
|
+
# TODO refactor other methods in this class to use this method were possible (repurposing if necessary)
|
426
|
+
def read_contents target, opts = {}
|
427
|
+
doc = @document
|
428
|
+
if (Helpers.uriish? target) || ((start = opts[:start]) && (Helpers.uriish? start) &&
|
429
|
+
(target = (@path_resolver ||= PathResolver.new).web_path target, start))
|
430
|
+
if doc.attr? 'allow-uri-read'
|
431
|
+
Helpers.require_library 'open-uri/cached', 'open-uri-cached' if doc.attr? 'cache-uri'
|
432
|
+
begin
|
433
|
+
data = ::OpenURI.open_uri(target) {|fd| fd.read }
|
434
|
+
data = (Helpers.normalize_lines_from_string data) * EOL if opts[:normalize]
|
435
|
+
rescue
|
436
|
+
warn %(asciidoctor: WARNING: could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
|
437
|
+
data = nil
|
438
|
+
end
|
439
|
+
else
|
440
|
+
warn %(asciidoctor: WARNING: cannot retrieve contents of #{opts[:label] || 'asset'} at URI: #{target} (allow-uri-read attribute not enabled)) if opts.fetch :warn_on_failure, true
|
441
|
+
data = nil
|
442
|
+
end
|
443
|
+
else
|
444
|
+
target = normalize_system_path target, opts[:start], nil, :target_name => (opts[:label] || 'asset')
|
445
|
+
data = read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true)
|
446
|
+
end
|
447
|
+
data
|
448
|
+
end
|
449
|
+
|
398
450
|
# Public: Read the contents of the file at the specified path.
|
399
451
|
# This method assumes that the path is safe to read. It checks
|
400
452
|
# that the file is readable before attempting to read it.
|
@@ -413,9 +465,9 @@ class AbstractNode
|
|
413
465
|
opts = { :warn_on_failure => (opts != false) } unless ::Hash === opts
|
414
466
|
if ::File.readable? path
|
415
467
|
if opts[:normalize]
|
416
|
-
# QUESTION should we strip content?
|
417
468
|
Helpers.normalize_lines_from_string(::IO.read(path)) * EOL
|
418
469
|
else
|
470
|
+
# QUESTION should we chomp or rstrip content?
|
419
471
|
::IO.read(path)
|
420
472
|
end
|
421
473
|
else
|
@@ -434,7 +486,7 @@ class AbstractNode
|
|
434
486
|
#
|
435
487
|
# Returns the resolved [String] path
|
436
488
|
def normalize_web_path(target, start = nil, preserve_uri_target = true)
|
437
|
-
if preserve_uri_target &&
|
489
|
+
if preserve_uri_target && (Helpers.uriish? target)
|
438
490
|
target
|
439
491
|
else
|
440
492
|
(@path_resolver ||= PathResolver.new).web_path target, start
|
@@ -497,8 +549,10 @@ class AbstractNode
|
|
497
549
|
|
498
550
|
# Public: Check whether the specified String is a URI by
|
499
551
|
# matching it against the Asciidoctor::UriSniffRx regex.
|
552
|
+
#
|
553
|
+
# @deprecated Use Helpers.uriish? instead
|
500
554
|
def is_uri? str
|
501
|
-
|
555
|
+
Helpers.uriish? str
|
502
556
|
end
|
503
557
|
|
504
558
|
# Public: Retrieve the list marker keyword for the specified list type.
|
@@ -511,6 +565,5 @@ class AbstractNode
|
|
511
565
|
def list_marker_keyword(list_type = nil)
|
512
566
|
ORDERED_LIST_KEYWORDS[list_type || @style]
|
513
567
|
end
|
514
|
-
|
515
568
|
end
|
516
569
|
end
|
@@ -163,7 +163,7 @@ class AttributeList
|
|
163
163
|
case name
|
164
164
|
when 'options', 'opts'
|
165
165
|
name = 'options'
|
166
|
-
value.split(',').each {|
|
166
|
+
value.tr(' ', '').split(',').each {|opt| @attributes[%(#{opt}-option)] = '' }
|
167
167
|
@attributes[name] = value
|
168
168
|
when 'title'
|
169
169
|
@attributes[name] = value
|
data/lib/asciidoctor/block.rb
CHANGED
@@ -9,7 +9,7 @@ module Asciidoctor
|
|
9
9
|
# => "<em>This</em> is a <test>"
|
10
10
|
class Block < AbstractBlock
|
11
11
|
|
12
|
-
DEFAULT_CONTENT_MODEL = {
|
12
|
+
(DEFAULT_CONTENT_MODEL = {
|
13
13
|
# TODO should probably fill in all known blocks
|
14
14
|
:audio => :empty,
|
15
15
|
:image => :empty,
|
@@ -21,8 +21,7 @@ class Block < AbstractBlock
|
|
21
21
|
:pass => :raw,
|
22
22
|
:thematic_break => :empty,
|
23
23
|
:video => :empty
|
24
|
-
}
|
25
|
-
DEFAULT_CONTENT_MODEL.default = :simple
|
24
|
+
}).default = :simple
|
26
25
|
|
27
26
|
# Public: Create alias for context to be consistent w/ AsciiDoc
|
28
27
|
alias :blockname :context
|
@@ -43,7 +43,8 @@ Example: asciidoctor -b html5 source.asciidoc
|
|
43
43
|
|
44
44
|
EOS
|
45
45
|
|
46
|
-
opts.on('-b', '--backend BACKEND', 'set output format backend (default: html5)'
|
46
|
+
opts.on('-b', '--backend BACKEND', 'set output format backend: [html5, xhtml5, docbook5, docbook45, manpage] (default: html5)',
|
47
|
+
'additional backends are supported via extensions (e.g., pdf, latex)') do |backend|
|
47
48
|
self[:attributes]['backend'] = backend
|
48
49
|
end
|
49
50
|
opts.on('-d', '--doctype DOCTYPE', ['article', 'book', 'manpage', 'inline'],
|
@@ -76,24 +77,22 @@ Example: asciidoctor -b html5 source.asciidoc
|
|
76
77
|
end
|
77
78
|
opts.on('-C', '--compact', 'compact the output by removing blank lines. (No longer in use)') do
|
78
79
|
end
|
79
|
-
opts.on('-a', '--attribute key[=value],
|
80
|
-
'
|
81
|
-
'
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
self[:attributes][key] = val || ''
|
90
|
-
end
|
80
|
+
opts.on('-a', '--attribute key[=value]', 'a document attribute to set in the form of key, key! or key=value pair',
|
81
|
+
'unless @ is appended to the value, this attributes takes precedence over attributes',
|
82
|
+
'defined in the source document') do |attr|
|
83
|
+
key, val = attr.split '=', 2
|
84
|
+
val = val ? (FORCE_ENCODING ? (val.force_encoding ::Encoding::UTF_8) : val) : ''
|
85
|
+
# move leading ! to end for internal processing
|
86
|
+
#if !val && key.start_with?('!')
|
87
|
+
# key = "#{key[1..-1]}!"
|
88
|
+
#end
|
89
|
+
self[:attributes][key] = val
|
91
90
|
end
|
92
91
|
opts.on('-T', '--template-dir DIR', 'a directory containing custom converter templates that override the built-in converter (requires tilt gem)',
|
93
92
|
'may be specified multiple times') do |template_dir|
|
94
93
|
if self[:template_dirs].nil?
|
95
94
|
self[:template_dirs] = [template_dir]
|
96
|
-
elsif self[:template_dirs]
|
95
|
+
elsif ::Array === self[:template_dirs]
|
97
96
|
self[:template_dirs].push template_dir
|
98
97
|
else
|
99
98
|
self[:template_dirs] = [self[:template_dirs], template_dir]
|
@@ -108,7 +107,7 @@ Example: asciidoctor -b html5 source.asciidoc
|
|
108
107
|
opts.on('-D', '--destination-dir DIR', 'destination output directory (default: directory of source file)') do |dest_dir|
|
109
108
|
self[:destination_dir] = dest_dir
|
110
109
|
end
|
111
|
-
opts.on('-IDIRECTORY', '--load-path
|
110
|
+
opts.on('-IDIRECTORY', '--load-path DIRECTORY', 'add a directory to the $LOAD_PATH',
|
112
111
|
'may be specified more than once') do |path|
|
113
112
|
(self[:load_paths] ||= []).concat(path.split ::File::PATH_SEPARATOR)
|
114
113
|
end
|
@@ -33,21 +33,21 @@ module Asciidoctor
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def inline_anchor node
|
36
|
-
target = node.target
|
37
36
|
case node.type
|
38
37
|
when :ref
|
39
|
-
%(<anchor#{common_attributes target, nil, node.text}/>)
|
38
|
+
%(<anchor#{common_attributes node.target, nil, node.text}/>)
|
40
39
|
when :xref
|
41
|
-
if node.
|
42
|
-
|
43
|
-
|
40
|
+
if (path = node.attributes['path'])
|
41
|
+
# QUESTION should we use refid as fallback text instead? (like the html5 backend?)
|
42
|
+
%(<ulink url="#{node.target}">#{node.text || path}</ulink>)
|
44
43
|
else
|
45
|
-
|
46
|
-
%(<
|
44
|
+
linkend = node.attributes['fragment'] || node.target
|
45
|
+
(text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
|
47
46
|
end
|
48
47
|
when :link
|
49
|
-
%(<ulink url="#{target}">#{node.text}</ulink>)
|
48
|
+
%(<ulink url="#{node.target}">#{node.text}</ulink>)
|
50
49
|
when :bibref
|
50
|
+
target = node.target
|
51
51
|
%(<anchor#{common_attributes target, nil, "[#{target}]"}/>[#{target}])
|
52
52
|
end
|
53
53
|
end
|
@@ -83,7 +83,7 @@ module Asciidoctor
|
|
83
83
|
result * EOL
|
84
84
|
end
|
85
85
|
|
86
|
-
DLIST_TAGS = {
|
86
|
+
(DLIST_TAGS = {
|
87
87
|
'labeled' => {
|
88
88
|
:list => 'variablelist',
|
89
89
|
:entry => 'varlistentry',
|
@@ -103,8 +103,7 @@ module Asciidoctor
|
|
103
103
|
:term => 'glossterm',
|
104
104
|
:item => 'glossdef'
|
105
105
|
}
|
106
|
-
}
|
107
|
-
DLIST_TAGS.default = DLIST_TAGS['labeled']
|
106
|
+
}).default = DLIST_TAGS['labeled']
|
108
107
|
|
109
108
|
def dlist node
|
110
109
|
result = []
|
@@ -188,6 +187,8 @@ module Asciidoctor
|
|
188
187
|
def image node
|
189
188
|
width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : nil
|
190
189
|
depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : nil
|
190
|
+
# FIXME if scaledwidth is set, we should remove width & depth
|
191
|
+
# See http://www.docbook.org/tdg/en/html/imagedata.html#d0e92271 for details
|
191
192
|
swidth_attribute = (node.attr? 'scaledwidth') ? %( width="#{node.attr 'scaledwidth'}" scalefit="1") : nil
|
192
193
|
scale_attribute = (node.attr? 'scale') ? %( scale="#{node.attr 'scale'}") : nil
|
193
194
|
align_attribute = (node.attr? 'align') ? %( align="#{node.attr 'align'}") : nil
|
@@ -254,9 +255,11 @@ module Asciidoctor
|
|
254
255
|
if node.style == 'latexmath'
|
255
256
|
equation_data = %(<alt><![CDATA[#{equation}]]></alt>
|
256
257
|
<mediaobject><textobject><phrase></phrase></textobject></mediaobject>)
|
257
|
-
|
258
|
+
elsif node.style == 'asciimath' && ((defined? ::AsciiMath) || (!(defined? @asciimath_loaded) ?
|
259
|
+
(@asciimath_loaded = Helpers.require_library 'asciimath', true, :warn) : @asciimath_loaded))
|
260
|
+
equation_data = ::AsciiMath.parse(equation).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'
|
258
261
|
else
|
259
|
-
#
|
262
|
+
# Unsupported math style, so output raw expression in text object
|
260
263
|
equation_data = %(<mediaobject><textobject><phrase><![CDATA[#{equation}]]></phrase></textobject></mediaobject>)
|
261
264
|
end
|
262
265
|
if node.title?
|
@@ -374,6 +377,11 @@ module Asciidoctor
|
|
374
377
|
result = []
|
375
378
|
pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : nil
|
376
379
|
result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{node.attr 'frame', 'all'}" rowsep="#{['none', 'cols'].include?(node.attr 'grid') ? 0 : 1}" colsep="#{['none', 'rows'].include?(node.attr 'grid') ? 0 : 1}">)
|
380
|
+
if (node.option? 'unbreakable')
|
381
|
+
result << '<?dbfo keep-together="always"?>'
|
382
|
+
elsif (node.option? 'breakable')
|
383
|
+
result << '<?dbfo keep-together="auto"?>'
|
384
|
+
end
|
377
385
|
result << %(<title>#{node.title}</title>) if tag_name == 'table'
|
378
386
|
if (width = (node.attr? 'width') ? (node.attr 'width') : nil)
|
379
387
|
TABLE_PI_NAMES.each do |pi_name|
|
@@ -488,16 +496,18 @@ module Asciidoctor
|
|
488
496
|
when :ref
|
489
497
|
%(<anchor#{common_attributes node.target, nil, node.text}/>)
|
490
498
|
when :xref
|
491
|
-
if node.
|
492
|
-
|
493
|
-
|
499
|
+
if (path = node.attributes['path'])
|
500
|
+
# QUESTION should we use refid as fallback text instead? (like the html5 backend?)
|
501
|
+
%(<link xlink:href="#{node.target}">#{node.text || path}</link>)
|
494
502
|
else
|
495
|
-
|
503
|
+
linkend = node.attributes['fragment'] || node.target
|
504
|
+
(text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
|
496
505
|
end
|
497
506
|
when :link
|
498
507
|
%(<link xlink:href="#{node.target}">#{node.text}</link>)
|
499
508
|
when :bibref
|
500
|
-
|
509
|
+
target = node.target
|
510
|
+
%(<anchor#{common_attributes target, nil, "[#{target}]"}/>[#{target}])
|
501
511
|
else
|
502
512
|
warn %(asciidoctor: WARNING: unknown anchor type: #{node.type.inspect})
|
503
513
|
end
|
@@ -561,8 +571,7 @@ module Asciidoctor
|
|
561
571
|
if (keys = node.attr 'keys').size == 1
|
562
572
|
%(<keycap>#{keys[0]}</keycap>)
|
563
573
|
else
|
564
|
-
|
565
|
-
%(<keycombo>#{key_combo}</keycombo>)
|
574
|
+
%(<keycombo>#{keys.map {|key| "<keycap>#{key}</keycap>" }.join}</keycombo>)
|
566
575
|
end
|
567
576
|
end
|
568
577
|
|
@@ -578,7 +587,7 @@ module Asciidoctor
|
|
578
587
|
end
|
579
588
|
end
|
580
589
|
|
581
|
-
QUOTE_TAGS = {
|
590
|
+
(QUOTE_TAGS = {
|
582
591
|
:emphasis => ['<emphasis>', '</emphasis>', true],
|
583
592
|
:strong => ['<emphasis role="strong">', '</emphasis>', true],
|
584
593
|
:monospaced => ['<literal>', '</literal>', false],
|
@@ -587,8 +596,7 @@ module Asciidoctor
|
|
587
596
|
:double => ['“', '”', true],
|
588
597
|
:single => ['‘', '’', true],
|
589
598
|
:mark => ['<emphasis role="marked">', '</emphasis>', false]
|
590
|
-
}
|
591
|
-
QUOTE_TAGS.default = [nil, nil, true]
|
599
|
+
}).default = [nil, nil, true]
|
592
600
|
|
593
601
|
def inline_quoted node
|
594
602
|
if (type = node.type) == :latexmath
|
@@ -672,8 +680,8 @@ module Asciidoctor
|
|
672
680
|
result << %(</revision>
|
673
681
|
</revhistory>)
|
674
682
|
end
|
675
|
-
unless (
|
676
|
-
result <<
|
683
|
+
unless (head_docinfo = doc.docinfo).empty?
|
684
|
+
result << head_docinfo
|
677
685
|
end
|
678
686
|
result << %(<orgname>#{doc.attr 'orgname'}</orgname>) if doc.attr? 'orgname'
|
679
687
|
end
|
@@ -49,7 +49,7 @@ module Asciidoctor
|
|
49
49
|
require 'thread_safe'.to_s unless defined? ::ThreadSafe
|
50
50
|
new ::ThreadSafe::Cache.new
|
51
51
|
rescue ::LoadError
|
52
|
-
warn 'asciidoctor: WARNING: gem \'thread_safe\' is not installed. This gem recommended when registering custom converters.'
|
52
|
+
warn 'asciidoctor: WARNING: gem \'thread_safe\' is not installed. This gem is recommended when registering custom converters.'
|
53
53
|
new
|
54
54
|
end
|
55
55
|
end
|
@@ -207,6 +207,11 @@ module Asciidoctor
|
|
207
207
|
require 'asciidoctor/converter/docbook45'.to_s
|
208
208
|
end
|
209
209
|
DocBook45Converter.new backend, opts
|
210
|
+
when 'manpage'
|
211
|
+
unless defined? ::Asciidoctor::Converter::ManPageConverter
|
212
|
+
require 'asciidoctor/converter/manpage'.to_s
|
213
|
+
end
|
214
|
+
ManPageConverter.new backend, opts
|
210
215
|
end
|
211
216
|
|
212
217
|
return base_converter unless opts.key? :template_dirs
|
@@ -3,7 +3,7 @@ module Asciidoctor
|
|
3
3
|
# A built-in {Converter} implementation that generates HTML 5 output
|
4
4
|
# consistent with the html5 backend from AsciiDoc Python.
|
5
5
|
class Converter::Html5Converter < Converter::BuiltIn
|
6
|
-
QUOTE_TAGS = {
|
6
|
+
(QUOTE_TAGS = {
|
7
7
|
:emphasis => ['<em>', '</em>', true],
|
8
8
|
:strong => ['<strong>', '</strong>', true],
|
9
9
|
:monospaced => ['<code>', '</code>', true],
|
@@ -17,8 +17,11 @@ module Asciidoctor
|
|
17
17
|
# Opal can't resolve these constants when referenced here
|
18
18
|
#:asciimath => INLINE_MATH_DELIMITERS[:asciimath] + [false],
|
19
19
|
#:latexmath => INLINE_MATH_DELIMITERS[:latexmath] + [false]
|
20
|
-
}
|
21
|
-
|
20
|
+
}).default = [nil, nil, nil]
|
21
|
+
|
22
|
+
SvgPreambleRx = /\A.*?(?=<svg[ >])/m
|
23
|
+
SvgStartTagRx = /\A<svg[^>]*>/
|
24
|
+
DimensionAttributeRx = /\s(?:width|height|style)=(["']).*?\1/
|
22
25
|
|
23
26
|
def initialize backend, opts = {}
|
24
27
|
@xml_mode = opts[:htmlsyntax] == 'xml'
|
@@ -30,8 +33,9 @@ module Asciidoctor
|
|
30
33
|
result = []
|
31
34
|
slash = @void_element_slash
|
32
35
|
br = %(<br#{slash}>)
|
33
|
-
asset_uri_scheme = (node.attr 'asset-uri-scheme', 'https')
|
34
|
-
|
36
|
+
unless (asset_uri_scheme = (node.attr 'asset-uri-scheme', 'https')).empty?
|
37
|
+
asset_uri_scheme = %(#{asset_uri_scheme}:)
|
38
|
+
end
|
35
39
|
cdn_base = %(#{asset_uri_scheme}//cdnjs.cloudflare.com/ajax/libs)
|
36
40
|
linkcss = node.safe >= SafeMode::SECURE || (node.attr? 'linkcss')
|
37
41
|
result << '<!DOCTYPE html>'
|
@@ -52,7 +56,7 @@ module Asciidoctor
|
|
52
56
|
result << %(<title>#{node.doctitle :sanitize => true, :use_fallback => true}</title>)
|
53
57
|
if DEFAULT_STYLESHEET_KEYS.include?(node.attr 'stylesheet')
|
54
58
|
if (webfonts = node.attr 'webfonts')
|
55
|
-
result << %(<link rel="stylesheet" href="#{asset_uri_scheme}//fonts.googleapis.com/css?family=#{webfonts.empty? ? 'Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400' : webfonts}"#{slash}>)
|
59
|
+
result << %(<link rel="stylesheet" href="#{asset_uri_scheme}//fonts.googleapis.com/css?family=#{webfonts.empty? ? 'Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700' : webfonts}"#{slash}>)
|
56
60
|
end
|
57
61
|
if linkcss
|
58
62
|
result << %(<link rel="stylesheet" href="#{node.normalize_web_path DEFAULT_STYLESHEET_NAME, (node.attr 'stylesdir', ''), false}"#{slash}>)
|
@@ -71,14 +75,14 @@ module Asciidoctor
|
|
71
75
|
|
72
76
|
if node.attr? 'icons', 'font'
|
73
77
|
if node.attr? 'iconfont-remote'
|
74
|
-
result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/4.
|
78
|
+
result << %(<link rel="stylesheet" href="#{node.attr 'iconfont-cdn', %[#{cdn_base}/font-awesome/4.4.0/css/font-awesome.min.css]}"#{slash}>)
|
75
79
|
else
|
76
80
|
iconfont_stylesheet = %(#{node.attr 'iconfont-name', 'font-awesome'}.css)
|
77
81
|
result << %(<link rel="stylesheet" href="#{node.normalize_web_path iconfont_stylesheet, (node.attr 'stylesdir', ''), false}"#{slash}>)
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
81
|
-
case node.attr 'source-highlighter'
|
85
|
+
case (highlighter = node.attr 'source-highlighter')
|
82
86
|
when 'coderay'
|
83
87
|
if (node.attr 'coderay-css', 'class') == 'class'
|
84
88
|
if linkcss
|
@@ -96,38 +100,6 @@ module Asciidoctor
|
|
96
100
|
result << (@stylesheets.embed_pygments_stylesheet pygments_style)
|
97
101
|
end
|
98
102
|
end
|
99
|
-
when 'highlightjs', 'highlight.js'
|
100
|
-
highlightjs_path = node.attr 'highlightjsdir', %(#{cdn_base}/highlight.js/8.4)
|
101
|
-
result << %(<link rel="stylesheet" href="#{highlightjs_path}/styles/#{node.attr 'highlightjs-theme', 'github'}.min.css"#{slash}>
|
102
|
-
<script src="#{highlightjs_path}/highlight.min.js"></script>
|
103
|
-
<script>hljs.initHighlightingOnLoad()</script>)
|
104
|
-
when 'prettify'
|
105
|
-
prettify_path = node.attr 'prettifydir', %(#{cdn_base}/prettify/r298)
|
106
|
-
result << %(<link rel="stylesheet" href="#{prettify_path}/#{node.attr 'prettify-theme', 'prettify'}.min.css"#{slash}>
|
107
|
-
<script src="#{prettify_path}/prettify.min.js"></script>
|
108
|
-
<script>document.addEventListener('DOMContentLoaded', prettyPrint)</script>)
|
109
|
-
end
|
110
|
-
|
111
|
-
if node.attr? 'stem'
|
112
|
-
# IMPORTANT to_s calls on delimiter arrays are intentional for JavaScript compat (emulates JSON.stringify)
|
113
|
-
eqnums_val = node.attr 'eqnums', 'none'
|
114
|
-
eqnums_val = 'AMS' if eqnums_val == ''
|
115
|
-
eqnums_opt = %( equationNumbers: { autoNumber: "#{eqnums_val}" } )
|
116
|
-
result << %(<script type="text/x-mathjax-config">
|
117
|
-
MathJax.Hub.Config({
|
118
|
-
tex2jax: {
|
119
|
-
inlineMath: [#{INLINE_MATH_DELIMITERS[:latexmath].to_s}],
|
120
|
-
displayMath: [#{BLOCK_MATH_DELIMITERS[:latexmath].to_s}],
|
121
|
-
ignoreClass: "nostem|nolatexmath"
|
122
|
-
},
|
123
|
-
asciimath2jax: {
|
124
|
-
delimiters: [#{BLOCK_MATH_DELIMITERS[:asciimath].to_s}],
|
125
|
-
ignoreClass: "nostem|noasciimath"
|
126
|
-
},
|
127
|
-
TeX: {#{eqnums_opt}}
|
128
|
-
});
|
129
|
-
</script>
|
130
|
-
<script src="#{cdn_base}/mathjax/2.4.0/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>)
|
131
103
|
end
|
132
104
|
|
133
105
|
unless (docinfo_content = node.docinfo).empty?
|
@@ -159,6 +131,7 @@ MathJax.Hub.Config({
|
|
159
131
|
#{outline node}
|
160
132
|
</div>)
|
161
133
|
end
|
134
|
+
# QUESTION should this h2 have an auto-generated id?
|
162
135
|
result << %(<h2>#{node.attr 'manname-title'}</h2>
|
163
136
|
<div class="sectionbody">
|
164
137
|
<p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
|
@@ -221,22 +194,57 @@ MathJax.Hub.Config({
|
|
221
194
|
end
|
222
195
|
result << '</div>'
|
223
196
|
end
|
197
|
+
|
224
198
|
unless node.nofooter
|
225
199
|
result << '<div id="footer">'
|
226
200
|
result << '<div id="footer-text">'
|
227
|
-
if node.attr? 'revnumber'
|
228
|
-
|
229
|
-
end
|
230
|
-
if node.attr? 'last-update-label'
|
231
|
-
result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'})
|
232
|
-
end
|
201
|
+
result << %(#{node.attr 'version-label'} #{node.attr 'revnumber'}#{br}) if node.attr? 'revnumber'
|
202
|
+
result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'}) if node.attr? 'last-update-label'
|
233
203
|
result << '</div>'
|
234
|
-
unless (docinfo_content = node.docinfo :footer).empty?
|
235
|
-
result << docinfo_content
|
236
|
-
end
|
237
204
|
result << '</div>'
|
238
205
|
end
|
239
206
|
|
207
|
+
unless (docinfo_content = node.docinfo :footer).empty?
|
208
|
+
result << docinfo_content
|
209
|
+
end
|
210
|
+
|
211
|
+
# Load Javascript at the end of body for performance
|
212
|
+
# See http://www.html5rocks.com/en/tutorials/speed/script-loading/
|
213
|
+
case highlighter
|
214
|
+
when 'highlightjs', 'highlight.js'
|
215
|
+
highlightjs_path = node.attr 'highlightjsdir', %(#{cdn_base}/highlight.js/8.9.1)
|
216
|
+
result << %(<link rel="stylesheet" href="#{highlightjs_path}/styles/#{node.attr 'highlightjs-theme', 'github'}.min.css"#{slash}>)
|
217
|
+
result << %(<script src="#{highlightjs_path}/highlight.min.js"></script>
|
218
|
+
<script>hljs.initHighlighting()</script>)
|
219
|
+
when 'prettify'
|
220
|
+
prettify_path = node.attr 'prettifydir', %(#{cdn_base}/prettify/r298)
|
221
|
+
result << %(<link rel="stylesheet" href="#{prettify_path}/#{node.attr 'prettify-theme', 'prettify'}.min.css"#{slash}>)
|
222
|
+
result << %(<script src="#{prettify_path}/prettify.min.js"></script>
|
223
|
+
<script>prettyPrint()</script>)
|
224
|
+
end
|
225
|
+
|
226
|
+
if node.attr? 'stem'
|
227
|
+
# IMPORTANT to_s calls on delimiter arrays are intentional for JavaScript compat (emulates JSON.stringify)
|
228
|
+
eqnums_val = node.attr 'eqnums', 'none'
|
229
|
+
eqnums_val = 'AMS' if eqnums_val == ''
|
230
|
+
eqnums_opt = %( equationNumbers: { autoNumber: "#{eqnums_val}" } )
|
231
|
+
result << %(<script type="text/x-mathjax-config">
|
232
|
+
MathJax.Hub.Config({
|
233
|
+
tex2jax: {
|
234
|
+
inlineMath: [#{INLINE_MATH_DELIMITERS[:latexmath].inspect}],
|
235
|
+
displayMath: [#{BLOCK_MATH_DELIMITERS[:latexmath].inspect}],
|
236
|
+
ignoreClass: "nostem|nolatexmath"
|
237
|
+
},
|
238
|
+
asciimath2jax: {
|
239
|
+
delimiters: [#{BLOCK_MATH_DELIMITERS[:asciimath].inspect}],
|
240
|
+
ignoreClass: "nostem|noasciimath"
|
241
|
+
},
|
242
|
+
TeX: {#{eqnums_opt}}
|
243
|
+
});
|
244
|
+
</script>
|
245
|
+
<script src="#{cdn_base}/mathjax/2.5.3/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>)
|
246
|
+
end
|
247
|
+
|
240
248
|
result << '</body>'
|
241
249
|
result << '</html>'
|
242
250
|
result * EOL
|
@@ -244,9 +252,29 @@ MathJax.Hub.Config({
|
|
244
252
|
|
245
253
|
def embedded node
|
246
254
|
result = []
|
247
|
-
if
|
248
|
-
|
249
|
-
|
255
|
+
if node.doctype == 'manpage'
|
256
|
+
# QUESTION should notitle control the manual page title?
|
257
|
+
unless node.notitle
|
258
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
259
|
+
result << %(<h1#{id_attr}>#{node.doctitle} Manual Page</h1>)
|
260
|
+
end
|
261
|
+
# QUESTION should this h2 have an auto-generated id?
|
262
|
+
result << %(<h2>#{node.attr 'manname-title'}</h2>
|
263
|
+
<div class="sectionbody">
|
264
|
+
<p>#{node.attr 'manname'} - #{node.attr 'manpurpose'}</p>
|
265
|
+
</div>)
|
266
|
+
else
|
267
|
+
if node.has_header? && !node.notitle
|
268
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
269
|
+
result << %(<h1#{id_attr}>#{node.header.title}</h1>)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
if (node.attr? 'toc') && !['macro', 'preamble'].include?(node.attr 'toc-placement')
|
274
|
+
result << %(<div id="toc" class="toc">
|
275
|
+
<div id="toctitle">#{node.attr 'toc-title'}</div>
|
276
|
+
#{outline node}
|
277
|
+
</div>)
|
250
278
|
end
|
251
279
|
|
252
280
|
result << node.content
|
@@ -256,10 +284,9 @@ MathJax.Hub.Config({
|
|
256
284
|
<hr#{@void_element_slash}>)
|
257
285
|
node.footnotes.each do |footnote|
|
258
286
|
result << %(<div class="footnote" id="_footnote_#{footnote.index}">
|
259
|
-
<a href="#_footnoteref_#{footnote.index}">#{footnote.index}</a
|
287
|
+
<a href="#_footnoteref_#{footnote.index}">#{footnote.index}</a>. #{footnote.text}
|
260
288
|
</div>)
|
261
289
|
end
|
262
|
-
|
263
290
|
result << '</div>'
|
264
291
|
end
|
265
292
|
|
@@ -510,29 +537,34 @@ Your browser does not support the audio tag.
|
|
510
537
|
end
|
511
538
|
|
512
539
|
def image node
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
540
|
+
target = node.attr 'target'
|
541
|
+
width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : nil
|
542
|
+
height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : nil
|
543
|
+
if ((node.attr? 'format', 'svg', false) || (target.include? '.svg')) && node.document.safe < SafeMode::SECURE
|
544
|
+
((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
|
545
|
+
if svg
|
546
|
+
img = (read_svg_contents node, target) || %(<span class="alt">#{node.attr 'alt'}</span>)
|
547
|
+
elsif obj
|
548
|
+
fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{node.attr 'alt'}"#{width_attr}#{height_attr}#{@void_element_slash}>) : %(<span class="alt">#{node.attr 'alt'}</span>)
|
549
|
+
img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{width_attr}#{height_attr}>#{fallback}</object>)
|
550
|
+
end
|
518
551
|
end
|
519
|
-
|
520
|
-
width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : nil
|
521
|
-
height_attribute = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : nil
|
522
|
-
|
523
|
-
img_element = %(<img src="#{node.image_uri node.attr('target')}" alt="#{node.attr 'alt'}"#{width_attribute}#{height_attribute}#{@void_element_slash}>)
|
552
|
+
img ||= %(<img src="#{node.image_uri target}" alt="#{node.attr 'alt'}"#{width_attr}#{height_attr}#{@void_element_slash}>)
|
524
553
|
if (link = node.attr 'link')
|
525
|
-
|
554
|
+
img = %(<a class="image" href="#{link}">#{img}</a>)
|
526
555
|
end
|
527
|
-
|
556
|
+
id_attr = node.id ? %( id="#{node.id}") : nil
|
528
557
|
classes = ['imageblock', node.style, node.role].compact
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
%(
|
558
|
+
class_attr = %( class="#{classes * ' '}")
|
559
|
+
styles = []
|
560
|
+
styles << %(text-align: #{node.attr 'align'}) if node.attr? 'align'
|
561
|
+
styles << %(float: #{node.attr 'float'}) if node.attr? 'float'
|
562
|
+
style_attr = styles.empty? ? nil : %( style="#{styles * ';'}")
|
563
|
+
title_el = node.title? ? %(\n<div class="title">#{node.captioned_title}</div>) : nil
|
564
|
+
%(<div#{id_attr}#{class_attr}#{style_attr}>
|
533
565
|
<div class="content">
|
534
|
-
#{
|
535
|
-
</div>#{
|
566
|
+
#{img}
|
567
|
+
</div>#{title_el}
|
536
568
|
</div>)
|
537
569
|
end
|
538
570
|
|
@@ -663,25 +695,16 @@ Your browser does not support the audio tag.
|
|
663
695
|
end
|
664
696
|
|
665
697
|
def paragraph node
|
666
|
-
|
667
|
-
|
668
|
-
%( id="#{node.id}" class="paragraph #{node.role}")
|
669
|
-
else
|
670
|
-
%( id="#{node.id}" class="paragraph")
|
671
|
-
end
|
672
|
-
elsif node.role
|
673
|
-
%( class="paragraph #{node.role}")
|
674
|
-
else
|
675
|
-
' class="paragraph"'
|
676
|
-
end
|
698
|
+
class_attribute = node.role ? %(class="paragraph #{node.role}") : 'class="paragraph"'
|
699
|
+
attributes = node.id ? %(id="#{node.id}" #{class_attribute}) : class_attribute
|
677
700
|
|
678
701
|
if node.title?
|
679
|
-
%(<div#{attributes}>
|
702
|
+
%(<div #{attributes}>
|
680
703
|
<div class="title">#{node.title}</div>
|
681
704
|
<p>#{node.content}</p>
|
682
705
|
</div>)
|
683
706
|
else
|
684
|
-
%(<div#{attributes}>
|
707
|
+
%(<div #{attributes}>
|
685
708
|
<p>#{node.content}</p>
|
686
709
|
</div>)
|
687
710
|
end
|
@@ -911,6 +934,9 @@ Your browser does not support the audio tag.
|
|
911
934
|
height_attribute = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : nil
|
912
935
|
case node.attr 'poster'
|
913
936
|
when 'vimeo'
|
937
|
+
unless (asset_uri_scheme = (node.document.attr 'asset-uri-scheme', 'https')).empty?
|
938
|
+
asset_uri_scheme = %(#{asset_uri_scheme}:)
|
939
|
+
end
|
914
940
|
start_anchor = (node.attr? 'start', nil, false) ? %(#at=#{node.attr 'start'}) : nil
|
915
941
|
delimiter = '?'
|
916
942
|
autoplay_param = (node.option? 'autoplay') ? %(#{delimiter}autoplay=1) : nil
|
@@ -918,10 +944,13 @@ Your browser does not support the audio tag.
|
|
918
944
|
loop_param = (node.option? 'loop') ? %(#{delimiter}loop=1) : nil
|
919
945
|
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
920
946
|
<div class="content">
|
921
|
-
<iframe#{width_attribute}#{height_attribute} src="//player.vimeo.com/video/#{node.attr 'target'}#{start_anchor}#{autoplay_param}#{loop_param}" frameborder="0"#{(node.option? 'nofullscreen') ? nil : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
|
947
|
+
<iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//player.vimeo.com/video/#{node.attr 'target'}#{start_anchor}#{autoplay_param}#{loop_param}" frameborder="0"#{(node.option? 'nofullscreen') ? nil : (append_boolean_attribute 'allowfullscreen', xml)}></iframe>
|
922
948
|
</div>
|
923
949
|
</div>)
|
924
950
|
when 'youtube'
|
951
|
+
unless (asset_uri_scheme = (node.document.attr 'asset-uri-scheme', 'https')).empty?
|
952
|
+
asset_uri_scheme = %(#{asset_uri_scheme}:)
|
953
|
+
end
|
925
954
|
rel_param_val = (node.option? 'related') ? 1 : 0
|
926
955
|
start_param = (node.attr? 'start', nil, false) ? %(&start=#{node.attr 'start'}) : nil
|
927
956
|
end_param = (node.attr? 'end', nil, false) ? %(&end=#{node.attr 'end'}) : nil
|
@@ -951,13 +980,14 @@ Your browser does not support the audio tag.
|
|
951
980
|
# INFO playlist bar doesn't appear in Firefox unless showinfo=1 and modestbranding=1
|
952
981
|
list_param = %(&playlist=#{playlist})
|
953
982
|
else
|
954
|
-
|
983
|
+
# NOTE for loop to work, playlist must be specified; use VIDEO_ID if there's no explicit playlist
|
984
|
+
list_param = loop_param ? %(&playlist=#{target}) : nil
|
955
985
|
end
|
956
986
|
end
|
957
987
|
|
958
988
|
%(<div#{id_attribute}#{class_attribute}>#{title_element}
|
959
989
|
<div class="content">
|
960
|
-
<iframe#{width_attribute}#{height_attribute} src="//www.youtube.com/embed/#{target}?rel=#{rel_param_val}#{start_param}#{end_param}#{autoplay_param}#{loop_param}#{controls_param}#{list_param}#{fs_param}#{modest_param}#{theme_param}#{hl_param}" frameborder="0"#{fs_attribute}></iframe>
|
990
|
+
<iframe#{width_attribute}#{height_attribute} src="#{asset_uri_scheme}//www.youtube.com/embed/#{target}?rel=#{rel_param_val}#{start_param}#{end_param}#{autoplay_param}#{loop_param}#{controls_param}#{list_param}#{fs_param}#{modest_param}#{theme_param}#{hl_param}" frameborder="0"#{fs_attribute}></iframe>
|
961
991
|
</div>
|
962
992
|
</div>)
|
963
993
|
else
|
@@ -979,7 +1009,7 @@ Your browser does not support the video tag.
|
|
979
1009
|
target = node.target
|
980
1010
|
case node.type
|
981
1011
|
when :xref
|
982
|
-
refid =
|
1012
|
+
refid = node.attributes['refid'] || target
|
983
1013
|
# NOTE we lookup text in converter because DocBook doesn't need this logic
|
984
1014
|
text = node.text || (node.document.references[:ids][refid] || %([#{refid}]))
|
985
1015
|
# FIXME shouldn't target be refid? logic seems confused here
|
@@ -992,8 +1022,8 @@ Your browser does not support the video tag.
|
|
992
1022
|
if (role = node.role)
|
993
1023
|
attrs << %( class="#{role}")
|
994
1024
|
end
|
995
|
-
attrs << %( title="#{node.attr 'title'}") if node.attr? 'title'
|
996
|
-
attrs << %( target="#{node.attr 'window'}") if node.attr? 'window'
|
1025
|
+
attrs << %( title="#{node.attr 'title'}") if node.attr? 'title', nil, false
|
1026
|
+
attrs << %( target="#{node.attr 'window'}") if node.attr? 'window', nil, false
|
997
1027
|
%(<a href="#{target}"#{attrs.join}>#{node.text}</a>)
|
998
1028
|
when :bibref
|
999
1029
|
%(<a id="#{target}"></a>[#{target}])
|
@@ -1024,51 +1054,47 @@ Your browser does not support the video tag.
|
|
1024
1054
|
def inline_footnote node
|
1025
1055
|
if (index = node.attr 'index')
|
1026
1056
|
if node.type == :xref
|
1027
|
-
%(<
|
1057
|
+
%(<sup class="footnoteref">[<a class="footnote" href="#_footnote_#{index}" title="View footnote.">#{index}</a>]</sup>)
|
1028
1058
|
else
|
1029
1059
|
id_attr = node.id ? %( id="_footnote_#{node.id}") : nil
|
1030
|
-
%(<
|
1060
|
+
%(<sup class="footnote"#{id_attr}>[<a id="_footnoteref_#{index}" class="footnote" href="#_footnote_#{index}" title="View footnote.">#{index}</a>]</sup>)
|
1031
1061
|
end
|
1032
1062
|
elsif node.type == :xref
|
1033
|
-
%(<
|
1063
|
+
%(<sup class="footnoteref red" title="Unresolved footnote reference.">[#{node.text}]</sup>)
|
1034
1064
|
end
|
1035
1065
|
end
|
1036
1066
|
|
1037
1067
|
def inline_image node
|
1038
1068
|
if (type = node.type) == 'icon' && (node.document.attr? 'icons', 'font')
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
end
|
1043
|
-
if node.attr? 'rotate'
|
1044
|
-
style_class = %(#{style_class} fa-rotate-#{node.attr 'rotate'})
|
1045
|
-
end
|
1046
|
-
if node.attr? 'flip'
|
1047
|
-
style_class = %(#{style_class} fa-flip-#{node.attr 'flip'})
|
1069
|
+
class_attr_val = %(fa fa-#{node.target})
|
1070
|
+
{'size' => 'fa-', 'rotate' => 'fa-rotate-', 'flip' => 'fa-flip-'}.each do |(key, prefix)|
|
1071
|
+
class_attr_val = %(#{class_attr_val} #{prefix}#{node.attr key}) if node.attr? key
|
1048
1072
|
end
|
1049
|
-
|
1050
|
-
img = %(<i class="#{
|
1073
|
+
title_attr = (node.attr? 'title') ? %( title="#{node.attr 'title'}") : nil
|
1074
|
+
img = %(<i class="#{class_attr_val}"#{title_attr}></i>)
|
1051
1075
|
elsif type == 'icon' && !(node.document.attr? 'icons')
|
1052
1076
|
img = %([#{node.attr 'alt'}])
|
1053
1077
|
else
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1078
|
+
target = node.target
|
1079
|
+
attrs = ['width', 'height', 'title'].map {|name| (node.attr? name) ? %( #{name}="#{node.attr name}") : nil }.join
|
1080
|
+
if type != 'icon' && ((node.attr? 'format', 'svg', false) || (target.include? '.svg')) &&
|
1081
|
+
node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
|
1082
|
+
if svg
|
1083
|
+
img = (read_svg_contents node, target) || %(<span class="alt">#{node.attr 'alt'}</span>)
|
1084
|
+
elsif obj
|
1085
|
+
fallback = (node.attr? 'fallback') ? %(<img src="#{node.image_uri(node.attr 'fallback')}" alt="#{node.attr 'alt'}"#{attrs}#{@void_element_slash}>) : %(<span class="alt">#{node.attr 'alt'}</span>)
|
1086
|
+
img = %(<object type="image/svg+xml" data="#{node.image_uri target}"#{attrs}>#{fallback}</object>)
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{node.attr 'alt'}"#{attrs}#{@void_element_slash}>)
|
1061
1090
|
end
|
1062
|
-
|
1063
1091
|
if node.attr? 'link'
|
1064
1092
|
window_attr = (node.attr? 'window') ? %( target="#{node.attr 'window'}") : nil
|
1065
1093
|
img = %(<a class="image" href="#{node.attr 'link'}"#{window_attr}>#{img}</a>)
|
1066
1094
|
end
|
1067
|
-
|
1068
|
-
style_classes = (role = node.role) ? %(#{type} #{role}) : type
|
1095
|
+
class_attr_val = (role = node.role) ? %(#{type} #{role}) : type
|
1069
1096
|
style_attr = (node.attr? 'float') ? %( style="float: #{node.attr 'float'}") : nil
|
1070
|
-
|
1071
|
-
%(<span class="#{style_classes}"#{style_attr}>#{img}</span>)
|
1097
|
+
%(<span class="#{class_attr_val}"#{style_attr}>#{img}</span>)
|
1072
1098
|
end
|
1073
1099
|
|
1074
1100
|
def inline_indexterm node
|
@@ -1114,5 +1140,21 @@ Your browser does not support the video tag.
|
|
1114
1140
|
def append_boolean_attribute name, xml
|
1115
1141
|
xml ? %( #{name}="#{name}") : %( #{name})
|
1116
1142
|
end
|
1143
|
+
|
1144
|
+
def read_svg_contents node, target
|
1145
|
+
if (svg = node.read_contents target, :start => (node.document.attr 'imagesdir'), :normalize => true, :label => 'SVG')
|
1146
|
+
svg = svg.sub SvgPreambleRx, ''
|
1147
|
+
start_tag = nil
|
1148
|
+
['width', 'height'].each do |dim|
|
1149
|
+
if node.attr? dim
|
1150
|
+
# NOTE width, height and style attributes are removed if either width or height is specified
|
1151
|
+
start_tag ||= (svg.match SvgStartTagRx)[0].gsub DimensionAttributeRx, ''
|
1152
|
+
start_tag = %(#{start_tag.chop} #{dim}="#{node.attr dim}px">)
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
svg = svg.sub SvgStartTagRx, start_tag if start_tag
|
1156
|
+
end
|
1157
|
+
svg
|
1158
|
+
end
|
1117
1159
|
end
|
1118
1160
|
end
|