From 13bba28f421d0028b443ce1570f5789be7bda7d2 2014-02-09 16:29:11 From: Doug Blank Date: 2014-02-09 16:29:11 Subject: [PATCH] Merge remote-tracking branch 'upstream/master' --- diff --git a/IPython/core/display.py b/IPython/core/display.py index b871906..6c48c6b 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -24,7 +24,7 @@ import struct from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode, unicode_type) - +from IPython.testing.skipdoctest import skip_doctest from .displaypub import publish_display_data #----------------------------------------------------------------------------- @@ -271,6 +271,24 @@ def display_javascript(*objs, **kwargs): """ _display_mimetype('application/javascript', objs, **kwargs) + +def display_pdf(*objs, **kwargs): + """Display the PDF representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw javascript data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('application/pdf', objs, **kwargs) + + #----------------------------------------------------------------------------- # Smart classes #----------------------------------------------------------------------------- @@ -699,3 +717,56 @@ def clear_output(wait=False): io.stdout.flush() print('\033[2K\r', file=io.stderr, end='') io.stderr.flush() + + +@skip_doctest +def set_matplotlib_formats(*formats, **kwargs): + """Select figure formats for the inline backend. Optionally pass quality for JPEG. + + For example, this enables PNG and JPEG output with a JPEG quality of 90%:: + + In [1]: set_matplotlib_formats('png', 'jpeg', quality=90) + + To set this in your config files use the following:: + + c.InlineBackend.figure_formats = {'pdf', 'png', 'svg'} + c.InlineBackend.quality = 90 + + Parameters + ---------- + *formats : list, tuple + One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'. + quality : int + A percentage for the quality of JPEG figures. Defaults to 90. + """ + from IPython.core.interactiveshell import InteractiveShell + from IPython.core.pylabtools import select_figure_formats + shell = InteractiveShell.instance() + select_figure_formats(shell, formats, quality=90) + +@skip_doctest +def set_matplotlib_close(close): + """Set whether the inline backend closes all figures automatically or not. + + By default, the inline backend used in the IPython Notebook will close all + matplotlib figures automatically after each cell is run. This means that + plots in different cells won't interfere. Sometimes, you may want to make + a plot in one cell and then refine it in later cells. This can be accomplished + by:: + + In [1]: set_matplotlib_close(False) + + To set this in your config files use the following:: + + c.InlineBackend.close_figures = False + + Parameters + ---------- + close : bool + Should all matplotlib figures be automatically closed after each cell is + run? + """ + from IPython.kernel.zmq.pylab.backend_inline import InlineBackend + ilbe = InlineBackend.instance() + ilbe.close_figures = close + diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index 9a2644c..a54fa42 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -93,6 +93,7 @@ class DisplayFormatter(Configurable): HTMLFormatter, SVGFormatter, PNGFormatter, + PDFFormatter, JPEGFormatter, LatexFormatter, JSONFormatter, @@ -116,6 +117,7 @@ class DisplayFormatter(Configurable): * text/latex * application/json * application/javascript + * application/pdf * image/png * image/jpeg * image/svg+xml @@ -766,11 +768,29 @@ class JavascriptFormatter(BaseFormatter): print_method = ObjectName('_repr_javascript_') + +class PDFFormatter(BaseFormatter): + """A PDF formatter. + + To defined the callables that compute to PDF representation of your + objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type` + or :meth:`for_type_by_name` methods to register functions that handle + this. + + The return value of this formatter should be raw PDF data, *not* + base64 encoded. + """ + format_type = Unicode('application/pdf') + + print_method = ObjectName('_repr_pdf_') + + FormatterABC.register(BaseFormatter) FormatterABC.register(PlainTextFormatter) FormatterABC.register(HTMLFormatter) FormatterABC.register(SVGFormatter) FormatterABC.register(PNGFormatter) +FormatterABC.register(PDFFormatter) FormatterABC.register(JPEGFormatter) FormatterABC.register(LatexFormatter) FormatterABC.register(JSONFormatter) @@ -789,6 +809,7 @@ def format_display_data(obj, include=None, exclude=None): * text/latex * application/json * application/javascript + * application/pdf * image/png * image/jpeg * image/svg+xml diff --git a/IPython/core/magics/pylab.py b/IPython/core/magics/pylab.py index bf616b0..05faf3c 100644 --- a/IPython/core/magics/pylab.py +++ b/IPython/core/magics/pylab.py @@ -47,28 +47,32 @@ class PylabMagics(Magics): """Set up matplotlib to work interactively. This function lets you activate matplotlib interactive support - at any point during an IPython session. - It does not import anything into the interactive namespace. + at any point during an IPython session. It does not import anything + into the interactive namespace. - If you are using the inline matplotlib backend for embedded figures, - you can adjust its behavior via the %config magic:: - - # enable SVG figures, necessary for SVG+XHTML export in the qtconsole - In [1]: %config InlineBackend.figure_format = 'svg' + If you are using the inline matplotlib backend in the IPython Notebook + you can set which figure formats are enabled using the following:: + + In [1]: from IPython.display import set_matplotlib_formats + + In [2]: set_matplotlib_formats('pdf', 'svg') - # change the behavior of closing all figures at the end of each - # execution (cell), or allowing reuse of active figures across - # cells: - In [2]: %config InlineBackend.close_figures = False + See the docstring of `IPython.display.set_matplotlib_formats` and + `IPython.display.set_matplotlib_close` for more information on + changing the behavior of the inline backend. Examples -------- - In this case, where the MPL default is TkAgg:: + To enable the inline backend for usage with the IPython Notebook:: + + In [1]: %matplotlib inline + + In this case, where the matplotlib default is TkAgg:: In [2]: %matplotlib Using matplotlib backend: TkAgg - But you can explicitly request a different backend:: + But you can explicitly request a different GUI backend:: In [3]: %matplotlib qt """ diff --git a/IPython/core/pylabtools.py b/IPython/core/pylabtools.py index a8779d2..106b856 100644 --- a/IPython/core/pylabtools.py +++ b/IPython/core/pylabtools.py @@ -25,6 +25,7 @@ from io import BytesIO from IPython.core.display import _pngxy from IPython.utils.decorators import flag_calls +from IPython.utils import py3compat # If user specifies a GUI, that dictates the backend, otherwise we read the # user's mpl default from the mpl rc structure @@ -165,10 +166,17 @@ def mpl_runner(safe_execfile): return mpl_execfile -def select_figure_format(shell, fmt, quality=90): - """Select figure format for inline backend, can be 'png', 'retina', 'jpg', or 'svg'. +def select_figure_formats(shell, formats, quality=90): + """Select figure formats for the inline backend. - Using this method ensures only one figure format is active at a time. + Parameters + ========== + shell : InteractiveShell + The main IPython instance. + formats : list + One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'. + quality : int + A percentage for the quality of JPEG figures. """ from matplotlib.figure import Figure from IPython.kernel.zmq.pylab import backend_inline @@ -176,22 +184,26 @@ def select_figure_format(shell, fmt, quality=90): svg_formatter = shell.display_formatter.formatters['image/svg+xml'] png_formatter = shell.display_formatter.formatters['image/png'] jpg_formatter = shell.display_formatter.formatters['image/jpeg'] + pdf_formatter = shell.display_formatter.formatters['application/pdf'] - [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ] + if isinstance(formats, py3compat.string_types): + formats = {formats} - if fmt == 'png': - png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png')) - elif fmt in ('png2x', 'retina'): - png_formatter.for_type(Figure, retina_figure) - elif fmt in ('jpg', 'jpeg'): - jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality)) - elif fmt == 'svg': - svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg')) - else: - raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', not %r" % fmt) + [ f.type_printers.pop(Figure, None) for f in {svg_formatter, png_formatter, jpg_formatter} ] - # set the format to be used in the backend() - backend_inline._figure_format = fmt + for fmt in formats: + if fmt == 'png': + png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png')) + elif fmt in ('png2x', 'retina'): + png_formatter.for_type(Figure, retina_figure) + elif fmt in ('jpg', 'jpeg'): + jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality)) + elif fmt == 'svg': + svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg')) + elif fmt == 'pdf': + pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf')) + else: + raise ValueError("supported formats are: 'png', 'retina', 'svg', 'jpg', 'pdf' not %r" % fmt) #----------------------------------------------------------------------------- # Code for initializing matplotlib and importing pylab @@ -342,5 +354,5 @@ def configure_inline_support(shell, backend): del shell._saved_rcParams # Setup the default figure format - select_figure_format(shell, cfg.figure_format, cfg.quality) + select_figure_formats(shell, cfg.figure_formats, cfg.quality) diff --git a/IPython/core/tests/test_formatters.py b/IPython/core/tests/test_formatters.py index 184d5d5..710ce1b 100644 --- a/IPython/core/tests/test_formatters.py +++ b/IPython/core/tests/test_formatters.py @@ -8,7 +8,9 @@ except: numpy = None import nose.tools as nt -from IPython.core.formatters import PlainTextFormatter, HTMLFormatter, _mod_name_key +from IPython.core.formatters import ( + PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key +) from IPython.utils.io import capture_output class A(object): @@ -279,4 +281,11 @@ def test_warn_error_pretty_method(): nt.assert_in("text/plain", captured.stderr) nt.assert_in("argument", captured.stderr) +class MakePDF(object): + def _repr_pdf_(self): + return 'PDF' +def test_pdf_formatter(): + pdf = MakePDF() + f = PDFFormatter() + nt.assert_equal(f(pdf), 'PDF') diff --git a/IPython/html/auth/login.py b/IPython/html/auth/login.py index 51ab621..1ad4673 100644 --- a/IPython/html/auth/login.py +++ b/IPython/html/auth/login.py @@ -32,13 +32,13 @@ class LoginHandler(IPythonHandler): def _render(self, message=None): self.write(self.render_template('login.html', - next=url_escape(self.get_argument('next', default=self.base_project_url)), + next=url_escape(self.get_argument('next', default=self.base_url)), message=message, )) def get(self): if self.current_user: - self.redirect(self.get_argument('next', default=self.base_project_url)) + self.redirect(self.get_argument('next', default=self.base_url)) else: self._render() @@ -51,7 +51,7 @@ class LoginHandler(IPythonHandler): self._render(message={'error': 'Invalid password'}) return - self.redirect(self.get_argument('next', default=self.base_project_url)) + self.redirect(self.get_argument('next', default=self.base_url)) #----------------------------------------------------------------------------- diff --git a/IPython/html/base/handlers.py b/IPython/html/base/handlers.py index 4cb674b..5941d11 100644 --- a/IPython/html/base/handlers.py +++ b/IPython/html/base/handlers.py @@ -133,8 +133,8 @@ class IPythonHandler(AuthenticatedHandler): return self.settings.get('mathjax_url', '') @property - def base_project_url(self): - return self.settings.get('base_project_url', '/') + def base_url(self): + return self.settings.get('base_url', '/') @property def base_kernel_url(self): @@ -180,7 +180,7 @@ class IPythonHandler(AuthenticatedHandler): @property def template_namespace(self): return dict( - base_project_url=self.base_project_url, + base_url=self.base_url, base_kernel_url=self.base_kernel_url, logged_in=self.logged_in, login_available=self.login_available, diff --git a/IPython/html/notebook/handlers.py b/IPython/html/notebook/handlers.py index e442d00..fe415e6 100644 --- a/IPython/html/notebook/handlers.py +++ b/IPython/html/notebook/handlers.py @@ -58,7 +58,7 @@ class NotebookRedirectHandler(IPythonHandler): nbm = self.notebook_manager if nbm.path_exists(path): # it's a *directory*, redirect to /tree - url = url_path_join(self.base_project_url, 'tree', path) + url = url_path_join(self.base_url, 'tree', path) else: # otherwise, redirect to /files if '/files/' in path: @@ -73,7 +73,7 @@ class NotebookRedirectHandler(IPythonHandler): if not os.path.exists(files_path): path = path.replace('/files/', '/', 1) - url = url_path_join(self.base_project_url, 'files', path) + url = url_path_join(self.base_url, 'files', path) url = url_escape(url) self.log.debug("Redirecting %s to %s", self.request.path, url) self.redirect(url) diff --git a/IPython/html/notebookapp.py b/IPython/html/notebookapp.py index 51902a2..1168451 100644 --- a/IPython/html/notebookapp.py +++ b/IPython/html/notebookapp.py @@ -133,42 +133,42 @@ def load_handlers(name): class NotebookWebApplication(web.Application): def __init__(self, ipython_app, kernel_manager, notebook_manager, - cluster_manager, session_manager, log, base_project_url, + cluster_manager, session_manager, log, base_url, settings_overrides): settings = self.init_settings( ipython_app, kernel_manager, notebook_manager, cluster_manager, - session_manager, log, base_project_url, settings_overrides) + session_manager, log, base_url, settings_overrides) handlers = self.init_handlers(settings) super(NotebookWebApplication, self).__init__(handlers, **settings) def init_settings(self, ipython_app, kernel_manager, notebook_manager, - cluster_manager, session_manager, log, base_project_url, + cluster_manager, session_manager, log, base_url, settings_overrides): # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and - # base_project_url will always be unicode, which will in turn + # base_url will always be unicode, which will in turn # make the patterns unicode, and ultimately result in unicode # keys in kwargs to handler._execute(**kwargs) in tornado. - # This enforces that base_project_url be ascii in that situation. + # This enforces that base_url be ascii in that situation. # # Note that the URLs these patterns check against are escaped, # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'. - base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii') + base_url = py3compat.unicode_to_str(base_url, 'ascii') template_path = settings_overrides.get("template_path", os.path.join(os.path.dirname(__file__), "templates")) settings = dict( # basics log_function=log_request, - base_project_url=base_project_url, + base_url=base_url, base_kernel_url=ipython_app.base_kernel_url, template_path=template_path, static_path=ipython_app.static_file_path, static_handler_class = FileFindHandler, - static_url_prefix = url_path_join(base_project_url,'/static/'), + static_url_prefix = url_path_join(base_url,'/static/'), # authentication cookie_secret=ipython_app.cookie_secret, - login_url=url_path_join(base_project_url,'/login'), + login_url=url_path_join(base_url,'/login'), password=ipython_app.password, # managers @@ -206,10 +206,10 @@ class NotebookWebApplication(web.Application): (r"/files/(.*)", AuthenticatedFileHandler, {'path' : settings['notebook_manager'].notebook_dir}), (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}), ]) - # prepend base_project_url onto the patterns that we match + # prepend base_url onto the patterns that we match new_handlers = [] for handler in handlers: - pattern = url_path_join(settings['base_project_url'], handler[0]) + pattern = url_path_join(settings['base_url'], handler[0]) new_handler = tuple([pattern] + list(handler[1:])) new_handlers.append(new_handler) # add 404 on the end, which will catch everything that falls through @@ -414,17 +414,22 @@ class NotebookApp(BaseIPythonApplication): if not new: self.mathjax_url = u'' - base_project_url = Unicode('/', config=True, + base_url = Unicode('/', config=True, help='''The base URL for the notebook server. Leading and trailing slashes can be omitted, and will automatically be added. ''') - def _base_project_url_changed(self, name, old, new): + def _base_url_changed(self, name, old, new): if not new.startswith('/'): - self.base_project_url = '/'+new + self.base_url = '/'+new elif not new.endswith('/'): - self.base_project_url = new+'/' + self.base_url = new+'/' + + base_project_url = Unicode('/', config=True, help="""DEPRECATED use base_url""") + def _base_project_url_changed(self, name, old, new): + self.log.warn("base_project_url is deprecated, use base_url") + self.base_url = new base_kernel_url = Unicode('/', config=True, help='''The base URL for the kernel server @@ -473,12 +478,12 @@ class NotebookApp(BaseIPythonApplication): if not self.enable_mathjax: return u'' static_url_prefix = self.webapp_settings.get("static_url_prefix", - url_path_join(self.base_project_url, "static") + url_path_join(self.base_url, "static") ) # try local mathjax, either in nbextensions/mathjax or static/mathjax for (url_prefix, search_path) in [ - (url_path_join(self.base_project_url, "nbextensions"), self.nbextensions_path), + (url_path_join(self.base_url, "nbextensions"), self.nbextensions_path), (static_url_prefix, self.static_file_path), ]: self.log.debug("searching for local mathjax in %s", search_path) @@ -586,7 +591,7 @@ class NotebookApp(BaseIPythonApplication): self.web_app = NotebookWebApplication( self, self.kernel_manager, self.notebook_manager, self.cluster_manager, self.session_manager, - self.log, self.base_project_url, self.webapp_settings + self.log, self.base_url, self.webapp_settings ) if self.certfile: ssl_options = dict(certfile=self.certfile) @@ -639,7 +644,7 @@ class NotebookApp(BaseIPythonApplication): def _url(self, ip): proto = 'https' if self.certfile else 'http' - return "%s://%s:%i%s" % (proto, ip, self.port, self.base_project_url) + return "%s://%s:%i%s" % (proto, ip, self.port, self.base_url) def init_signal(self): if not sys.platform.startswith('win'): @@ -745,7 +750,7 @@ class NotebookApp(BaseIPythonApplication): 'hostname': self.ip if self.ip else 'localhost', 'port': self.port, 'secure': bool(self.certfile), - 'base_project_url': self.base_project_url, + 'base_url': self.base_url, 'notebook_dir': os.path.abspath(self.notebook_manager.notebook_dir), } diff --git a/IPython/html/services/notebooks/handlers.py b/IPython/html/services/notebooks/handlers.py index ac44dd6..dc34c88 100644 --- a/IPython/html/services/notebooks/handlers.py +++ b/IPython/html/services/notebooks/handlers.py @@ -47,7 +47,7 @@ class NotebookHandler(IPythonHandler): The URL path of the notebook. """ return url_escape(url_path_join( - self.base_project_url, 'api', 'notebooks', path, name + self.base_url, 'api', 'notebooks', path, name )) def _finish_model(self, model, location=True): @@ -242,7 +242,7 @@ class NotebookCheckpointsHandler(IPythonHandler): nbm = self.notebook_manager checkpoint = nbm.create_checkpoint(name, path) data = json.dumps(checkpoint, default=date_default) - location = url_path_join(self.base_project_url, 'api/notebooks', + location = url_path_join(self.base_url, 'api/notebooks', path, name, 'checkpoints', checkpoint['id']) self.set_header('Location', url_escape(location)) self.set_status(201) diff --git a/IPython/html/static/auth/js/loginwidget.js b/IPython/html/static/auth/js/loginwidget.js index 1998aca..0ed24ab 100644 --- a/IPython/html/static/auth/js/loginwidget.js +++ b/IPython/html/static/auth/js/loginwidget.js @@ -10,10 +10,11 @@ //============================================================================ var IPython = (function (IPython) { + "use strict"; var LoginWidget = function (selector, options) { - var options = options || {}; - this.base_url = options.baseProjectUrl || $('body').data('baseProjectUrl') ; + options = options || {}; + this.base_url = options.base_url || IPython.utils.get_body_data("baseUrl"); this.selector = selector; if (this.selector !== undefined) { this.element = $(selector); @@ -30,10 +31,16 @@ var IPython = (function (IPython) { LoginWidget.prototype.bind_events = function () { var that = this; this.element.find("button#logout").click(function () { - window.location = that.base_url+"logout"; + window.location = IPythin.utils.url_join_encode( + that.base_url, + "logout" + ); }); this.element.find("button#login").click(function () { - window.location = that.base_url+"login"; + window.location = IPythin.utils.url_join_encode( + that.base_url, + "login" + ); }); }; diff --git a/IPython/html/static/base/js/utils.js b/IPython/html/static/base/js/utils.js index 0e6c9fb..c110ecc 100644 --- a/IPython/html/static/base/js/utils.js +++ b/IPython/html/static/base/js/utils.js @@ -417,15 +417,29 @@ IPython.utils = (function (IPython) { url = url + arguments[i]; } } + url = url.replace(/\/\/+/, '/'); return url; }; + var parse_url = function (url) { + // an `a` element with an href allows attr-access to the parsed segments of a URL + // a = parse_url("http://localhost:8888/path/name#hash") + // a.protocol = "http:" + // a.host = "localhost:8888" + // a.hostname = "localhost" + // a.port = 8888 + // a.pathname = "/path/name" + // a.hash = "#hash" + var a = document.createElement("a"); + a.href = url; + return a; + }; var encode_uri_components = function (uri) { // encode just the components of a multi-segment uri, // leaving '/' separators return uri.split('/').map(encodeURIComponent).join('/'); - } + }; var url_join_encode = function () { // join a sequence of url components with '/', @@ -443,7 +457,15 @@ IPython.utils = (function (IPython) { } else { return [filename, '']; } - } + }; + + + var get_body_data = function(key) { + // get a url-encoded item from body.data and decode it + // we should never have any encoded URLs anywhere else in code + // until we are building an actual request + return decodeURIComponent($('body').data(key)); + }; // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript @@ -508,6 +530,8 @@ IPython.utils = (function (IPython) { fixCarriageReturn : fixCarriageReturn, autoLinkUrls : autoLinkUrls, points_to_pixels : points_to_pixels, + get_body_data : get_body_data, + parse_url : parse_url, url_path_join : url_path_join, url_join_encode : url_join_encode, encode_uri_components : encode_uri_components, diff --git a/IPython/html/static/notebook/js/keyboardmanager.js b/IPython/html/static/notebook/js/keyboardmanager.js index c5611af..a6df32a 100644 --- a/IPython/html/static/notebook/js/keyboardmanager.js +++ b/IPython/html/static/notebook/js/keyboardmanager.js @@ -510,7 +510,7 @@ var IPython = (function (IPython) { }, 'h' : { help : 'keyboard shortcuts', - help_index : 'gd', + help_index : 'ge', handler : function (event) { IPython.quick_help.show_keyboard_shortcuts(); return false; @@ -532,6 +532,14 @@ var IPython = (function (IPython) { return false; } }, + 'q' : { + help : 'close pager', + help_index : 'gd', + handler : function (event) { + IPython.pager.collapse(); + return false; + } + }, } diff --git a/IPython/html/static/notebook/js/main.js b/IPython/html/static/notebook/js/main.js index 32a9090..59e69f6 100644 --- a/IPython/html/static/notebook/js/main.js +++ b/IPython/html/static/notebook/js/main.js @@ -8,7 +8,6 @@ //============================================================================ // On document ready //============================================================================ -"use strict"; // for the time beeing, we have to pass marked as a parameter here, // as injecting require.js make marked not to put itself in the globals, @@ -18,28 +17,28 @@ require(['components/marked/lib/marked', 'notebook/js/widgets/init'], function (marked) { + "use strict"; - window.marked = marked + window.marked = marked; // monkey patch CM to be able to syntax highlight cell magics // bug reported upstream, // see https://github.com/marijnh/CodeMirror2/issues/670 - if(CodeMirror.getMode(1,'text/plain').indent == undefined ){ + if(CodeMirror.getMode(1,'text/plain').indent === undefined ){ console.log('patching CM for undefined indent'); CodeMirror.modes.null = function() { - return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0}} - } + return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0;}}; + }; } CodeMirror.patchedGetMode = function(config, mode){ var cmmode = CodeMirror.getMode(config, mode); - if(cmmode.indent == null) - { + if(cmmode.indent === null) { console.log('patch mode "' , mode, '" on the fly'); - cmmode.indent = function(){return 0}; + cmmode.indent = function(){return 0;}; } return cmmode; - } + }; // end monkey patching CodeMirror IPython.mathjaxutils.init(); @@ -47,35 +46,32 @@ function (marked) { $('#ipython-main-app').addClass('border-box-sizing'); $('div#notebook_panel').addClass('border-box-sizing'); - var baseProjectUrl = $('body').data('baseProjectUrl'); - var notebookPath = $('body').data('notebookPath'); - var notebookName = $('body').data('notebookName'); - notebookName = decodeURIComponent(notebookName); - notebookPath = decodeURIComponent(notebookPath); - console.log(notebookName); - if (notebookPath == 'None'){ - notebookPath = ""; - } + var opts = { + base_url : IPython.utils.get_body_data("baseUrl"), + base_kernel_url : IPython.utils.get_body_data("baseKernelUrl"), + notebook_path : IPython.utils.get_body_data("notebookPath"), + notebook_name : IPython.utils.get_body_data('notebookName') + }; IPython.page = new IPython.Page(); IPython.layout_manager = new IPython.LayoutManager(); IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter'); IPython.quick_help = new IPython.QuickHelp(); - IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl}); - IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, notebookPath:notebookPath, notebookName:notebookName}); + IPython.login_widget = new IPython.LoginWidget('span#login_widget', opts); + IPython.notebook = new IPython.Notebook('div#notebook', opts); IPython.keyboard_manager = new IPython.KeyboardManager(); IPython.save_widget = new IPython.SaveWidget('span#save_widget'); - IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl, notebookPath: notebookPath}) - IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container') - IPython.tooltip = new IPython.Tooltip() - IPython.notification_area = new IPython.NotificationArea('#notification_area') + IPython.menubar = new IPython.MenuBar('#menubar', opts); + IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container'); + IPython.tooltip = new IPython.Tooltip(); + IPython.notification_area = new IPython.NotificationArea('#notification_area'); IPython.notification_area.init_notification_widgets(); IPython.layout_manager.do_resize(); $('body').append('
x'+
                      'x'+
