Show More
@@ -1,125 +1,216 b'' | |||
|
1 | from converters.markdown import ConverterMarkdown | |
|
2 | from IPython.utils.text import indent, dedent | |
|
3 | from converters.utils import highlight, remove_ansi | |
|
4 | import io | |
|
5 | import os | |
|
6 | import itertools | |
|
1 | from __future__ import absolute_import | |
|
7 | 2 | |
|
8 | class ConverterReveal(ConverterMarkdown): | |
|
9 | """ | |
|
10 | Convert a notebook to a html slideshow. | |
|
11 | ||
|
12 | It generates a static html slideshow based in markdown and reveal.js. | |
|
13 | The delimiters for each slide, subslide, and fragment are retrieved | |
|
14 | from the 'slideshow' metadata. | |
|
15 | """ | |
|
3 | from converters.html import ConverterHTML | |
|
4 | from converters.utils import text_cell#, output_container | |
|
5 | from converters.utils import highlight, coalesce_streams#, ansi2html | |
|
16 | 6 | |
|
17 | def __init__(self, infile, highlight_source=False, show_prompts=True, | |
|
18 | inline_prompt=True): | |
|
19 | super(ConverterReveal, self).__init__(infile) | |
|
20 | self.highlight_source = highlight_source | |
|
21 | self.show_prompts = show_prompts | |
|
22 | self.inline_prompt = inline_prompt | |
|
23 | ||
|
24 | def switch_meta(self, m_list): | |
|
25 | "sort order m_list to [new_section, new_subsection, new_fragment]" | |
|
26 | if len(m_list) > 1: | |
|
27 | # do not sort when m_list = [new_subsection, new_fragment] | |
|
28 | if not (len(m_list) == 2 and m_list[1] == [u'new_fragment = True']): | |
|
29 | m_list[0], m_list[1] = m_list[1], m_list[0] | |
|
30 | return m_list | |
|
7 | #from IPython.utils import path | |
|
8 | #from IPython.utils.text import indent, dedent | |
|
9 | from markdown import markdown | |
|
10 | import os | |
|
11 | import io | |
|
12 | import itertools | |
|
31 | 13 | |
|
32 | def meta2str(self, meta): | |
|
33 | "transform metadata dict (containing slides delimiters) to string " | |
|
34 | try: | |
|
35 | meta_tuple = meta[u'slideshow'].items() | |
|
36 | except KeyError as e: | |
|
37 | meta_tuple = () # if there is not slideshow metadata | |
|
38 | meta_list = [[x + ' = ' + unicode(y)] for x, y in meta_tuple] | |
|
39 | meta_list = self.switch_meta(meta_list) | |
|
40 | return u'\n'.join(list(itertools.chain(*meta_list))) | |
|
41 | 14 | |
|
15 | class ConverterReveal(ConverterHTML): | |
|
16 | #""" | |
|
17 | #Convert a notebook to a html slideshow. | |
|
18 | ||
|
19 | #It generates a static html slideshow based in markdown and reveal.js. | |
|
20 | #The delimiters for each slide, subslide, and fragment are retrieved | |
|
21 | #from the 'slideshow' metadata. | |
|
22 | #""" | |
|
23 | ||
|
24 | #def __init__(self): | |
|
25 | #super(ConverterReveal, self).__init__() | |
|
26 | ||
|
27 | #extension = 'html' | |
|
28 | #blank_symbol = ' ' | |
|
29 | ||
|
30 | #def in_tag(self, tag, src, attrs=None): | |
|
31 | #"""Return a list of elements bracketed by the given tag""" | |
|
32 | #attr_s = '' if attrs is None else \ | |
|
33 | #' '.join("%s=%s" % (attr, value) | |
|
34 | #for attr, value in attrs.iteritems()) | |
|
35 | #return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag] | |
|
36 | ||
|
37 | #def _ansi_colored(self, text): | |
|
38 | #return ['<pre>%s</pre>' % ansi2html(text)] | |
|
39 | ||
|
40 | #def _stylesheet(self, fname): | |
|
41 | #with io.open(fname, encoding='utf-8') as f: | |
|
42 | #s = f.read() | |
|
43 | #return self.in_tag('style', s, dict(type='"text/css"')) | |
|
44 | ||
|
45 | #def _out_prompt(self, output): | |
|
46 | #if output.output_type == 'pyout': | |
|
47 | #content = 'Out[%s]:' % self._get_prompt_number(output) | |
|
48 | #else: | |
|
49 | #content = '' | |
|
50 | #return ['<div class="prompt output_prompt">%s</div>' % content] | |
|
51 | ||
|
52 | #def header_body(self): | |
|
53 | #"""Return the body of the header as a list of strings.""" | |
|
54 | ||
|
55 | #from pygments.formatters import HtmlFormatter | |
|
56 | ||
|
57 | #header = [] | |
|
58 | #static = os.path.join(path.get_ipython_package_dir(), | |
|
59 | #'frontend', 'html', 'notebook', 'static', | |
|
60 | #) | |
|
61 | #here = os.path.split(os.path.realpath(__file__))[0] | |
|
62 | #css = os.path.join(static, 'css') | |
|
63 | #for sheet in [ | |
|
64 | ## do we need jquery and prettify? | |
|
65 | ## os.path.join(static, 'jquery', 'css', 'themes', 'base', | |
|
66 | ## 'jquery-ui.min.css'), | |
|
67 | ## os.path.join(static, 'prettify', 'prettify.css'), | |
|
68 | #os.path.join(css, 'boilerplate.css'), | |
|
69 | #os.path.join(css, 'fbm.css'), | |
|
70 | #os.path.join(css, 'notebook.css'), | |
|
71 | #os.path.join(css, 'renderedhtml.css'), | |
|
72 | ## our overrides: | |
|
73 | #os.path.join(here, '..', 'css', 'static_html.css'), | |
|
74 | #]: | |
|
75 | #header.extend(self._stylesheet(sheet)) | |
|
76 | ||
|
77 | ## pygments css | |
|
78 | #pygments_css = HtmlFormatter().get_style_defs('.highlight') | |
|
79 | #header.extend(['<meta charset="UTF-8">']) | |
|
80 | #header.extend(self.in_tag('style', pygments_css, | |
|
81 | #dict(type='"text/css"'))) | |
|
82 | ||
|
83 | ## TODO: this should be allowed to use local mathjax: | |
|
84 | #header.extend(self.in_tag('script', '', {'type': '"text/javascript"', | |
|
85 | #'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/' | |
|
86 | #'latest/MathJax.js?config=TeX-AMS_HTML"', | |
|
87 | #})) | |
|
88 | #with io.open(os.path.join(here, '..', 'js', 'initmathjax.js'), | |
|
89 | #encoding='utf-8') as f: | |
|
90 | #header.extend(self.in_tag('script', f.read(), | |
|
91 | #{'type': '"text/javascript"'})) | |
|
92 | #return header | |
|
93 | ||
|
94 | #def optional_header(self): | |
|
95 | #return ['<html>', '<head>'] + self.header_body() + \ | |
|
96 | #['</head>', '<body>'] | |
|
97 | ||
|
98 | #def optional_footer(self): | |
|
99 | #return ['</body>', '</html>'] | |
|
100 | ||
|
101 | @text_cell | |
|
42 | 102 | def render_heading(self, cell): |
|
103 | marker = cell.level | |
|
43 | 104 | return [self.meta2str(cell.metadata), |
|
44 |
'{0} |
|
|
105 | u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)] | |
|
45 | 106 | |
|
46 | 107 | def render_code(self, cell): |
|
47 | 108 | if not cell.input: |
|
48 | 109 | return [] |
|
110 | ||
|
49 | 111 | lines = [] |
|
50 | 112 | meta_code = self.meta2str(cell.metadata) |
|
51 | 113 | lines.extend([meta_code]) |
|
52 | lines.extend(['<!-- hack -->']) # to be proper parsed | |
|
114 | ||
|
115 | lines.extend(['<div class="cell border-box-sizing code_cell vbox">']) | |
|
116 | ||
|
117 | lines.append('<div class="input hbox">') | |
|
53 | 118 | n = self._get_prompt_number(cell) |
|
54 | if self.show_prompts and not self.inline_prompt: | |
|
55 | lines.extend(['*In[%s]:*' % n, '']) | |
|
56 | if self.show_prompts and self.inline_prompt: | |
|
57 | prompt = 'In[%s]: ' % n | |
|
58 | input_lines = cell.input.split('\n') | |
|
59 | src = (prompt + input_lines[0] + '\n' + | |
|
60 | indent('\n'.join(input_lines[1:]), nspaces=len(prompt))) | |
|
61 | else: | |
|
62 |
|
|
|
63 | src = highlight(src) if self.highlight_source else indent(src) | |
|
64 | lines.extend([src, '']) | |
|
65 | if cell.outputs and self.show_prompts and not self.inline_prompt: | |
|
66 | lines.extend(['*Out[%s]:*' % n, '']) | |
|
67 | for output in cell.outputs: | |
|
119 | lines.append( | |
|
120 | '<div class="prompt input_prompt">In [%s]:</div>' % n | |
|
121 | ) | |
|
122 | lines.append('<div class="input_area box-flex1">') | |
|
123 | lines.append(highlight(cell.input)) | |
|
124 | lines.append('</div>') # input_area | |
|
125 | lines.append('</div>') # input | |
|
126 | ||
|
127 | if cell.outputs: | |
|
128 | lines.append('<div class="vbox output_wrapper">') | |
|
129 | lines.append('<div class="output vbox">') | |
|
130 | ||
|
131 | for output in coalesce_streams(cell.outputs): | |
|
68 | 132 | conv_fn = self.dispatch(output.output_type) |
|
69 | 133 | lines.extend(conv_fn(output)) |
|
70 | #lines.append('') | |
|
134 | ||
|
135 | lines.append('</div>') # output | |
|
136 | lines.append('</div>') # output_wrapper | |
|
137 | ||
|
138 | lines.append('</div>') # cell | |
|
139 | ||
|
71 | 140 | return lines |
|
72 | 141 | |
|
142 | @text_cell | |
|
73 | 143 | def render_markdown(self, cell): |
|
74 |
return [self.meta2str(cell.metadata), cell.source |
|
|
144 | return [self.meta2str(cell.metadata), markdown(cell.source)] | |
|
75 | 145 | |
|
76 | 146 | def render_raw(self, cell): |
|
77 | 147 | if self.raw_as_verbatim: |
|
78 |
return [ |
|
|
79 |
|
|
|
148 | return [self.in_tag('pre', self.meta2str(cell.metadata)), | |
|
149 | self.in_tag('pre', cell.source)] # testing | |
|
80 | 150 | else: |
|
81 |
return [self.meta2str(cell.metadata), cell.source |
|
|
151 | return [self.meta2str(cell.metadata), cell.source] | |
|
82 | 152 | |
|
83 | def render_pyout(self, output): | |
|
84 | for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']: | |
|
85 | if fmt in output: | |
|
86 | conv_fn = self.dispatch_display_format(fmt) | |
|
87 | return conv_fn(output) | |
|
88 | return [] | |
|
153 | #@output_container | |
|
154 | #def render_pyout(self, output): | |
|
155 | #for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']: | |
|
156 | #if fmt in output: | |
|
157 | #conv_fn = self.dispatch_display_format(fmt) | |
|
158 | #return conv_fn(output) | |
|
159 | #return [] | |
|
160 | ||
|
161 | #render_display_data = render_pyout | |
|
162 | ||
|
163 | #@output_container | |
|
164 | #def render_stream(self, output): | |
|
165 | #return self._ansi_colored(output.text) | |
|
89 | 166 | |
|
90 | def render_pyerr(self, output): | |
|
91 | # Note: a traceback is a *list* of frames. | |
|
92 | return [indent(remove_ansi('\n'.join(output.traceback))), ''] | |
|
167 | #@output_container | |
|
168 | #def render_pyerr(self, output): | |
|
169 | ## Note: a traceback is a *list* of frames. | |
|
170 | ## lines = [] | |
|
93 | 171 | |
|
94 | def _img_lines(self, img_file): | |
|
95 | return ['![](%s)' % img_file, ''] | |
|
172 | ## stb = | |
|
173 | #return self._ansi_colored('\n'.join(output.traceback)) | |
|
96 | 174 | |
|
97 | def render_display_format_png(self, output): | |
|
98 | return ['<img src="data:image/png;base64,%s"></img>' % output.png, ''] | |
|
175 | #def _img_lines(self, img_file): | |
|
176 | #return ['<img src="%s">' % img_file, '</img>'] | |
|
99 | 177 | |
|
100 | def render_display_format_svg(self, output): | |
|
101 | return [output.svg, ''] | |
|
178 | #def _unknown_lines(self, data): | |
|
179 | #return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data) | |
|
102 | 180 | |
|
103 |
def render_display_format_ |
|
|
104 |
return ['<img src="data:image/ |
|
|
181 | #def render_display_format_png(self, output): | |
|
182 | #return ['<img src="data:image/png;base64,%s"></img>' % output.png] | |
|
105 | 183 | |
|
106 |
def render_display_format_ |
|
|
107 |
return [ |
|
|
184 | #def render_display_format_svg(self, output): | |
|
185 | #return [output.svg] | |
|
108 | 186 | |
|
109 | def _unknown_lines(self, data): | |
|
110 | return ['Warning: Unknown cell', data, ''] | |
|
187 | #def render_display_format_jpeg(self, output): | |
|
188 | #return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg] | |
|
111 | 189 | |
|
112 |
def render_display_format_ |
|
|
113 |
return |
|
|
190 | #def render_display_format_text(self, output): | |
|
191 | #return self._ansi_colored(output.text) | |
|
114 | 192 | |
|
115 |
def render_display_format_l |
|
|
116 |
return [ |
|
|
193 | #def render_display_format_html(self, output): | |
|
194 | #return [output.html] | |
|
117 | 195 | |
|
118 |
def render_display_format_ |
|
|
119 |
return [ |
|
|
196 | #def render_display_format_latex(self, output): | |
|
197 | #return [output.latex] | |
|
120 | 198 | |
|
121 |
def render_display_format_j |
|
|
122 | return ['JavaScript:', indent(output.javascript), ''] | |
|
199 | #def render_display_format_json(self, output): | |
|
200 | ## html ignores json | |
|
201 | #return [] | |
|
202 | ||
|
203 | #def render_display_format_javascript(self, output): | |
|
204 | #return [output.javascript] | |
|
205 | ||
|
206 | def meta2str(self, meta): | |
|
207 | "transform metadata dict (containing slides delimiters) to string " | |
|
208 | try: | |
|
209 | meta_tuple = meta[u'slideshow'].items() | |
|
210 | except KeyError as e: # if there is not slideshow metadata | |
|
211 | meta_tuple = [(u'slide_type', u'untouched')] | |
|
212 | meta_list = [[x + ' = ' + unicode(y)] for x, y in meta_tuple] | |
|
213 | return u'\n'.join(list(itertools.chain(*meta_list))) | |
|
123 | 214 | |
|
124 | 215 | def convert(self, cell_separator='\n'): |
|
125 | 216 | """ |
@@ -139,55 +230,77 b' class ConverterReveal(ConverterMarkdown):' | |||
|
139 | 230 | lines.extend(self.optional_header()) |
|
140 | 231 | begin = ['<div class="reveal"><div class="slides">'] |
|
141 | 232 | lines.extend(begin) |
|
142 |
slides_list = self.build_slides( |
|
|
233 | slides_list = self.build_slides() | |
|
143 | 234 | lines.extend(slides_list) |
|
144 | 235 | end = ['</div></div>'] |
|
145 | 236 | lines.extend(end) |
|
146 | 237 | lines.extend(self.optional_footer()) |
|
147 | 238 | return u'\n'.join(lines) |
|
148 | 239 | |
|
149 |
def |
|
|
150 | "build the slides structure from text list and delimiters" | |
|
240 | def clean_text(self, cell_separator='\n'): | |
|
241 | "clean and reorganize the text list to be slided" | |
|
151 | 242 | text = self.main_body(cell_separator) |
|
152 | delim_false = [u'new_section = False', | |
|
153 |
|
|
|
154 |
|
|
|
155 | text = [x for x in text if not x in delim_false] | |
|
156 | left = '<section data-markdown><script type="text/template">' | |
|
157 | right = '</script></section>' | |
|
158 | # build lists representing each slide delimited by new_section | |
|
243 | self.delim = [u'slide_type = untouched', | |
|
244 | u'slide_type = -', | |
|
245 | u'slide_type = header_slide', | |
|
246 | u'slide_type = slide', | |
|
247 | u'slide_type = fragment', | |
|
248 | u'slide_type = skip'] | |
|
249 | text_cell_render = \ | |
|
250 | u'<div class="text_cell_render border-box-sizing rendered_html">' | |
|
251 | for i, j in enumerate(text): | |
|
252 | if j in self.delim and text[i - 1] == text_cell_render: | |
|
253 | if j == self.delim[0]: | |
|
254 | text[i - 1] = self.delim[0] | |
|
255 | elif j == self.delim[1]: | |
|
256 | text[i - 1] = self.delim[1] | |
|
257 | elif j == self.delim[2]: | |
|
258 | text[i - 1] = self.delim[2] | |
|
259 | elif j == self.delim[3]: | |
|
260 | text[i - 1] = self.delim[3] | |
|
261 | elif j == self.delim[4]: | |
|
262 | text[i - 1] = self.delim[4] | |
|
263 | else: | |
|
264 | text[i - 1] = self.delim[5] | |
|
265 | text[i] = text_cell_render | |
|
266 | text[0] = u'slide_type = header_slide' # defensive code | |
|
267 | text.append(u'slide_type = untouched') # to end search of skipped | |
|
268 | return text | |
|
269 | ||
|
270 | def build_slides(self): | |
|
271 | "build the slides structure from text list and delimiters" | |
|
272 | text = self.clean_text() | |
|
273 | left = '<section>' | |
|
274 | right = '</section>' | |
|
275 | set_delim = self.delim[:5] | |
|
276 | #elimination of skipped cells | |
|
277 | for i, j in enumerate(text): | |
|
278 | if j == u'slide_type = skip': | |
|
279 | text.pop(i) | |
|
280 | while not text[i] in set_delim: | |
|
281 | text.pop(i) | |
|
282 | # elimination of none names | |
|
283 | for i, j in enumerate(text): | |
|
284 | if j in [u'slide_type = untouched', u'slide_type = -']: | |
|
285 | text.pop(i) | |
|
286 | #generation of slides as a list of list | |
|
159 | 287 | slides = [list(x[1]) for x in itertools.groupby(text, |
|
160 |
lambda x: x == u' |
|
|
288 | lambda x: x == u'slide_type = header_slide') if not x[0]] | |
|
161 | 289 | for slide in slides: |
|
162 | slide.insert(0, u'') # for proper interline in html file | |
|
163 | 290 | slide.insert(0, left) |
|
164 | 291 | slide.append(right) |
|
165 | # build each vertical slide delimited by new_subsection | |
|
166 | if slide[2] == u'new_subsection = True': | |
|
167 | slide.pop(2) | |
|
168 | slide.insert(0, '<section>') | |
|
169 | slide.append('</section>') | |
|
292 | # encapsulation of each fragment | |
|
170 | 293 |
|
|
171 |
|
|
|
172 |
|
|
|
173 | slide.insert(i + 1, u'') # for proper interline | |
|
174 | #defensive: if user do not begin new_subsection with a new_section | |
|
175 | elif slide[4] == u'new_subsection = True': | |
|
176 |
slide |
|
|
177 | slide.insert(5, u'') # for proper interline | |
|
178 | slide.insert(5, left) | |
|
179 | slide.insert(5, '<section>') | |
|
294 | if j == u'slide_type = fragment': | |
|
295 | slide.pop(i) | |
|
296 | slide[i] = slide[i][:4] + ' class="fragment"' + slide[i][4:] | |
|
297 | # encapsulation of each nested slide | |
|
298 | if u'slide_type = slide' in slide: | |
|
299 | slide.insert(0, '<section>') | |
|
180 | 300 | slide.append('</section>') |
|
181 | 301 |
|
|
182 |
|
|
|
302 | if j == u'slide_type = slide': | |
|
183 | 303 |
|
|
184 | slide.insert(i + 1, u'') # for proper interline | |
|
185 | # build each fragment delimited by new_fragment | |
|
186 | for i, j in enumerate(slide): | |
|
187 | if j == u'new_fragment = True': | |
|
188 | slide[i] = '<p class="fragment">' | |
|
189 | slide[i + 2] = '</p>' | |
|
190 | slide.insert(i + 3, u'') # for proper interline | |
|
191 | 304 | return list(itertools.chain(*slides)) |
|
192 | 305 | |
|
193 | 306 | def save(self, outfile=None, encoding=None): |
@@ -224,4 +337,3 b' class ConverterReveal(ConverterMarkdown):' | |||
|
224 | 337 | def optional_footer(self): |
|
225 | 338 | optional_footer_body = self.template_split() |
|
226 | 339 |
return optional_footer_body[1] |
|
No newline at end of file | ||
|
227 |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now