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",