-                     'x
') + 'x'); var nh = $('#test1').innerHeight(); var bh = $('#test2').innerHeight(); var ih = $('#test3').innerHeight(); @@ -101,7 +97,7 @@ function (marked) { $([IPython.events]).on('notebook_loaded.Notebook', first_load); $([IPython.events]).trigger('app_initialized.NotebookApp'); - IPython.notebook.load_notebook(notebookName, notebookPath); + IPython.notebook.load_notebook(opts.notebook_name, opts.notebook_path); if (marked) { marked.setOptions({ @@ -121,8 +117,6 @@ function (marked) { } return highlighted.value; } - }) + }); } -} - -); +}); diff --git a/IPython/html/static/notebook/js/maintoolbar.js b/IPython/html/static/notebook/js/maintoolbar.js index ec00315..6db1956 100644 --- a/IPython/html/static/notebook/js/maintoolbar.js +++ b/IPython/html/static/notebook/js/maintoolbar.js @@ -100,8 +100,9 @@ var IPython = (function (IPython) { label : 'Run Cell', icon : 'icon-play', callback : function () { - IPython.notebook.execute_cell(); - } + // emulate default shift-enter behavior + IPython.notebook.execute_cell_and_select_below(); + } }, { id : 'interrupt_b', diff --git a/IPython/html/static/notebook/js/menubar.js b/IPython/html/static/notebook/js/menubar.js index 622d76e..dacf18e 100644 --- a/IPython/html/static/notebook/js/menubar.js +++ b/IPython/html/static/notebook/js/menubar.js @@ -30,16 +30,14 @@ var IPython = (function (IPython) { * * @param selector {string} selector for the menubar element in DOM * @param {object} [options] - * @param [options.baseProjectUrl] {String} String to use for the - * Base Project url, default would be to inspect - * $('body').data('baseProjectUrl'); + * @param [options.base_url] {String} String to use for the + * base project url. Default is to inspect + * $('body').data('baseUrl'); * does not support change for now is set through this option */ var MenuBar = function (selector, options) { options = options || {}; - if (options.baseProjectUrl !== undefined) { - this._baseProjectUrl = options.baseProjectUrl; - } + this.base_url = options.base_url || IPython.utils.get_body_data("baseUrl"); this.selector = selector; if (this.selector !== undefined) { this.element = $(selector); @@ -48,16 +46,6 @@ var IPython = (function (IPython) { } }; - MenuBar.prototype.baseProjectUrl = function(){ - return this._baseProjectUrl || $('body').data('baseProjectUrl'); - }; - - MenuBar.prototype.notebookPath = function() { - var path = $('body').data('notebookPath'); - path = decodeURIComponent(path); - return path; - }; - MenuBar.prototype.style = function () { this.element.addClass('border-box-sizing'); this.element.find("li").click(function (event, ui) { @@ -71,20 +59,21 @@ var IPython = (function (IPython) { MenuBar.prototype._nbconvert = function (format, download) { download = download || false; - var notebook_name = IPython.notebook.get_notebook_name(); + var notebook_path = IPython.notebook.notebook_path; + var notebook_name = IPython.notebook.notebook_name; if (IPython.notebook.dirty) { IPython.notebook.save_notebook({async : false}); } - var url = utils.url_path_join( - this.baseProjectUrl(), + var url = utils.url_join_encode( + this.base_url, 'nbconvert', format, - this.notebookPath(), - notebook_name + '.ipynb' + notebook_path, + notebook_name ) + "?download=" + download.toString(); window.open(url); - } + }; MenuBar.prototype.bind_events = function () { // File @@ -94,9 +83,9 @@ var IPython = (function (IPython) { }); this.element.find('#open_notebook').click(function () { window.open(utils.url_join_encode( - that.baseProjectUrl(), + IPython.notebook.base_url, 'tree', - that.notebookPath() + IPython.notebook.notebook_path )); }); this.element.find('#copy_notebook').click(function () { @@ -104,16 +93,18 @@ var IPython = (function (IPython) { return false; }); this.element.find('#download_ipynb').click(function () { - var notebook_name = IPython.notebook.get_notebook_name(); + var base_url = IPython.notebook.base_url; + var notebook_path = IPython.notebook.notebook_path; + var notebook_name = IPython.notebook.notebook_name; if (IPython.notebook.dirty) { IPython.notebook.save_notebook({async : false}); } var url = utils.url_join_encode( - that.baseProjectUrl(), + base_url, 'files', - that.notebookPath(), - notebook_name + '.ipynb' + notebook_path, + notebook_name ); window.location.assign(url); }); diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index 2cbb08b..e4c45c7 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -23,10 +23,10 @@ var IPython = (function (IPython) { * @param {Object} [options] A config object */ var Notebook = function (selector, options) { - var options = options || {}; - this._baseProjectUrl = options.baseProjectUrl; - this.notebook_path = options.notebookPath; - this.notebook_name = options.notebookName; + this.options = options = options || {}; + this.base_url = options.base_url; + this.notebook_path = options.notebook_path; + this.notebook_name = options.notebook_name; this.element = $(selector); this.element.scroll(); this.element.data("notebook", this); @@ -53,8 +53,8 @@ var IPython = (function (IPython) { // single worksheet for now this.worksheet_metadata = {}; this.notebook_name_blacklist_re = /[\/\\:]/; - this.nbformat = 3 // Increment this when changing the nbformat - this.nbformat_minor = 0 // Increment this when changing the nbformat + this.nbformat = 3; // Increment this when changing the nbformat + this.nbformat_minor = 0; // Increment this when changing the nbformat this.style(); this.create_elements(); this.bind_events(); @@ -70,24 +70,6 @@ var IPython = (function (IPython) { }; /** - * Get the root URL of the notebook server. - * - * @method baseProjectUrl - * @return {String} The base project URL - */ - Notebook.prototype.baseProjectUrl = function() { - return this._baseProjectUrl || $('body').data('baseProjectUrl'); - }; - - Notebook.prototype.notebookName = function() { - return $('body').data('notebookName'); - }; - - Notebook.prototype.notebookPath = function() { - return $('body').data('notebookPath'); - }; - - /** * Create an HTML and CSS representation of the notebook. * * @method create_elements @@ -163,7 +145,7 @@ var IPython = (function (IPython) { }; this.element.bind('collapse_pager', function (event, extrap) { - var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; + var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast'; collapse_time(time); }); @@ -176,7 +158,7 @@ var IPython = (function (IPython) { }; this.element.bind('expand_pager', function (event, extrap) { - var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; + var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast'; expand_time(time); }); @@ -205,7 +187,7 @@ var IPython = (function (IPython) { } else { return "Unsaved changes will be lost."; } - }; + } // Null is the *only* return value that will make the browser not // pop up the "don't leave" dialog. return null; @@ -237,7 +219,7 @@ var IPython = (function (IPython) { */ Notebook.prototype.scroll_to_cell = function (cell_number, time) { var cells = this.get_cells(); - var time = time || 0; + time = time || 0; cell_number = Math.min(cells.length-1,cell_number); cell_number = Math.max(0 ,cell_number); var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ; @@ -349,7 +331,7 @@ var IPython = (function (IPython) { result = ce.data('cell'); } return result; - } + }; /** * Get the cell below a given cell. @@ -365,7 +347,7 @@ var IPython = (function (IPython) { result = this.get_cell(index+1); } return result; - } + }; /** * Get the cell above a given cell. @@ -383,7 +365,7 @@ var IPython = (function (IPython) { result = this.get_cell(index-1); } return result; - } + }; /** * Get the numeric index of a given cell. @@ -397,7 +379,7 @@ var IPython = (function (IPython) { this.get_cell_elements().filter(function (index) { if ($(this).data("cell") === cell) { result = index; - }; + } }); return result; }; @@ -444,8 +426,8 @@ var IPython = (function (IPython) { return true; } else { return false; - }; - } + } + }; /** * Get the index of the currently selected cell. @@ -458,7 +440,7 @@ var IPython = (function (IPython) { this.get_cell_elements().filter(function (index) { if ($(this).data("cell").selected === true) { result = index; - }; + } }); return result; }; @@ -475,11 +457,11 @@ var IPython = (function (IPython) { */ Notebook.prototype.select = function (index) { if (this.is_valid_cell_index(index)) { - var sindex = this.get_selected_index() + var sindex = this.get_selected_index(); if (sindex !== null && index !== sindex) { this.command_mode(); this.get_cell(sindex).unselect(); - }; + } var cell = this.get_cell(index); cell.select(); if (cell.cell_type === 'heading') { @@ -490,8 +472,8 @@ var IPython = (function (IPython) { $([IPython.events]).trigger('selected_cell_type_changed.Notebook', {'cell_type':cell.cell_type} ); - }; - }; + } + } return this; }; @@ -527,25 +509,27 @@ var IPython = (function (IPython) { this.get_cell_elements().filter(function (index) { if ($(this).data("cell").mode === 'edit') { result = index; - }; + } }); return result; }; Notebook.prototype.command_mode = function () { if (this.mode !== 'command') { + $([IPython.events]).trigger('command_mode.Notebook'); var index = this.get_edit_index(); var cell = this.get_cell(index); if (cell) { cell.command_mode(); - }; + } this.mode = 'command'; IPython.keyboard_manager.command_mode(); - }; + } }; Notebook.prototype.edit_mode = function () { if (this.mode !== 'edit') { + $([IPython.events]).trigger('edit_mode.Notebook'); var cell = this.get_selected_cell(); if (cell === null) {return;} // No cell is selected // We need to set the mode to edit to prevent reentering this method @@ -553,7 +537,7 @@ var IPython = (function (IPython) { this.mode = 'edit'; IPython.keyboard_manager.edit_mode(); cell.edit_mode(); - }; + } }; Notebook.prototype.focus_cell = function () { @@ -582,9 +566,9 @@ var IPython = (function (IPython) { this.select(i-1); var cell = this.get_selected_cell(); cell.focus_cell(); - }; + } this.set_dirty(true); - }; + } return this; }; @@ -607,8 +591,8 @@ var IPython = (function (IPython) { this.select(i+1); var cell = this.get_selected_cell(); cell.focus_cell(); - }; - }; + } + } this.set_dirty(); return this; }; @@ -648,10 +632,10 @@ var IPython = (function (IPython) { this.select(i); this.undelete_index = i; this.undelete_below = false; - }; + } $([IPython.events]).trigger('delete.Cell', {'cell': cell, 'index': i}); this.set_dirty(true); - }; + } return this; }; @@ -689,7 +673,7 @@ var IPython = (function (IPython) { this.undelete_index = null; } $('#undelete_cell').addClass('disabled'); - } + }; /** * Insert a cell so that after insertion the cell is at given index. @@ -707,8 +691,8 @@ var IPython = (function (IPython) { Notebook.prototype.insert_cell_at_index = function(type, index){ var ncells = this.ncells(); - var index = Math.min(index,ncells); - index = Math.max(index,0); + index = Math.min(index,ncells); + index = Math.max(index,0); var cell = null; if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) { @@ -848,8 +832,8 @@ var IPython = (function (IPython) { source_element.remove(); this.select(i); this.set_dirty(true); - }; - }; + } + } }; /** @@ -868,7 +852,7 @@ var IPython = (function (IPython) { var text = source_cell.get_text(); if (text === source_cell.placeholder) { text = ''; - }; + } // We must show the editor before setting its contents target_cell.unrender(); target_cell.set_text(text); @@ -881,8 +865,8 @@ var IPython = (function (IPython) { target_cell.render(); } this.set_dirty(true); - }; - }; + } + } }; /** @@ -902,7 +886,7 @@ var IPython = (function (IPython) { var text = source_cell.get_text(); if (text === source_cell.placeholder) { text = ''; - }; + } // We must show the editor before setting its contents target_cell.unrender(); target_cell.set_text(text); @@ -912,8 +896,8 @@ var IPython = (function (IPython) { source_element.remove(); this.select(i); this.set_dirty(true); - }; - }; + } + } }; /** @@ -937,7 +921,7 @@ var IPython = (function (IPython) { var text = source_cell.get_text(); if (text === source_cell.placeholder) { text = ''; - }; + } // We must show the editor before setting its contents target_cell.set_level(level); target_cell.unrender(); @@ -950,12 +934,12 @@ var IPython = (function (IPython) { if ((source_cell instanceof IPython.TextCell) && source_cell.rendered) { target_cell.render(); } - }; + } this.set_dirty(true); $([IPython.events]).trigger('selected_cell_type_changed.Notebook', {'cell_type':'heading',level:level} ); - }; + } }; @@ -976,7 +960,7 @@ var IPython = (function (IPython) { $('#paste_cell_below').removeClass('disabled') .on('click', function () {that.paste_cell_below();}); this.paste_enabled = true; - }; + } }; /** @@ -990,7 +974,7 @@ var IPython = (function (IPython) { $('#paste_cell_above').addClass('disabled').off('click'); $('#paste_cell_below').addClass('disabled').off('click'); this.paste_enabled = false; - }; + } }; /** @@ -1001,7 +985,7 @@ var IPython = (function (IPython) { Notebook.prototype.cut_cell = function () { this.copy_cell(); this.delete_cell(); - } + }; /** * Copy a cell. @@ -1027,7 +1011,7 @@ var IPython = (function (IPython) { var old_cell = this.get_next_cell(new_cell); this.delete_cell(this.find_cell_index(old_cell)); this.select(this.find_cell_index(new_cell)); - }; + } }; /** @@ -1041,7 +1025,7 @@ var IPython = (function (IPython) { var new_cell = this.insert_cell_above(cell_data.cell_type); new_cell.fromJSON(cell_data); new_cell.focus_cell(); - }; + } }; /** @@ -1055,7 +1039,7 @@ var IPython = (function (IPython) { var new_cell = this.insert_cell_below(cell_data.cell_type); new_cell.fromJSON(cell_data); new_cell.focus_cell(); - }; + } }; // Split/merge @@ -1086,7 +1070,7 @@ var IPython = (function (IPython) { new_cell.unrender(); new_cell.set_text(texta); } - }; + } }; /** @@ -1120,10 +1104,10 @@ var IPython = (function (IPython) { // that of the original selected cell; cell.render(); } - }; + } this.delete_cell(index-1); this.select(this.find_cell_index(cell)); - }; + } }; /** @@ -1157,10 +1141,10 @@ var IPython = (function (IPython) { // that of the original selected cell; cell.render(); } - }; + } this.delete_cell(index+1); this.select(this.find_cell_index(cell)); - }; + } }; @@ -1363,7 +1347,7 @@ var IPython = (function (IPython) { * @method start_session */ Notebook.prototype.start_session = function () { - this.session = new IPython.Session(this.notebook_name, this.notebook_path, this); + this.session = new IPython.Session(this, this.options); this.session.start($.proxy(this._session_started, this)); }; @@ -1380,8 +1364,8 @@ var IPython = (function (IPython) { var cell = this.get_cell(i); if (cell instanceof IPython.CodeCell) { cell.set_kernel(this.session.kernel); - }; - }; + } + } }; /** @@ -1422,7 +1406,7 @@ var IPython = (function (IPython) { this.command_mode(); cell.focus_cell(); this.set_dirty(true); - } + }; /** * Execute or render cell outputs and insert a new cell below. @@ -1518,7 +1502,7 @@ var IPython = (function (IPython) { for (var i=start; i0) { + if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) { return true; } else { return false; - }; + } }; /** @@ -1575,7 +1559,7 @@ var IPython = (function (IPython) { for (i=0; i 1) { IPython.dialog.modal({ title : "Multiple worksheets", @@ -1628,7 +1612,7 @@ var IPython = (function (IPython) { var cell_array = new Array(ncells); for (var i=0; i').append( $("

