##// END OF EJS Templates
Merge pull request #4048 from minrk/finish-notes...
Min RK -
r12517:829790a6 merge
parent child Browse files
Show More
@@ -4,11 +4,9 b' python:'
4 4 - 2.7
5 5 - 3.3
6 6 before_install:
7 - pip install jinja2
8 7 - easy_install -q pyzmq
8 - pip install jinja2 sphinx pygments tornado
9 9 - sudo apt-get install pandoc
10 - pip install pygments
11 - pip install sphinx
12 10 install:
13 11 - python setup.py install -q
14 12 script:
@@ -63,8 +63,7 b' nbconvert_aliases.update({'
63 63 'writer' : 'NbConvertApp.writer_class',
64 64 'post': 'NbConvertApp.postprocessor_class',
65 65 'output': 'NbConvertApp.output_base',
66 'offline-slides': 'RevealHelpPreprocessor.url_prefix',
67 'slide-notes': 'RevealHelpPreprocessor.speaker_notes'
66 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
68 67 })
69 68
70 69 nbconvert_flags = {}
@@ -1,3 +1,8 b''
1 1 from .base import PostProcessorBase
2 2 from .pdf import PDFPostProcessor
3
4 # protect against unavailable tornado
5 try:
3 6 from .serve import ServePostProcessor
7 except ImportError:
8 pass
@@ -1,6 +1,4 b''
1 """
2 Contains postprocessor for serving nbconvert output.
3 """
1 """PostProcessor for serving reveal.js HTML slideshows."""
4 2 #-----------------------------------------------------------------------------
5 3 #Copyright (c) 2013, the IPython Development Team.
6 4 #
@@ -16,40 +14,94 b' Contains postprocessor for serving nbconvert output.'
16 14 import os
17 15 import webbrowser
18 16
19 from BaseHTTPServer import HTTPServer
20 from SimpleHTTPServer import SimpleHTTPRequestHandler
17 from tornado import web, ioloop, httpserver
18 from tornado.httpclient import AsyncHTTPClient
21 19
22 from IPython.utils.traitlets import Bool
20 from IPython.utils.traitlets import Bool, Unicode, Int
23 21
24 22 from .base import PostProcessorBase
25 23
26 24 #-----------------------------------------------------------------------------
27 25 # Classes
28 26 #-----------------------------------------------------------------------------
27
28 class ProxyHandler(web.RequestHandler):
29 """handler the proxies requests from a local prefix to a CDN"""
30 @web.asynchronous
31 def get(self, prefix, url):
32 """proxy a request to a CDN"""
33 proxy_url = "/".join([self.settings['cdn'], url])
34 client = self.settings['client']
35 client.fetch(proxy_url, callback=self.finish_get)
36
37 def finish_get(self, response):
38 """finish the request"""
39 # copy potentially relevant headers
40 for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]:
41 if header in response.headers:
42 self.set_header(header, response.headers[header])
43 self.finish(response.body)
44
29 45 class ServePostProcessor(PostProcessorBase):
30 """Post processor designed to serve files"""
46 """Post processor designed to serve files
47
48 Proxies reveal.js requests to a CDN if no local reveal.js is present
49 """
31 50
32 51
33 52 open_in_browser = Bool(True, config=True,
34 help="""Set to False to deactivate
35 the opening of the browser""")
53 help="""Should the browser be opened automatically?"""
54 )
55 reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.4.0", config=True,
56 help="""URL for reveal.js CDN."""
57 )
58 reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js")
59 ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.")
60 port = Int(8000, config=True, help="port for the server to listen on.")
36 61
37 62 def postprocess(self, input):
38 """
39 Simple implementation to serve the build directory.
40 """
41
42 try:
63 """Serve the build directory with a webserver."""
43 64 dirname, filename = os.path.split(input)
44 if dirname:
45 os.chdir(dirname)
46 httpd = HTTPServer(('127.0.0.1', 8000), SimpleHTTPRequestHandler)
47 sa = httpd.socket.getsockname()
48 url = "http://" + sa[0] + ":" + str(sa[1]) + "/" + filename
65 handlers = [
66 (r"/(.+)", web.StaticFileHandler, {'path' : dirname}),
67 (r"/", web.RedirectHandler, {"url": "/%s" % filename})
68 ]
69
70 if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")):
71 # reveal specifically from CDN, nothing to do
72 pass
73 elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)):
74 # reveal prefix exists
75 self.log.info("Serving local %s", self.reveal_prefix)
76 else:
77 self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn)
78 handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler))
79
80 app = web.Application(handlers,
81 cdn=self.reveal_cdn,
82 client=AsyncHTTPClient(),
83 )
84 # hook up tornado logging to our logger
85 from tornado import log
86 log.app_log = self.log
87
88 http_server = httpserver.HTTPServer(app)
89 http_server.listen(self.port, address=self.ip)
90 url = "http://%s:%i/%s" % (self.ip, self.port, filename)
91 print("Serving your slides at %s" % url)
92 print("Use Control-C to stop this server")
49 93 if self.open_in_browser:
50 94 webbrowser.open(url, new=2)
51 print("Serving your slides on " + url)
52 print("Use Control-C to stop this server.")
53 httpd.serve_forever()
95 try:
96 ioloop.IOLoop.instance().start()
54 97 except KeyboardInterrupt:
55 print("The server is shut down.")
98 print("\nInterrupted")
99
100 def main(path):
101 """allow running this module to serve the slides"""
102 server = ServePostProcessor()
103 server(path)
104
105 if __name__ == '__main__':
106 import sys
107 main(sys.argv[1])
@@ -24,15 +24,14 b' from IPython.utils.traitlets import Unicode, Bool'
24 24
25 25 class RevealHelpPreprocessor(Preprocessor):
26 26
27 url_prefix = Unicode('//cdn.jsdelivr.net/reveal.js/2.4.0',
28 config=True,
29 help="""If you want to use a local reveal.js library,
30 use 'url_prefix':'reveal.js' in your config object.""")
27 url_prefix = Unicode('reveal.js', config=True,
28 help="""The URL prefix for reveal.js.
29 This can be a a relative URL for a local copy of reveal.js,
30 or point to a CDN.
31 31
32 speaker_notes = Bool(False,
33 config=True,
34 help="""If you want to use the speaker notes
35 set this to True.""")
32 For speaker notes to work, a local reveal.js prefix must be used.
33 """
34 )
36 35
37 36 def preprocess(self, nb, resources):
38 37 """
@@ -65,30 +64,4 b' class RevealHelpPreprocessor(Preprocessor):'
65 64 if not isinstance(resources['reveal'], dict):
66 65 resources['reveal'] = {}
67 66 resources['reveal']['url_prefix'] = self.url_prefix
68 resources['reveal']['notes_prefix'] = self.url_prefix
69
70 cdn = 'http://cdn.jsdelivr.net/reveal.js/2.4.0'
71 local = 'local'
72 html_path = 'plugin/notes/notes.html'
73 js_path = 'plugin/notes/notes.js'
74
75 html_infile = os.path.join(cdn, html_path)
76 js_infile = os.path.join(cdn, js_path)
77 html_outfile = os.path.join(local, html_path)
78 js_outfile = os.path.join(local, js_path)
79
80 if self.speaker_notes:
81 if 'outputs' not in resources:
82 resources['outputs'] = {}
83 resources['outputs'][html_outfile] = self.notes_helper(html_infile)
84 resources['outputs'][js_outfile] = self.notes_helper(js_infile)
85 resources['reveal']['notes_prefix'] = local
86
87 67 return nb, resources
88
89 def notes_helper(self, infile):
90 """Helper function to get the content from an url."""
91
92 content = urllib2.urlopen(infile).read()
93
94 return content
@@ -135,7 +135,7 b" transition: Reveal.getQueryHash().transition || 'linear', // default/cube/page/c"
135 135 dependencies: [
136 136 { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } },
137 137 { src: "{{resources.reveal.url_prefix}}/plugin/highlight/highlight.js", async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
138 { src: "{{resources.reveal.notes_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } }
138 { src: "{{resources.reveal.url_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } }
139 139 // { src: 'http://s7.addthis.com/js/300/addthis_widget.js', async: true},
140 140 ]
141 141 });
@@ -293,6 +293,8 b' def make_exclude():'
293 293
294 294 if not have['tornado']:
295 295 exclusions.append(ipjoin('html'))
296 exclusions.append(ipjoin('nbconvert', 'post_processors', 'serve'))
297 exclusions.append(ipjoin('nbconvert', 'post_processors', 'tests', 'test_serve'))
296 298
297 299 if not have['jinja2']:
298 300 exclusions.append(ipjoin('html', 'notebookapp'))
@@ -61,13 +61,8 b' The currently supported export formats are:'
61 61 * ``--to slides``
62 62
63 63 This generates a Reveal.js HTML slideshow.
64 It must be served by an HTTP server. The easiest way to get this is to add
64 It must be served by an HTTP server. The easiest way to do this is adding
65 65 ``--post serve`` on the command-line.
66 If you want to use the speaker notes plugin, just add
67 ``--slide-notes=True`` on the command-line.
68 For low connectivity environments, you can use a local copy of the reveal.js library,
69 just add ``--offline-slides=reveal.js`` on the command-line, and do not forget to move
70 your downloaded ``reveal.js`` library to the same folder where your slides are located.
71 66
72 67 * ``--to markdown``
73 68
General Comments 0
You need to be logged in to leave comments. Login now