Show More
@@ -1,112 +1,112 b'' | |||
|
1 | 1 | """PostProcessor for serving reveal.js HTML slideshows.""" |
|
2 | 2 | from __future__ import print_function |
|
3 | 3 | #----------------------------------------------------------------------------- |
|
4 | 4 | #Copyright (c) 2013, the IPython Development Team. |
|
5 | 5 | # |
|
6 | 6 | #Distributed under the terms of the Modified BSD License. |
|
7 | 7 | # |
|
8 | 8 | #The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | # Imports |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | 15 | import os |
|
16 | 16 | import webbrowser |
|
17 | 17 | |
|
18 | 18 | from tornado import web, ioloop, httpserver |
|
19 | 19 | from tornado.httpclient import AsyncHTTPClient |
|
20 | 20 | |
|
21 | 21 | from IPython.utils.traitlets import Bool, Unicode, Int |
|
22 | 22 | |
|
23 | 23 | from .base import PostProcessorBase |
|
24 | 24 | |
|
25 | 25 | #----------------------------------------------------------------------------- |
|
26 | 26 | # Classes |
|
27 | 27 | #----------------------------------------------------------------------------- |
|
28 | 28 | |
|
29 | 29 | class ProxyHandler(web.RequestHandler): |
|
30 | 30 | """handler the proxies requests from a local prefix to a CDN""" |
|
31 | 31 | @web.asynchronous |
|
32 | 32 | def get(self, prefix, url): |
|
33 | 33 | """proxy a request to a CDN""" |
|
34 | 34 | proxy_url = "/".join([self.settings['cdn'], url]) |
|
35 | 35 | client = self.settings['client'] |
|
36 | 36 | client.fetch(proxy_url, callback=self.finish_get) |
|
37 | 37 | |
|
38 | 38 | def finish_get(self, response): |
|
39 | 39 | """finish the request""" |
|
40 | 40 | # copy potentially relevant headers |
|
41 | 41 | for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]: |
|
42 | 42 | if header in response.headers: |
|
43 | 43 | self.set_header(header, response.headers[header]) |
|
44 | 44 | self.finish(response.body) |
|
45 | 45 | |
|
46 | 46 | class ServePostProcessor(PostProcessorBase): |
|
47 | 47 | """Post processor designed to serve files |
|
48 | 48 | |
|
49 | 49 | Proxies reveal.js requests to a CDN if no local reveal.js is present |
|
50 | 50 | """ |
|
51 | 51 | |
|
52 | 52 | |
|
53 | 53 | open_in_browser = Bool(True, config=True, |
|
54 | 54 | help="""Should the browser be opened automatically?""" |
|
55 | 55 | ) |
|
56 |
reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2. |
|
|
56 | reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.6.2", config=True, | |
|
57 | 57 | help="""URL for reveal.js CDN.""" |
|
58 | 58 | ) |
|
59 | 59 | reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js") |
|
60 | 60 | ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.") |
|
61 | 61 | port = Int(8000, config=True, help="port for the server to listen on.") |
|
62 | 62 | |
|
63 | 63 | def postprocess(self, input): |
|
64 | 64 | """Serve the build directory with a webserver.""" |
|
65 | 65 | dirname, filename = os.path.split(input) |
|
66 | 66 | handlers = [ |
|
67 | 67 | (r"/(.+)", web.StaticFileHandler, {'path' : dirname}), |
|
68 | 68 | (r"/", web.RedirectHandler, {"url": "/%s" % filename}) |
|
69 | 69 | ] |
|
70 | 70 | |
|
71 | 71 | if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")): |
|
72 | 72 | # reveal specifically from CDN, nothing to do |
|
73 | 73 | pass |
|
74 | 74 | elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)): |
|
75 | 75 | # reveal prefix exists |
|
76 | 76 | self.log.info("Serving local %s", self.reveal_prefix) |
|
77 | 77 | else: |
|
78 | 78 | self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn) |
|
79 | 79 | handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler)) |
|
80 | 80 | |
|
81 | 81 | app = web.Application(handlers, |
|
82 | 82 | cdn=self.reveal_cdn, |
|
83 | 83 | client=AsyncHTTPClient(), |
|
84 | 84 | ) |
|
85 | 85 | # hook up tornado logging to our logger |
|
86 | 86 | try: |
|
87 | 87 | from tornado import log |
|
88 | 88 | log.app_log = self.log |
|
89 | 89 | except ImportError: |
|
90 | 90 | # old tornado (<= 3), ignore |
|
91 | 91 | pass |
|
92 | 92 | |
|
93 | 93 | http_server = httpserver.HTTPServer(app) |
|
94 | 94 | http_server.listen(self.port, address=self.ip) |
|
95 | 95 | url = "http://%s:%i/%s" % (self.ip, self.port, filename) |
|
96 | 96 | print("Serving your slides at %s" % url) |
|
97 | 97 | print("Use Control-C to stop this server") |
|
98 | 98 | if self.open_in_browser: |
|
99 | 99 | webbrowser.open(url, new=2) |
|
100 | 100 | try: |
|
101 | 101 | ioloop.IOLoop.instance().start() |
|
102 | 102 | except KeyboardInterrupt: |
|
103 | 103 | print("\nInterrupted") |
|
104 | 104 | |
|
105 | 105 | def main(path): |
|
106 | 106 | """allow running this module to serve the slides""" |
|
107 | 107 | server = ServePostProcessor() |
|
108 | 108 | server(path) |
|
109 | 109 | |
|
110 | 110 | if __name__ == '__main__': |
|
111 | 111 | import sys |
|
112 | 112 | main(sys.argv[1]) |
@@ -1,72 +1,75 b'' | |||
|
1 | 1 | """Module that pre-processes the notebook for export via Reveal. |
|
2 | 2 | """ |
|
3 | 3 | #----------------------------------------------------------------------------- |
|
4 | 4 | # Copyright (c) 2013, the IPython Development Team. |
|
5 | 5 | # |
|
6 | 6 | # Distributed under the terms of the Modified BSD License. |
|
7 | 7 | # |
|
8 | 8 | # The full license is in the file COPYING.txt, distributed with this software. |
|
9 | 9 | #----------------------------------------------------------------------------- |
|
10 | 10 | |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | # Imports |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | |
|
15 | 15 | from .base import Preprocessor |
|
16 | 16 | from IPython.utils.traitlets import Unicode |
|
17 | 17 | |
|
18 | 18 | #----------------------------------------------------------------------------- |
|
19 | 19 | # Classes and functions |
|
20 | 20 | #----------------------------------------------------------------------------- |
|
21 | 21 | |
|
22 | 22 | class RevealHelpPreprocessor(Preprocessor): |
|
23 | 23 | |
|
24 | 24 | url_prefix = Unicode('reveal.js', config=True, |
|
25 | 25 | help="""The URL prefix for reveal.js. |
|
26 | 26 | This can be a a relative URL for a local copy of reveal.js, |
|
27 | 27 | or point to a CDN. |
|
28 | 28 | |
|
29 | 29 | For speaker notes to work, a local reveal.js prefix must be used. |
|
30 | 30 | """ |
|
31 | 31 | ) |
|
32 | 32 | |
|
33 | 33 | def preprocess(self, nb, resources): |
|
34 | 34 | """ |
|
35 | 35 | Called once to 'preprocess' contents of the notebook. |
|
36 | 36 | |
|
37 | 37 | Parameters |
|
38 | 38 | ---------- |
|
39 | 39 | nb : NotebookNode |
|
40 | 40 | Notebook being converted |
|
41 | 41 | resources : dictionary |
|
42 | 42 | Additional resources used in the conversion process. Allows |
|
43 | 43 | preprocessors to pass variables into the Jinja engine. |
|
44 | 44 | """ |
|
45 | 45 | |
|
46 | 46 | for worksheet in nb.worksheets: |
|
47 | 47 | for index, cell in enumerate(worksheet.cells): |
|
48 | 48 | |
|
49 | 49 | #Make sure the cell has slideshow metadata. |
|
50 | 50 | cell.metadata.slide_type = cell.get('metadata', {}).get('slideshow', {}).get('slide_type', '-') |
|
51 | 51 | |
|
52 | 52 | #Get the slide type. If type is start of subslide or slide, |
|
53 | 53 | #end the last subslide/slide. |
|
54 | 54 | if cell.metadata.slide_type in ['slide']: |
|
55 | 55 | worksheet.cells[index - 1].metadata.slide_helper = 'slide_end' |
|
56 | 56 | if cell.metadata.slide_type in ['subslide']: |
|
57 | 57 | worksheet.cells[index - 1].metadata.slide_helper = 'subslide_end' |
|
58 | 58 | #Prevent the rendering of "do nothing" cells before fragments |
|
59 | #Group fragments passing frag_number to the data-fragment-index | |
|
59 | 60 | if cell.metadata.slide_type in ['fragment']: |
|
61 | worksheet.cells[index].metadata.frag_number = index | |
|
60 | 62 | i = 1 |
|
61 | 63 | while i < len(worksheet.cells) - index: |
|
62 | 64 | worksheet.cells[index + i].metadata.frag_helper = 'fragment_end' |
|
65 | worksheet.cells[index + i].metadata.frag_number = index | |
|
63 | 66 | i += 1 |
|
64 | 67 | #Restart the slide_helper when the cell status is changed |
|
65 | 68 | #to "do nothing". |
|
66 | 69 | if cell.metadata.slide_type in ['-']: |
|
67 | 70 | worksheet.cells[index - 1].metadata.slide_helper = '-' |
|
68 | 71 | |
|
69 | 72 | if not isinstance(resources['reveal'], dict): |
|
70 | 73 | resources['reveal'] = {} |
|
71 | 74 | resources['reveal']['url_prefix'] = self.url_prefix |
|
72 | 75 | return nb, resources |
@@ -1,203 +1,203 b'' | |||
|
1 | 1 | {%- extends 'basic.tpl' -%} |
|
2 | 2 | {% from 'mathjax.tpl' import mathjax %} |
|
3 | 3 | |
|
4 | 4 | {%- block any_cell scoped -%} |
|
5 | 5 | {%- if cell.metadata.slide_type in ['slide'] -%} |
|
6 | 6 | <section> |
|
7 | 7 | <section> |
|
8 | 8 | {{ super() }} |
|
9 | 9 | {%- elif cell.metadata.slide_type in ['subslide'] -%} |
|
10 | 10 | <section> |
|
11 | 11 | {{ super() }} |
|
12 | 12 | {%- elif cell.metadata.slide_type in ['-'] -%} |
|
13 | 13 | {%- if cell.metadata.frag_helper in ['fragment_end'] -%} |
|
14 | <div class="fragment"> | |
|
14 | <div class="fragment" data-fragment-index="{{ cell.metadata.frag_number }}"> | |
|
15 | 15 | {{ super() }} |
|
16 | 16 | </div> |
|
17 | 17 | {%- else -%} |
|
18 | 18 | {{ super() }} |
|
19 | 19 | {%- endif -%} |
|
20 | 20 | {%- elif cell.metadata.slide_type in ['skip'] -%} |
|
21 | 21 | <div style=display:none> |
|
22 | 22 | {{ super() }} |
|
23 | 23 | </div> |
|
24 | 24 | {%- elif cell.metadata.slide_type in ['notes'] -%} |
|
25 | 25 | <aside class="notes"> |
|
26 | 26 | {{ super() }} |
|
27 | 27 | </aside> |
|
28 | 28 | {%- elif cell.metadata.slide_type in ['fragment'] -%} |
|
29 | <div class="fragment"> | |
|
29 | <div class="fragment" data-fragment-index="{{ cell.metadata.frag_number }}"> | |
|
30 | 30 | {{ super() }} |
|
31 | 31 | </div> |
|
32 | 32 | {%- endif -%} |
|
33 | 33 | {%- if cell.metadata.slide_helper in ['subslide_end'] -%} |
|
34 | 34 | </section> |
|
35 | 35 | {%- elif cell.metadata.slide_helper in ['slide_end'] -%} |
|
36 | 36 | </section> |
|
37 | 37 | </section> |
|
38 | 38 | {%- endif -%} |
|
39 | 39 | {%- endblock any_cell -%} |
|
40 | 40 | |
|
41 | 41 | {% block header %} |
|
42 | 42 | <!DOCTYPE html> |
|
43 | 43 | <html> |
|
44 | 44 | <head> |
|
45 | 45 | |
|
46 | 46 | <meta charset="utf-8" /> |
|
47 | 47 | <meta http-equiv="X-UA-Compatible" content="chrome=1" /> |
|
48 | 48 | |
|
49 | 49 | <meta name="apple-mobile-web-app-capable" content="yes" /> |
|
50 | 50 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> |
|
51 | 51 | |
|
52 | 52 | <title>{{resources['metadata']['name']}} slides</title> |
|
53 | 53 | |
|
54 | 54 | <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script> |
|
55 | 55 | <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> |
|
56 | 56 | |
|
57 | 57 | <!-- General and theme style sheets --> |
|
58 | 58 | <link rel="stylesheet" href="{{resources.reveal.url_prefix}}/css/reveal.css"> |
|
59 | 59 | <link rel="stylesheet" href="{{resources.reveal.url_prefix}}/css/theme/simple.css" id="theme"> |
|
60 | 60 | |
|
61 | 61 | <!-- For syntax highlighting --> |
|
62 | 62 | <link rel="stylesheet" href="{{resources.reveal.url_prefix}}/lib/css/zenburn.css"> |
|
63 | 63 | |
|
64 | 64 | <!-- If the query includes 'print-pdf', include the PDF print sheet --> |
|
65 | 65 | <script> |
|
66 | 66 | if( window.location.search.match( /print-pdf/gi ) ) { |
|
67 | 67 | var link = document.createElement( 'link' ); |
|
68 | 68 | link.rel = 'stylesheet'; |
|
69 | 69 | link.type = 'text/css'; |
|
70 | 70 | link.href = '{{resources.reveal.url_prefix}}/css/print/pdf.css'; |
|
71 | 71 | document.getElementsByTagName( 'head' )[0].appendChild( link ); |
|
72 | 72 | } |
|
73 | 73 | |
|
74 | 74 | </script> |
|
75 | 75 | |
|
76 | 76 | <!--[if lt IE 9]> |
|
77 | 77 | <script src="{{resources.reveal.url_prefix}}/lib/js/html5shiv.js"></script> |
|
78 | 78 | <![endif]--> |
|
79 | 79 | |
|
80 | 80 | <!-- Get Font-awesome from cdn --> |
|
81 | 81 | <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css"> |
|
82 | 82 | |
|
83 | 83 | {% for css in resources.inlining.css -%} |
|
84 | 84 | <style type="text/css"> |
|
85 | 85 | {{ css }} |
|
86 | 86 | </style> |
|
87 | 87 | {% endfor %} |
|
88 | 88 | |
|
89 | 89 | <style type="text/css"> |
|
90 | 90 | /* Overrides of notebook CSS for static HTML export */ |
|
91 | 91 | html { |
|
92 | 92 | overflow-y: auto; |
|
93 | 93 | } |
|
94 | 94 | .reveal { |
|
95 | 95 | font-size: 160%; |
|
96 | 96 | } |
|
97 | 97 | .reveal pre { |
|
98 | 98 | width: inherit; |
|
99 | 99 | padding: 0.4em; |
|
100 | 100 | margin: 0px; |
|
101 | 101 | font-family: monospace, sans-serif; |
|
102 | 102 | font-size: 80%; |
|
103 | 103 | box-shadow: 0px 0px 0px rgba(0, 0, 0, 0); |
|
104 | 104 | } |
|
105 | 105 | .reveal section img { |
|
106 | 106 | border: 0px solid black; |
|
107 | 107 | box-shadow: 0 0 10px rgba(0, 0, 0, 0); |
|
108 | 108 | } |
|
109 | 109 | .reveal i { |
|
110 | 110 | font-style: normal; |
|
111 | 111 | font-family: FontAwesome; |
|
112 | 112 | font-size: 2em; |
|
113 | 113 | } |
|
114 | 114 | .reveal .slides { |
|
115 | 115 | text-align: left; |
|
116 | 116 | } |
|
117 | 117 | .reveal.fade { |
|
118 | 118 | opacity: 1; |
|
119 | 119 | } |
|
120 | 120 | .reveal .progress { |
|
121 | 121 | position: static; |
|
122 | 122 | } |
|
123 | 123 | div.input_area { |
|
124 | 124 | padding: 0.06em; |
|
125 | 125 | } |
|
126 | 126 | div.code_cell { |
|
127 | 127 | background-color: transparent; |
|
128 | 128 | } |
|
129 | 129 | div.prompt { |
|
130 | 130 | width: 11ex; |
|
131 | 131 | padding: 0.4em; |
|
132 | 132 | margin: 0px; |
|
133 | 133 | font-family: monospace, sans-serif; |
|
134 | 134 | font-size: 80%; |
|
135 | 135 | text-align: right; |
|
136 | 136 | } |
|
137 | 137 | div.output_area pre { |
|
138 | 138 | font-family: monospace, sans-serif; |
|
139 | 139 | font-size: 80%; |
|
140 | 140 | } |
|
141 | 141 | div.output_prompt { |
|
142 | 142 | /* 5px right shift to account for margin in parent container */ |
|
143 | 143 | margin: 5px 5px 0 0; |
|
144 | 144 | } |
|
145 | 145 | .rendered_html p { |
|
146 | 146 | text-align: inherit; |
|
147 | 147 | } |
|
148 | 148 | </style> |
|
149 | 149 | |
|
150 | 150 | <!-- Custom stylesheet, it must be in the same directory as the html file --> |
|
151 | 151 | <link rel="stylesheet" href="custom.css"> |
|
152 | 152 | |
|
153 | 153 | </head> |
|
154 | 154 | {% endblock header%} |
|
155 | 155 | |
|
156 | 156 | |
|
157 | 157 | {% block body %} |
|
158 | 158 | <body> |
|
159 | 159 | <div class="reveal"> |
|
160 | 160 | <div class="slides"> |
|
161 | 161 | {{ super() }} |
|
162 | 162 | </div> |
|
163 | 163 | </div> |
|
164 | 164 | |
|
165 | 165 | <script src="{{resources.reveal.url_prefix}}/lib/js/head.min.js"></script> |
|
166 | 166 | |
|
167 | 167 | <script src="{{resources.reveal.url_prefix}}/js/reveal.js"></script> |
|
168 | 168 | |
|
169 | 169 | <script> |
|
170 | 170 | |
|
171 | 171 | // Full list of configuration options available here: https://github.com/hakimel/reveal.js#configuration |
|
172 | 172 | Reveal.initialize({ |
|
173 | 173 | controls: true, |
|
174 | 174 | progress: true, |
|
175 | 175 | history: true, |
|
176 | 176 | |
|
177 | 177 | theme: Reveal.getQueryHash().theme, // available themes are in /css/theme |
|
178 | 178 | transition: Reveal.getQueryHash().transition || 'linear', // default/cube/page/concave/zoom/linear/none |
|
179 | 179 | |
|
180 | 180 | // Optional libraries used to extend on reveal.js |
|
181 | 181 | dependencies: [ |
|
182 | 182 | { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } }, |
|
183 | 183 | { src: "{{resources.reveal.url_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } } |
|
184 | 184 | ] |
|
185 | 185 | }); |
|
186 | 186 | </script> |
|
187 | 187 | |
|
188 | 188 | <!-- Loading mathjax macro --> |
|
189 | 189 | {{ mathjax() }} |
|
190 | 190 | |
|
191 | 191 | <script> |
|
192 | 192 | Reveal.addEventListener( 'slidechanged', function( event ) { |
|
193 | 193 | window.scrollTo(0,0); |
|
194 | 194 | MathJax.Hub.Rerender(event.currentSlide); |
|
195 | 195 | }); |
|
196 | 196 | </script> |
|
197 | 197 | |
|
198 | 198 | </body> |
|
199 | 199 | {% endblock body %} |
|
200 | 200 | |
|
201 | 201 | {% block footer %} |
|
202 | 202 | </html> |
|
203 | 203 | {% endblock footer %} No newline at end of file |
General Comments 0
You need to be logged in to leave comments.
Login now