").addClass("rename-message") .text('This notebook name already exists.') - ) + ); $([IPython.events]).trigger('notebook_rename_failed.Notebook', [xhr, status, error]); IPython.dialog.modal({ title: "Notebook Rename Error!", @@ -1894,7 +1880,7 @@ var IPython = (function (IPython) { that.find('input[type="text"]').focus(); } }); - } + }; /** * Request a notebook's data from the server. @@ -1917,7 +1903,7 @@ var IPython = (function (IPython) { }; $([IPython.events]).trigger('notebook_loading.Notebook'); var url = utils.url_join_encode( - this._baseProjectUrl, + this.base_url, 'api/notebooks', this.notebook_path, this.notebook_name @@ -1944,7 +1930,7 @@ var IPython = (function (IPython) { } else { this.select(0); this.command_mode(); - }; + } this.set_dirty(false); this.scroll_to_top(); if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) { @@ -1969,7 +1955,7 @@ var IPython = (function (IPython) { var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor; var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " + this_vs + ". You can still work with this notebook, but some features " + - "introduced in later notebook versions may not be available." + "introduced in later notebook versions may not be available."; IPython.dialog.modal({ title : "Newer Notebook", @@ -1985,7 +1971,7 @@ var IPython = (function (IPython) { // Create the session after the notebook is completely loaded to prevent // code execution upon loading, which is a security risk. - if (this.session == null) { + if (this.session === null) { this.start_session(); } // load our checkpoint list @@ -2010,10 +1996,11 @@ var IPython = (function (IPython) { */ Notebook.prototype.load_notebook_error = function (xhr, status, error) { $([IPython.events]).trigger('notebook_load_failed.Notebook', [xhr, status, error]); + var msg; if (xhr.status === 400) { - var msg = error; + msg = error; } else if (xhr.status === 500) { - var msg = "An unknown error occurred while loading this notebook. " + + msg = "An unknown error occurred while loading this notebook. " + "This version can load notebook formats " + "v" + this.nbformat + " or earlier."; } @@ -2024,7 +2011,7 @@ var IPython = (function (IPython) { "OK": {} } }); - } + }; /********************* checkpoint-related *********************/ @@ -2067,7 +2054,7 @@ var IPython = (function (IPython) { */ Notebook.prototype.list_checkpoints = function () { var url = utils.url_join_encode( - this._baseProjectUrl, + this.base_url, 'api/notebooks', this.notebook_path, this.notebook_name, @@ -2089,7 +2076,7 @@ var IPython = (function (IPython) { * @param {jqXHR} xhr jQuery Ajax object */ Notebook.prototype.list_checkpoints_success = function (data, status, xhr) { - var data = $.parseJSON(data); + data = $.parseJSON(data); this.checkpoints = data; if (data.length) { this.last_checkpoint = data[data.length - 1]; @@ -2118,9 +2105,9 @@ var IPython = (function (IPython) { */ Notebook.prototype.create_checkpoint = function () { var url = utils.url_join_encode( - this._baseProjectUrl, + this.base_url, 'api/notebooks', - this.notebookPath(), + this.notebook_path, this.notebook_name, 'checkpoints' ); @@ -2140,7 +2127,7 @@ var IPython = (function (IPython) { * @param {jqXHR} xhr jQuery Ajax object */ Notebook.prototype.create_checkpoint_success = function (data, status, xhr) { - var data = $.parseJSON(data); + data = $.parseJSON(data); this.add_checkpoint(data); $([IPython.events]).trigger('checkpoint_created.Notebook', data); }; @@ -2159,7 +2146,7 @@ var IPython = (function (IPython) { Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) { var that = this; - var checkpoint = checkpoint || this.last_checkpoint; + checkpoint = checkpoint || this.last_checkpoint; if ( ! checkpoint ) { console.log("restore dialog, but no checkpoint to restore to!"); return; @@ -2194,7 +2181,7 @@ var IPython = (function (IPython) { Cancel : {} } }); - } + }; /** * Restore the notebook to a checkpoint state. @@ -2205,9 +2192,9 @@ var IPython = (function (IPython) { Notebook.prototype.restore_checkpoint = function (checkpoint) { $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint); var url = utils.url_join_encode( - this._baseProjectUrl, + this.base_url, 'api/notebooks', - this.notebookPath(), + this.notebook_path, this.notebook_name, 'checkpoints', checkpoint @@ -2253,9 +2240,9 @@ var IPython = (function (IPython) { Notebook.prototype.delete_checkpoint = function (checkpoint) { $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint); var url = utils.url_join_encode( - this._baseProjectUrl, + this.base_url, 'api/notebooks', - this.notebookPath(), + this.notebook_path, this.notebook_name, 'checkpoints', checkpoint diff --git a/IPython/html/static/notebook/js/notificationarea.js b/IPython/html/static/notebook/js/notificationarea.js index 68444f7..8656b49 100644 --- a/IPython/html/static/notebook/js/notificationarea.js +++ b/IPython/html/static/notebook/js/notificationarea.js @@ -69,17 +69,29 @@ var IPython = (function (IPython) { NotificationArea.prototype.init_notification_widgets = function() { var knw = this.new_notification_widget('kernel'); - var $kernel_indic = $("#kernel_indicator"); + var $kernel_ind_icon = $("#kernel_indicator_icon"); + var $modal_ind_icon = $("#modal_indicator_icon"); + + // Command/Edit mode + $([IPython.events]).on('edit_mode.Notebook',function () { + IPython.save_widget.update_document_title(); + $modal_ind_icon.attr('class','icon-pencil').attr('title','Edit Mode'); + }); + + $([IPython.events]).on('command_mode.Notebook',function () { + IPython.save_widget.update_document_title(); + $modal_ind_icon.attr('class','').attr('title','Command Mode'); + }); // Kernel events $([IPython.events]).on('status_idle.Kernel',function () { IPython.save_widget.update_document_title(); - $kernel_indic.attr('class','icon-circle-blank').attr('title','Kernel Idle'); + $kernel_ind_icon.attr('class','icon-circle-blank').attr('title','Kernel Idle'); }); $([IPython.events]).on('status_busy.Kernel',function () { window.document.title='(Busy) '+window.document.title; - $kernel_indic.attr('class','icon-circle').attr('title','Kernel Busy'); + $kernel_ind_icon.attr('class','icon-circle').attr('title','Kernel Busy'); }); $([IPython.events]).on('status_restarting.Kernel',function () { diff --git a/IPython/html/static/notebook/js/outputarea.js b/IPython/html/static/notebook/js/outputarea.js index 563565a..6e2e1f5 100644 --- a/IPython/html/static/notebook/js/outputarea.js +++ b/IPython/html/static/notebook/js/outputarea.js @@ -252,6 +252,7 @@ var IPython = (function (IPython) { 'image/svg+xml', 'image/png', 'image/jpeg', + 'application/pdf', 'text/plain' ]; @@ -620,6 +621,17 @@ var IPython = (function (IPython) { }; + OutputArea.prototype.append_pdf = function (pdf, md, element) { + var type = 'application/pdf'; + var toinsert = this.create_output_subarea(md, "output_pdf", type); + var a = $('').attr('href', 'data:application/pdf;base64,'+pdf); + a.attr('target', '_blank'); + a.text('View PDF') + toinsert.append(a); + element.append(toinsert); + return toinsert; + } + OutputArea.prototype.append_latex = function (latex, md, element) { // This method cannot do the typesetting because the latex first has to // be on the page. @@ -807,6 +819,7 @@ var IPython = (function (IPython) { "image/svg+xml" : "svg", "image/png" : "png", "image/jpeg" : "jpeg", + "application/pdf" : "pdf", "text/latex" : "latex", "application/json" : "json", "application/javascript" : "javascript", @@ -818,6 +831,7 @@ var IPython = (function (IPython) { "svg" : "image/svg+xml", "png" : "image/png", "jpeg" : "image/jpeg", + "pdf" : "application/pdf", "latex" : "text/latex", "json" : "application/json", "javascript" : "application/javascript", @@ -830,6 +844,7 @@ var IPython = (function (IPython) { 'image/svg+xml', 'image/png', 'image/jpeg', + 'application/pdf', 'text/plain' ]; @@ -842,6 +857,7 @@ var IPython = (function (IPython) { "text/latex" : OutputArea.prototype.append_latex, "application/json" : OutputArea.prototype.append_json, "application/javascript" : OutputArea.prototype.append_javascript, + "application/pdf" : OutputArea.prototype.append_pdf }; IPython.OutputArea = OutputArea; diff --git a/IPython/html/static/notebook/js/savewidget.js b/IPython/html/static/notebook/js/savewidget.js index c6c11f8..87d3ad6 100644 --- a/IPython/html/static/notebook/js/savewidget.js +++ b/IPython/html/static/notebook/js/savewidget.js @@ -127,7 +127,7 @@ var IPython = (function (IPython) { SaveWidget.prototype.update_address_bar = function(){ var nbname = IPython.notebook.notebook_name; - var path = IPython.notebook.notebookPath(); + var path = IPython.notebook.notebook_path; var state = {path : utils.url_join_encode(path, nbname)}; window.history.replaceState(state, "", utils.url_join_encode( "/notebooks", diff --git a/IPython/html/static/notebook/less/notificationarea.less b/IPython/html/static/notebook/less/notificationarea.less index d9dc0fa..634f31b 100644 --- a/IPython/html/static/notebook/less/notificationarea.less +++ b/IPython/html/static/notebook/less/notificationarea.less @@ -1,3 +1,18 @@ #notification_area { z-index: 10; } + +.indicator_area { + color: @navbarLinkColor; + padding: 4px 3px; + margin: 0px; + width: 11px; + z-index: 10; + text-align: center; +} + +#kernel_indicator { + // Pull it to the right, outside the container boundary + margin-right: -16px; +} + diff --git a/IPython/html/static/notebook/less/notificationwidget.less b/IPython/html/static/notebook/less/notificationwidget.less index 1f58252..3f9cd8f 100644 --- a/IPython/html/static/notebook/less/notificationwidget.less +++ b/IPython/html/static/notebook/less/notificationwidget.less @@ -1,4 +1,4 @@ -.notification_widget{ +.notification_widget { color: @navbarLinkColor; padding: 1px 12px; margin: 2px 4px; @@ -10,13 +10,4 @@ &.span { padding-right:2px; } - -} - -#indicator_area{ - color: @navbarLinkColor; - padding: 2px 2px; - margin: 2px -9px 2px 4px; - z-index: 10; - } diff --git a/IPython/html/static/services/kernels/js/kernel.js b/IPython/html/static/services/kernels/js/kernel.js index 4d0c45e..c321f55 100644 --- a/IPython/html/static/services/kernels/js/kernel.js +++ b/IPython/html/static/services/kernels/js/kernel.js @@ -25,12 +25,12 @@ var IPython = (function (IPython) { * A Kernel Class to communicate with the Python kernel * @Class Kernel */ - var Kernel = function (base_url) { + var Kernel = function (kernel_service_url) { this.kernel_id = null; this.shell_channel = null; this.iopub_channel = null; this.stdin_channel = null; - this.base_url = base_url; + this.kernel_service_url = kernel_service_url; this.running = false; this.username = "username"; this.session_id = utils.uuid(); @@ -94,8 +94,7 @@ var IPython = (function (IPython) { params = params || {}; if (!this.running) { var qs = $.param(params); - var url = this.base_url + '?' + qs; - $.post(url, + $.post(utils.url_join_encode(this.kernel_service_url) + '?' + qs, $.proxy(this._kernel_started, this), 'json' ); @@ -114,8 +113,7 @@ var IPython = (function (IPython) { $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this}); if (this.running) { this.stop_channels(); - var url = utils.url_join_encode(this.kernel_url, "restart"); - $.post(url, + $.post(utils.url_join_encode(this.kernel_url, "restart"), $.proxy(this._kernel_started, this), 'json' ); @@ -133,8 +131,10 @@ var IPython = (function (IPython) { var prot = location.protocol.replace('http', 'ws') + "//"; ws_url = prot + location.host + ws_url; } - this.ws_url = ws_url; - this.kernel_url = utils.url_join_encode(this.base_url, this.kernel_id); + var parsed = utils.parse_url(ws_url); + this.ws_host = parsed.protocol + "//" + parsed.host; + this.kernel_url = utils.url_path_join(this.kernel_service_url, this.kernel_id); + this.ws_url = utils.url_path_join(parsed.pathname, this.kernel_url); this.start_channels(); }; @@ -155,12 +155,18 @@ var IPython = (function (IPython) { Kernel.prototype.start_channels = function () { var that = this; this.stop_channels(); - var ws_url = this.ws_url + this.kernel_url; - console.log("Starting WebSockets:", ws_url); - this.shell_channel = new this.WebSocket(ws_url + "/shell"); - this.stdin_channel = new this.WebSocket(ws_url + "/stdin"); - this.iopub_channel = new this.WebSocket(ws_url + "/iopub"); + console.log("Starting WebSockets:", this.ws_host + this.ws_url); + this.shell_channel = new this.WebSocket( + this.ws_host + utils.url_join_encode(this.ws_url, "shell") + ); + this.stdin_channel = new this.WebSocket( + this.ws_host + utils.url_join_encode(this.ws_url, "stdin") + ); + this.iopub_channel = new this.WebSocket( + this.ws_host + utils.url_join_encode(this.ws_url, "iopub") + ); + var ws_host_url = this.ws_host + this.ws_url; var already_called_onclose = false; // only alert once var ws_closed_early = function(evt){ if (already_called_onclose){ @@ -168,7 +174,7 @@ var IPython = (function (IPython) { } already_called_onclose = true; if ( ! evt.wasClean ){ - that._websocket_closed(ws_url, true); + that._websocket_closed(ws_host_url, true); } }; var ws_closed_late = function(evt){ @@ -177,7 +183,7 @@ var IPython = (function (IPython) { } already_called_onclose = true; if ( ! evt.wasClean ){ - that._websocket_closed(ws_url, false); + that._websocket_closed(ws_host_url, false); } }; var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel]; @@ -387,7 +393,7 @@ var IPython = (function (IPython) { Kernel.prototype.interrupt = function () { if (this.running) { $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this}); - $.post(this.kernel_url + "/interrupt"); + $.post(utils.url_join_encode(this.kernel_url, "interrupt")); } }; @@ -399,7 +405,7 @@ var IPython = (function (IPython) { cache : false, type : "DELETE" }; - $.ajax(this.kernel_url, settings); + $.ajax(utils.url_join_encode(this.kernel_url), settings); } }; diff --git a/IPython/html/static/services/sessions/js/session.js b/IPython/html/static/services/sessions/js/session.js index 0f0c4a9..bb3ab6c 100644 --- a/IPython/html/static/services/sessions/js/session.js +++ b/IPython/html/static/services/sessions/js/session.js @@ -14,13 +14,14 @@ var IPython = (function (IPython) { var utils = IPython.utils; - var Session = function(notebook_name, notebook_path, notebook){ + var Session = function(notebook, options){ this.kernel = null; this.id = null; - this.name = notebook_name; - this.path = notebook_path; this.notebook = notebook; - this._baseProjectUrl = notebook.baseProjectUrl(); + this.name = notebook.notebook_name; + this.path = notebook.notebook_path; + this.base_url = notebook.base_url; + this.base_kernel_url = options.base_kernel_url || utils.get_body_data("baseKernelUrl"); }; Session.prototype.start = function(callback) { @@ -44,7 +45,7 @@ var IPython = (function (IPython) { } }, }; - var url = utils.url_join_encode(this._baseProjectUrl, 'api/sessions'); + var url = utils.url_join_encode(this.base_url, 'api/sessions'); $.ajax(url, settings); }; @@ -64,7 +65,7 @@ var IPython = (function (IPython) { data: JSON.stringify(model), dataType : "json", }; - var url = utils.url_join_encode(this._baseProjectUrl, 'api/sessions', this.id); + var url = utils.url_join_encode(this.base_url, 'api/sessions', this.id); $.ajax(url, settings); }; @@ -76,7 +77,7 @@ var IPython = (function (IPython) { dataType : "json", }; this.kernel.running = false; - var url = utils.url_join_encode(this._baseProjectUrl, 'api/sessions', this.id); + var url = utils.url_join_encode(this.base_url, 'api/sessions', this.id); $.ajax(url, settings); }; @@ -88,8 +89,8 @@ var IPython = (function (IPython) { */ Session.prototype._handle_start_success = function (data, status, xhr) { this.id = data.id; - var base_url = utils.url_path_join($('body').data('baseKernelUrl'), "api/kernels"); - this.kernel = new IPython.Kernel(base_url); + var kernel_service_url = utils.url_path_join(this.base_kernel_url, "api/kernels"); + this.kernel = new IPython.Kernel(kernel_service_url); this.kernel._kernel_started(data.kernel); }; diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index e0d430a..cbcbb3e 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -1502,8 +1502,9 @@ p{margin-bottom:0} i.menu-icon{padding-top:4px} ul#help_menu li a{overflow:hidden;padding-right:2.2em}ul#help_menu li a i{margin-right:-1.2em} #notification_area{z-index:10} +.indicator_area{color:#777;padding:4px 3px;margin:0;width:11px;z-index:10;text-align:center} +#kernel_indicator{margin-right:-16px} .notification_widget{color:#777;padding:1px 12px;margin:2px 4px;z-index:10;border:1px solid #ccc;border-radius:4px;background:rgba(240,240,240,0.5)}.notification_widget.span{padding-right:2px} -#indicator_area{color:#777;padding:2px 2px;margin:2px -9px 2px 4px;z-index:10} div#pager_splitter{height:8px} #pager-container{position:relative;padding:15px 0} div#pager{overflow:auto;display:none}div#pager pre{font-size:13px;line-height:1.231em;color:#000;background-color:#f7f7f7;padding:.4em} diff --git a/IPython/html/static/tree/js/clusterlist.js b/IPython/html/static/tree/js/clusterlist.js index 9d607d5..48de0d3 100644 --- a/IPython/html/static/tree/js/clusterlist.js +++ b/IPython/html/static/tree/js/clusterlist.js @@ -14,17 +14,17 @@ var IPython = (function (IPython) { var utils = IPython.utils; - var ClusterList = function (selector) { + var ClusterList = function (selector, options) { this.selector = selector; if (this.selector !== undefined) { this.element = $(selector); this.style(); this.bind_events(); } - }; - - ClusterList.prototype.baseProjectUrl = function(){ - return this._baseProjectUrl || $('body').data('baseProjectUrl'); + options = options || {}; + this.options = options; + this.base_url = options.base_url || utils.get_body_data("baseUrl"); + this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath"); }; ClusterList.prototype.style = function () { @@ -51,7 +51,7 @@ var IPython = (function (IPython) { dataType : "json", success : $.proxy(this.load_list_success, this) }; - var url = utils.url_join_encode(this.baseProjectUrl(), 'clusters'); + var url = utils.url_join_encode(this.base_url, 'clusters'); $.ajax(url, settings); }; @@ -65,7 +65,7 @@ var IPython = (function (IPython) { var len = data.length; for (var i=0; i'); - var item = new ClusterItem(element); + var item = new ClusterItem(element, this.options); item.update_state(data[i]); element.data('item', item); this.element.append(element); @@ -73,17 +73,14 @@ var IPython = (function (IPython) { }; - var ClusterItem = function (element) { + var ClusterItem = function (element, options) { this.element = $(element); + this.base_url = options.base_url || utils.get_body_data("baseUrl"); + this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath"); this.data = null; this.style(); }; - ClusterItem.prototype.baseProjectUrl = function(){ - return this._baseProjectUrl || $('body').data('baseProjectUrl'); - }; - - ClusterItem.prototype.style = function () { this.element.addClass('list_item').addClass("row-fluid"); }; @@ -138,7 +135,7 @@ var IPython = (function (IPython) { }; status_col.text('starting'); var url = utils.url_join_encode( - that.baseProjectUrl(), + that.base_url, 'clusters', that.data.profile, 'start' @@ -180,7 +177,7 @@ var IPython = (function (IPython) { }; status_col.text('stopping'); var url = utils.url_join_encode( - that.baseProjectUrl(), + that.base_url, 'clusters', that.data.profile, 'stop' diff --git a/IPython/html/static/tree/js/main.js b/IPython/html/static/tree/js/main.js index ddcd9da..d66bb68 100644 --- a/IPython/html/static/tree/js/main.js +++ b/IPython/html/static/tree/js/main.js @@ -15,12 +15,16 @@ $(document).ready(function () { IPython.page = new IPython.Page(); $('#new_notebook').button().click(function (e) { - IPython.notebook_list.new_notebook($('body').data('baseProjectUrl')) + IPython.notebook_list.new_notebook() }); - - IPython.notebook_list = new IPython.NotebookList('#notebook_list'); - IPython.cluster_list = new IPython.ClusterList('#cluster_list'); - IPython.login_widget = new IPython.LoginWidget('#login_widget'); + + var opts = { + base_url : IPython.utils.get_body_data("baseUrl"), + notebook_path : IPython.utils.get_body_data("notebookPath"), + }; + IPython.notebook_list = new IPython.NotebookList('#notebook_list', opts); + IPython.cluster_list = new IPython.ClusterList('#cluster_list', opts); + IPython.login_widget = new IPython.LoginWidget('#login_widget', opts); var interval_id=0; // auto refresh every xx secondes, no need to be fast, diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index e92c343..fbfed9d 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -14,7 +14,7 @@ var IPython = (function (IPython) { var utils = IPython.utils; - var NotebookList = function (selector) { + var NotebookList = function (selector, options) { this.selector = selector; if (this.selector !== undefined) { this.element = $(selector); @@ -23,16 +23,10 @@ var IPython = (function (IPython) { } this.notebooks_list = []; this.sessions = {}; + this.base_url = options.base_url || utils.get_body_data("baseUrl"); + this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath"); }; - NotebookList.prototype.baseProjectUrl = function () { - return $('body').data('baseProjectUrl'); - }; - - NotebookList.prototype.notebookPath = function() { - return $('body').data('notebookPath'); - }; - NotebookList.prototype.style = function () { $('#notebook_toolbar').addClass('list_toolbar'); $('#drag_info').addClass('toolbar_info'); @@ -112,7 +106,7 @@ var IPython = (function (IPython) { dataType : "json", success : $.proxy(that.sessions_loaded, this) }; - var url = this.baseProjectUrl() + 'api/sessions'; + var url = utils.url_join_encode(this.base_url, 'api/sessions'); $.ajax(url,settings); }; @@ -152,10 +146,10 @@ var IPython = (function (IPython) { }; var url = utils.url_join_encode( - this.baseProjectUrl(), + this.base_url, 'api', 'notebooks', - this.notebookPath() + this.notebook_path ); $.ajax(url, settings); }; @@ -175,7 +169,7 @@ var IPython = (function (IPython) { span12.empty(); span12.append($('

').text(message)); } - var path = this.notebookPath(); + var path = this.notebook_path; var offset = 0; if (path !== '') { item = this.new_notebook_item(0); @@ -233,7 +227,7 @@ var IPython = (function (IPython) { item.find("a.item_link") .attr('href', utils.url_join_encode( - this.baseProjectUrl(), + this.base_url, "tree", path, name @@ -250,7 +244,7 @@ var IPython = (function (IPython) { item.find("a.item_link") .attr('href', utils.url_join_encode( - this.baseProjectUrl(), + this.base_url, "notebooks", path, nbname @@ -291,7 +285,7 @@ var IPython = (function (IPython) { } }; var url = utils.url_join_encode( - that.baseProjectUrl(), + that.base_url, 'api/sessions', session ); @@ -331,9 +325,9 @@ var IPython = (function (IPython) { } }; var url = utils.url_join_encode( - notebooklist.baseProjectUrl(), + notebooklist.base_url, 'api/notebooks', - notebooklist.notebookPath(), + notebooklist.notebook_path, nbname ); $.ajax(url, settings); @@ -357,7 +351,7 @@ var IPython = (function (IPython) { if (nbname.slice(nbname.length-6, nbname.length) != ".ipynb") { nbname = nbname + ".ipynb"; } - var path = that.notebookPath(); + var path = that.notebook_path; var nbdata = item.data('nbdata'); var content_type = 'application/json'; var model = { @@ -380,9 +374,9 @@ var IPython = (function (IPython) { }; var url = utils.url_join_encode( - that.baseProjectUrl(), + that.base_url, 'api/notebooks', - that.notebookPath(), + that.notebook_path, nbname ); $.ajax(url, settings); @@ -402,8 +396,8 @@ var IPython = (function (IPython) { NotebookList.prototype.new_notebook = function(){ - var path = this.notebookPath(); - var base_project_url = this.baseProjectUrl(); + var path = this.notebook_path; + var base_url = this.base_url; var settings = { processData : false, cache : false, @@ -414,7 +408,7 @@ var IPython = (function (IPython) { var notebook_name = data.name; window.open( utils.url_join_encode( - base_project_url, + base_url, 'notebooks', path, notebook_name), @@ -423,7 +417,7 @@ var IPython = (function (IPython) { } }; var url = utils.url_join_encode( - base_project_url, + base_url, 'api/notebooks', path ); diff --git a/IPython/html/templates/login.html b/IPython/html/templates/login.html index f388c6e..7244bee 100644 --- a/IPython/html/templates/login.html +++ b/IPython/html/templates/login.html @@ -20,7 +20,7 @@
- diff --git a/IPython/html/templates/logout.html b/IPython/html/templates/logout.html index 5efbe8b..35c65c5 100644 --- a/IPython/html/templates/logout.html +++ b/IPython/html/templates/logout.html @@ -21,9 +21,9 @@ {% endif %} {% if not login_available %} - Proceed to the dashboard. + Proceed to the dashboard. {% else %} - Proceed to the login page. + Proceed to the login page. {% endif %} diff --git a/IPython/html/templates/notebook.html b/IPython/html/templates/notebook.html index 047aa49..b05ec42 100644 --- a/IPython/html/templates/notebook.html +++ b/IPython/html/templates/notebook.html @@ -22,7 +22,7 @@ window.mathjax_url = "{{mathjax_url}}"; {% block params %} data-project="{{project}}" -data-base-project-url="{{base_project_url}}" +data-base-url="{{base_url}}" data-base-kernel-url="{{base_kernel_url}}" data-notebook-name="{{notebook_name}}" data-notebook-path="{{notebook_path}}" @@ -251,8 +251,11 @@ class="notebook_app" -
-
+
+ +
+
diff --git a/IPython/html/templates/page.html b/IPython/html/templates/page.html index a9751b6..4d882f4 100644 --- a/IPython/html/templates/page.html +++ b/IPython/html/templates/page.html @@ -21,7 +21,7 @@ require.config({ baseUrl: '{{static_url("")}}', paths: { - nbextensions : '{{ base_project_url }}nbextensions', + nbextensions : '{{ base_url }}nbextensions', underscore : '{{static_url("components/underscore/underscore-min")}}', backbone : '{{static_url("components/backbone/backbone-min")}}', }, @@ -54,7 +54,7 @@