Show More
@@ -1,125 +1,216 | |||||
1 | from converters.markdown import ConverterMarkdown |
|
1 | from __future__ import absolute_import | |
2 | from IPython.utils.text import indent, dedent |
|
2 | ||
3 |
from converters. |
|
3 | from converters.html import ConverterHTML | |
4 | import io |
|
4 | from converters.utils import text_cell#, output_container | |
|
5 | from converters.utils import highlight, coalesce_streams#, ansi2html | |||
|
6 | ||||
|
7 | #from IPython.utils import path | |||
|
8 | #from IPython.utils.text import indent, dedent | |||
|
9 | from markdown import markdown | |||
5 | import os |
|
10 | import os | |
|
11 | import io | |||
6 | import itertools |
|
12 | import itertools | |
7 |
|
13 | |||
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 | """ |
|
|||
16 |
|
||||
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 |
|
|||
31 |
|
||||
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 | def render_heading(self, cell): |
|
102 | def render_heading(self, cell): | |
|
103 | marker = cell.level | |||
43 | return [self.meta2str(cell.metadata), |
|
104 | return [self.meta2str(cell.metadata), | |
44 |
'{0} |
|
105 | u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)] | |
45 |
|
106 | |||
46 | def render_code(self, cell): |
|
107 | def render_code(self, cell): | |
47 | if not cell.input: |
|
108 | if not cell.input: | |
48 | return [] |
|
109 | return [] | |
|
110 | ||||
49 | lines = [] |
|
111 | lines = [] | |
50 | meta_code = self.meta2str(cell.metadata) |
|
112 | meta_code = self.meta2str(cell.metadata) | |
51 | lines.extend([meta_code]) |
|
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 | n = self._get_prompt_number(cell) |
|
118 | n = self._get_prompt_number(cell) | |
54 | if self.show_prompts and not self.inline_prompt: |
|
119 | lines.append( | |
55 | lines.extend(['*In[%s]:*' % n, '']) |
|
120 | '<div class="prompt input_prompt">In [%s]:</div>' % n | |
56 | if self.show_prompts and self.inline_prompt: |
|
121 | ) | |
57 | prompt = 'In[%s]: ' % n |
|
122 | lines.append('<div class="input_area box-flex1">') | |
58 | input_lines = cell.input.split('\n') |
|
123 | lines.append(highlight(cell.input)) | |
59 | src = (prompt + input_lines[0] + '\n' + |
|
124 | lines.append('</div>') # input_area | |
60 | indent('\n'.join(input_lines[1:]), nspaces=len(prompt))) |
|
125 | lines.append('</div>') # input | |
61 | else: |
|
126 | ||
62 |
|
|
127 | if cell.outputs: | |
63 | src = highlight(src) if self.highlight_source else indent(src) |
|
128 | lines.append('<div class="vbox output_wrapper">') | |
64 | lines.extend([src, '']) |
|
129 | lines.append('<div class="output vbox">') | |
65 | if cell.outputs and self.show_prompts and not self.inline_prompt: |
|
130 | ||
66 | lines.extend(['*Out[%s]:*' % n, '']) |
|
131 | for output in coalesce_streams(cell.outputs): | |
67 | for output in cell.outputs: |
|
132 | conv_fn = self.dispatch(output.output_type) | |
68 | conv_fn = self.dispatch(output.output_type) |
|
133 | lines.extend(conv_fn(output)) | |
69 | lines.extend(conv_fn(output)) |
|
134 | ||
70 |
|
|
135 | lines.append('</div>') # output | |
|
136 | lines.append('</div>') # output_wrapper | |||
|
137 | ||||
|
138 | lines.append('</div>') # cell | |||
|
139 | ||||
71 | return lines |
|
140 | return lines | |
72 |
|
141 | |||
|
142 | @text_cell | |||
73 | def render_markdown(self, cell): |
|
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 | def render_raw(self, cell): |
|
146 | def render_raw(self, cell): | |
77 | if self.raw_as_verbatim: |
|
147 | if self.raw_as_verbatim: | |
78 |
return [ |
|
148 | return [self.in_tag('pre', self.meta2str(cell.metadata)), | |
79 |
|
|
149 | self.in_tag('pre', cell.source)] # testing | |
80 | else: |
|
150 | else: | |
81 |
return [self.meta2str(cell.metadata), cell.source |
|
151 | return [self.meta2str(cell.metadata), cell.source] | |
|
152 | ||||
|
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) | |||
82 |
|
166 | |||
83 | def render_pyout(self, output): |
|
167 | #@output_container | |
84 | for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']: |
|
168 | #def render_pyerr(self, output): | |
85 | if fmt in output: |
|
169 | ## Note: a traceback is a *list* of frames. | |
86 | conv_fn = self.dispatch_display_format(fmt) |
|
170 | ## lines = [] | |
87 | return conv_fn(output) |
|
|||
88 | return [] |
|
|||
89 |
|
171 | |||
90 | def render_pyerr(self, output): |
|
172 | ## stb = | |
91 | # Note: a traceback is a *list* of frames. |
|
173 | #return self._ansi_colored('\n'.join(output.traceback)) | |
92 | return [indent(remove_ansi('\n'.join(output.traceback))), ''] |
|
|||
93 |
|
174 | |||
94 | def _img_lines(self, img_file): |
|
175 | #def _img_lines(self, img_file): | |
95 |
return [' |
|
176 | #return ['<img src="%s">' % img_file, '</img>'] | |
96 |
|
177 | |||
97 | def render_display_format_png(self, output): |
|
178 | #def _unknown_lines(self, data): | |
98 | return ['<img src="data:image/png;base64,%s"></img>' % output.png, ''] |
|
179 | #return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data) | |
99 |
|
180 | |||
100 |
def render_display_format_ |
|
181 | #def render_display_format_png(self, output): | |
101 | return [output.svg, ''] |
|
182 | #return ['<img src="data:image/png;base64,%s"></img>' % output.png] | |
102 |
|
183 | |||
103 |
def render_display_format_ |
|
184 | #def render_display_format_svg(self, output): | |
104 | return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg, ''] |
|
185 | #return [output.svg] | |
105 |
|
186 | |||
106 |
def render_display_format_ |
|
187 | #def render_display_format_jpeg(self, output): | |
107 | return [indent(output.text), ''] |
|
188 | #return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg] | |
108 |
|
189 | |||
109 | def _unknown_lines(self, data): |
|
190 | #def render_display_format_text(self, output): | |
110 | return ['Warning: Unknown cell', data, ''] |
|
191 | #return self._ansi_colored(output.text) | |
111 |
|
192 | |||
112 | def render_display_format_html(self, output): |
|
193 | #def render_display_format_html(self, output): | |
113 |
return [ |
|
194 | #return [output.html] | |
114 |
|
195 | |||
115 | def render_display_format_latex(self, output): |
|
196 | #def render_display_format_latex(self, output): | |
116 |
return [ |
|
197 | #return [output.latex] | |
117 |
|
198 | |||
118 | def render_display_format_json(self, output): |
|
199 | #def render_display_format_json(self, output): | |
119 | return ['JSON:', indent(output.json), ''] |
|
200 | ## html ignores json | |
|
201 | #return [] | |||
120 |
|
202 | |||
121 | def render_display_format_javascript(self, output): |
|
203 | #def render_display_format_javascript(self, output): | |
122 |
return [ |
|
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 | def convert(self, cell_separator='\n'): |
|
215 | def convert(self, cell_separator='\n'): | |
125 | """ |
|
216 | """ | |
@@ -139,55 +230,77 class ConverterReveal(ConverterMarkdown): | |||||
139 | lines.extend(self.optional_header()) |
|
230 | lines.extend(self.optional_header()) | |
140 | begin = ['<div class="reveal"><div class="slides">'] |
|
231 | begin = ['<div class="reveal"><div class="slides">'] | |
141 | lines.extend(begin) |
|
232 | lines.extend(begin) | |
142 |
slides_list = self.build_slides( |
|
233 | slides_list = self.build_slides() | |
143 | lines.extend(slides_list) |
|
234 | lines.extend(slides_list) | |
144 | end = ['</div></div>'] |
|
235 | end = ['</div></div>'] | |
145 | lines.extend(end) |
|
236 | lines.extend(end) | |
146 | lines.extend(self.optional_footer()) |
|
237 | lines.extend(self.optional_footer()) | |
147 | return u'\n'.join(lines) |
|
238 | return u'\n'.join(lines) | |
148 |
|
239 | |||
149 |
def |
|
240 | def clean_text(self, cell_separator='\n'): | |
150 | "build the slides structure from text list and delimiters" |
|
241 | "clean and reorganize the text list to be slided" | |
151 | text = self.main_body(cell_separator) |
|
242 | text = self.main_body(cell_separator) | |
152 | delim_false = [u'new_section = False', |
|
243 | self.delim = [u'slide_type = untouched', | |
153 |
|
|
244 | u'slide_type = -', | |
154 |
|
|
245 | u'slide_type = header_slide', | |
155 | text = [x for x in text if not x in delim_false] |
|
246 | u'slide_type = slide', | |
156 | left = '<section data-markdown><script type="text/template">' |
|
247 | u'slide_type = fragment', | |
157 | right = '</script></section>' |
|
248 | u'slide_type = skip'] | |
158 | # build lists representing each slide delimited by new_section |
|
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 | slides = [list(x[1]) for x in itertools.groupby(text, |
|
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 | for slide in slides: |
|
289 | for slide in slides: | |
162 | slide.insert(0, u'') # for proper interline in html file |
|
|||
163 | slide.insert(0, left) |
|
290 | slide.insert(0, left) | |
164 | slide.append(right) |
|
291 | slide.append(right) | |
165 | # build each vertical slide delimited by new_subsection |
|
292 | # encapsulation of each fragment | |
166 | if slide[2] == u'new_subsection = True': |
|
293 | for i, j in enumerate(slide): | |
167 | slide.pop(2) |
|
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: | |||
168 | slide.insert(0, '<section>') |
|
299 | slide.insert(0, '<section>') | |
169 | slide.append('</section>') |
|
300 | slide.append('</section>') | |
170 | for i, j in enumerate(slide): |
|
|||
171 | if j == u'new_subsection = True': |
|
|||
172 | slide[i] = right + left |
|
|||
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[4] = right |
|
|||
177 | slide.insert(5, u'') # for proper interline |
|
|||
178 | slide.insert(5, left) |
|
|||
179 | slide.insert(5, '<section>') |
|
|||
180 | slide.append('</section>') |
|
|||
181 | for i, j in enumerate(slide): |
|
|||
182 | if j == u'new_subsection = True': |
|
|||
183 | slide[i] = right + left |
|
|||
184 | slide.insert(i + 1, u'') # for proper interline |
|
|||
185 | # build each fragment delimited by new_fragment |
|
|||
186 | for i, j in enumerate(slide): |
|
301 | for i, j in enumerate(slide): | |
187 |
if j == u' |
|
302 | if j == u'slide_type = slide': | |
188 |
slide[i] = |
|
303 | slide[i] = right + left | |
189 | slide[i + 2] = '</p>' |
|
|||
190 | slide.insert(i + 3, u'') # for proper interline |
|
|||
191 | return list(itertools.chain(*slides)) |
|
304 | return list(itertools.chain(*slides)) | |
192 |
|
305 | |||
193 | def save(self, outfile=None, encoding=None): |
|
306 | def save(self, outfile=None, encoding=None): | |
@@ -223,5 +336,4 class ConverterReveal(ConverterMarkdown): | |||||
223 |
|
336 | |||
224 | def optional_footer(self): |
|
337 | def optional_footer(self): | |
225 | optional_footer_body = self.template_split() |
|
338 | optional_footer_body = self.template_split() | |
226 |
return optional_footer_body[1] |
|
339 | return optional_footer_body[1] No newline at end of file | |
227 |
|
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now