asciidoctor-revealjs 3.0.0 → 4.1.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.adoc +1 -1
  3. data/README.adoc +390 -63
  4. data/Rakefile +30 -7
  5. data/asciidoctor-revealjs.gemspec +6 -4
  6. data/bin/asciidoctor-revealjs +7 -0
  7. data/examples/a11y-dark.css +99 -0
  8. data/examples/autoslide.adoc +22 -0
  9. data/examples/custom-layout.adoc +10 -0
  10. data/examples/customcss.adoc +1 -1
  11. data/examples/data-attributes.adoc +245 -0
  12. data/examples/docinfo-footer-revealjs.html +10 -0
  13. data/examples/docinfo-revealjs.html +7 -0
  14. data/examples/font-awesome-specific-version.adoc +11 -0
  15. data/examples/font-awesome.adoc +23 -4
  16. data/examples/font-awesome.css +3 -0
  17. data/examples/footnotes.adoc +46 -0
  18. data/examples/fragments.adoc +44 -0
  19. data/examples/fragments.css +18 -0
  20. data/examples/grid-layout-3x2.adoc +50 -0
  21. data/examples/grid-layout-docinfo-revealjs.html +11 -0
  22. data/examples/grid-layout.adoc +194 -0
  23. data/examples/history-hash.adoc +19 -0
  24. data/examples/history-regression-tests.adoc +0 -5
  25. data/examples/history.adoc +4 -4
  26. data/examples/images/asciidoctor-logo.svg +102 -0
  27. data/examples/images/cute-cat-1.jpg +0 -0
  28. data/examples/images/cute-cat-2.jpg +0 -0
  29. data/examples/images/cute-cat-3.jpg +0 -0
  30. data/examples/images/flock-of-seagulls_daniel-simion.mp3 +0 -0
  31. data/examples/issue-grid-layout-images.adoc +25 -0
  32. data/examples/level-sectnums.adoc +24 -0
  33. data/examples/links-preview.adoc +32 -0
  34. data/examples/links.adoc +39 -0
  35. data/examples/mathjax-cdn.adoc +21 -0
  36. data/examples/mathjax.adoc +20 -0
  37. data/examples/release-4.0.adoc +192 -0
  38. data/examples/release-4.0.css +23 -0
  39. data/examples/release-4.1.adoc +133 -0
  40. data/examples/release-4.1.css +50 -0
  41. data/examples/revealjs-custom-theme.adoc +10 -0
  42. data/examples/source-coderay.adoc +15 -0
  43. data/examples/source-emphasis.adoc +128 -0
  44. data/examples/source-highlightjs-html.adoc +1 -1
  45. data/examples/source-highlightjs-languages.adoc +27 -0
  46. data/examples/source-highlightjs.adoc +85 -2
  47. data/examples/source-pygments.adoc +12 -0
  48. data/examples/source-rouge-docinfo.html +8 -0
  49. data/examples/source-rouge.adoc +18 -0
  50. data/examples/steps.adoc +87 -0
  51. data/examples/text-alignments.adoc +44 -0
  52. data/examples/video.adoc +30 -8
  53. data/examples/with-docinfo-shared.adoc +13 -0
  54. data/lib/asciidoctor-revealjs/converter.rb +1053 -769
  55. data/lib/asciidoctor-revealjs/highlightjs.rb +333 -2
  56. data/lib/asciidoctor-revealjs/version.rb +1 -1
  57. data/templates/admonition.html.slim +1 -1
  58. data/templates/asciidoctor-compatibility.css +390 -0
  59. data/templates/audio.html.slim +1 -1
  60. data/templates/colist.html.slim +1 -1
  61. data/templates/dlist.html.slim +3 -3
  62. data/templates/document.html.slim +76 -59
  63. data/templates/example.html.slim +1 -1
  64. data/templates/helpers.rb +170 -5
  65. data/templates/image.html.slim +3 -3
  66. data/templates/inline_anchor.html.slim +6 -4
  67. data/templates/inline_button.html.slim +2 -1
  68. data/templates/inline_footnote.html.slim +11 -4
  69. data/templates/inline_image.html.slim +5 -8
  70. data/templates/inline_kbd.html.slim +3 -2
  71. data/templates/inline_menu.html.slim +4 -3
  72. data/templates/inline_quoted.html.slim +13 -21
  73. data/templates/listing.html.slim +15 -10
  74. data/templates/literal.html.slim +1 -1
  75. data/templates/olist.html.slim +2 -2
  76. data/templates/open.html.slim +3 -3
  77. data/templates/paragraph.html.slim +1 -1
  78. data/templates/quote.html.slim +1 -1
  79. data/templates/section.html.slim +51 -43
  80. data/templates/sidebar.html.slim +1 -1
  81. data/templates/stem.html.slim +1 -1
  82. data/templates/stretch_nested_elements.js.slim +65 -0
  83. data/templates/table.html.slim +3 -2
  84. data/templates/title_slide.html.slim +28 -0
  85. data/templates/ulist.html.slim +3 -3
  86. data/templates/verse.html.slim +1 -1
  87. data/templates/video.html.slim +14 -8
  88. metadata +81 -18
  89. data/CHANGELOG.adoc +0 -425
  90. data/HACKING.adoc +0 -386
  91. data/examples/revealjs-features.adoc +0 -23
  92. data/templates/asciidoctor_revealjs.css.slim +0 -59
