asciidoctor-html5s 0.1.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.adoc +62 -0
- data/asciidoctor-html5s.gemspec +34 -0
- data/data/templates/_attribution.html.slim +4 -0
- data/data/templates/_footer.html.slim +8 -0
- data/data/templates/_footnotes.html.slim +11 -0
- data/data/templates/_hdlist.html.slim +20 -0
- data/data/templates/_header.html.slim +27 -0
- data/data/templates/_qanda.html.slim +12 -0
- data/data/templates/_toc.html.slim +4 -0
- data/data/templates/admonition.html.slim +10 -0
- data/data/templates/audio.html.slim +7 -0
- data/data/templates/colist.html.slim +4 -0
- data/data/templates/dlist.html.slim +13 -0
- data/data/templates/document.html.slim +30 -0
- data/data/templates/embedded.html.slim +5 -0
- data/data/templates/example.html.slim +2 -0
- data/data/templates/floating_title.html.slim +2 -0
- data/data/templates/helpers.rb +665 -0
- data/data/templates/image.html.slim +3 -0
- data/data/templates/inline_anchor.html.slim +12 -0
- data/data/templates/inline_break.html.slim +2 -0
- data/data/templates/inline_button.html.slim +1 -0
- data/data/templates/inline_callout.html.slim +1 -0
- data/data/templates/inline_footnote.html.slim +9 -0
- data/data/templates/inline_image.html.slim +10 -0
- data/data/templates/inline_indexterm.html.slim +2 -0
- data/data/templates/inline_kbd.html.slim +7 -0
- data/data/templates/inline_menu.html.slim +18 -0
- data/data/templates/inline_quoted.html.slim +26 -0
- data/data/templates/listing.html.slim +16 -0
- data/data/templates/literal.html.slim +2 -0
- data/data/templates/olist.html.slim +4 -0
- data/data/templates/open.html.slim +7 -0
- data/data/templates/outline.html.slim +9 -0
- data/data/templates/page_break.html.slim +1 -0
- data/data/templates/paragraph.html.slim +6 -0
- data/data/templates/pass.html.slim +1 -0
- data/data/templates/preamble.html.slim +4 -0
- data/data/templates/quote.html.slim +6 -0
- data/data/templates/section.html.slim +13 -0
- data/data/templates/sidebar.html.slim +4 -0
- data/data/templates/stem.html.slim +2 -0
- data/data/templates/table.html.slim +39 -0
- data/data/templates/thematic_break.html.slim +1 -0
- data/data/templates/toc.html.slim +9 -0
- data/data/templates/ulist.html.slim +11 -0
- data/data/templates/verse.html.slim +7 -0
- data/data/templates/video.html.slim +18 -0
- data/lib/asciidoctor-html5s.rb +1 -0
- data/lib/asciidoctor/html5s.rb +8 -0
- data/lib/asciidoctor/html5s/attached_colist_treeprocessor.rb +24 -0
- data/lib/asciidoctor/html5s/converter.rb +3009 -0
- data/lib/asciidoctor/html5s/version.rb +5 -0
- metadata +226 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c16c90fa30631f549c6e9a6f9677919571dc2d69
|
4
|
+
data.tar.gz: 73ea5fbfd2ee70c5d361e1c8b6d5ef3ef4b6d804
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9fd08e3a180262d33d54d187fe373286306405b65d0a49e5a295e0b5c7116c790dcf678e4a6300d6a20c43ea4f6746463539b292f177e23a30fc4985e2c7a30e
|
7
|
+
data.tar.gz: 87bc0fba47221dd500b4ffe14c1f507aa608d9816ca809b18788c29a3cd8f27eefa49ce488b81122e0f93f3e97669d8faed8a89250d37613d5210f6d46b8ddc2
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright 2014-2017 Jakub Jirutka <jakub@jirutka.cz> and the Asciidoctor Project.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.adoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= Semantic HTML5 Backend For Asciidoctor
|
2
|
+
// custom
|
3
|
+
:gem-name: asciidoctor-html5s
|
4
|
+
:gh-name: jirutka/{gem-name}
|
5
|
+
:gh-branch: master
|
6
|
+
|
7
|
+
ifdef::env-github[]
|
8
|
+
image:https://travis-ci.org/{gh-name}.svg?branch={gh-branch}[Build Status, link="https://travis-ci.org/{gh-name}"]
|
9
|
+
image:https://img.shields.io/gem/v/{gem-name}.svg?style=flat[Gem Version, link="https://rubygems.org/gems/{gem-name}"]
|
10
|
+
endif::env-github[]
|
11
|
+
|
12
|
+
This project provides alternative HTML5 converter (backend) for http://asciidoctor.org/[Asciidoctor] that focuses on correct semantics, accessibility and compatibility with common typographic CSS styles.
|
13
|
+
|
14
|
+
|
15
|
+
== Goals
|
16
|
+
|
17
|
+
* Clean markup with correct HTML5 semantics.
|
18
|
+
* Good accessibility for people with disabilities.
|
19
|
+
* Compatibility with common typographic CSS styles when possible and especially with GitHub and GitLab.
|
20
|
+
* Full standalone converter without fallback to the built-in Asciidoctor converters.
|
21
|
+
* Easy to use and integrate into third-party projects.
|
22
|
+
* Well readable and maintainable code – this should be never sacrificed for performance (I’m looking at you, Asciidoctor!).
|
23
|
+
|
24
|
+
|
25
|
+
== Non-goals
|
26
|
+
|
27
|
+
* Compatibility with existing Asciidoctor CSS styles.
|
28
|
+
|
29
|
+
|
30
|
+
== Requirements
|
31
|
+
|
32
|
+
* https://www.ruby-lang.org/[Ruby] 2.0+ or http://jruby.org/[JRuby] 9.1+
|
33
|
+
* https://rubygems.org/gems/asciidoctor/[Asciidoctor] 1.5.5+
|
34
|
+
* https://rubygems.org/gems/thread_safe/[thread_safe] (not required, but recommended)
|
35
|
+
|
36
|
+
Note: This converter consists of https://github.com/slim-template/slim/[Slim] templates, but they are precompiled into pure Ruby code using https://github.com/jirutka/asciidoctor-templates-compiler/[asciidoctor-templates-compiler], so you don’t need Slim to use it!
|
37
|
+
|
38
|
+
|
39
|
+
== Installation
|
40
|
+
|
41
|
+
Install {gem-name} from Rubygems:
|
42
|
+
|
43
|
+
[source, sh, subs="+attributes"]
|
44
|
+
gem install {gem-name}
|
45
|
+
|
46
|
+
or to get the latest development version:
|
47
|
+
|
48
|
+
[source, sh, subs="+attributes"]
|
49
|
+
gem install --pre {gem-name}
|
50
|
+
|
51
|
+
|
52
|
+
== Usage
|
53
|
+
|
54
|
+
[source, sh, subs="+attributes"]
|
55
|
+
asciidoctor -r {gem-name} -b html5s FILE...
|
56
|
+
|
57
|
+
|
58
|
+
== License
|
59
|
+
|
60
|
+
This project is licensed under http://opensource.org/licenses/MIT/[MIT License].
|
61
|
+
For the full text of the license, see the link:LICENSE[LICENSE] file.
|
62
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('lib/asciidoctor/html5s/version', __dir__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'asciidoctor-html5s'
|
6
|
+
s.version = Asciidoctor::Html5s::VERSION
|
7
|
+
s.author = 'Jakub Jirutka'
|
8
|
+
s.email = 'jakub@jirutka.cz'
|
9
|
+
s.homepage = 'https://github.com/jirutka/asciidoctor-html5s'
|
10
|
+
s.license = 'MIT'
|
11
|
+
|
12
|
+
s.summary = 'Semantic HTML5 converter (backend) for Asciidoctor'
|
13
|
+
s.description = <<-EOF
|
14
|
+
This project provides alternative HTML5 converter (backend) for Asciidoctor
|
15
|
+
that focuses on correct semantics, accessibility and compatibility with common
|
16
|
+
typographic CSS styles.
|
17
|
+
EOF
|
18
|
+
|
19
|
+
s.files = Dir['data/**/*', 'lib/**/*', '*.gemspec', 'LICENSE*', 'README*']
|
20
|
+
s.has_rdoc = false
|
21
|
+
|
22
|
+
s.required_ruby_version = '>= 2.0'
|
23
|
+
|
24
|
+
s.add_runtime_dependency 'asciidoctor', '~> 1.5.5'
|
25
|
+
s.add_runtime_dependency 'thread_safe', '~> 0.3.4'
|
26
|
+
|
27
|
+
s.add_development_dependency 'asciidoctor-doctest', '= 2.0.0.beta.4'
|
28
|
+
s.add_development_dependency 'asciidoctor-templates-compiler', '~> 0.1.2'
|
29
|
+
s.add_development_dependency 'bundler', '~> 1.6'
|
30
|
+
s.add_development_dependency 'coderay', '~> 1.1'
|
31
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
32
|
+
s.add_development_dependency 'slim', '~> 3.0'
|
33
|
+
s.add_development_dependency 'slim-htag', '~> 0.1.0'
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
section.footnotes aria-label='Footnotes' role='doc-endnotes'
|
2
|
+
hr
|
3
|
+
ol.footnotes
|
4
|
+
- footnotes.each do |fn|
|
5
|
+
li.footnote id=(footnote_id fn.index) role='doc-endnote'
|
6
|
+
="#{fn.text} "
|
7
|
+
a.footnote-backref [
|
8
|
+
href="##{footnoteref_id fn.index}"
|
9
|
+
role='doc-backlink'
|
10
|
+
title='Jump to the first occurrence in the text' ]
|
11
|
+
| ↩
|
@@ -0,0 +1,20 @@
|
|
1
|
+
= block_with_title :class=>'hdlist'
|
2
|
+
table
|
3
|
+
- if (attr? :labelwidth) || (attr? :itemwidth)
|
4
|
+
colgroup
|
5
|
+
col style=style_value(width: [(attr :labelwidth), '%'])
|
6
|
+
col style=style_value(width: [(attr :itemwidth), '%'])
|
7
|
+
- items.each do |terms, dd|
|
8
|
+
tr
|
9
|
+
th.hdlist1 class=('strong' if option? 'strong')
|
10
|
+
- terms = [*terms]
|
11
|
+
- terms.each_with_index do |dt, idx|
|
12
|
+
=dt.text
|
13
|
+
- unless idx >= terms.count - 1
|
14
|
+
br
|
15
|
+
td.hdlist2
|
16
|
+
- unless dd.nil?
|
17
|
+
- if dd.text?
|
18
|
+
p =dd.text
|
19
|
+
- if dd.blocks?
|
20
|
+
=dd.content
|
@@ -0,0 +1,27 @@
|
|
1
|
+
- if has_header?
|
2
|
+
- unless notitle
|
3
|
+
h1 =header.title
|
4
|
+
- if [:author, :revnumber, :revdate, :revremark].any? {|a| attr? a }
|
5
|
+
.details
|
6
|
+
- if attr? :author
|
7
|
+
span.author#author =(attr :author)
|
8
|
+
br
|
9
|
+
- if attr? :email
|
10
|
+
span.email#email =sub_macros(attr :email)
|
11
|
+
br
|
12
|
+
- if (authorcount = (attr :authorcount).to_i) > 1
|
13
|
+
- (2..authorcount).each do |idx|
|
14
|
+
span.author id="author#{idx}" =(attr "author_#{idx}")
|
15
|
+
br
|
16
|
+
- if attr? "email_#{idx}"
|
17
|
+
span.email id="email#{idx}" =sub_macros(attr "email_#{idx}")
|
18
|
+
- if attr? :revnumber
|
19
|
+
span#revnumber #{((attr 'version-label') || '').downcase} #{attr :revnumber}#{',' if attr? :revdate}
|
20
|
+
'
|
21
|
+
- if attr? :revdate
|
22
|
+
time#revdate datetime=revdate_iso =(attr :revdate)
|
23
|
+
- if attr? :revremark
|
24
|
+
br
|
25
|
+
span#revremark =(attr :revremark)
|
26
|
+
- if (attr? :toc) && (attr? 'toc-placement', 'auto')
|
27
|
+
include _toc.html
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= block_with_title :class=>'qlist qanda', :role=>'doc-qna'
|
2
|
+
dl.qanda
|
3
|
+
- items.each do |questions, answer|
|
4
|
+
- [*questions].each do |question|
|
5
|
+
dt.qanda-question =question.text
|
6
|
+
- unless answer.nil?
|
7
|
+
dd.qanda-answer
|
8
|
+
- if answer.text?
|
9
|
+
= html_tag_if answer.blocks?, :p
|
10
|
+
=answer.text
|
11
|
+
- if answer.blocks?
|
12
|
+
=answer.content
|
@@ -0,0 +1,10 @@
|
|
1
|
+
- capture
|
2
|
+
h6 =(title || caption)
|
3
|
+
= html_tag_if !blocks?, :p
|
4
|
+
=content
|
5
|
+
- if admonition_aside?
|
6
|
+
aside.admonitionblock id=id class=[(attr :name), role] role=admonition_aria
|
7
|
+
- yield_capture
|
8
|
+
- else
|
9
|
+
section.admonitionblock id=id class=[(attr :name), role] role=admonition_aria
|
10
|
+
- yield_capture
|
@@ -0,0 +1,13 @@
|
|
1
|
+
- case style
|
2
|
+
- when 'qanda'
|
3
|
+
include _qanda.html
|
4
|
+
- when 'horizontal'
|
5
|
+
include _hdlist.html
|
6
|
+
- else
|
7
|
+
= block_with_title :class=>['dlist', style]
|
8
|
+
dl
|
9
|
+
- items.each do |terms, dd|
|
10
|
+
- [*terms].each do |dt|
|
11
|
+
dt =dt.text
|
12
|
+
- unless dd.nil?
|
13
|
+
dd =(print_item_content dd)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
doctype 5
|
2
|
+
html lang=(attr :lang, 'en' unless attr? :nolang)
|
3
|
+
head
|
4
|
+
meta charset=(attr :encoding, 'UTF-8')
|
5
|
+
/[if IE]
|
6
|
+
meta http-equiv="X-UA-Compatible" content="IE=edge"
|
7
|
+
meta name='viewport' content='width=device-width, initial-scale=1.0'
|
8
|
+
meta name='generator' content="Asciidoctor #{attr 'asciidoctor-version'}"
|
9
|
+
= html_meta_if 'application-name', (attr 'app-name')
|
10
|
+
= html_meta_if 'author', (attr :authors)
|
11
|
+
= html_meta_if 'copyright', (attr :copyright)
|
12
|
+
= html_meta_if 'description', (attr :description)
|
13
|
+
= html_meta_if 'keywords', (attr :keywords)
|
14
|
+
title=((doctitle sanitize: true) || (attr 'untitled-label'))
|
15
|
+
= styles_and_scripts
|
16
|
+
- unless (docinfo_content = docinfo).empty?
|
17
|
+
=docinfo_content
|
18
|
+
body [
|
19
|
+
id=id
|
20
|
+
class=[(attr :doctype), ("#{attr 'toc-class'} toc-#{attr 'toc-position', 'left'}" if (attr? 'toc-class') && (attr? :toc) && (attr? 'toc-placement', 'auto'))]
|
21
|
+
style=style_value(max_width: (attr 'max-width')) ]
|
22
|
+
- unless noheader
|
23
|
+
header
|
24
|
+
include _header.html
|
25
|
+
#content =content
|
26
|
+
- unless !footnotes? || (attr? :nofootnotes)
|
27
|
+
include _footnotes.html
|
28
|
+
- unless nofooter
|
29
|
+
footer
|
30
|
+
include _footer.html
|
@@ -0,0 +1,665 @@
|
|
1
|
+
require 'asciidoctor'
|
2
|
+
require 'asciidoctor/html5s'
|
3
|
+
require 'date'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
# Needed only in compile-time.
|
7
|
+
require 'slim-htag' if defined? Slim
|
8
|
+
|
9
|
+
if Gem::Version.new(Asciidoctor::VERSION) <= Gem::Version.new('1.5.1')
|
10
|
+
fail 'asciidoctor: FAILED: HTML5/Slim backend needs Asciidoctor >=1.5.2!'
|
11
|
+
end
|
12
|
+
|
13
|
+
# Add custom functions to this module that you want to use in your Slim
|
14
|
+
# templates. Within the template you can invoke them as top-level functions
|
15
|
+
# just like in Haml.
|
16
|
+
module Slim::Helpers
|
17
|
+
|
18
|
+
# URIs of external assets.
|
19
|
+
FONT_AWESOME_URI = '//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-awesome.min.css'
|
20
|
+
HIGHLIGHTJS_BASE_URI = '//cdnjs.cloudflare.com/ajax/libs/highlight.js/7.4'
|
21
|
+
MATHJAX_JS_URI = '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_HTMLorMML'
|
22
|
+
|
23
|
+
# Defaults
|
24
|
+
DEFAULT_HIGHLIGHTJS_THEME = 'github'
|
25
|
+
DEFAULT_SECTNUMLEVELS = 3
|
26
|
+
DEFAULT_TOCLEVELS = 2
|
27
|
+
|
28
|
+
# The MathJax configuration.
|
29
|
+
MATHJAX_CONFIG = {
|
30
|
+
tex2jax: {
|
31
|
+
inlineMath: [::Asciidoctor::INLINE_MATH_DELIMITERS[:latexmath].inspect],
|
32
|
+
displayMath: [::Asciidoctor::BLOCK_MATH_DELIMITERS[:latexmath].inspect],
|
33
|
+
ignoreClass: 'nostem|nolatexmath',
|
34
|
+
},
|
35
|
+
asciimath2jax: {
|
36
|
+
delimiters: [::Asciidoctor::BLOCK_MATH_DELIMITERS[:asciimath].inspect],
|
37
|
+
ignoreClass: 'nostem|noasciimath',
|
38
|
+
}
|
39
|
+
}.to_json
|
40
|
+
|
41
|
+
VOID_ELEMENTS = %w(area base br col command embed hr img input keygen link
|
42
|
+
meta param source track wbr)
|
43
|
+
|
44
|
+
|
45
|
+
##
|
46
|
+
# Captures the given block for later yield.
|
47
|
+
#
|
48
|
+
# @example Basic capture usage.
|
49
|
+
# - capture
|
50
|
+
# img src=image_uri
|
51
|
+
# - if title?
|
52
|
+
# figure.image
|
53
|
+
# - yield_capture
|
54
|
+
# figcaption =captioned_title
|
55
|
+
# - else
|
56
|
+
# - yield_capture
|
57
|
+
#
|
58
|
+
# @example Capture with passing parameters.
|
59
|
+
# - capture do |id|
|
60
|
+
# img src=image_uri
|
61
|
+
# - if title?
|
62
|
+
# figure id=@id
|
63
|
+
# - yield_capture
|
64
|
+
# figcaption =caption
|
65
|
+
# - else
|
66
|
+
# - yield_capture @id
|
67
|
+
#
|
68
|
+
# @see yield_capture
|
69
|
+
def capture(&block)
|
70
|
+
@_html5s_capture = block
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Yields the captured block (see {#capture}).
|
76
|
+
#
|
77
|
+
# @param *params parameters to pass to the block.
|
78
|
+
# @return A content of the captured block.
|
79
|
+
# @see capture
|
80
|
+
def yield_capture(*params)
|
81
|
+
@_html5s_capture.call(*params) if @_html5s_capture
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Creates an HTML tag with the given name and optionally attributes. Can take
|
86
|
+
# a block that will run between the opening and closing tags.
|
87
|
+
#
|
88
|
+
# @param name [#to_s] the name of the tag.
|
89
|
+
# @param attributes [Hash] (default: {})
|
90
|
+
# @param content [#to_s] the content; +nil+ to call the block. (default: nil).
|
91
|
+
# @yield The block of Slim/HTML code within the tag (optional).
|
92
|
+
# @return [String] a rendered HTML element.
|
93
|
+
#
|
94
|
+
def html_tag(name, attributes = {}, content = nil)
|
95
|
+
attrs = attributes.inject([]) do |attrs, (k, v)|
|
96
|
+
next attrs if !v || v.nil_or_empty?
|
97
|
+
v = v.compact.join(' ') if v.is_a? Array
|
98
|
+
attrs << (v == true ? k : %(#{k}="#{v}"))
|
99
|
+
end
|
100
|
+
attrs_str = attrs.empty? ? '' : attrs.join(' ').prepend(' ')
|
101
|
+
|
102
|
+
if VOID_ELEMENTS.include? name.to_s
|
103
|
+
%(<#{name}#{attrs_str}>)
|
104
|
+
else
|
105
|
+
content ||= yield if block_given?
|
106
|
+
%(<#{name}#{attrs_str}>#{content}</#{name}>)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Conditionally wraps a block in an element. If condition is +true+ then it
|
112
|
+
# renders the specified tag with optional attributes and the given
|
113
|
+
# block inside, otherwise it just renders the block.
|
114
|
+
#
|
115
|
+
# For example:
|
116
|
+
#
|
117
|
+
# = html_tag_if link?, 'a', {class: 'image', href: (attr :link)}
|
118
|
+
# img src='./img/tux.png'
|
119
|
+
#
|
120
|
+
# will produce:
|
121
|
+
#
|
122
|
+
# <a href="http://example.org" class="image">
|
123
|
+
# <img src="./img/tux.png">
|
124
|
+
# </a>
|
125
|
+
#
|
126
|
+
# if +link?+ is truthy, and just
|
127
|
+
#
|
128
|
+
# <img src="./img/tux.png">
|
129
|
+
#
|
130
|
+
# otherwise.
|
131
|
+
#
|
132
|
+
# @param condition [Boolean] the condition to test to determine whether to
|
133
|
+
# render the enclosing tag.
|
134
|
+
# @param name (see #html_tag)
|
135
|
+
# @param attributes (see #html_tag)
|
136
|
+
# @param content (see #html_tag)
|
137
|
+
# @yield (see #html_tag)
|
138
|
+
# @return [String] a rendered HTML fragment.
|
139
|
+
#
|
140
|
+
def html_tag_if(condition, name, attributes = {}, content = nil, &block)
|
141
|
+
if condition
|
142
|
+
html_tag name, attributes, content, &block
|
143
|
+
else
|
144
|
+
content || yield
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Wraps a block in a div element with the specified class and optionally
|
150
|
+
# the node's +id+ and +role+(s). If the node's +title+ is not empty, then a
|
151
|
+
# nested div with the class "title" and the title's content is added as well.
|
152
|
+
#
|
153
|
+
# @example When @id, @role and @title attributes are set.
|
154
|
+
# = block_with_title :class=>['quoteblock', 'center']
|
155
|
+
# blockquote =content
|
156
|
+
#
|
157
|
+
# <section id="myid" class="quoteblock center myrole1 myrole2">
|
158
|
+
# <h6>Block Title</h6>
|
159
|
+
# <blockquote>Lorem ipsum</blockquote>
|
160
|
+
# </section>
|
161
|
+
#
|
162
|
+
# @example When @id, @role and @title attributes are empty.
|
163
|
+
# = block_with_title :class=>'quoteblock center', :style=>style_value(float: 'left')
|
164
|
+
# blockquote =content
|
165
|
+
#
|
166
|
+
# <div class="quoteblock center" style="float: left;">
|
167
|
+
# <blockquote>Lorem ipsum</blockquote>
|
168
|
+
# </div>
|
169
|
+
#
|
170
|
+
# @example When shorthand style for class attribute is used.
|
171
|
+
# = block_with_title 'quoteblock center'
|
172
|
+
# blockquote =content
|
173
|
+
#
|
174
|
+
# <div class="quoteblock center">
|
175
|
+
# <blockquote>Lorem ipsum</blockquote>
|
176
|
+
# </div>
|
177
|
+
#
|
178
|
+
# @param attrs [Hash, String] the tag's attributes as Hash),
|
179
|
+
# or the tag's class if it's not a Hash.
|
180
|
+
# @param title [String, nil] the title.
|
181
|
+
# @yield The block of Slim/HTML code within the tag (optional).
|
182
|
+
# @return [String] a rendered HTML fragment.
|
183
|
+
#
|
184
|
+
def block_with_title(attrs = {}, title = @title, &block)
|
185
|
+
if (klass = attrs[:class]).is_a? String
|
186
|
+
klass = klass.split(' ')
|
187
|
+
end
|
188
|
+
attrs[:class] = [klass, role].flatten.uniq
|
189
|
+
attrs[:id] = id
|
190
|
+
|
191
|
+
if title.nil_or_empty?
|
192
|
+
# XXX quick hack
|
193
|
+
nested = is_a?(::Asciidoctor::List) &&
|
194
|
+
(parent.is_a?(::Asciidoctor::ListItem) || parent.is_a?(::Asciidoctor::List))
|
195
|
+
html_tag_if !nested, :div, attrs, yield
|
196
|
+
else
|
197
|
+
html_tag :section, attrs do
|
198
|
+
[html_tag(:h6, {}, title), yield].join("\n")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def block_with_caption(position = :bottom, attrs = {}, &block)
|
204
|
+
if (klass = attrs[:class]).is_a? String
|
205
|
+
klass = klass.split(' ')
|
206
|
+
end
|
207
|
+
attrs[:class] = [klass, role].flatten.uniq
|
208
|
+
attrs[:id] = id
|
209
|
+
|
210
|
+
if title.nil_or_empty?
|
211
|
+
html_tag :div, attrs, yield
|
212
|
+
else
|
213
|
+
html_tag :figure, attrs do
|
214
|
+
ary = [yield, html_tag(:figcaption) { captioned_title }]
|
215
|
+
ary.reverse! if position == :top
|
216
|
+
ary.compact.join("\n")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Delimite the given equation as a STEM of the specified type.
|
223
|
+
#
|
224
|
+
# @param equation [String] the equation to delimite.
|
225
|
+
# @param type [#to_sym] the type of the STEM renderer (latexmath, or asciimath).
|
226
|
+
# @return [String] the delimited equation.
|
227
|
+
#
|
228
|
+
def delimit_stem(equation, type)
|
229
|
+
if is_a? ::Asciidoctor::Block
|
230
|
+
open, close = ::Asciidoctor::BLOCK_MATH_DELIMITERS[type.to_sym]
|
231
|
+
else
|
232
|
+
open, close = ::Asciidoctor::INLINE_MATH_DELIMITERS[type.to_sym]
|
233
|
+
end
|
234
|
+
|
235
|
+
if !equation.start_with?(open) || !equation.end_with?(close)
|
236
|
+
equation = [open, equation, close].join
|
237
|
+
end
|
238
|
+
equation
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Formats the given hash as CSS declarations for an inline style.
|
243
|
+
#
|
244
|
+
# @example
|
245
|
+
# style_value(text_align: 'right', float: 'left')
|
246
|
+
# => "text-align: right; float: left;"
|
247
|
+
#
|
248
|
+
# style_value(text_align: nil, float: 'left')
|
249
|
+
# => "float: left;"
|
250
|
+
#
|
251
|
+
# style_value(width: [90, '%'], height: '50px')
|
252
|
+
# => "width: 90%; height: 50px;"
|
253
|
+
#
|
254
|
+
# style_value(width: ['120px', 'px'])
|
255
|
+
# => "width: 90px;"
|
256
|
+
#
|
257
|
+
# style_value(width: [nil, 'px'])
|
258
|
+
# => nil
|
259
|
+
#
|
260
|
+
# @param declarations [Hash]
|
261
|
+
# @return [String, nil]
|
262
|
+
#
|
263
|
+
def style_value(declarations)
|
264
|
+
decls = []
|
265
|
+
|
266
|
+
declarations.each do |prop, value|
|
267
|
+
next if value.nil?
|
268
|
+
|
269
|
+
if value.is_a? Array
|
270
|
+
value, unit = value
|
271
|
+
next if value.nil?
|
272
|
+
value = value.to_s + unit unless value.end_with? unit
|
273
|
+
end
|
274
|
+
prop = prop.to_s.gsub('_', '-')
|
275
|
+
decls << "#{prop}: #{value}"
|
276
|
+
end
|
277
|
+
|
278
|
+
decls.empty? ? nil : decls.join('; ') + ';'
|
279
|
+
end
|
280
|
+
|
281
|
+
def urlize(*segments)
|
282
|
+
path = segments * '/'
|
283
|
+
if path.start_with? '//'
|
284
|
+
@_uri_scheme ||= document.attr('asset-uri-scheme', 'https')
|
285
|
+
path = "#{@_uri_scheme}:#{path}" unless @_uri_scheme.empty?
|
286
|
+
end
|
287
|
+
normalize_web_path path
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
##
|
292
|
+
# Gets the value of the specified attribute in this node.
|
293
|
+
#
|
294
|
+
# This is just an alias for +attr+ method with disabled _inherit_ to make it
|
295
|
+
# more clear.
|
296
|
+
#
|
297
|
+
# @param name [String, Symbol] the name of the attribute to lookup.
|
298
|
+
# @param default_val the value to return if the attribute is not found.
|
299
|
+
# @return value of the attribute or +default_val+ if not found.
|
300
|
+
#
|
301
|
+
def local_attr(name, default_val = nil)
|
302
|
+
attr(name, default_val, false)
|
303
|
+
end
|
304
|
+
|
305
|
+
##
|
306
|
+
# Checks if the attribute is defined on this node, optionally performing
|
307
|
+
# a comparison of its value if +expect_val+ is not nil.
|
308
|
+
#
|
309
|
+
# This is just an alias for +attr?+ method with disabled _inherit_ to make it
|
310
|
+
# more clear.
|
311
|
+
#
|
312
|
+
# @param name [String, Symbol] the name of the attribute to lookup.
|
313
|
+
# @param default_val the expected value of the attribute.
|
314
|
+
# @return [Boolean] whether the attribute exists and, if +expect_val+ is
|
315
|
+
# specified, whether the value of the attribute matches the +expect_val+.
|
316
|
+
#
|
317
|
+
def local_attr?(name, expect_val = nil)
|
318
|
+
attr?(name, expect_val, false)
|
319
|
+
end
|
320
|
+
|
321
|
+
##
|
322
|
+
# @param index [Integer] the footnote's index.
|
323
|
+
# @return [String] footnote id to be used in a link.
|
324
|
+
def footnote_id(index = local_attr(:index))
|
325
|
+
"_footnote_#{index}"
|
326
|
+
end
|
327
|
+
|
328
|
+
##
|
329
|
+
# @param index (see #footnote_id)
|
330
|
+
# @return [String] footnoteref id to be used in a link.
|
331
|
+
def footnoteref_id(index = local_attr(:index))
|
332
|
+
"_footnoteref_#{index}"
|
333
|
+
end
|
334
|
+
|
335
|
+
def icons?
|
336
|
+
document.attr? :icons
|
337
|
+
end
|
338
|
+
|
339
|
+
def font_icons?
|
340
|
+
document.attr? :icons, 'font'
|
341
|
+
end
|
342
|
+
|
343
|
+
def nowrap?
|
344
|
+
'nowrap' if !document.attr?(:prewrap) || option?('nowrap')
|
345
|
+
end
|
346
|
+
|
347
|
+
def print_item_content(item)
|
348
|
+
wrap = item.blocks? && !item.blocks.all? { |b| b.is_a? ::Asciidoctor::List }
|
349
|
+
[ (html_tag_if(wrap, :p) { item.text } if item.text?), item.content ].join
|
350
|
+
end
|
351
|
+
|
352
|
+
##
|
353
|
+
# Returns corrected section level.
|
354
|
+
#
|
355
|
+
# @param sec [Asciidoctor::Section] the section node (default: self).
|
356
|
+
# @return [Integer]
|
357
|
+
#
|
358
|
+
def section_level(sec = self)
|
359
|
+
@_section_level ||= (sec.level == 0 && sec.special) ? 1 : sec.level
|
360
|
+
end
|
361
|
+
|
362
|
+
##
|
363
|
+
# Returns the captioned section's title, optionally numbered.
|
364
|
+
#
|
365
|
+
# @param sec [Asciidoctor::Section] the section node (default: self).
|
366
|
+
# @return [String]
|
367
|
+
#
|
368
|
+
def section_title(sec = self)
|
369
|
+
sectnumlevels = document.attr(:sectnumlevels, DEFAULT_SECTNUMLEVELS).to_i
|
370
|
+
|
371
|
+
if sec.numbered && !sec.caption && sec.level <= sectnumlevels
|
372
|
+
[sec.sectnum, sec.captioned_title].join(' ')
|
373
|
+
else
|
374
|
+
sec.captioned_title
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def link_rel
|
379
|
+
'noopener' if option?('noopener') || attr(:window) == '_blank'
|
380
|
+
end
|
381
|
+
|
382
|
+
#--------------------------------------------------------
|
383
|
+
# block_admonition
|
384
|
+
#
|
385
|
+
|
386
|
+
##
|
387
|
+
# @return [Boolean] should be this admonition wrapped in aside element?
|
388
|
+
def admonition_aside?
|
389
|
+
%w[note tip].include? attr(:name)
|
390
|
+
end
|
391
|
+
|
392
|
+
##
|
393
|
+
# @return [String, nil] WAI-ARIA role of this admonition.
|
394
|
+
def admonition_aria
|
395
|
+
case attr(:name)
|
396
|
+
when 'note'
|
397
|
+
'note' # https://www.w3.org/TR/wai-aria/roles#note
|
398
|
+
when 'tip'
|
399
|
+
'doc-tip' # https://www.w3.org/TR/dpub-aria-1.0/#doc-tip
|
400
|
+
when 'caution', 'important', 'warning'
|
401
|
+
'doc-notice' # https://www.w3.org/TR/dpub-aria-1.0/#doc-notice
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
#--------------------------------------------------------
|
406
|
+
# block_listing
|
407
|
+
#
|
408
|
+
|
409
|
+
##
|
410
|
+
# @return [String] a canonical name of the source-highlighter to be used as
|
411
|
+
# a style class.
|
412
|
+
def highlighter
|
413
|
+
@_highlighter ||=
|
414
|
+
case (highlighter = document.attr('source-highlighter'))
|
415
|
+
when 'coderay'; 'CodeRay'
|
416
|
+
when 'highlight.js'; 'highlightjs'
|
417
|
+
else highlighter
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
##
|
422
|
+
# Returns the callout list attached to this listing node, or +nil+ if none.
|
423
|
+
#
|
424
|
+
# Note: This variable is set by extension
|
425
|
+
# {Asciidoctor::Html5s::AttachedColistTreeprocessor}.
|
426
|
+
#
|
427
|
+
# @return [Asciidoctor::List, nil]
|
428
|
+
def callout_list
|
429
|
+
@html5s_colist
|
430
|
+
end
|
431
|
+
|
432
|
+
def source_lang
|
433
|
+
local_attr :language, false
|
434
|
+
end
|
435
|
+
|
436
|
+
#--------------------------------------------------------
|
437
|
+
# block_open
|
438
|
+
#
|
439
|
+
|
440
|
+
##
|
441
|
+
# Returns +true+ if an abstract block is allowed in this document type,
|
442
|
+
# otherwise prints warning and returns +false+.
|
443
|
+
def abstract_allowed?
|
444
|
+
if result = (parent == document && document.doctype == 'book')
|
445
|
+
puts 'asciidoctor: WARNING: abstract block cannot be used in a document
|
446
|
+
without a title when doctype is book. Excluding block content.'
|
447
|
+
end
|
448
|
+
!result
|
449
|
+
end
|
450
|
+
|
451
|
+
##
|
452
|
+
# Returns +true+ if a partintro block is allowed in this context, otherwise
|
453
|
+
# prints warning and returns +false+.
|
454
|
+
def partintro_allowed?
|
455
|
+
if result = (level != 0 || parent.context != :section || document.doctype != 'book')
|
456
|
+
puts "asciidoctor: ERROR: partintro block can only be used when doctype
|
457
|
+
is book and it's a child of a book part. Excluding block content."
|
458
|
+
end
|
459
|
+
!result
|
460
|
+
end
|
461
|
+
|
462
|
+
#--------------------------------------------------------
|
463
|
+
# block_table
|
464
|
+
#
|
465
|
+
|
466
|
+
def autowidth?
|
467
|
+
option? :autowidth
|
468
|
+
end
|
469
|
+
|
470
|
+
def spread?
|
471
|
+
if !autowidth? || local_attr?('width')
|
472
|
+
'spread' if attr? :tablepcwidth, 100
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
#--------------------------------------------------------
|
477
|
+
# block_video
|
478
|
+
#
|
479
|
+
|
480
|
+
# @return [Boolean] +true+ if the video should be embedded in an iframe.
|
481
|
+
def video_iframe?
|
482
|
+
['vimeo', 'youtube'].include? attr(:poster)
|
483
|
+
end
|
484
|
+
|
485
|
+
def video_uri
|
486
|
+
case attr(:poster, '').to_sym
|
487
|
+
when :vimeo
|
488
|
+
params = {
|
489
|
+
autoplay: (1 if option? 'autoplay'),
|
490
|
+
loop: (1 if option? 'loop')
|
491
|
+
}
|
492
|
+
start_anchor = "#at=#{attr :start}" if attr? :start
|
493
|
+
"//player.vimeo.com/video/#{attr :target}#{start_anchor}#{url_query params}"
|
494
|
+
|
495
|
+
when :youtube
|
496
|
+
video_id, list_id = attr(:target).split('/', 2)
|
497
|
+
params = {
|
498
|
+
rel: 0,
|
499
|
+
start: (attr :start),
|
500
|
+
end: (attr :end),
|
501
|
+
list: (attr :list, list_id),
|
502
|
+
autoplay: (1 if option? 'autoplay'),
|
503
|
+
loop: (1 if option? 'loop'),
|
504
|
+
controls: (0 if option? 'nocontrols')
|
505
|
+
}
|
506
|
+
"//www.youtube.com/embed/#{video_id}#{url_query params}"
|
507
|
+
else
|
508
|
+
anchor = [attr(:start), attr(:end)].join(',').chomp(',')
|
509
|
+
anchor.prepend('#t=') unless anchor.empty?
|
510
|
+
media_uri "#{attr :target}#{anchor}"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
# Formats URL query parameters.
|
515
|
+
def url_query(params)
|
516
|
+
str = params.map { |k, v|
|
517
|
+
next if v.nil? || v.to_s.empty?
|
518
|
+
[k, v] * '='
|
519
|
+
}.compact.join('&')
|
520
|
+
|
521
|
+
str.prepend('?') unless str.empty?
|
522
|
+
end
|
523
|
+
|
524
|
+
#--------------------------------------------------------
|
525
|
+
# document
|
526
|
+
#
|
527
|
+
|
528
|
+
##
|
529
|
+
# @return [String, nil] the revision date in ISO 8601, or nil if not
|
530
|
+
# available or in invalid format.
|
531
|
+
def revdate_iso
|
532
|
+
::Date.parse(revdate).iso8601
|
533
|
+
rescue ArgumentError
|
534
|
+
nil
|
535
|
+
end
|
536
|
+
|
537
|
+
##
|
538
|
+
# Returns HTML meta tag if the given +content+ is not +nil+.
|
539
|
+
#
|
540
|
+
# @param name [#to_s] the name for the metadata.
|
541
|
+
# @param content [#to_s, nil] the value of the metadata, or +nil+.
|
542
|
+
# @return [String, nil] the meta tag, or +nil+ if the +content+ is +nil+.
|
543
|
+
#
|
544
|
+
def html_meta_if(name, content)
|
545
|
+
%(<meta name="#{name}" content="#{content}">) if content
|
546
|
+
end
|
547
|
+
|
548
|
+
# Returns formatted style/link and script tags for header.
|
549
|
+
def styles_and_scripts
|
550
|
+
scripts = []
|
551
|
+
styles = []
|
552
|
+
tags = []
|
553
|
+
|
554
|
+
stylesheet = attr :stylesheet
|
555
|
+
stylesdir = attr :stylesdir, ''
|
556
|
+
default_style = ::Asciidoctor::DEFAULT_STYLESHEET_KEYS.include? stylesheet
|
557
|
+
linkcss = attr?(:linkcss) || safe >= ::Asciidoctor::SafeMode::SECURE
|
558
|
+
ss = ::Asciidoctor::Stylesheets.instance
|
559
|
+
|
560
|
+
if linkcss
|
561
|
+
path = default_style ? ::Asciidoctor::DEFAULT_STYLESHEET_NAME : stylesheet
|
562
|
+
styles << { href: [stylesdir, path] }
|
563
|
+
elsif default_style
|
564
|
+
styles << { text: ss.primary_stylesheet_data }
|
565
|
+
else
|
566
|
+
styles << { text: read_asset(normalize_system_path(stylesheet, stylesdir), true) }
|
567
|
+
end
|
568
|
+
|
569
|
+
if attr? :icons, 'font'
|
570
|
+
if attr? 'iconfont-remote'
|
571
|
+
styles << { href: attr('iconfont-cdn', FONT_AWESOME_URI) }
|
572
|
+
else
|
573
|
+
styles << { href: [stylesdir, "#{attr 'iconfont-name', 'font-awesome'}.css"] }
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
if attr? 'stem'
|
578
|
+
scripts << { src: MATHJAX_JS_URI }
|
579
|
+
scripts << { type: 'text/x-mathjax-config', text: "MathJax.Hub.Config(#{MATHJAX_CONFIG});" }
|
580
|
+
end
|
581
|
+
|
582
|
+
case attr 'source-highlighter'
|
583
|
+
when 'coderay'
|
584
|
+
if attr('coderay-css', 'class') == 'class'
|
585
|
+
if linkcss
|
586
|
+
styles << { href: [stylesdir, ss.coderay_stylesheet_name] }
|
587
|
+
else
|
588
|
+
styles << { text: ss.coderay_stylesheet_data }
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
when 'highlightjs'
|
593
|
+
hjs_base = attr :highlightjsdir, HIGHLIGHTJS_BASE_URI
|
594
|
+
hjs_theme = attr 'highlightjs-theme', DEFAULT_HIGHLIGHTJS_THEME
|
595
|
+
|
596
|
+
scripts << { src: [hjs_base, 'highlight.min.js'] }
|
597
|
+
scripts << { src: [hjs_base, 'lang/common.min.js'] }
|
598
|
+
scripts << { text: 'hljs.initHighlightingOnLoad()' }
|
599
|
+
styles << { href: [hjs_base, "styles/#{hjs_theme}.min.css"] }
|
600
|
+
end
|
601
|
+
|
602
|
+
styles.each do |item|
|
603
|
+
if item.key?(:text)
|
604
|
+
tags << html_tag(:style) { item[:text] }
|
605
|
+
else
|
606
|
+
tags << html_tag(:link, rel: 'stylesheet', href: urlize(*item[:href]))
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
scripts.each do |item|
|
611
|
+
if item.key? :text
|
612
|
+
tags << html_tag(:script, type: item[:type]) { item[:text] }
|
613
|
+
else
|
614
|
+
tags << html_tag(:script, type: item[:type], src: urlize(*item[:src]))
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
tags.join("\n")
|
619
|
+
end
|
620
|
+
|
621
|
+
#--------------------------------------------------------
|
622
|
+
# inline_anchor
|
623
|
+
#
|
624
|
+
|
625
|
+
# @return [String] text of the xref anchor.
|
626
|
+
def xref_text
|
627
|
+
str =
|
628
|
+
if text
|
629
|
+
text
|
630
|
+
elsif (path = local_attr :path)
|
631
|
+
path
|
632
|
+
elsif document.respond_to? :catalog # Asciidoctor >=1.5.6
|
633
|
+
ref = document.catalog[:refs][attr :refid]
|
634
|
+
if ref.kind_of? Asciidoctor::AbstractNode
|
635
|
+
ref.xreftext((@_xrefstyle ||= document.attributes['xrefstyle']))
|
636
|
+
end
|
637
|
+
else # Asciidoctor < 1.5.6
|
638
|
+
document.references[:ids][attr :refid || target]
|
639
|
+
end
|
640
|
+
(str || "[#{attr :refid}]").tr_s("\n", ' ')
|
641
|
+
end
|
642
|
+
|
643
|
+
# @return [String, nil] text of the bibref anchor, or +nil+ if not found.
|
644
|
+
def bibref_text
|
645
|
+
if document.respond_to? :catalog # Asciidoctor >=1.5.6
|
646
|
+
# NOTE: Technically it should be `reftext`, but subs have already been applied to text.
|
647
|
+
text
|
648
|
+
else
|
649
|
+
"[#{target}]"
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
#--------------------------------------------------------
|
654
|
+
# inline_image
|
655
|
+
#
|
656
|
+
|
657
|
+
# @return [Array] style classes for a Font Awesome icon.
|
658
|
+
def icon_fa_classes
|
659
|
+
[ "fa fa-#{target}",
|
660
|
+
("fa-#{attr :size}" if attr? :size),
|
661
|
+
("fa-rotate-#{attr :rotate}" if attr? :rotate),
|
662
|
+
("fa-flip-#{attr :flip}" if attr? :flip)
|
663
|
+
].compact
|
664
|
+
end
|
665
|
+
end
|