From 829790a6e4b6505e700d80befc050439f5214216 2013-09-13 18:36:47 From: Min RK Date: 2013-09-13 18:36:47 Subject: [PATCH] Merge pull request #4048 from minrk/finish-notes finish up speaker-notes PR This removes the special handling of speaker notes, simply informing the user that speaker notes require local require.js. It also renames the alias name for reveal.js url prefix to something more descriptive (reveal-prefix). --- diff --git a/.travis.yml b/.travis.yml index 742511f..774f063 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,9 @@ python: - 2.7 - 3.3 before_install: - - pip install jinja2 - easy_install -q pyzmq + - pip install jinja2 sphinx pygments tornado - sudo apt-get install pandoc - - pip install pygments - - pip install sphinx install: - python setup.py install -q script: diff --git a/IPython/nbconvert/nbconvertapp.py b/IPython/nbconvert/nbconvertapp.py index 08bc0b8..370b2b3 100755 --- a/IPython/nbconvert/nbconvertapp.py +++ b/IPython/nbconvert/nbconvertapp.py @@ -63,8 +63,7 @@ nbconvert_aliases.update({ 'writer' : 'NbConvertApp.writer_class', 'post': 'NbConvertApp.postprocessor_class', 'output': 'NbConvertApp.output_base', - 'offline-slides': 'RevealHelpPreprocessor.url_prefix', - 'slide-notes': 'RevealHelpPreprocessor.speaker_notes' + 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix', }) nbconvert_flags = {} diff --git a/IPython/nbconvert/postprocessors/__init__.py b/IPython/nbconvert/postprocessors/__init__.py index 6cae641..9f954f3 100644 --- a/IPython/nbconvert/postprocessors/__init__.py +++ b/IPython/nbconvert/postprocessors/__init__.py @@ -1,3 +1,8 @@ from .base import PostProcessorBase from .pdf import PDFPostProcessor -from .serve import ServePostProcessor + +# protect against unavailable tornado +try: + from .serve import ServePostProcessor +except ImportError: + pass diff --git a/IPython/nbconvert/postprocessors/serve.py b/IPython/nbconvert/postprocessors/serve.py index 828f5f3..8a83419 100644 --- a/IPython/nbconvert/postprocessors/serve.py +++ b/IPython/nbconvert/postprocessors/serve.py @@ -1,6 +1,4 @@ -""" -Contains postprocessor for serving nbconvert output. -""" +"""PostProcessor for serving reveal.js HTML slideshows.""" #----------------------------------------------------------------------------- #Copyright (c) 2013, the IPython Development Team. # @@ -16,40 +14,94 @@ Contains postprocessor for serving nbconvert output. import os import webbrowser -from BaseHTTPServer import HTTPServer -from SimpleHTTPServer import SimpleHTTPRequestHandler +from tornado import web, ioloop, httpserver +from tornado.httpclient import AsyncHTTPClient -from IPython.utils.traitlets import Bool +from IPython.utils.traitlets import Bool, Unicode, Int from .base import PostProcessorBase #----------------------------------------------------------------------------- # Classes #----------------------------------------------------------------------------- + +class ProxyHandler(web.RequestHandler): + """handler the proxies requests from a local prefix to a CDN""" + @web.asynchronous + def get(self, prefix, url): + """proxy a request to a CDN""" + proxy_url = "/".join([self.settings['cdn'], url]) + client = self.settings['client'] + client.fetch(proxy_url, callback=self.finish_get) + + def finish_get(self, response): + """finish the request""" + # copy potentially relevant headers + for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]: + if header in response.headers: + self.set_header(header, response.headers[header]) + self.finish(response.body) + class ServePostProcessor(PostProcessorBase): - """Post processor designed to serve files""" + """Post processor designed to serve files + + Proxies reveal.js requests to a CDN if no local reveal.js is present + """ open_in_browser = Bool(True, config=True, - help="""Set to False to deactivate - the opening of the browser""") + help="""Should the browser be opened automatically?""" + ) + reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.4.0", config=True, + help="""URL for reveal.js CDN.""" + ) + reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js") + ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.") + port = Int(8000, config=True, help="port for the server to listen on.") def postprocess(self, input): - """ - Simple implementation to serve the build directory. - """ - + """Serve the build directory with a webserver.""" + dirname, filename = os.path.split(input) + handlers = [ + (r"/(.+)", web.StaticFileHandler, {'path' : dirname}), + (r"/", web.RedirectHandler, {"url": "/%s" % filename}) + ] + + if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")): + # reveal specifically from CDN, nothing to do + pass + elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)): + # reveal prefix exists + self.log.info("Serving local %s", self.reveal_prefix) + else: + self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn) + handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler)) + + app = web.Application(handlers, + cdn=self.reveal_cdn, + client=AsyncHTTPClient(), + ) + # hook up tornado logging to our logger + from tornado import log + log.app_log = self.log + + http_server = httpserver.HTTPServer(app) + http_server.listen(self.port, address=self.ip) + url = "http://%s:%i/%s" % (self.ip, self.port, filename) + print("Serving your slides at %s" % url) + print("Use Control-C to stop this server") + if self.open_in_browser: + webbrowser.open(url, new=2) try: - dirname, filename = os.path.split(input) - if dirname: - os.chdir(dirname) - httpd = HTTPServer(('127.0.0.1', 8000), SimpleHTTPRequestHandler) - sa = httpd.socket.getsockname() - url = "http://" + sa[0] + ":" + str(sa[1]) + "/" + filename - if self.open_in_browser: - webbrowser.open(url, new=2) - print("Serving your slides on " + url) - print("Use Control-C to stop this server.") - httpd.serve_forever() + ioloop.IOLoop.instance().start() except KeyboardInterrupt: - print("The server is shut down.") + print("\nInterrupted") + +def main(path): + """allow running this module to serve the slides""" + server = ServePostProcessor() + server(path) + +if __name__ == '__main__': + import sys + main(sys.argv[1]) diff --git a/IPython/nbconvert/preprocessors/revealhelp.py b/IPython/nbconvert/preprocessors/revealhelp.py index ae1110e..d9e213f 100755 --- a/IPython/nbconvert/preprocessors/revealhelp.py +++ b/IPython/nbconvert/preprocessors/revealhelp.py @@ -24,15 +24,14 @@ from IPython.utils.traitlets import Unicode, Bool class RevealHelpPreprocessor(Preprocessor): - url_prefix = Unicode('//cdn.jsdelivr.net/reveal.js/2.4.0', - config=True, - help="""If you want to use a local reveal.js library, - use 'url_prefix':'reveal.js' in your config object.""") - - speaker_notes = Bool(False, - config=True, - help="""If you want to use the speaker notes - set this to True.""") + url_prefix = Unicode('reveal.js', config=True, + help="""The URL prefix for reveal.js. + This can be a a relative URL for a local copy of reveal.js, + or point to a CDN. + + For speaker notes to work, a local reveal.js prefix must be used. + """ + ) def preprocess(self, nb, resources): """ @@ -65,30 +64,4 @@ class RevealHelpPreprocessor(Preprocessor): if not isinstance(resources['reveal'], dict): resources['reveal'] = {} resources['reveal']['url_prefix'] = self.url_prefix - resources['reveal']['notes_prefix'] = self.url_prefix - - cdn = 'http://cdn.jsdelivr.net/reveal.js/2.4.0' - local = 'local' - html_path = 'plugin/notes/notes.html' - js_path = 'plugin/notes/notes.js' - - html_infile = os.path.join(cdn, html_path) - js_infile = os.path.join(cdn, js_path) - html_outfile = os.path.join(local, html_path) - js_outfile = os.path.join(local, js_path) - - if self.speaker_notes: - if 'outputs' not in resources: - resources['outputs'] = {} - resources['outputs'][html_outfile] = self.notes_helper(html_infile) - resources['outputs'][js_outfile] = self.notes_helper(js_infile) - resources['reveal']['notes_prefix'] = local - return nb, resources - - def notes_helper(self, infile): - """Helper function to get the content from an url.""" - - content = urllib2.urlopen(infile).read() - - return content diff --git a/IPython/nbconvert/templates/slides_reveal.tpl b/IPython/nbconvert/templates/slides_reveal.tpl index 72444c5..2581c4d 100644 --- a/IPython/nbconvert/templates/slides_reveal.tpl +++ b/IPython/nbconvert/templates/slides_reveal.tpl @@ -135,7 +135,7 @@ transition: Reveal.getQueryHash().transition || 'linear', // default/cube/page/c dependencies: [ { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } }, { src: "{{resources.reveal.url_prefix}}/plugin/highlight/highlight.js", async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, -{ src: "{{resources.reveal.notes_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } } +{ src: "{{resources.reveal.url_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } } // { src: 'http://s7.addthis.com/js/300/addthis_widget.js', async: true}, ] }); diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 2a20d3c..2fbb848 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -293,6 +293,8 @@ def make_exclude(): if not have['tornado']: exclusions.append(ipjoin('html')) + exclusions.append(ipjoin('nbconvert', 'post_processors', 'serve')) + exclusions.append(ipjoin('nbconvert', 'post_processors', 'tests', 'test_serve')) if not have['jinja2']: exclusions.append(ipjoin('html', 'notebookapp')) diff --git a/docs/source/interactive/nbconvert.rst b/docs/source/interactive/nbconvert.rst index 0423274..1789a62 100644 --- a/docs/source/interactive/nbconvert.rst +++ b/docs/source/interactive/nbconvert.rst @@ -61,14 +61,9 @@ The currently supported export formats are: * ``--to slides`` This generates a Reveal.js HTML slideshow. - It must be served by an HTTP server. The easiest way to get this is to add + It must be served by an HTTP server. The easiest way to do this is adding ``--post serve`` on the command-line. - If you want to use the speaker notes plugin, just add - ``--slide-notes=True`` on the command-line. - For low connectivity environments, you can use a local copy of the reveal.js library, - just add ``--offline-slides=reveal.js`` on the command-line, and do not forget to move - your downloaded ``reveal.js`` library to the same folder where your slides are located. - + * ``--to markdown`` Simple markdown output. Markdown cells are unaffected,