@@ -1,4 +1,4 @@
1
- .audioblock id=@id class=[@style,role]
1
+ = html_tag('div', { :id => @id, :class => ['audioblock', @style, role] }.merge(data_attrs(@attributes)))
2
2
  - if title?
3
3
  .title=captioned_title
4
4
  .content
@@ -1,4 +1,4 @@
1
- .colist id=@id class=[@style,role]
1
+ = html_tag('div', { :id => @id, :class => ['colist', @style, role, ('fragment' if (option? :step) || (attr? 'step'))] }.merge(data_attrs(@attributes)))
2
2
  - if title?
3
3
  .title=title
4
4
  - if @document.attr? :icons
@@ -1,6 +1,6 @@
1
1
  - case @style
2
2
  - when 'qanda'
3
- .qlist id=@id class=['qanda',role]
3
+ = html_tag('div', { :id => @id, :class => ['qlist', @style, role] }.merge(data_attrs(@attributes)))
4
4
  - if title?
5
5
  .title=title
6
6
  ol
@@ -14,7 +14,7 @@
14
14
  - if answer.blocks?
15
15
  =answer.content
16
16
  - when 'horizontal'
17
- .hdlist id=@id class=role
17
+ = html_tag('div', { :id => @id, :class => ['hdlist', role] }.merge(data_attrs(@attributes)))
18
18
  - if title?
19
19
  .title=title
20
20
  table
@@ -38,7 +38,7 @@
38
38
  - if dd.blocks?
39
39
  =dd.content
40
40
  - else
41
- .dlist id=@id class=[@style,role]
41
+ = html_tag('div', { :id => @id, :class => ['dlist', @style, role] }.merge(data_attrs(@attributes)))
42
42
  - if title?
43
43
  .title=title
44
44
  dl
@@ -2,6 +2,10 @@ doctype 5
2
2
  html lang=(attr :lang, 'en' unless attr? :nolang)
3
3
  head
4
4
  meta charset="utf-8"
5
+ meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui"
6
+
7
+ title=(doctitle sanitize: true, use_fallback: true)
8
+
5
9
  - if RUBY_ENGINE == 'opal' && JAVASCRIPT_PLATFORM == 'node'
6
10
  - revealjsdir = (attr :revealjsdir, 'node_modules/reveal.js')
7
11
  - else
@@ -13,27 +17,31 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
13
17
  - if attr? key
14
18
  meta name=key content=(attr key)
15
19
  - linkcss = (attr? 'linkcss')
16
- title=(doctitle sanitize: true, use_fallback: true)
17
- meta content="yes" name="apple-mobile-web-app-capable"
18
- meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"
19
- meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui" name="viewport"
20
- link href="#{revealjsdir}/css/reveal.css" rel="stylesheet"
20
+ link rel="stylesheet" href="#{revealjsdir}/css/reset.css"
21
+ link rel="stylesheet" href="#{revealjsdir}/css/reveal.css"
22
+
21
23
  / Default theme required even when using custom theme
