From 54c3e061e1b3eaacfc674a9cdf044bd0df8544ad 2012-02-02 21:41:01 From: Fernando Perez Date: 2012-02-02 21:41:01 Subject: [PATCH] Merge pull request #1361 from ellisonbg/nbissues A number of bug fixes for notebook issues that had crept up recently with all the major improvements done on multiple fronts. In closing #1359, we've changed slightly how Math() works: it now unconditionally surrounds its input with $$...$$, so that it always appears in displayed math mode. We have also introduced a new display object, Latex(), which does *not* add any latex markup, for other constructs beyond simple math expressions. This change makes Math() friendlier to use in simple cases and means that Math(sympy.latex(foo)) will produce the expected displayed math results without the user having to add any $ markup. Summary of fixes: Fixes #1344: Ctrl + M + L does not toggle line numbering in htmlnotebook. Fixes #1337: Tab in the notebook after `(` should not indent, only give a tooltip. Fixes #1339: Notebook printing broken. Fixes #1348: `Ctrl-m-Ctrl-m` does not switch to markdown cell Fixes #1359: [sympyprinting] MathJax can't render \root{m}{n} --- diff --git a/IPython/core/display.py b/IPython/core/display.py index 7f5cc6d..dc80c3a 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -298,6 +298,13 @@ class HTML(DisplayObject): class Math(DisplayObject): def _repr_latex_(self): + s = self.data.strip('$') + return "$$%s$$" % s + + +class Latex(DisplayObject): + + def _repr_latex_(self): return self.data diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index cadf59d..893e0a9 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -524,7 +524,8 @@ class LatexFormatter(BaseFormatter): this. The return value of this formatter should be a valid LaTeX equation, - enclosed in either ```$``` or ```$$```. + enclosed in either ```$```, ```$$``` or another LaTeX equation + environment. """ format_type = Unicode('text/latex') diff --git a/IPython/extensions/sympyprinting.py b/IPython/extensions/sympyprinting.py index a7c4a43..a607ddd 100644 --- a/IPython/extensions/sympyprinting.py +++ b/IPython/extensions/sympyprinting.py @@ -58,9 +58,10 @@ def print_png(o): def print_latex(o): """A function to generate the latex representation of sympy expressions.""" - s = latex(o, mode='equation', itex=True) + s = latex(o, mode='plain') s = s.replace('\\dag','\\dagger') - return s + s = s.strip('$') + return '$$%s$$' % s _loaded = False diff --git a/IPython/frontend/html/notebook/static/codemirror/README-IPython.rst b/IPython/frontend/html/notebook/static/codemirror/README-IPython.rst index 5c5e8e6..1bd9184 100644 --- a/IPython/frontend/html/notebook/static/codemirror/README-IPython.rst +++ b/IPython/frontend/html/notebook/static/codemirror/README-IPython.rst @@ -5,7 +5,7 @@ We carry a mostly unmodified copy of CodeMirror. The current version we use is (*please update this information when updating versions*):: - CodeMirror 7f93a5c + CodeMirror c813c94 The only changes we've applied so far are these:: diff --git a/IPython/frontend/html/notebook/static/codemirror/lib/codemirror.js b/IPython/frontend/html/notebook/static/codemirror/lib/codemirror.js index e3b210f..7a88564 100755 --- a/IPython/frontend/html/notebook/static/codemirror/lib/codemirror.js +++ b/IPython/frontend/html/notebook/static/codemirror/lib/codemirror.js @@ -149,8 +149,10 @@ var CodeMirror = (function() { else if (option == "theme") themeChanged(); else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); else if (option == "tabSize") operation(tabsChanged)(); - if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") + if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") { + gutterChanged(); updateDisplay(true); + } }, getOption: function(option) {return options[option];}, undo: operation(undo), @@ -278,8 +280,8 @@ var CodeMirror = (function() { return index; }, scrollTo: function(x, y) { - if (x != null) scroller.scrollTop = x; - if (y != null) scroller.scrollLeft = y; + if (x != null) scroller.scrollLeft = x; + if (y != null) scroller.scrollTop = y; updateDisplay([]); }, @@ -440,10 +442,10 @@ var CodeMirror = (function() { try { var text = e.dataTransfer.getData("Text"); if (text) { - var end = replaceRange(text, pos, pos); - var curFrom = sel.from, curTo = sel.to; - setSelectionUser(pos, end); + var curFrom = sel.from, curTo = sel.to; + setSelectionUser(pos, pos); if (draggingText) replaceRange("", curFrom, curTo); + replaceSelection(text); focusInput(); } } @@ -511,9 +513,9 @@ var CodeMirror = (function() { } } function onKeyPress(e) { + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; if (window.opera && !e.which && handleKeyBinding(e)) return; if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { var ch = String.fromCharCode(charCode == null ? keyCode : charCode); @@ -565,20 +567,22 @@ var CodeMirror = (function() { } updateLinesNoUndo(from, to, newText, selFrom, selTo); } - function unredoHelper(from, to) { - var change = from.pop(); - if (change) { + function unredoHelper(from, to, dir) { + var set = from.pop(), len = set ? set.length : 0, out = []; + for (var i = dir > 0 ? 0 : len - 1, e = dir > 0 ? len : -1; i != e; i += dir) { + var change = set[i]; var replaced = [], end = change.start + change.added; doc.iter(change.start, end, function(line) { replaced.push(line.text); }); - to.push({start: change.start, added: change.old.length, old: replaced}); + out.push({start: change.start, added: change.old.length, old: replaced}); var pos = clipPos({line: change.start + change.old.length - 1, ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}); updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); - updateInput = true; } + updateInput = true; + to.push(out); } - function undo() {unredoHelper(history.done, history.undone);} - function redo() {unredoHelper(history.undone, history.done);} + function undo() {unredoHelper(history.done, history.undone, -1);} + function redo() {unredoHelper(history.undone, history.done, 1);} function updateLinesNoUndo(from, to, newText, selFrom, selTo) { if (suppressEdits) return; @@ -780,7 +784,7 @@ var CodeMirror = (function() { if (!posEq(sel.from, sel.to)) { prevInput = ""; input.value = getSelection(); - input.select(); + selectInput(input); } else if (user) prevInput = input.value = ""; } @@ -1537,7 +1541,7 @@ var CodeMirror = (function() { leaveInputAlone = true; var val = input.value = getSelection(); focusInput(); - input.select(); + selectInput(input); function rehide() { var newVal = splitLines(input.value).join("\n"); if (newVal != val) operation(replaceSelection)(newVal, "end"); @@ -2551,11 +2555,13 @@ var CodeMirror = (function() { History.prototype = { addChange: function(start, added, old) { this.undone.length = 0; - var time = +new Date, last = this.done[this.done.length - 1]; - if (time - this.time > 400 || !last || - last.start > start + added || last.start + last.added < start - last.added + last.old.length) - this.done.push({start: start, added: added, old: old}); - else { + var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1]; + var dtime = time - this.time; + if (dtime > 400 || !last) { + this.done.push([{start: start, added: added, old: old}]); + } else if (last.start > start + added || last.start + last.added < start - last.added + last.old.length) { + cur.push({start: start, added: added, old: old}); + } else { var oldoff = 0; if (start < last.start) { for (var i = last.start - start - 1; i >= 0; --i) diff --git a/IPython/frontend/html/notebook/static/codemirror/mode/markdown/markdown.js b/IPython/frontend/html/notebook/static/codemirror/mode/markdown/markdown.js index 455bb43..1385bb5 100755 --- a/IPython/frontend/html/notebook/static/codemirror/mode/markdown/markdown.js +++ b/IPython/frontend/html/notebook/static/codemirror/mode/markdown/markdown.js @@ -15,7 +15,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var hrRE = /^[*-=_]/ , ulRE = /^[*-+]\s+/ - , olRE = /^[0-9]\.\s+/ + , olRE = /^[0-9]+\.\s+/ , headerRE = /^(?:\={3,}|-{3,})$/ , codeRE = /^(k:\t|\s{4,})/ , textRE = /^[^\[*_\\<>`]+/; @@ -213,6 +213,10 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { token: function(stream, state) { if (stream.sol()) { + // Reset EM state + state.em = false; + // Reset STRONG state + state.strong = false; state.f = state.block; var previousIndentation = state.indentation , currentIndentation = 0; diff --git a/IPython/frontend/html/notebook/static/css/notebook.css b/IPython/frontend/html/notebook/static/css/notebook.css index 73bc5be..6fa3ab9 100644 --- a/IPython/frontend/html/notebook/static/css/notebook.css +++ b/IPython/frontend/html/notebook/static/css/notebook.css @@ -55,6 +55,19 @@ span#notebook_name { margin: 0.3em 0; } +#menubar_container { + position: relative; +} + +#notification { + position: absolute; + right: 3px; + top: 3px; + height: 25px; + padding: 3px 6px; + z-index: 10; +} + #toolbar { /* Initially hidden to prevent FLOUC */ display: none; @@ -71,31 +84,6 @@ span#quick_help_area { margin: 0px 0px 0px 0px; } -span#kernel_status { - position: absolute; - padding: 8px 5px 5px 5px; - right: 10px; - font-weight: bold; -} - - -.status_idle { - color: gray; - visibility: hidden; -} - -.status_busy { - color: red; -} - -.status_restarting { - color: black; -} - -#kernel_persist { - float: right; -} - .help_string { float: right; width: 170px; diff --git a/IPython/frontend/html/notebook/static/js/cell.js b/IPython/frontend/html/notebook/static/js/cell.js index e24be0e..613dd9f 100644 --- a/IPython/frontend/html/notebook/static/js/cell.js +++ b/IPython/frontend/html/notebook/static/js/cell.js @@ -142,6 +142,16 @@ var IPython = (function (IPython) { }; + Cell.prototype.toggle_line_numbers = function () { + if (this.code_mirror.getOption('lineNumbers') == false) { + this.code_mirror.setOption('lineNumbers', true); + } else { + this.code_mirror.setOption('lineNumbers', false); + } + this.code_mirror.refresh(); + }; + + IPython.Cell = Cell; return IPython; diff --git a/IPython/frontend/html/notebook/static/js/codecell.js b/IPython/frontend/html/notebook/static/js/codecell.js index 29e2e3f..eac9a28 100644 --- a/IPython/frontend/html/notebook/static/js/codecell.js +++ b/IPython/frontend/html/notebook/static/js/codecell.js @@ -79,11 +79,11 @@ var IPython = (function (IPython) { var that = this; // whatever key is pressed, first, cancel the tooltip request before // they are sent, and remove tooltip if any - if(event.type === 'keydown' ){ + if(event.type === 'keydown' ) { that.remove_and_cancel_tooltip(); - } - - + }; + + if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { // Always ignore shift-enter in CodeMirror as we handle it. return true; @@ -123,6 +123,10 @@ var IPython = (function (IPython) { return false; } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) { that.request_tooltip_after_time(pre_cursor,0); + // Prevent the event from bubbling up. + event.stop(); + // Prevent CodeMirror from handling the tab. + return true; } else { pre_cursor.trim(); // Autocomplete the current line. @@ -132,7 +136,7 @@ var IPython = (function (IPython) { this.completion_cursor = cur; IPython.notebook.complete_cell(this, line, cur.ch); return true; - } + }; } else if (event.keyCode === 8 && event.type == 'keydown') { // If backspace and the line ends with 4 spaces, remove them. var cur = editor.getCursor(); @@ -147,13 +151,8 @@ var IPython = (function (IPython) { return true; } else { return false; - } - } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey - && event.type == 'keydown') { - // toggle line numbers with Ctrl-Shift-L - this.toggle_line_numbers(); - } - else { + }; + } else { // keypress/keyup also trigger on TAB press, and we don't want to // use those to disable tab completion. if (this.is_completing && event.keyCode !== 9) { @@ -162,8 +161,8 @@ var IPython = (function (IPython) { if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) { this.is_completing = false; this.completion_cursor = null; - } - } + }; + }; return false; }; return false; @@ -545,16 +544,6 @@ var IPython = (function (IPython) { }; - CodeCell.prototype.toggle_line_numbers = function () { - if (this.code_mirror.getOption('lineNumbers') == false) { - this.code_mirror.setOption('lineNumbers', true); - } else { - this.code_mirror.setOption('lineNumbers', false); - } - this.code_mirror.refresh(); - }; - - CodeCell.prototype.select = function () { IPython.Cell.prototype.select.apply(this); this.code_mirror.refresh(); diff --git a/IPython/frontend/html/notebook/static/js/events.js b/IPython/frontend/html/notebook/static/js/events.js new file mode 100644 index 0000000..0ff950d --- /dev/null +++ b/IPython/frontend/html/notebook/static/js/events.js @@ -0,0 +1,31 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2008-2011 The IPython Development Team +// +// Distributed under the terms of the BSD License. The full license is in +// the file COPYING, distributed as part of this software. +//---------------------------------------------------------------------------- + +//============================================================================ +// Events +//============================================================================ + +// Give us an object to bind all events to. This object should be created +// before all other objects so it exists when others register event handlers. +// To trigger an event handler: +// $([IPython.events]).trigger('event.Namespace); +// To handle it: +// $([IPython.events]).on('event.Namespace',function () {}); + +var IPython = (function (IPython) { + + var utils = IPython.utils; + + var Events = function () {}; + + IPython.Events = Events; + IPython.events = new Events(); + + return IPython; + +}(IPython)); + diff --git a/IPython/frontend/html/notebook/static/js/kernel.js b/IPython/frontend/html/notebook/static/js/kernel.js index 6f00922..99ac624 100644 --- a/IPython/frontend/html/notebook/static/js/kernel.js +++ b/IPython/frontend/html/notebook/static/js/kernel.js @@ -62,7 +62,7 @@ var IPython = (function (IPython) { Kernel.prototype.restart = function (callback) { - IPython.kernel_status_widget.status_restarting(); + $([IPython.events]).trigger('status_restarting.Kernel'); var url = this.kernel_url + "/restart"; var that = this; if (this.running) { @@ -84,20 +84,19 @@ var IPython = (function (IPython) { this.kernel_url = this.base_url + "/" + this.kernel_id; this.start_channels(); callback(); - IPython.kernel_status_widget.status_idle(); }; Kernel.prototype._websocket_closed = function(ws_url, early){ var msg; var parent_item = $('body'); if (early) { - msg = "Websocket connection to " + ws_url + " could not be established.
" + - " You will NOT be able to run code.
" + + msg = "Websocket connection to " + ws_url + " could not be established." + + " You will NOT be able to run code." + " Your browser may not be compatible with the websocket version in the server," + " or if the url does not look right, there could be an error in the" + " server's configuration."; } else { - msg = "Websocket connection closed unexpectedly.
" + + msg = "Websocket connection closed unexpectedly." + " The kernel will no longer be responsive."; } var dialog = $('
'); @@ -107,8 +106,10 @@ var IPython = (function (IPython) { resizable: false, modal: true, title: "Websocket closed", + closeText: "", + close: function(event, ui) {$(this).dialog('destroy').remove();}, buttons : { - "Okay": function () { + "OK": function () { $(this).dialog('close'); } } @@ -211,6 +212,7 @@ var IPython = (function (IPython) { Kernel.prototype.interrupt = function () { if (this.running) { + $([IPython.events]).trigger('status_interrupting.Kernel'); $.post(this.kernel_url + "/interrupt"); }; }; diff --git a/IPython/frontend/html/notebook/static/js/kernelstatus.js b/IPython/frontend/html/notebook/static/js/kernelstatus.js deleted file mode 100644 index 5b61157..0000000 --- a/IPython/frontend/html/notebook/static/js/kernelstatus.js +++ /dev/null @@ -1,65 +0,0 @@ -//---------------------------------------------------------------------------- -// Copyright (C) 2008-2011 The IPython Development Team -// -// Distributed under the terms of the BSD License. The full license is in -// the file COPYING, distributed as part of this software. -//---------------------------------------------------------------------------- - -//============================================================================ -// Kernel Status widget -//============================================================================ - -var IPython = (function (IPython) { - - var utils = IPython.utils; - - var KernelStatusWidget = function (selector) { - this.selector = selector; - if (this.selector !== undefined) { - this.element = $(selector); - this.style(); - } - }; - - - KernelStatusWidget.prototype.style = function () { - this.element.addClass('ui-widget'); - this.element.attr('title', "The kernel execution status." + - " If 'Busy', the kernel is currently running code." + - " If 'Idle', it is available for execution."); - }; - - - KernelStatusWidget.prototype.status_busy = function () { - this.element.removeClass("status_idle"); - this.element.removeClass("status_restarting"); - this.element.addClass("status_busy"); - window.document.title='(Busy) '+window.document.title; - this.element.text("Busy"); - }; - - - KernelStatusWidget.prototype.status_idle = function () { - this.element.removeClass("status_busy"); - this.element.removeClass("status_restarting"); - this.element.addClass("status_idle"); - IPython.save_widget.set_document_title(); - this.element.text("Idle"); - }; - - KernelStatusWidget.prototype.status_restarting = function () { - this.element.removeClass("status_busy"); - this.element.removeClass("status_idle"); - this.element.addClass("status_restarting"); - this.element.text("Restarting"); - }; - - - - - IPython.KernelStatusWidget = KernelStatusWidget; - - return IPython; - -}(IPython)); - diff --git a/IPython/frontend/html/notebook/static/js/menubar.js b/IPython/frontend/html/notebook/static/js/menubar.js index b513ecd..eef2e44 100644 --- a/IPython/frontend/html/notebook/static/js/menubar.js +++ b/IPython/frontend/html/notebook/static/js/menubar.js @@ -45,21 +45,21 @@ var IPython = (function (IPython) { IPython.save_widget.rename_notebook(); }); this.element.find('#copy_notebook').click(function () { - var notebook_id = IPython.save_widget.get_notebook_id(); + var notebook_id = IPython.notebook.get_notebook_id(); var url = $('body').data('baseProjectUrl') + notebook_id + '/copy'; window.open(url,'_newtab'); }); this.element.find('#save_notebook').click(function () { - IPython.save_widget.save_notebook(); + IPython.notebook.save_notebook(); }); this.element.find('#download_ipynb').click(function () { - var notebook_id = IPython.save_widget.get_notebook_id(); + var notebook_id = IPython.notebook.get_notebook_id(); var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id + '?format=json'; window.open(url,'_newtab'); }); this.element.find('#download_py').click(function () { - var notebook_id = IPython.save_widget.get_notebook_id(); + var notebook_id = IPython.notebook.get_notebook_id(); var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id + '?format=py'; window.open(url,'_newtab'); diff --git a/IPython/frontend/html/notebook/static/js/notebook.js b/IPython/frontend/html/notebook/static/js/notebook.js index df20219..898672b 100644 --- a/IPython/frontend/html/notebook/static/js/notebook.js +++ b/IPython/frontend/html/notebook/static/js/notebook.js @@ -26,6 +26,10 @@ var IPython = (function (IPython) { this.msg_cell_map = {}; this.metadata = {}; this.control_key_active = false; + this.notebook_id = null; + this.notebook_name = null; + this.notebook_name_blacklist_re = /[\/\\]/; + this.nbformat = 3 // Increment this when changing the nbformat this.style(); this.create_elements(); this.bind_events(); @@ -66,7 +70,7 @@ var IPython = (function (IPython) { // Save (CTRL+S) or (AppleKey+S) //metaKey = applekey on mac if ((event.ctrlKey || event.metaKey) && event.keyCode==83) { - IPython.save_widget.save_notebook(); + that.save_notebook(); event.preventDefault(); return false; } else if (event.which === 27) { @@ -92,7 +96,7 @@ var IPython = (function (IPython) { } else if (event.which === 13 && event.ctrlKey) { that.execute_selected_cell({terminal:true}); return false; - } else if (event.which === 77 && event.ctrlKey) { + } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) { that.control_key_active = true; return false; } else if (event.which === 88 && that.control_key_active) { @@ -177,7 +181,7 @@ var IPython = (function (IPython) { return false; } else if (event.which === 83 && that.control_key_active) { // Save notebook = s - IPython.save_widget.save_notebook(); + that.save_notebook(); that.control_key_active = false; return false; } else if (event.which === 74 && that.control_key_active) { @@ -207,12 +211,12 @@ var IPython = (function (IPython) { return false; } else if (event.which === 73 && that.control_key_active) { // Interrupt kernel = i - IPython.notebook.kernel.interrupt(); + that.kernel.interrupt(); that.control_key_active = false; return false; } else if (event.which === 190 && that.control_key_active) { // Restart kernel = . # matches qt console - IPython.notebook.restart_kernel(); + that.restart_kernel(); that.control_key_active = false; return false; } else if (event.which === 72 && that.control_key_active) { @@ -402,10 +406,14 @@ var IPython = (function (IPython) { var cell = this.get_cell(index) cell.select(); if (cell.cell_type === 'heading') { - IPython.toolbar.set_cell_type(cell.cell_type+cell.level); + $([IPython.events]).trigger('selected_cell_type_changed.Notebook', + {'cell_type':cell.cell_type,level:cell.level} + ); } else { - IPython.toolbar.set_cell_type(cell.cell_type) - } + $([IPython.events]).trigger('selected_cell_type_changed.Notebook', + {'cell_type':cell.cell_type} + ); + }; }; return this; }; @@ -676,7 +684,9 @@ var IPython = (function (IPython) { source_element.remove(); this.dirty = true; }; - IPython.toolbar.set_cell_type("heading"+level); + $([IPython.events]).trigger('selected_cell_type_changed.Notebook', + {'cell_type':'heading',level:level} + ); }; }; @@ -880,15 +890,12 @@ var IPython = (function (IPython) { Notebook.prototype.start_kernel = function () { this.kernel = new IPython.Kernel(); - var notebook_id = IPython.save_widget.get_notebook_id(); - this.kernel.start(notebook_id, $.proxy(this.kernel_started, this)); + this.kernel.start(this.notebook_id, $.proxy(this.kernel_started, this)); }; Notebook.prototype.restart_kernel = function () { var that = this; - var notebook_id = IPython.save_widget.get_notebook_id(); - var dialog = $('
'); dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.'); $(document).append(dialog); @@ -976,8 +983,8 @@ var IPython = (function (IPython) { var cell = this.cell_for_msg(reply.parent_header.msg_id); if (msg_type !== 'status' && !cell){ // message not from this notebook, but should be attached to a cell - console.log("Received IOPub message not caused by one of my cells"); - console.log(reply); + // console.log("Received IOPub message not caused by one of my cells"); + // console.log(reply); return; } var output_types = ['stream','display_data','pyout','pyerr']; @@ -985,9 +992,10 @@ var IPython = (function (IPython) { this.handle_output(cell, msg_type, content); } else if (msg_type === 'status') { if (content.execution_state === 'busy') { + $([IPython.events]).trigger('status_busy.Kernel'); IPython.kernel_status_widget.status_busy(); } else if (content.execution_state === 'idle') { - IPython.kernel_status_widget.status_idle(); + $([IPython.events]).trigger('status_idle.Kernel'); } else if (content.execution_state === 'dead') { this.handle_status_dead(); }; @@ -1142,8 +1150,33 @@ var IPython = (function (IPython) { this.msg_cell_map[msg_id] = cell.cell_id; }; + // Persistance and loading + Notebook.prototype.get_notebook_id = function () { + return this.notebook_id; + }; + + + Notebook.prototype.get_notebook_name = function () { + return this.notebook_name; + }; + + + Notebook.prototype.set_notebook_name = function (name) { + this.notebook_name = name; + }; + + + Notebook.prototype.test_notebook_name = function (nbname) { + nbname = nbname || ''; + if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) { + return true; + } else { + return false; + }; + }; + Notebook.prototype.fromJSON = function (data) { var ncells = this.ncells(); @@ -1152,8 +1185,9 @@ var IPython = (function (IPython) { // Always delete cell 0 as they get renumbered as they are deleted. this.delete_cell(0); }; - // Save the metadata + // Save the metadata and name. this.metadata = data.metadata; + this.notebook_name = data.metadata.name; // Only handle 1 worksheet for now. var worksheet = data.worksheets[0]; if (worksheet !== undefined) { @@ -1186,12 +1220,10 @@ var IPython = (function (IPython) { }; Notebook.prototype.save_notebook = function () { - var notebook_id = IPython.save_widget.get_notebook_id(); - var nbname = IPython.save_widget.get_notebook_name(); // We may want to move the name/id/nbformat logic inside toJSON? var data = this.toJSON(); - data.metadata.name = nbname; - data.nbformat = 3; + data.metadata.name = this.notebook_name; + data.nbformat = this.nbformat; // We do the call with settings so we can set cache to false. var settings = { processData : false, @@ -1199,63 +1231,108 @@ var IPython = (function (IPython) { type : "PUT", data : JSON.stringify(data), headers : {'Content-Type': 'application/json'}, - success : $.proxy(this.notebook_saved,this), - error : $.proxy(this.notebook_save_failed,this) + success : $.proxy(this.save_notebook_success,this), + error : $.proxy(this.save_notebook_error,this) }; - IPython.save_widget.status_saving(); - var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id; + $([IPython.events]).trigger('notebook_saving.Notebook'); + var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id; $.ajax(url, settings); }; - Notebook.prototype.notebook_saved = function (data, status, xhr) { + Notebook.prototype.save_notebook_success = function (data, status, xhr) { this.dirty = false; - IPython.save_widget.notebook_saved(); - IPython.save_widget.status_last_saved(); + $([IPython.events]).trigger('notebook_saved.Notebook'); }; - Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) { - IPython.save_widget.status_save_failed(); + Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) { + $([IPython.events]).trigger('notebook_save_failed.Notebook'); }; - Notebook.prototype.load_notebook = function () { + Notebook.prototype.load_notebook = function (notebook_id) { var that = this; - var notebook_id = IPython.save_widget.get_notebook_id(); + this.notebook_id = notebook_id; // We do the call with settings so we can set cache to false. var settings = { processData : false, cache : false, type : "GET", dataType : "json", - success : function (data, status, xhr) { - that.notebook_loaded(data, status, xhr); - } + success : $.proxy(this.load_notebook_success,this), + error : $.proxy(this.load_notebook_error,this), }; - IPython.save_widget.status_loading(); - var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id; + $([IPython.events]).trigger('notebook_loading.Notebook'); + var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id; $.ajax(url, settings); }; - Notebook.prototype.notebook_loaded = function (data, status, xhr) { + Notebook.prototype.load_notebook_success = function (data, status, xhr) { this.fromJSON(data); if (this.ncells() === 0) { this.insert_cell_below('code'); }; - IPython.save_widget.status_last_saved(); - IPython.save_widget.set_notebook_name(data.metadata.name); this.dirty = false; if (! this.read_only) { this.start_kernel(); } this.select(0); this.scroll_to_top(); - IPython.save_widget.update_url(); - IPython.layout_manager.do_resize(); - }; - + if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) { + msg = "This notebook has been converted from an older " + + "notebook format (v"+data.orig_nbformat+") to the current notebook " + + "format (v"+data.nbformat+"). The next time you save this notebook, the " + + "newer notebook format will be used and older verions of IPython " + + "may not be able to read it. To keep the older version, close the " + + "notebook without saving it."; + var dialog = $('
'); + dialog.html(msg); + this.element.append(dialog); + dialog.dialog({ + resizable: false, + modal: true, + title: "Notebook converted", + closeText: "", + close: function(event, ui) {$(this).dialog('destroy').remove();}, + buttons : { + "OK": function () { + $(this).dialog('close'); + } + }, + width: 400 + }); + } + $([IPython.events]).trigger('notebook_loaded.Notebook'); + }; + + + Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) { + if (xhr.status === 500) { + msg = "An error occurred while loading this notebook. Most likely " + + "this notebook is in a newer format than is supported by this " + + "version of IPython. This version can load notebook formats " + + "v"+this.nbformat+" or earlier."; + var dialog = $('
'); + dialog.html(msg); + this.element.append(dialog); + dialog.dialog({ + resizable: false, + modal: true, + title: "Error loading notebook", + closeText: "", + close: function(event, ui) {$(this).dialog('destroy').remove();}, + buttons : { + "OK": function () { + $(this).dialog('close'); + } + }, + width: 400 + }); + } + } + IPython.Notebook = Notebook; diff --git a/IPython/frontend/html/notebook/static/js/notebookmain.js b/IPython/frontend/html/notebook/static/js/notebookmain.js index bb5fd46..34cd61c 100644 --- a/IPython/frontend/html/notebook/static/js/notebookmain.js +++ b/IPython/frontend/html/notebook/static/js/notebookmain.js @@ -82,14 +82,13 @@ $(document).ready(function () { IPython.layout_manager = new IPython.LayoutManager(); IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter'); - IPython.save_widget = new IPython.SaveWidget('span#save_widget'); IPython.quick_help = new IPython.QuickHelp('span#quick_help_area'); IPython.login_widget = new IPython.LoginWidget('span#login_widget'); IPython.notebook = new IPython.Notebook('div#notebook'); - IPython.kernel_status_widget = new IPython.KernelStatusWidget('#kernel_status'); + IPython.save_widget = new IPython.SaveWidget('span#save_widget'); IPython.menubar = new IPython.MenuBar('#menubar') IPython.toolbar = new IPython.ToolBar('#toolbar') - IPython.kernel_status_widget.status_idle(); + IPython.notification_widget = new IPython.NotificationWidget('#notification') IPython.layout_manager.do_resize(); @@ -111,7 +110,11 @@ $(document).ready(function () { $('div#main_app').css('display','block'); IPython.layout_manager.do_resize(); - IPython.notebook.load_notebook(); + $([IPython.events]).on('notebook_loaded.Notebook', function () { + IPython.layout_manager.do_resize(); + IPython.save_widget.update_url(); + }) + IPython.notebook.load_notebook($('body').data('notebookId')); }); diff --git a/IPython/frontend/html/notebook/static/js/notificationwidget.js b/IPython/frontend/html/notebook/static/js/notificationwidget.js new file mode 100644 index 0000000..9147afc --- /dev/null +++ b/IPython/frontend/html/notebook/static/js/notificationwidget.js @@ -0,0 +1,102 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2008-2011 The IPython Development Team +// +// Distributed under the terms of the BSD License. The full license is in +// the file COPYING, distributed as part of this software. +//---------------------------------------------------------------------------- + +//============================================================================ +// Notification widget +//============================================================================ + +var IPython = (function (IPython) { + + var utils = IPython.utils; + + + var NotificationWidget = function (selector) { + this.selector = selector; + this.timeout = null; + this.busy = false; + if (this.selector !== undefined) { + this.element = $(selector); + this.style(); + this.bind_events(); + } + }; + + + NotificationWidget.prototype.style = function () { + this.element.addClass('ui-widget ui-widget-content ui-corner-all'); + this.element.addClass('border-box-sizing'); + }; + + + NotificationWidget.prototype.bind_events = function () { + var that = this; + // Kernel events + $([IPython.events]).on('status_idle.Kernel',function () { + IPython.save_widget.update_document_title(); + if (that.get_message() === 'Kernel busy') { + that.element.fadeOut(100, function () { + that.element.html(''); + }); + }; + }); + $([IPython.events]).on('status_busy.Kernel',function () { + window.document.title='(Busy) '+window.document.title; + that.set_message("Kernel busy"); + }); + $([IPython.events]).on('status_restarting.Kernel',function () { + that.set_message("Restarting kernel",500); + }); + $([IPython.events]).on('status_interrupting.Kernel',function () { + that.set_message("Interrupting kernel",500); + }); + // Notebook events + $([IPython.events]).on('notebook_loading.Notebook', function () { + that.set_message("Loading notebook",500); + }); + $([IPython.events]).on('notebook_loaded.Notebook', function () { + that.set_message("Notebook loaded",500); + }); + $([IPython.events]).on('notebook_saving.Notebook', function () { + that.set_message("Saving notebook",500); + }); + $([IPython.events]).on('notebook_saved.Notebook', function () { + that.set_message("Notebook saved",500); + }); + $([IPython.events]).on('notebook_save_failed.Notebook', function () { + that.set_message("Notebook save failed",500); + }); + }; + + + NotificationWidget.prototype.set_message = function (msg, timeout) { + var that = this; + this.element.html(msg); + this.element.fadeIn(100); + if (this.timeout !== null) { + clearTimeout(this.timeout); + this.timeout = null; + }; + if (timeout !== undefined) { + this.timeout = setTimeout(function () { + that.element.fadeOut(100, function () {that.element.html('');}); + that.timeout = null; + }, timeout) + }; + }; + + + NotificationWidget.prototype.get_message = function () { + return this.element.html(); + }; + + + IPython.NotificationWidget = NotificationWidget; + + return IPython; + +}(IPython)); + diff --git a/IPython/frontend/html/notebook/static/js/printnotebookmain.js b/IPython/frontend/html/notebook/static/js/printnotebookmain.js index ce5ec98..55f168c 100644 --- a/IPython/frontend/html/notebook/static/js/printnotebookmain.js +++ b/IPython/frontend/html/notebook/static/js/printnotebookmain.js @@ -79,16 +79,15 @@ $(document).ready(function () { $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content'); $('div#notebook_panel').addClass('border-box-sizing ui-widget'); - IPython.save_widget = new IPython.SaveWidget('span#save_widget'); IPython.login_widget = new IPython.LoginWidget('span#login_widget'); IPython.notebook = new IPython.Notebook('div#notebook'); + IPython.save_widget = new IPython.SaveWidget('span#save_widget'); // These have display: none in the css file and are made visible here to prevent FLOUC. $('div#header').css('display','block'); $('div#main_app').css('display','block'); - // Perform these actions after the notebook has been loaded. - IPython.notebook.load_notebook(function () {}); + IPython.notebook.load_notebook($('body').data('notebookId')); }); diff --git a/IPython/frontend/html/notebook/static/js/savewidget.js b/IPython/frontend/html/notebook/static/js/savewidget.js index ad1f5f3..2ffaf31 100644 --- a/IPython/frontend/html/notebook/static/js/savewidget.js +++ b/IPython/frontend/html/notebook/static/js/savewidget.js @@ -15,8 +15,6 @@ var IPython = (function (IPython) { var SaveWidget = function (selector) { this.selector = selector; - this.notebook_name_blacklist_re = /[\/\\]/; - this.last_saved_name = ''; if (this.selector !== undefined) { this.element = $(selector); this.style(); @@ -43,11 +41,19 @@ var IPython = (function (IPython) { }, function () { $(this).removeClass("ui-state-hover"); }); - }; - - - SaveWidget.prototype.save_notebook = function () { - IPython.notebook.save_notebook(); + $([IPython.events]).on('notebook_loaded.Notebook', function () { + that.set_last_saved(); + that.update_notebook_name(); + that.update_document_title(); + }); + $([IPython.events]).on('notebook_saved.Notebook', function () { + that.set_last_saved(); + that.update_notebook_name(); + that.update_document_title(); + }); + $([IPython.events]).on('notebook_save_failed.Notebook', function () { + that.set_save_status(''); + }); }; @@ -61,7 +67,7 @@ var IPython = (function (IPython) { dialog.append( $('').attr('type','text').attr('size','25') .addClass('ui-widget ui-widget-content') - .attr('value',that.get_notebook_name()) + .attr('value',IPython.notebook.get_notebook_name()) ); // $(document).append(dialog); dialog.dialog({ @@ -73,15 +79,15 @@ var IPython = (function (IPython) { buttons : { "OK": function () { var new_name = $(this).find('input').attr('value'); - if (!that.test_notebook_name(new_name)) { + if (!IPython.notebook.test_notebook_name(new_name)) { $(this).find('h3').html( "Invalid notebook name. Notebook names must "+ "have 1 or more characters and can contain any characters " + "except / and \\. Please enter a new notebook name:" ); } else { - that.set_notebook_name(new_name); - that.save_notebook(); + IPython.notebook.set_notebook_name(new_name); + IPython.notebook.save_notebook(); $(this).dialog('close'); } }, @@ -92,82 +98,36 @@ var IPython = (function (IPython) { }); } - SaveWidget.prototype.notebook_saved = function () { - this.set_document_title(); - this.last_saved_name = this.get_notebook_name(); - }; - - SaveWidget.prototype.get_notebook_name = function () { - return this.element.find('span#notebook_name').html(); - }; - - - SaveWidget.prototype.set_notebook_name = function (nbname) { + SaveWidget.prototype.update_notebook_name = function () { + var nbname = IPython.notebook.get_notebook_name(); this.element.find('span#notebook_name').html(nbname); - this.set_document_title(); - this.last_saved_name = nbname; }; - SaveWidget.prototype.set_document_title = function () { - nbname = this.get_notebook_name(); + SaveWidget.prototype.update_document_title = function () { + var nbname = IPython.notebook.get_notebook_name(); document.title = nbname; }; - - - SaveWidget.prototype.get_notebook_id = function () { - return $('body').data('notebookId'); - }; SaveWidget.prototype.update_url = function () { - var notebook_id = this.get_notebook_id(); - if (notebook_id !== '') { + var notebook_id = IPython.notebook.get_notebook_id(); + if (notebook_id !== null) { var new_url = $('body').data('baseProjectUrl') + notebook_id; window.history.replaceState({}, '', new_url); }; }; - SaveWidget.prototype.test_notebook_name = function (nbname) { - nbname = nbname || ''; - if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) { - return true; - } else { - return false; - }; - }; + SaveWidget.prototype.set_save_status = function (msg) { + this.element.find('span#save_status').html(msg); + } SaveWidget.prototype.set_last_saved = function () { var d = new Date(); - $('#save_status').html('Last saved: '+d.format('mmm dd h:MM TT')); - - }; - - SaveWidget.prototype.reset_status = function () { - this.element.find('span#save_status').html(''); - }; - - - SaveWidget.prototype.status_last_saved = function () { - this.set_last_saved(); - }; - - - SaveWidget.prototype.status_saving = function () { - this.element.find('span#save_status').html('Saving...'); - }; - - - SaveWidget.prototype.status_save_failed = function () { - this.element.find('span#save_status').html('Save failed'); - }; - - - SaveWidget.prototype.status_loading = function () { - this.element.find('span#save_status').html('Loading...'); + this.set_save_status('Last saved: '+d.format('mmm dd h:MM TT')); }; diff --git a/IPython/frontend/html/notebook/static/js/textcell.js b/IPython/frontend/html/notebook/static/js/textcell.js index 1c98dea..f48f452 100644 --- a/IPython/frontend/html/notebook/static/js/textcell.js +++ b/IPython/frontend/html/notebook/static/js/textcell.js @@ -49,7 +49,7 @@ var IPython = (function (IPython) { IPython.Cell.prototype.bind_events.apply(this); var that = this; this.element.keydown(function (event) { - if (event.which === 13) { + if (event.which === 13 && !event.shiftKey) { if (that.rendered) { that.edit(); return false; diff --git a/IPython/frontend/html/notebook/static/js/toolbar.js b/IPython/frontend/html/notebook/static/js/toolbar.js index df0de25..2ecdfa0 100644 --- a/IPython/frontend/html/notebook/static/js/toolbar.js +++ b/IPython/frontend/html/notebook/static/js/toolbar.js @@ -72,8 +72,9 @@ var IPython = (function (IPython) { ToolBar.prototype.bind_events = function () { + var that = this; this.element.find('#save_b').click(function () { - IPython.save_widget.save_notebook(); + IPython.notebook.save_notebook(); }); this.element.find('#cut_b').click(function () { IPython.notebook.cut_cell(); @@ -124,12 +125,13 @@ var IPython = (function (IPython) { IPython.notebook.to_heading(undefined, 6); }; }); - - }; - - - ToolBar.prototype.set_cell_type = function (cell_type) { - this.element.find('#cell_type').val(cell_type); + $([IPython.events]).on('selected_cell_type_changed', function (event, data) { + if (data.cell_type === 'heading') { + that.element.find('#cell_type').val(data.cell_type+data.level); + } else { + that.element.find('#cell_type').val(data.cell_type); + } + }); }; diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html index 242a5d0..7b0b087 100644 --- a/IPython/frontend/html/notebook/templates/notebook.html +++ b/IPython/frontend/html/notebook/templates/notebook.html @@ -54,10 +54,9 @@ {% end %} - - Idle
+ +
@@ -220,12 +222,12 @@ + - @@ -234,6 +236,7 @@ + diff --git a/IPython/frontend/html/notebook/templates/printnotebook.html b/IPython/frontend/html/notebook/templates/printnotebook.html index 003fe33..2849e61 100644 --- a/IPython/frontend/html/notebook/templates/printnotebook.html +++ b/IPython/frontend/html/notebook/templates/printnotebook.html @@ -17,10 +17,7 @@ - - - @@ -88,6 +85,7 @@ + diff --git a/IPython/nbformat/current.py b/IPython/nbformat/current.py index a1e29dc..c16efc4 100644 --- a/IPython/nbformat/current.py +++ b/IPython/nbformat/current.py @@ -28,14 +28,14 @@ from IPython.nbformat import v1 from IPython.nbformat.v3 import ( NotebookNode, new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, - parse_filename, new_metadata, new_author, new_heading_cell + parse_filename, new_metadata, new_author, new_heading_cell, nbformat ) #----------------------------------------------------------------------------- # Code #----------------------------------------------------------------------------- -current_nbformat = 3 +current_nbformat = nbformat class NBFormatError(Exception): @@ -45,8 +45,8 @@ class NBFormatError(Exception): def parse_json(s, **kwargs): """Parse a string into a (nbformat, dict) tuple.""" d = json.loads(s, **kwargs) - nbformat = d.get('nbformat',1) - return nbformat, d + nbf = d.get('nbformat',1) + return nbf, d def parse_py(s, **kwargs): @@ -54,25 +54,25 @@ def parse_py(s, **kwargs): pattern = r'# (?P\d+)' m = re.search(pattern,s) if m is not None: - nbformat = int(m.group('nbformat')) + nbf = int(m.group('nbformat')) else: - nbformat = 3 - return nbformat, s + nbf = current_nbformat + return nbf, s def reads_json(s, **kwargs): """Read a JSON notebook from a string and return the NotebookNode object.""" - nbformat, d = parse_json(s, **kwargs) - if nbformat == 1: + nbf, d = parse_json(s, **kwargs) + if nbf == 1: nb = v1.to_notebook_json(d, **kwargs) nb = v3.convert_to_this_nbformat(nb, orig_version=1) - elif nbformat == 2: + elif nbf == 2: nb = v2.to_notebook_json(d, **kwargs) nb = v3.convert_to_this_nbformat(nb, orig_version=2) - elif nbformat == 3: + elif nbf == 3: nb = v3.to_notebook_json(d, **kwargs) else: - raise NBFormatError('Unsupported JSON nbformat version: %i' % nbformat) + raise NBFormatError('Unsupported JSON nbformat version: %i' % nbf) return nb @@ -82,13 +82,13 @@ def writes_json(nb, **kwargs): def reads_py(s, **kwargs): """Read a .py notebook from a string and return the NotebookNode object.""" - nbformat, s = parse_py(s, **kwargs) - if nbformat == 2: + nbf, s = parse_py(s, **kwargs) + if nbf == 2: nb = v2.to_notebook_py(s, **kwargs) - elif nbformat == 3: + elif nbf == 3: nb = v3.to_notebook_py(s, **kwargs) else: - raise NBFormatError('Unsupported PY nbformat version: %i' % nbformat) + raise NBFormatError('Unsupported PY nbformat version: %i' % nbf) return nb diff --git a/IPython/nbformat/v3/__init__.py b/IPython/nbformat/v3/__init__.py index 2c86f51..c8fa25d 100644 --- a/IPython/nbformat/v3/__init__.py +++ b/IPython/nbformat/v3/__init__.py @@ -1,4 +1,4 @@ -"""The main API for the v2 notebook format. +"""The main API for the v3 notebook format. Authors: @@ -19,7 +19,7 @@ Authors: from .nbbase import ( NotebookNode, new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, - new_metadata, new_author, new_heading_cell + new_metadata, new_author, new_heading_cell, nbformat ) from .nbjson import reads as reads_json, writes as writes_json diff --git a/IPython/nbformat/v3/convert.py b/IPython/nbformat/v3/convert.py index 8480b9d..8914fef 100644 --- a/IPython/nbformat/v3/convert.py +++ b/IPython/nbformat/v3/convert.py @@ -27,7 +27,7 @@ from IPython.nbformat import v2 #----------------------------------------------------------------------------- def convert_to_this_nbformat(nb, orig_version=2): - """Convert a notebook to the v2 format. + """Convert a notebook to the v3 format. Parameters ---------- @@ -40,6 +40,9 @@ def convert_to_this_nbformat(nb, orig_version=2): nb = v2.convert_to_this_nbformat(nb) orig_version = 2 if orig_version == 2: + # Mark the original nbformat so consumers know it has been converted. + nb.nbformat = 3 + nb.orig_nbformat = 2 return nb elif orig_version == 3: return nb diff --git a/IPython/nbformat/v3/nbbase.py b/IPython/nbformat/v3/nbbase.py index 9d218eb..f0a97fd 100644 --- a/IPython/nbformat/v3/nbbase.py +++ b/IPython/nbformat/v3/nbbase.py @@ -30,6 +30,9 @@ from IPython.utils.ipstruct import Struct # Code #----------------------------------------------------------------------------- +# Change this when incrementing the nbformat version +nbformat = 3 + class NotebookNode(Struct): pass @@ -146,7 +149,7 @@ def new_worksheet(name=None, cells=None): def new_notebook(metadata=None, worksheets=None): """Create a notebook by name, id and a list of worksheets.""" nb = NotebookNode() - nb.nbformat = 3 + nb.nbformat = nbformat if worksheets is None: nb.worksheets = [] else: diff --git a/IPython/nbformat/v3/nbpy.py b/IPython/nbformat/v3/nbpy.py index ec7daf1..aab6523 100644 --- a/IPython/nbformat/v3/nbpy.py +++ b/IPython/nbformat/v3/nbpy.py @@ -20,7 +20,7 @@ import re from .rwbase import NotebookReader, NotebookWriter from .nbbase import ( new_code_cell, new_text_cell, new_worksheet, - new_notebook, new_heading_cell + new_notebook, new_heading_cell, nbformat ) #----------------------------------------------------------------------------- @@ -151,7 +151,7 @@ class PyWriter(NotebookWriter): def writes(self, nb, **kwargs): lines = [u'# -*- coding: utf-8 -*-'] - lines.extend([u'# 2','']) + lines.extend([u'# %i' % nbformat,'']) for ws in nb.worksheets: for cell in ws.cells: if cell.cell_type == u'code': diff --git a/IPython/nbformat/v3/tests/nbexamples.py b/IPython/nbformat/v3/tests/nbexamples.py index 6482fa7..e2a9350 100644 --- a/IPython/nbformat/v3/tests/nbexamples.py +++ b/IPython/nbformat/v3/tests/nbexamples.py @@ -4,7 +4,7 @@ from base64 import encodestring from ..nbbase import ( NotebookNode, new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, - new_metadata, new_author, new_heading_cell + new_metadata, new_author, new_heading_cell, nbformat ) # some random base64-encoded *bytes* @@ -92,7 +92,7 @@ nb0 = new_notebook( ) nb0_py = """# -*- coding: utf-8 -*- -# 2 +# %i # @@ -122,6 +122,6 @@ a = numpy.random.rand(100) print a -""" +""" % nbformat diff --git a/IPython/nbformat/v3/tests/test_nbbase.py b/IPython/nbformat/v3/tests/test_nbbase.py index 3dc4cda..3e992c0 100644 --- a/IPython/nbformat/v3/tests/test_nbbase.py +++ b/IPython/nbformat/v3/tests/test_nbbase.py @@ -3,7 +3,7 @@ from unittest import TestCase from ..nbbase import ( NotebookNode, new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, - new_author, new_metadata, new_heading_cell + new_author, new_metadata, new_heading_cell, nbformat ) class TestCell(TestCase): @@ -102,7 +102,7 @@ class TestNotebook(TestCase): nb = new_notebook() self.assertEquals(nb.worksheets, []) self.assertEquals(nb.metadata, NotebookNode()) - self.assertEquals(nb.nbformat,2) + self.assertEquals(nb.nbformat,nbformat) def test_notebook(self): worksheets = [new_worksheet(),new_worksheet()] @@ -110,7 +110,7 @@ class TestNotebook(TestCase): nb = new_notebook(metadata=metadata,worksheets=worksheets) self.assertEquals(nb.metadata.name,u'foo') self.assertEquals(nb.worksheets,worksheets) - self.assertEquals(nb.nbformat,2) + self.assertEquals(nb.nbformat,nbformat) class TestMetadata(TestCase): diff --git a/docs/examples/notebooks/00_notebook_tour.ipynb b/docs/examples/notebooks/00_notebook_tour.ipynb index c1db0df..1b34ca1 100644 --- a/docs/examples/notebooks/00_notebook_tour.ipynb +++ b/docs/examples/notebooks/00_notebook_tour.ipynb @@ -855,7 +855,7 @@ "Note that this is *different* from the above examples. Above we were typing mathematical expressions", "in Markdown cells (along with normal text) and letting the browser render them; now we are displaying", "the output of a Python computation as a LaTeX expression wrapped by the `Math()` object so the browser", - "renders it:" + "renders it. The `Math` object will add the needed LaTeX delimiters (`$$`) if they are not provided:" ] }, { @@ -863,22 +863,59 @@ "collapsed": false, "input": [ "from IPython.core.display import Math", - "Math(r'$F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx$')" + "Math(r'F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx')" ], "language": "python", "outputs": [ { "latex": [ - "$F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx$" + "$$F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} dx$$" ], "output_type": "pyout", - "prompt_number": 8, + "prompt_number": 1, "text": [ - "<IPython.core.display.Math at 0x45840d0>" + "" ] } ], - "prompt_number": 8 + "prompt_number": 1 + }, + { + "cell_type": "markdown", + "source": [ + "With the `Latex` class, you have to include the delimiters yourself. This allows you to use other LaTeX modes such as `eqnarray`:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "from IPython.core.display import Latex", + "Latex(r\"\"\"\\begin{eqnarray}", + "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\", + "\\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\", + "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\", + "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 ", + "\\end{eqnarray}\"\"\")" + ], + "language": "python", + "outputs": [ + { + "latex": [ + "\\begin{eqnarray}", + "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\", + "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\", + "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 ", + "\\end{eqnarray}" + ], + "output_type": "pyout", + "prompt_number": 5, + "text": [ + "" + ] + } + ], + "prompt_number": 5 }, { "cell_type": "markdown",