1"""HTML slide show Exporter class"""
2
3# Copyright (c) Jupyter Development Team.
4# Distributed under the terms of the Modified BSD License.
5
6from copy import deepcopy
7from warnings import warn
8
9from traitlets import Bool, Unicode, default
10
11from nbconvert.preprocessors.base import Preprocessor
12
13from .html import HTMLExporter
14
15
16class _RevealMetadataPreprocessor(Preprocessor):
17 # A custom preprocessor adding convenience metadata to cells
18
19 def preprocess(self, nb, resources=None):
20 nb = deepcopy(nb)
21
22 for cell in nb.cells:
23 # Make sure every cell has a slide_type
24 try:
25 slide_type = cell.metadata.get("slideshow", {}).get("slide_type", "-")
26 except AttributeError:
27 slide_type = "-"
28 cell.metadata.slide_type = slide_type
29
30 # Find the first visible cell
31 for index, cell in enumerate(nb.cells):
32 if cell.metadata.slide_type not in {"notes", "skip"}:
33 cell.metadata.slide_type = "slide"
34 cell.metadata.slide_start = True
35 cell.metadata.subslide_start = True
36 first_slide_ix = index
37 break
38 else:
39 msg = "All cells are hidden, cannot create slideshow"
40 raise ValueError(msg)
41
42 in_fragment = False
43
44 for index, cell in enumerate(nb.cells[first_slide_ix + 1 :], start=(first_slide_ix + 1)):
45 previous_cell = nb.cells[index - 1]
46
47 # Slides are <section> elements in the HTML, subslides (the vertically
48 # stacked slides) are also <section> elements inside the slides,
49 # and fragments are <div>s within subslides. Subslide and fragment
50 # elements can contain content:
51 # <section>
52 # <section>
53 # (content)
54 # <div class="fragment">(content)</div>
55 # </section>
56 # </section>
57
58 # Get the slide type. If type is subslide or slide,
59 # end the last slide/subslide/fragment as applicable.
60 if cell.metadata.slide_type == "slide":
61 previous_cell.metadata.slide_end = True
62 cell.metadata.slide_start = True
63 if cell.metadata.slide_type in {"subslide", "slide"}:
64 previous_cell.metadata.fragment_end = in_fragment
65 previous_cell.metadata.subslide_end = True
66 cell.metadata.subslide_start = True
67 in_fragment = False
68
69 elif cell.metadata.slide_type == "fragment":
70 cell.metadata.fragment_start = True
71 if in_fragment:
72 previous_cell.metadata.fragment_end = True
73 else:
74 in_fragment = True
75
76 # The last cell will always be the end of a slide
77 nb.cells[-1].metadata.fragment_end = in_fragment
78 nb.cells[-1].metadata.subslide_end = True
79 nb.cells[-1].metadata.slide_end = True
80
81 return nb, resources
82
83
84class SlidesExporter(HTMLExporter):
85 """Exports HTML slides with reveal.js"""
86
87 # Overrides from HTMLExporter
88 #################################
89 export_from_notebook = "Reveal.js slides"
90
91 @default("template_name")
92 def _template_name_default(self):
93 return "reveal"
94
95 @default("file_extension")
96 def _file_extension_default(self):
97 return ".slides.html"
98
99 @default("template_extension")
100 def _template_extension_default(self):
101 return ".html.j2"
102
103 # Extra resources
104 #################################
105 reveal_url_prefix = Unicode(
106 help="""The URL prefix for reveal.js (version 3.x).
107 This defaults to the reveal CDN, but can be any url pointing to a copy
108 of reveal.js.
109
110 For speaker notes to work, this must be a relative path to a local
111 copy of reveal.js: e.g., "reveal.js".
112
113 If a relative path is given, it must be a subdirectory of the
114 current directory (from which the server is run).
115
116 See the usage documentation
117 (https://nbconvert.readthedocs.io/en/latest/usage.html#reveal-js-html-slideshow)
118 for more details.
119 """
120 ).tag(config=True)
121
122 @default("reveal_url_prefix")
123 def _reveal_url_prefix_default(self):
124 if "RevealHelpPreprocessor.url_prefix" in self.config:
125 warn(
126 "Please update RevealHelpPreprocessor.url_prefix to "
127 "SlidesExporter.reveal_url_prefix in config files.",
128 stacklevel=2,
129 )
130 return self.config.RevealHelpPreprocessor.url_prefix
131 return "https://unpkg.com/reveal.js@4.0.2"
132
133 reveal_theme = Unicode(
134 "simple",
135 help="""
136 Name of the reveal.js theme to use.
137
138 We look for a file with this name under
139 ``reveal_url_prefix``/css/theme/``reveal_theme``.css.
140
141 https://github.com/hakimel/reveal.js/tree/master/css/theme has
142 list of themes that ship by default with reveal.js.
143 """,
144 ).tag(config=True)
145
146 reveal_transition = Unicode(
147 "slide",
148 help="""
149 Name of the reveal.js transition to use.
150
151 The list of transitions that ships by default with reveal.js are:
152 none, fade, slide, convex, concave and zoom.
153 """,
154 ).tag(config=True)
155
156 reveal_scroll = Bool(
157 False,
158 help="""
159 If True, enable scrolling within each slide
160 """,
161 ).tag(config=True)
162
163 reveal_number = Unicode(
164 "",
165 help="""
166 slide number format (e.g. 'c/t'). Choose from:
167 'c': current, 't': total, 'h': horizontal, 'v': vertical
168 """,
169 ).tag(config=True)
170
171 reveal_width = Unicode(
172 "",
173 help="""
174 width used to determine the aspect ratio of your presentation.
175 Use the horizontal pixels available on your intended presentation
176 equipment.
177 """,
178 ).tag(config=True)
179
180 reveal_height = Unicode(
181 "",
182 help="""
183 height used to determine the aspect ratio of your presentation.
184 Use the horizontal pixels available on your intended presentation
185 equipment.
186 """,
187 ).tag(config=True)
188
189 font_awesome_url = Unicode(
190 "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css",
191 help="""
192 URL to load font awesome from.
193
194 Defaults to loading from cdnjs.
195 """,
196 ).tag(config=True)
197
198 def _init_resources(self, resources):
199 resources = super()._init_resources(resources)
200 if "reveal" not in resources:
201 resources["reveal"] = {}
202 resources["reveal"]["url_prefix"] = self.reveal_url_prefix
203 resources["reveal"]["theme"] = self.reveal_theme
204 resources["reveal"]["transition"] = self.reveal_transition
205 resources["reveal"]["scroll"] = self.reveal_scroll
206 resources["reveal"]["number"] = self.reveal_number
207 resources["reveal"]["height"] = self.reveal_height
208 resources["reveal"]["width"] = self.reveal_width
209 return resources