22
- - if attr? :revealjs_customtheme
23
- link rel='stylesheet' href=(attr :revealjs_customtheme) id='theme'
24
- - else
25
- link rel='stylesheet' href='#{revealjsdir}/css/theme/#{attr 'revealjs_theme', 'black'}.css' id='theme'
26
- include asciidoctor_revealjs.css.slim
24
+ link rel='stylesheet' href=(attr :revealjs_customtheme, %(#{revealjsdir}/css/theme/#{attr 'revealjs_theme', 'black'}.css)) id='theme'
25
+ /! This CSS is generated by the Asciidoctor reveal.js converter to further integrate AsciiDoc's existing semantic with reveal.js
26
+ style type="text/css"
27
+ include asciidoctor-compatibility.css
27
28
  - if attr? :icons, 'font'
28
29
  / iconfont-remote is implicitly set by Asciidoctor core. See https://github.com/asciidoctor/asciidoctor.org/issues/361
29
30
  - if attr? 'iconfont-remote'
30
- link rel='stylesheet' href=(attr 'iconfont-cdn', %(#{cdn_base}/font-awesome/5.8.2/css/all.min.css))
31
+ - if (iconfont_cdn = (attr 'iconfont-cdn'))
32
+ link rel='stylesheet' href=iconfont_cdn
33
+ - else
34
+ / default icon font is Font Awesome
35
+ - font_awesome_version = (attr 'font-awesome-version', '5.15.1')
36
+ link rel='stylesheet' href=%(#{cdn_base}/font-awesome/#{font_awesome_version}/css/all.min.css)
37
+ link rel='stylesheet' href=%(#{cdn_base}/font-awesome/#{font_awesome_version}/css/v4-shims.min.css)
31
38
  - else
32
39
  link rel='stylesheet' href=(normalize_web_path %(#{attr 'iconfont-name', 'font-awesome'}.css), (attr 'stylesdir', ''), false)
33
40
  - if attr? :stem
34
41
  - eqnums_val = (attr 'eqnums', 'none')
35
42
  - eqnums_val = 'AMS' if eqnums_val == ''
36
43
  - eqnums_opt = %( equationNumbers: { autoNumber: "#{eqnums_val}" } )
44
+ - mathjaxdir = (attr 'mathjaxdir', "#{cdn_base}/mathjax/2.7.6")
37
45
  script type='text/x-mathjax-config'
38
46
  | MathJax.Hub.Config({
39
47
  tex2jax: {
@@ -47,60 +55,38 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
47
55
  },
48
56
  TeX: {#{eqnums_opt}}
49
57
  });
50
- script src='#{cdn_base}/mathjax/2.4.0/MathJax.js?config=TeX-MML-AM_HTMLorMML'
58
+ script src='#{mathjaxdir}/MathJax.js?config=TeX-MML-AM_HTMLorMML'
51
59
 
52
60
  - syntax_hl = self.syntax_highlighter
53
61
  - if syntax_hl && (syntax_hl.docinfo? :head)
54
62
  =syntax_hl.docinfo :head, self, cdn_base_url: cdn_base, linkcss: linkcss, self_closing_tag_slash: '/'
55
- / If the query includes 'print-pdf', use the PDF print sheet
63
+
64
+ /! Printing and PDF exports
56
65
  javascript:
57
66
  var link = document.createElement( 'link' );
58
67
  link.rel = 'stylesheet';
59
68
  link.type = 'text/css';
60
69
  link.href = window.location.search.match( /print-pdf/gi ) ? "#{revealjsdir}/css/print/pdf.css" : "#{revealjsdir}/css/print/paper.css";
61
70
  document.getElementsByTagName( 'head' )[0].appendChild( link );
62
- /[if lt IE 9]
63
- <script src="#{revealjsdir}/lib/js/html5shiv.js"></script>
64
- - unless (docinfo_content = docinfo :header, '.html').empty?
65
- =docinfo_content
71
+
66
72
  - if attr? :customcss
67
73
  link rel='stylesheet' href=((customcss = attr :customcss).empty? ? 'asciidoctor-revealjs.css' : customcss)
74
+ - unless (_docinfo = docinfo :head, '-revealjs.html').empty?
75
+ =_docinfo
68
76
  body
69
77
  .reveal
70
78
  / Any section element inside of this container is displayed as a slide
71
79
  .slides
72
- - unless notitle || !has_header?
73
- - bg_image = (attr? 'title-slide-background-image') ? (image_uri(attr 'title-slide-background-image')) : nil
74
- - bg_video = (attr? 'title-slide-background-video') ? (media_uri(attr 'title-slide-background-video')) : nil
75
- section.title(class = role
76
- data-state='title'
77
- data-transition=(attr 'title-slide-transition')
78
- data-transition-speed=(attr 'title-slide-transition-speed')
79
- data-background=(attr 'title-slide-background')
80
- data-background-size=(attr 'title-slide-background-size')
81
- data-background-image=bg_image
82
- data-background-video=bg_video
83
- data-background-video-loop=(attr 'title-slide-background-video-loop')
84
- data-background-video-muted=(attr 'title-slide-background-video-muted')
85
- data-background-opacity=(attr "background-opacity")
86
- data-background-iframe=(attr 'title-slide-background-iframe')
87
- data-background-color=(attr 'title-slide-background-color')
88
- data-background-repeat=(attr 'title-slide-background-repeat')
89
- data-background-position=(attr 'title-slide-background-position')
90
- data-background-transition=(attr 'title-slide-background-transition'))
91
- - if (_title_obj = doctitle partition: true, use_fallback: true).subtitle?
92
- h1=slice_text _title_obj.title, (_slice = header.option? :slice)
93
- h2=slice_text _title_obj.subtitle, _slice
94
- - else
95
- h1=@header.title
96
- - preamble = @document.find_by context: :preamble
97
- - unless preamble.nil? or preamble.length == 0
98
- div.preamble=preamble.pop.content
99
- - unless author.nil?
100
- p.author: small=author
101
- =content
102
- script src="#{revealjsdir}/lib/js/head.min.js"
80
+ - unless noheader
81
+ - unless (_docinfo = docinfo :header, '-revealjs.html').empty?
82
+ = _docinfo
83
+ - if header?
84
+ include title_slide.html.slim
85
+ = content
86
+ - unless (_docinfo = docinfo :footer, '-revealjs.html').empty?
87
+ = _docinfo
103
88
  script src="#{revealjsdir}/js/reveal.js"
89
+ / Supports easy AsciiDoc syntax for background color
104
90
  javascript:
105
91
  Array.prototype.slice.call(document.querySelectorAll('.slides section')).forEach(function(slide) {
106
92
  if (slide.getAttribute('data-background-color')) return;
@@ -111,9 +97,11 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
111
97
  slide.setAttribute('data-background-color', bgColor);
112
98
  slide.style.backgroundColor = 'transparent';
113
99
  }
114
- })
100
+ });
115
101
 
116
- // See https://github.com/hakimel/reveal.js#configuration for a full list of configuration options
102
+ // More info about config & dependencies:
103
+ // - https://github.com/hakimel/reveal.js#configuration
104
+ // - https://github.com/hakimel/reveal.js#dependencies
117
105
  Reveal.initialize({
118
106
  // Display presentation control arrows
119
107
  controls: #{to_boolean(attr 'revealjs_controls', true)},
@@ -131,12 +119,17 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
131
119
  slideNumber: #{to_valid_slidenumber(attr 'revealjs_slidenumber', false)},
132
120
  // Control which views the slide number displays on
133
121
  showSlideNumber: '#{attr 'revealjs_showslidenumber', 'all'}',
134
- // Push each slide change to the browser history
122
+ // Add the current slide number to the URL hash so that reloading the
123
+ // page/copying the URL will return you to the same slide
124
+ hash: #{to_boolean(attr 'revealjs_hash', false)},
125
+ // Push each slide change to the browser history. Implies `hash: true`
135
126
  history: #{to_boolean(attr 'revealjs_history', false)},
136
127
  // Enable keyboard shortcuts for navigation
137
128
  keyboard: #{to_boolean(attr 'revealjs_keyboard', true)},
138
129
  // Enable the slide overview mode
139
130
  overview: #{to_boolean(attr 'revealjs_overview', true)},
131
+ // Disables the default reveal.js slide layout so that you can use custom CSS layout
132
+ disableLayout: #{to_boolean(attr 'revealjs_disablelayout', false)},
140
133
  // Vertical centering of slides
141
134
  center: #{to_boolean(attr 'revealjs_center', true)},
142
135
  // Enables touch navigation on devices with touch input
@@ -145,6 +138,8 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
145
138
  loop: #{to_boolean(attr 'revealjs_loop', false)},
146
139
  // Change the presentation direction to be RTL
147
140
  rtl: #{to_boolean(attr 'revealjs_rtl', false)},
141
+ // See https://github.com/hakimel/reveal.js/#navigation-mode
142
+ navigationMode: '#{attr 'revealjs_navigationmode', 'default'}',
148
143
  // Randomizes the order of slides each time the presentation loads
149
144
  shuffle: #{to_boolean(attr 'revealjs_shuffle', false)},
150
145
  // Turns fragments on and off globally
@@ -165,6 +160,12 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
165
160
  // - true: All media will autoplay, regardless of individual setting
166
161
  // - false: No media will autoplay, regardless of individual setting
167
162
  autoPlayMedia: #{attr 'revealjs_autoplaymedia', 'null'},
163
+ // Global override for preloading lazy-loaded iframes
164
+ // - null: Iframes with data-src AND data-preload will be loaded when within
165
+ // the viewDistance, iframes with only data-src will be loaded when visible
166
+ // - true: All iframes with data-src will be loaded when within the viewDistance
167
+ // - false: All iframes with data-src will be loaded only when visible
168
+ preloadIframes: #{attr 'revealjs_preloadiframes', 'null'},
168
169
  // Number of milliseconds between automatically proceeding to the
169
170
  // next slide, disabled when set to 0, this value can be overwritten
170
171
  // by using a data-autoslide attribute on your slides
@@ -177,8 +178,23 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
177
178
  // presenting each slide. This is used to show a pacing timer in the
178
179
  // speaker view
179
180
  defaultTiming: #{attr 'revealjs_defaulttiming', 120},
181
+ // Specify the total time in seconds that is available to
182
+ // present. If this is set to a nonzero value, the pacing
183
+ // timer will work out the time available for each slide,
184
+ // instead of using the defaultTiming value
185
+ totalTime: #{attr 'revealjs_totaltime', 0},
186
+ // Specify the minimum amount of time you want to allot to
187
+ // each slide, if using the totalTime calculation method. If
188
+ // the automated time allocation causes slide pacing to fall
189
+ // below this threshold, then you will see an alert in the
190
+ // speaker notes window
191
+ minimumTimePerSlide: #{attr 'revealjs_minimumtimeperslide', 0},
180
192
  // Enable slide navigation via mouse wheel
181
193
  mouseWheel: #{to_boolean(attr 'revealjs_mousewheel', false)},
194
+ // Hide cursor if inactive
195
+ hideInactiveCursor: #{to_boolean(attr 'revealjs_hideinactivecursor', true)},
196
+ // Time before the cursor is hidden (in ms)
197
+ hideCursorTime: #{attr 'revealjs_hidecursortime', 5000},
182
198
  // Hides the address bar on mobile devices
183
199
  hideAddressBar: #{to_boolean(attr 'revealjs_hideaddressbar', true)},
184
200
  // Opens links in an iframe preview overlay
@@ -193,6 +209,10 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
193
209
  backgroundTransition: '#{attr 'revealjs_backgroundtransition', 'fade'}',
194
210
  // Number of slides away from the current that are visible
195
211
  viewDistance: #{attr 'revealjs_viewdistance', 3},
212
+ // Number of slides away from the current that are visible on mobile
213
+ // devices. It is advisable to set this to a lower number than
214
+ // viewDistance in order to save resources.
215
+ mobileViewDistance: #{attr 'revealjs_mobileviewdistance', 3},
196
216
  // Parallax background image (e.g., "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'")
197
217
  parallaxBackgroundImage: '#{attr 'revealjs_parallaxbackgroundimage', ''}',
198
218
  // Parallax background size in CSS syntax (e.g., "2100px 900px")
@@ -226,19 +246,16 @@ html lang=(attr :lang, 'en' unless attr? :nolang)
226
246
 
227
247
  // Optional libraries used to extend on reveal.js
228
248
  dependencies: [
229
- { src: '#{revealjsdir}/lib/js/classList.js', condition: function() { return !document.body.classList; } },
230
- #{(document.attr? 'source-highlighter', 'highlightjs') ? "{ src: '#{revealjsdir}/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }," : nil}
231
- #{(attr? 'revealjs_plugin_zoom', 'disabled') ? "" : "{ src: '#{revealjsdir}/plugin/zoom-js/zoom.js', async: true }," }
232
- #{(attr? 'revealjs_plugin_notes', 'disabled') ? "" : "{ src: '#{revealjsdir}/plugin/notes/notes.js', async: true }," }
233
- #{(attr? 'revealjs_plugin_marked', 'enabled') ? "{ src: '#{revealjsdir}/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }," : "" }
234
- #{(attr? 'revealjs_plugin_markdown', 'enabled') ? "{ src: '#{revealjsdir}/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }," : "" }
235
- #{(attr? 'revealjs_plugin_pdf', 'enabled') ? "{ src: '#{revealjsdir}/plugin/print-pdf/print-pdf.js', async: true }," : "" }
236
- #{(attr? 'revealjs_plugins') ? File.read(attr('revealjs_plugins', '')) : ""}
249
+ #{revealjs_dependencies(document, self, revealjsdir)}
237
250
  ],
238
251
 
239
252
  #{(attr? 'revealjs_plugins_configuration') ? File.read(attr('revealjs_plugins_configuration', '')) : ""}
240
253
 
241
254
  });
255
+ / Workaround the "Only direct descendants of a slide section can be stretched" limitation in reveal.js
256
+ / https://github.com/hakimel/reveal.js/issues/2584
257
+ include stretch_nested_elements.js.slim
258
+
242
259
  - if syntax_hl && (syntax_hl.docinfo? :footer)
243
260
  =syntax_hl.docinfo :footer, self, cdn_base_url: cdn_base, linkcss: linkcss, self_closing_tag_slash: '/'
244
261
 
@@ -1,4 +1,4 @@
1
- .exampleblock id=@id class=role
1
+ = html_tag('div', { :id => @id, :class => ['exampleblock', role, ('fragment' if (option? :step) || (attr? 'step'))] }.merge(data_attrs(@attributes)))
2
2
  - if title?
3
3
  .title=captioned_title
4
4
  .content=content
@@ -24,6 +24,23 @@ module Slim::Helpers
24
24
  val && val != 'false' && val.to_s != '0' || false
25
25
  end
26
26
 
27
+ # bool_data_attr
28
+ # If the AsciiDoc attribute doesn't exist, no HTML attribute is added
29
+ # If the AsciiDoc attribute exist and is a true value, HTML attribute is enabled (bool)
30
+ # If the AsciiDoc attribute exist and is a false value, HTML attribute is a false string
31
+ # Ex: a feature is enabled globally but can be disabled using a data- attribute on individual items
32
+ # :revealjs_previewlinks: True
33
+ # then link::example.com[Link text, preview=false]
34
+ # Here the template must have data-preview-link="false" not just no data-preview-link attribute
35
+ def bool_data_attr val
36
+ return false unless attr?(val)
37
+ if attr(val).downcase == 'false' || attr(val) == '0'
38
+ 'false'
39
+ else
40
+ true
41
+ end
42
+ end
43
+
27
44
  # false needs to be verbatim everything else is a string.
28
45
  # Calling side isn't responsible for quoting so we are doing it here
29
46
  def to_valid_slidenumber val
@@ -55,25 +72,52 @@ module Slim::Helpers
55
72
  # @yield The block of Slim/HTML code within the tag (optional).
56
73
  # @return [String] a rendered HTML element.
57
74
  #
58
-
59
75
  def html_tag(name, attributes = {}, content = nil)
60
76
  attrs = attributes.inject([]) do |attrs, (k, v)|
61
- next attrs if !v || v.nil_or_empty?
77
+ next attrs unless v && (v == true || !v.nil_or_empty?)
62
78
  v = v.compact.join(' ') if v.is_a? Array
63
79
  attrs << (v == true ? k : %(#{k}="#{v}"))
64
80
  end
65
81
  attrs_str = attrs.empty? ? '' : ' ' + attrs.join(' ')
66
82
 
67
-
68
83
  if VOID_ELEMENTS.include? name.to_s
69
84
  %(<#{name}#{attrs_str}>)
70
85
  else
71
- content ||= yield if block_given?
86
+ content ||= (yield if block_given?)
72
87
  %(<#{name}#{attrs_str}>#{content}</#{name}>)
73
88
  end
74
89
  end
75
90
 
76
91
 
92
+ #
93
+ # Extracts data- attributes from the attributes.
94
+ # @param attributes [Hash] (default: {})
95
+ # @return [Hash] a Hash that contains only data- attributes
96
+ #
97
+ def data_attrs(attributes)
98
+ # key can be an Integer (for positional attributes)
99
+ attributes.map { |key, value| (key == 'step') ? ['data-fragment-index', value] : [key, value] }
100
+ .to_h
101
+ .select { |key, _| key.to_s.start_with?('data-') }
102
+ end
103
+
104
+
105
+ #
106
+ # Wrap an inline text in a <span> element if the node contains a role, an id or data- attributes.
107
+ # @param content [#to_s] the content; +nil+ to call the block. (default: nil).
108
+ # @return [String] the content or the content wrapped in a <span> element as string
109
+ #
110
+ def inline_text_container(content = nil)
111
+ data_attrs = data_attrs(@attributes)
112
+ classes = [role, ('fragment' if (option? :step) || (attr? 'step') || (roles.include? 'step'))].compact
113
+ if !roles.empty? || !data_attrs.empty? || !@id.nil?
114
+ html_tag('span', { :id => @id, :class => classes }.merge(data_attrs), (content || (yield if block_given?)))
115
+ else
116
+ content || (yield if block_given?)
117
+ end
118
+ end
119
+
120
+
77
121
  ##
78
122
  # Returns corrected section level.
79
123
  #
@@ -84,6 +128,52 @@ module Slim::Helpers
84
128
  @_section_level ||= (sec.level == 0 && sec.special) ? 1 : sec.level
85
129
  end
86
130
 
131
+ ##
132
+ # Display footnotes per slide
133
+ #
134
+ @@slide_footnotes = {}
135
+ @@section_footnotes = {}
136
+
137
+ def slide_footnote(footnote)
138
+ footnote_parent = footnote.parent
139
+ # footnotes declared on the section title are processed during the parsing/substitution.
140
+ # as a result, we need to store them to display them on the right slide/section
141
+ if footnote_parent.instance_of?(::Asciidoctor::Section)
142
+ footnote_parent_object_id = footnote_parent.object_id
143
+ section_footnotes = (@@section_footnotes[footnote_parent_object_id] || [])
144
+ footnote_index = section_footnotes.length + 1
145
+ attributes = footnote.attributes.merge({ 'index' => footnote_index })
146
+ inline_footnote = Asciidoctor::Inline.new(footnote_parent, footnote.context, footnote.text, :attributes => attributes)
147
+ section_footnotes << Asciidoctor::Document::Footnote.new(inline_footnote.attr(:index), inline_footnote.id, inline_footnote.text)
148
+ @@section_footnotes[footnote_parent_object_id] = section_footnotes
149
+ inline_footnote
150
+ else
151
+ parent = footnote.parent
152
+ until parent == nil || parent.instance_of?(::Asciidoctor::Section)
153
+ parent = parent.parent
154
+ end
155
+ # check if there is any footnote attached on the section title
156
+ section_footnotes = parent != nil ? @@section_footnotes[parent.object_id] || [] : []
157
+ initial_index = footnote.attr(:index)
158
+ # reset the footnote numbering to 1 on each slide
159
+ # make sure that if a footnote is used more than once it will use the same index/number
160
+ slide_index = (existing_footnote = @@slide_footnotes[initial_index]) ? existing_footnote.index : @@slide_footnotes.length + section_footnotes.length + 1
161
+ attributes = footnote.attributes.merge({ 'index' => slide_index })
162
+ inline_footnote = Asciidoctor::Inline.new(footnote_parent, footnote.context, footnote.text, :attributes => attributes)
163
+ @@slide_footnotes[initial_index] = Asciidoctor::Document::Footnote.new(inline_footnote.attr(:index), inline_footnote.id, inline_footnote.text)
164
+ inline_footnote
165
+ end
166
+ end
167
+
168
+ def clear_slide_footnotes
169
+ @@slide_footnotes = {}
170
+ end
171
+
172
+ def slide_footnotes(section)
173
+ section_object_id = section.object_id
174
+ section_footnotes = @@section_footnotes[section_object_id] || []
175
+ section_footnotes + @@slide_footnotes.values
176
+ end
87
177
 
88
178
  ##
89
179
  # Returns the captioned section's title, optionally numbered.
@@ -101,6 +191,20 @@ module Slim::Helpers
101
191
  end
102
192
  end
103
193
 
194
+ def revealjs_dependencies(document, node, revealjsdir)
195
+ dependencies = []
196
+ dependencies << "{ src: '#{revealjsdir}/plugin/zoom-js/zoom.js', async: true }" unless (node.attr? 'revealjs_plugin_zoom', 'disabled')
197
+ dependencies << "{ src: '#{revealjsdir}/plugin/notes/notes.js', async: true }" unless (node.attr? 'revealjs_plugin_notes', 'disabled')
198
+ dependencies << "{ src: '#{revealjsdir}/plugin/markdown/marked.js', async: true }" if (node.attr? 'revealjs_plugin_marked', 'enabled')
199
+ dependencies << "{ src: '#{revealjsdir}/plugin/markdown/markdown.js', async: true }" if (node.attr? 'revealjs_plugin_markdown', 'enabled')
200
+ if (node.attr? 'revealjs_plugins') &&
201
+ !(revealjs_plugins_file = (node.attr 'revealjs_plugins', '').strip).empty? &&
202
+ !(revealjs_plugins_content = (File.read revealjs_plugins_file).strip).empty?
203
+ dependencies << revealjs_plugins_content
204
+ end
205
+ dependencies.join(",\n ")
206
+ end
207
+
104
208
 
105
209
  # Between delimiters (--) is code taken from asciidoctor-bespoke 1.0.0.alpha.1
106
210
  # Licensed under MIT, Copyright (C) 2015-2016 Dan Allen and the Asciidoctor Project
@@ -113,8 +217,69 @@ module Slim::Helpers
113
217
  def resolve_content
114
218
  @content_model == :simple ? %(<p>#{content}</p>) : content
115
219
  end
116
- #--
117
220
 
221
+ # Capture nested template content and register it with the specified key, to
222
+ # be executed at a later time.
223
+ #
224
+ # This method must be invoked using the control code directive (i.e., -). By
225
+ # using a control code directive, the block is set up to append the result
226
+ # directly to the output buffer. (Integrations often hide the distinction
227
+ # between a control code directive and an output directive in this context).
228
+ #
229
+ # key - The Symbol under which to save the template block.
230
+ # opts - A Hash of options to control processing (default: {}):
231
+ # * :append - A Boolean that indicates whether to append this block
232
+ # to others registered with this key (default: false).
233
+ # * :content - String content to be used if template content is not
234
+ # provided (optional).
235
+ # block - The template content (in Slim template syntax).
236
+ #
237
+ # Examples
238
+ #
239
+ # - content_for :body
240
+ # p content
241
+ # - content_for :body, append: true
242
+ # p more content
243
+ #
244
+ # Returns nothing.
245
+ def content_for key, opts = {}, &block
246
+ @content = {} unless defined? @content
247
+ (opts[:append] ? (@content[key] ||= []) : (@content[key] = [])) << (block_given? ? block : lambda { opts[:content] })
248
+ nil
249
+ end
250
+
251
+ # Checks whether deferred template content has been registered for the specified key.
252
+ #
253
+ # key - The Symbol under which to look for saved template blocks.
254
+ #
255
+ # Returns a Boolean indicating whether content has been registered for this key.
256
+ def content_for? key
257
+ (defined? @content) && (@content.key? key)
258
+ end
259
+
260
+ # Evaluates the deferred template content registered with the specified key.
261
+ #
262
+ # When the corresponding content_for method is invoked using a control code
263
+ # directive, the block is set up to append the result to the output buffer
264
+ # directly.
265
+ #
266
+ # key - The Symbol under which to look for template blocks to yield.
267
+ # opts - A Hash of options to control processing (default: {}):
268
+ # * :drain - A Boolean indicating whether to drain the key of blocks
269
+ # after calling them (default: true).
270
+ #
271
+ # Examples
272
+ #
273
+ # - yield_content :body
274
+ #
275
+ # Returns nothing (assuming the content has been captured in the context of control code).
276
+ def yield_content key, opts = {}
277
+ if (defined? @content) && (blks = (opts.fetch :drain, true) ? (@content.delete key) : @content[key])
278
+ blks.map {|b| b.call }.join
279
+ end
280
+ nil
281
+ end
282
+ #--
118
283
  end
119
284
 
120
285
  # More custom functions can be added in another namespace if required