##// END OF EJS Templates
Allow switching kernel from the notebook UI
Thomas Kluyver -
Show More
@@ -0,0 +1,58 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 'base/js/namespace',
6 'jquery',
7 'base/js/utils',
8 ], function(IPython, $, utils) {
9 "use strict";
10
11 var KernelSelector = function(selector, notebook) {
12 this.selector = selector;
13 this.notebook = notebook;
14 this.kernelspecs = {};
15 this.current = "python";
16 if (this.selector !== undefined) {
17 this.element = $(selector);
18 this.style();
19 this.request_kernelspecs();
20 }
21 };
22
23 KernelSelector.prototype.style = function() {
24 };
25
26 KernelSelector.prototype.request_kernelspecs = function() {
27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
28 $.ajax(url, {success: $.proxy(this.got_kernelspecs, this)});
29 };
30
31 KernelSelector.prototype.got_kernelspecs = function(data, status, xhr) {
32 this.kernelspecs = {};
33 var menu = this.element.find("#kernel_selector");
34 for (var i = 0; i < data.length; i++) {
35 var ks = data[i];
36 this.kernelspecs[ks.name] = ks;
37 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
38 .attr('href', '#')
39 .click($.proxy(this.change_kernel, this, ks.name))
40 .text(ks.display_name));
41 menu.append(ksentry);
42 }
43 };
44
45 KernelSelector.prototype.change_kernel = function(kernel_name) {
46 console.log("change_kernel " + kernel_name + " from " + this.current);
47 if (kernel_name === this.current) {
48 return;
49 }
50 this.notebook.session.delete();
51 this.notebook.start_session(kernel_name);
52 this.current = kernel_name;
53 var display_name = this.kernelspecs[kernel_name].display_name;
54 this.element.find("#current_kernel_spec").text(display_name);
55 };
56
57 return {'KernelSelector': KernelSelector};
58 });
@@ -1,138 +1,142 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 require([
4 require([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'notebook/js/notebook',
7 'notebook/js/notebook',
8 'base/js/utils',
8 'base/js/utils',
9 'base/js/page',
9 'base/js/page',
10 'notebook/js/layoutmanager',
10 'notebook/js/layoutmanager',
11 'base/js/events',
11 'base/js/events',
12 'auth/js/loginwidget',
12 'auth/js/loginwidget',
13 'notebook/js/maintoolbar',
13 'notebook/js/maintoolbar',
14 'notebook/js/pager',
14 'notebook/js/pager',
15 'notebook/js/quickhelp',
15 'notebook/js/quickhelp',
16 'notebook/js/menubar',
16 'notebook/js/menubar',
17 'notebook/js/notificationarea',
17 'notebook/js/notificationarea',
18 'notebook/js/savewidget',
18 'notebook/js/savewidget',
19 'notebook/js/keyboardmanager',
19 'notebook/js/keyboardmanager',
20 'notebook/js/config',
20 'notebook/js/config',
21 'notebook/js/kernelselector',
21 // only loaded, not used:
22 // only loaded, not used:
22 'custom/custom',
23 'custom/custom',
23 ], function(
24 ], function(
24 IPython,
25 IPython,
25 $,
26 $,
26 notebook,
27 notebook,
27 utils,
28 utils,
28 page,
29 page,
29 layoutmanager,
30 layoutmanager,
30 events,
31 events,
31 loginwidget,
32 loginwidget,
32 maintoolbar,
33 maintoolbar,
33 pager,
34 pager,
34 quickhelp,
35 quickhelp,
35 menubar,
36 menubar,
36 notificationarea,
37 notificationarea,
37 savewidget,
38 savewidget,
38 keyboardmanager,
39 keyboardmanager,
39 config
40 config,
41 kernelselector
40 ) {
42 ) {
41 "use strict";
43 "use strict";
42
44
43 $('#ipython-main-app').addClass('border-box-sizing');
45 $('#ipython-main-app').addClass('border-box-sizing');
44 $('div#notebook_panel').addClass('border-box-sizing');
46 $('div#notebook_panel').addClass('border-box-sizing');
45
47
46 var common_options = {
48 var common_options = {
47 base_url : utils.get_body_data("baseUrl"),
49 base_url : utils.get_body_data("baseUrl"),
48 ws_url : IPython.utils.get_body_data("wsUrl"),
50 ws_url : IPython.utils.get_body_data("wsUrl"),
49 notebook_path : utils.get_body_data("notebookPath"),
51 notebook_path : utils.get_body_data("notebookPath"),
50 notebook_name : utils.get_body_data('notebookName')
52 notebook_name : utils.get_body_data('notebookName')
51 };
53 };
52
54
53 var user_config = $.extend({}, config.default_config);
55 var user_config = $.extend({}, config.default_config);
54 var page = new page.Page();
56 var page = new page.Page();
55 var layout_manager = new layoutmanager.LayoutManager();
57 var layout_manager = new layoutmanager.LayoutManager();
56 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
58 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
57 layout_manager: layout_manager,
59 layout_manager: layout_manager,
58 events: events});
60 events: events});
59 var keyboard_manager = new keyboardmanager.KeyboardManager({
61 var keyboard_manager = new keyboardmanager.KeyboardManager({
60 pager: pager,
62 pager: pager,
61 events: events});
63 events: events});
62 var save_widget = new savewidget.SaveWidget('span#save_widget', {
64 var save_widget = new savewidget.SaveWidget('span#save_widget', {
63 events: events,
65 events: events,
64 keyboard_manager: keyboard_manager});
66 keyboard_manager: keyboard_manager});
65 var notebook = new notebook.Notebook('div#notebook', $.extend({
67 var notebook = new notebook.Notebook('div#notebook', $.extend({
66 events: events,
68 events: events,
67 keyboard_manager: keyboard_manager,
69 keyboard_manager: keyboard_manager,
68 save_widget: save_widget,
70 save_widget: save_widget,
69 config: user_config},
71 config: user_config},
70 common_options));
72 common_options));
71 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
73 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
72 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
74 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
73 notebook: notebook,
75 notebook: notebook,
74 events: events});
76 events: events});
75 var quick_help = new quickhelp.QuickHelp({
77 var quick_help = new quickhelp.QuickHelp({
76 keyboard_manager: keyboard_manager,
78 keyboard_manager: keyboard_manager,
77 events: events,
79 events: events,
78 notebook: notebook});
80 notebook: notebook});
79 var menubar = new menubar.MenuBar('#menubar', $.extend({
81 var menubar = new menubar.MenuBar('#menubar', $.extend({
80 notebook: notebook,
82 notebook: notebook,
81 layout_manager: layout_manager,
83 layout_manager: layout_manager,
82 events: events,
84 events: events,
83 save_widget: save_widget,
85 save_widget: save_widget,
84 quick_help: quick_help},
86 quick_help: quick_help},
85 common_options));
87 common_options));
86 var notification_area = new notificationarea.NotificationArea(
88 var notification_area = new notificationarea.NotificationArea(
87 '#notification_area', {
89 '#notification_area', {
88 events: events,
90 events: events,
89 save_widget: save_widget,
91 save_widget: save_widget,
90 notebook: notebook,
92 notebook: notebook,
91 keyboard_manager: keyboard_manager});
93 keyboard_manager: keyboard_manager});
92 notification_area.init_notification_widgets();
94 notification_area.init_notification_widgets();
95 var kernel_selector = new kernelselector.KernelSelector(
96 '#kernel_selector_widget', notebook);
93
97
94 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
98 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
95 '<span id="test2" style="font-weight: bold;">x</span>'+
99 '<span id="test2" style="font-weight: bold;">x</span>'+
96 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
100 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
97 var nh = $('#test1').innerHeight();
101 var nh = $('#test1').innerHeight();
98 var bh = $('#test2').innerHeight();
102 var bh = $('#test2').innerHeight();
99 var ih = $('#test3').innerHeight();
103 var ih = $('#test3').innerHeight();
100 if(nh != bh || nh != ih) {
104 if(nh != bh || nh != ih) {
101 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
105 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
102 }
106 }
103 $('#fonttest').remove();
107 $('#fonttest').remove();
104
108
105 page.show();
109 page.show();
106
110
107 layout_manager.do_resize();
111 layout_manager.do_resize();
108 var first_load = function () {
112 var first_load = function () {
109 layout_manager.do_resize();
113 layout_manager.do_resize();
110 var hash = document.location.hash;
114 var hash = document.location.hash;
111 if (hash) {
115 if (hash) {
112 document.location.hash = '';
116 document.location.hash = '';
113 document.location.hash = hash;
117 document.location.hash = hash;
114 }
118 }
115 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
119 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
116 // only do this once
120 // only do this once
117 events.off('notebook_loaded.Notebook', first_load);
121 events.off('notebook_loaded.Notebook', first_load);
118 };
122 };
119 events.on('notebook_loaded.Notebook', first_load);
123 events.on('notebook_loaded.Notebook', first_load);
120
124
121 IPython.page = page;
125 IPython.page = page;
122 IPython.layout_manager = layout_manager;
126 IPython.layout_manager = layout_manager;
123 IPython.notebook = notebook;
127 IPython.notebook = notebook;
124 IPython.pager = pager;
128 IPython.pager = pager;
125 IPython.quick_help = quick_help;
129 IPython.quick_help = quick_help;
126 IPython.login_widget = login_widget;
130 IPython.login_widget = login_widget;
127 IPython.menubar = menubar;
131 IPython.menubar = menubar;
128 IPython.toolbar = toolbar;
132 IPython.toolbar = toolbar;
129 IPython.notification_area = notification_area;
133 IPython.notification_area = notification_area;
130 IPython.keyboard_manager = keyboard_manager;
134 IPython.keyboard_manager = keyboard_manager;
131 IPython.save_widget = save_widget;
135 IPython.save_widget = save_widget;
132 IPython.config = user_config;
136 IPython.config = user_config;
133 IPython.tooltip = notebook.tooltip;
137 IPython.tooltip = notebook.tooltip;
134
138
135 events.trigger('app_initialized.NotebookApp');
139 events.trigger('app_initialized.NotebookApp');
136 notebook.load_notebook(common_options.notebook_name, common_options.notebook_path);
140 notebook.load_notebook(common_options.notebook_name, common_options.notebook_path);
137
141
138 });
142 });
@@ -1,2539 +1,2543 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'base/js/dialog',
8 'base/js/dialog',
9 'notebook/js/textcell',
9 'notebook/js/textcell',
10 'notebook/js/codecell',
10 'notebook/js/codecell',
11 'services/sessions/js/session',
11 'services/sessions/js/session',
12 'notebook/js/celltoolbar',
12 'notebook/js/celltoolbar',
13 'components/marked/lib/marked',
13 'components/marked/lib/marked',
14 'highlight',
14 'highlight',
15 'notebook/js/mathjaxutils',
15 'notebook/js/mathjaxutils',
16 'base/js/keyboard',
16 'base/js/keyboard',
17 'notebook/js/tooltip',
17 'notebook/js/tooltip',
18 'notebook/js/celltoolbarpresets/default',
18 'notebook/js/celltoolbarpresets/default',
19 'notebook/js/celltoolbarpresets/rawcell',
19 'notebook/js/celltoolbarpresets/rawcell',
20 'notebook/js/celltoolbarpresets/slideshow',
20 'notebook/js/celltoolbarpresets/slideshow',
21 ], function (
21 ], function (
22 IPython,
22 IPython,
23 $,
23 $,
24 utils,
24 utils,
25 dialog,
25 dialog,
26 textcell,
26 textcell,
27 codecell,
27 codecell,
28 session,
28 session,
29 celltoolbar,
29 celltoolbar,
30 marked,
30 marked,
31 hljs,
31 hljs,
32 mathjaxutils,
32 mathjaxutils,
33 keyboard,
33 keyboard,
34 tooltip,
34 tooltip,
35 default_celltoolbar,
35 default_celltoolbar,
36 rawcell_celltoolbar,
36 rawcell_celltoolbar,
37 slideshow_celltoolbar
37 slideshow_celltoolbar
38 ) {
38 ) {
39
39
40 var Notebook = function (selector, options) {
40 var Notebook = function (selector, options) {
41 // Constructor
41 // Constructor
42 //
42 //
43 // A notebook contains and manages cells.
43 // A notebook contains and manages cells.
44 //
44 //
45 // Parameters:
45 // Parameters:
46 // selector: string
46 // selector: string
47 // options: dictionary
47 // options: dictionary
48 // Dictionary of keyword arguments.
48 // Dictionary of keyword arguments.
49 // events: $(Events) instance
49 // events: $(Events) instance
50 // keyboard_manager: KeyboardManager instance
50 // keyboard_manager: KeyboardManager instance
51 // save_widget: SaveWidget instance
51 // save_widget: SaveWidget instance
52 // config: dictionary
52 // config: dictionary
53 // base_url : string
53 // base_url : string
54 // notebook_path : string
54 // notebook_path : string
55 // notebook_name : string
55 // notebook_name : string
56 this.config = options.config || {};
56 this.config = options.config || {};
57 this.base_url = options.base_url;
57 this.base_url = options.base_url;
58 this.notebook_path = options.notebook_path;
58 this.notebook_path = options.notebook_path;
59 this.notebook_name = options.notebook_name;
59 this.notebook_name = options.notebook_name;
60 this.events = options.events;
60 this.events = options.events;
61 this.keyboard_manager = options.keyboard_manager;
61 this.keyboard_manager = options.keyboard_manager;
62 this.save_widget = options.save_widget;
62 this.save_widget = options.save_widget;
63 this.tooltip = new tooltip.Tooltip(this.events);
63 this.tooltip = new tooltip.Tooltip(this.events);
64 this.ws_url = options.ws_url;
64 this.ws_url = options.ws_url;
65 // default_kernel_name is a temporary measure while we implement proper
65 // default_kernel_name is a temporary measure while we implement proper
66 // kernel selection and delayed start. Do not rely on it.
66 // kernel selection and delayed start. Do not rely on it.
67 this.default_kernel_name = 'python';
67 this.default_kernel_name = 'python';
68 // TODO: This code smells (and the other `= this` line a couple lines down)
68 // TODO: This code smells (and the other `= this` line a couple lines down)
69 // We need a better way to deal with circular instance references.
69 // We need a better way to deal with circular instance references.
70 this.keyboard_manager.notebook = this;
70 this.keyboard_manager.notebook = this;
71 this.save_widget.notebook = this;
71 this.save_widget.notebook = this;
72
72
73 mathjaxutils.init();
73 mathjaxutils.init();
74
74
75 if (marked) {
75 if (marked) {
76 marked.setOptions({
76 marked.setOptions({
77 gfm : true,
77 gfm : true,
78 tables: true,
78 tables: true,
79 langPrefix: "language-",
79 langPrefix: "language-",
80 highlight: function(code, lang) {
80 highlight: function(code, lang) {
81 if (!lang) {
81 if (!lang) {
82 // no language, no highlight
82 // no language, no highlight
83 return code;
83 return code;
84 }
84 }
85 var highlighted;
85 var highlighted;
86 try {
86 try {
87 highlighted = hljs.highlight(lang, code, false);
87 highlighted = hljs.highlight(lang, code, false);
88 } catch(err) {
88 } catch(err) {
89 highlighted = hljs.highlightAuto(code);
89 highlighted = hljs.highlightAuto(code);
90 }
90 }
91 return highlighted.value;
91 return highlighted.value;
92 }
92 }
93 });
93 });
94 }
94 }
95
95
96 this.element = $(selector);
96 this.element = $(selector);
97 this.element.scroll();
97 this.element.scroll();
98 this.element.data("notebook", this);
98 this.element.data("notebook", this);
99 this.next_prompt_number = 1;
99 this.next_prompt_number = 1;
100 this.session = null;
100 this.session = null;
101 this.kernel = null;
101 this.kernel = null;
102 this.clipboard = null;
102 this.clipboard = null;
103 this.undelete_backup = null;
103 this.undelete_backup = null;
104 this.undelete_index = null;
104 this.undelete_index = null;
105 this.undelete_below = false;
105 this.undelete_below = false;
106 this.paste_enabled = false;
106 this.paste_enabled = false;
107 // It is important to start out in command mode to match the intial mode
107 // It is important to start out in command mode to match the intial mode
108 // of the KeyboardManager.
108 // of the KeyboardManager.
109 this.mode = 'command';
109 this.mode = 'command';
110 this.set_dirty(false);
110 this.set_dirty(false);
111 this.metadata = {};
111 this.metadata = {};
112 this._checkpoint_after_save = false;
112 this._checkpoint_after_save = false;
113 this.last_checkpoint = null;
113 this.last_checkpoint = null;
114 this.checkpoints = [];
114 this.checkpoints = [];
115 this.autosave_interval = 0;
115 this.autosave_interval = 0;
116 this.autosave_timer = null;
116 this.autosave_timer = null;
117 // autosave *at most* every two minutes
117 // autosave *at most* every two minutes
118 this.minimum_autosave_interval = 120000;
118 this.minimum_autosave_interval = 120000;
119 // single worksheet for now
119 // single worksheet for now
120 this.worksheet_metadata = {};
120 this.worksheet_metadata = {};
121 this.notebook_name_blacklist_re = /[\/\\:]/;
121 this.notebook_name_blacklist_re = /[\/\\:]/;
122 this.nbformat = 3; // Increment this when changing the nbformat
122 this.nbformat = 3; // Increment this when changing the nbformat
123 this.nbformat_minor = 0; // Increment this when changing the nbformat
123 this.nbformat_minor = 0; // Increment this when changing the nbformat
124 this.style();
124 this.style();
125 this.create_elements();
125 this.create_elements();
126 this.bind_events();
126 this.bind_events();
127 this.save_notebook = function() { // don't allow save until notebook_loaded
127 this.save_notebook = function() { // don't allow save until notebook_loaded
128 this.save_notebook_error(null, null, "Load failed, save is disabled");
128 this.save_notebook_error(null, null, "Load failed, save is disabled");
129 };
129 };
130
130
131 // Trigger cell toolbar registration.
131 // Trigger cell toolbar registration.
132 default_celltoolbar.register(this, options.events);
132 default_celltoolbar.register(this, options.events);
133 rawcell_celltoolbar.register(this, options.events);
133 rawcell_celltoolbar.register(this, options.events);
134 slideshow_celltoolbar.register(this, options.events);
134 slideshow_celltoolbar.register(this, options.events);
135 };
135 };
136
136
137 /**
137 /**
138 * Tweak the notebook's CSS style.
138 * Tweak the notebook's CSS style.
139 *
139 *
140 * @method style
140 * @method style
141 */
141 */
142 Notebook.prototype.style = function () {
142 Notebook.prototype.style = function () {
143 $('div#notebook').addClass('border-box-sizing');
143 $('div#notebook').addClass('border-box-sizing');
144 };
144 };
145
145
146 /**
146 /**
147 * Create an HTML and CSS representation of the notebook.
147 * Create an HTML and CSS representation of the notebook.
148 *
148 *
149 * @method create_elements
149 * @method create_elements
150 */
150 */
151 Notebook.prototype.create_elements = function () {
151 Notebook.prototype.create_elements = function () {
152 var that = this;
152 var that = this;
153 this.element.attr('tabindex','-1');
153 this.element.attr('tabindex','-1');
154 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
154 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
155 // We add this end_space div to the end of the notebook div to:
155 // We add this end_space div to the end of the notebook div to:
156 // i) provide a margin between the last cell and the end of the notebook
156 // i) provide a margin between the last cell and the end of the notebook
157 // ii) to prevent the div from scrolling up when the last cell is being
157 // ii) to prevent the div from scrolling up when the last cell is being
158 // edited, but is too low on the page, which browsers will do automatically.
158 // edited, but is too low on the page, which browsers will do automatically.
159 var end_space = $('<div/>').addClass('end_space');
159 var end_space = $('<div/>').addClass('end_space');
160 end_space.dblclick(function (e) {
160 end_space.dblclick(function (e) {
161 var ncells = that.ncells();
161 var ncells = that.ncells();
162 that.insert_cell_below('code',ncells-1);
162 that.insert_cell_below('code',ncells-1);
163 });
163 });
164 this.element.append(this.container);
164 this.element.append(this.container);
165 this.container.append(end_space);
165 this.container.append(end_space);
166 };
166 };
167
167
168 /**
168 /**
169 * Bind JavaScript events: key presses and custom IPython events.
169 * Bind JavaScript events: key presses and custom IPython events.
170 *
170 *
171 * @method bind_events
171 * @method bind_events
172 */
172 */
173 Notebook.prototype.bind_events = function () {
173 Notebook.prototype.bind_events = function () {
174 var that = this;
174 var that = this;
175
175
176 this.events.on('set_next_input.Notebook', function (event, data) {
176 this.events.on('set_next_input.Notebook', function (event, data) {
177 var index = that.find_cell_index(data.cell);
177 var index = that.find_cell_index(data.cell);
178 var new_cell = that.insert_cell_below('code',index);
178 var new_cell = that.insert_cell_below('code',index);
179 new_cell.set_text(data.text);
179 new_cell.set_text(data.text);
180 that.dirty = true;
180 that.dirty = true;
181 });
181 });
182
182
183 this.events.on('set_dirty.Notebook', function (event, data) {
183 this.events.on('set_dirty.Notebook', function (event, data) {
184 that.dirty = data.value;
184 that.dirty = data.value;
185 });
185 });
186
186
187 this.events.on('trust_changed.Notebook', function (event, data) {
187 this.events.on('trust_changed.Notebook', function (event, data) {
188 that.trusted = data.value;
188 that.trusted = data.value;
189 });
189 });
190
190
191 this.events.on('select.Cell', function (event, data) {
191 this.events.on('select.Cell', function (event, data) {
192 var index = that.find_cell_index(data.cell);
192 var index = that.find_cell_index(data.cell);
193 that.select(index);
193 that.select(index);
194 });
194 });
195
195
196 this.events.on('edit_mode.Cell', function (event, data) {
196 this.events.on('edit_mode.Cell', function (event, data) {
197 that.handle_edit_mode(data.cell);
197 that.handle_edit_mode(data.cell);
198 });
198 });
199
199
200 this.events.on('command_mode.Cell', function (event, data) {
200 this.events.on('command_mode.Cell', function (event, data) {
201 that.handle_command_mode(data.cell);
201 that.handle_command_mode(data.cell);
202 });
202 });
203
203
204 this.events.on('status_autorestarting.Kernel', function () {
204 this.events.on('status_autorestarting.Kernel', function () {
205 dialog.modal({
205 dialog.modal({
206 notebook: that,
206 notebook: that,
207 keyboard_manager: that.keyboard_manager,
207 keyboard_manager: that.keyboard_manager,
208 title: "Kernel Restarting",
208 title: "Kernel Restarting",
209 body: "The kernel appears to have died. It will restart automatically.",
209 body: "The kernel appears to have died. It will restart automatically.",
210 buttons: {
210 buttons: {
211 OK : {
211 OK : {
212 class : "btn-primary"
212 class : "btn-primary"
213 }
213 }
214 }
214 }
215 });
215 });
216 });
216 });
217
217
218 var collapse_time = function (time) {
218 var collapse_time = function (time) {
219 var app_height = $('#ipython-main-app').height(); // content height
219 var app_height = $('#ipython-main-app').height(); // content height
220 var splitter_height = $('div#pager_splitter').outerHeight(true);
220 var splitter_height = $('div#pager_splitter').outerHeight(true);
221 var new_height = app_height - splitter_height;
221 var new_height = app_height - splitter_height;
222 that.element.animate({height : new_height + 'px'}, time);
222 that.element.animate({height : new_height + 'px'}, time);
223 };
223 };
224
224
225 this.element.bind('collapse_pager', function (event, extrap) {
225 this.element.bind('collapse_pager', function (event, extrap) {
226 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
226 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
227 collapse_time(time);
227 collapse_time(time);
228 });
228 });
229
229
230 var expand_time = function (time) {
230 var expand_time = function (time) {
231 var app_height = $('#ipython-main-app').height(); // content height
231 var app_height = $('#ipython-main-app').height(); // content height
232 var splitter_height = $('div#pager_splitter').outerHeight(true);
232 var splitter_height = $('div#pager_splitter').outerHeight(true);
233 var pager_height = $('div#pager').outerHeight(true);
233 var pager_height = $('div#pager').outerHeight(true);
234 var new_height = app_height - pager_height - splitter_height;
234 var new_height = app_height - pager_height - splitter_height;
235 that.element.animate({height : new_height + 'px'}, time);
235 that.element.animate({height : new_height + 'px'}, time);
236 };
236 };
237
237
238 this.element.bind('expand_pager', function (event, extrap) {
238 this.element.bind('expand_pager', function (event, extrap) {
239 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
239 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
240 expand_time(time);
240 expand_time(time);
241 });
241 });
242
242
243 // Firefox 22 broke $(window).on("beforeunload")
243 // Firefox 22 broke $(window).on("beforeunload")
244 // I'm not sure why or how.
244 // I'm not sure why or how.
245 window.onbeforeunload = function (e) {
245 window.onbeforeunload = function (e) {
246 // TODO: Make killing the kernel configurable.
246 // TODO: Make killing the kernel configurable.
247 var kill_kernel = false;
247 var kill_kernel = false;
248 if (kill_kernel) {
248 if (kill_kernel) {
249 that.session.kill_kernel();
249 that.session.kill_kernel();
250 }
250 }
251 // if we are autosaving, trigger an autosave on nav-away.
251 // if we are autosaving, trigger an autosave on nav-away.
252 // still warn, because if we don't the autosave may fail.
252 // still warn, because if we don't the autosave may fail.
253 if (that.dirty) {
253 if (that.dirty) {
254 if ( that.autosave_interval ) {
254 if ( that.autosave_interval ) {
255 // schedule autosave in a timeout
255 // schedule autosave in a timeout
256 // this gives you a chance to forcefully discard changes
256 // this gives you a chance to forcefully discard changes
257 // by reloading the page if you *really* want to.
257 // by reloading the page if you *really* want to.
258 // the timer doesn't start until you *dismiss* the dialog.
258 // the timer doesn't start until you *dismiss* the dialog.
259 setTimeout(function () {
259 setTimeout(function () {
260 if (that.dirty) {
260 if (that.dirty) {
261 that.save_notebook();
261 that.save_notebook();
262 }
262 }
263 }, 1000);
263 }, 1000);
264 return "Autosave in progress, latest changes may be lost.";
264 return "Autosave in progress, latest changes may be lost.";
265 } else {
265 } else {
266 return "Unsaved changes will be lost.";
266 return "Unsaved changes will be lost.";
267 }
267 }
268 }
268 }
269 // Null is the *only* return value that will make the browser not
269 // Null is the *only* return value that will make the browser not
270 // pop up the "don't leave" dialog.
270 // pop up the "don't leave" dialog.
271 return null;
271 return null;
272 };
272 };
273 };
273 };
274
274
275 /**
275 /**
276 * Set the dirty flag, and trigger the set_dirty.Notebook event
276 * Set the dirty flag, and trigger the set_dirty.Notebook event
277 *
277 *
278 * @method set_dirty
278 * @method set_dirty
279 */
279 */
280 Notebook.prototype.set_dirty = function (value) {
280 Notebook.prototype.set_dirty = function (value) {
281 if (value === undefined) {
281 if (value === undefined) {
282 value = true;
282 value = true;
283 }
283 }
284 if (this.dirty == value) {
284 if (this.dirty == value) {
285 return;
285 return;
286 }
286 }
287 this.events.trigger('set_dirty.Notebook', {value: value});
287 this.events.trigger('set_dirty.Notebook', {value: value});
288 };
288 };
289
289
290 /**
290 /**
291 * Scroll the top of the page to a given cell.
291 * Scroll the top of the page to a given cell.
292 *
292 *
293 * @method scroll_to_cell
293 * @method scroll_to_cell
294 * @param {Number} cell_number An index of the cell to view
294 * @param {Number} cell_number An index of the cell to view
295 * @param {Number} time Animation time in milliseconds
295 * @param {Number} time Animation time in milliseconds
296 * @return {Number} Pixel offset from the top of the container
296 * @return {Number} Pixel offset from the top of the container
297 */
297 */
298 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
298 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
299 var cells = this.get_cells();
299 var cells = this.get_cells();
300 time = time || 0;
300 time = time || 0;
301 cell_number = Math.min(cells.length-1,cell_number);
301 cell_number = Math.min(cells.length-1,cell_number);
302 cell_number = Math.max(0 ,cell_number);
302 cell_number = Math.max(0 ,cell_number);
303 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
303 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
304 this.element.animate({scrollTop:scroll_value}, time);
304 this.element.animate({scrollTop:scroll_value}, time);
305 return scroll_value;
305 return scroll_value;
306 };
306 };
307
307
308 /**
308 /**
309 * Scroll to the bottom of the page.
309 * Scroll to the bottom of the page.
310 *
310 *
311 * @method scroll_to_bottom
311 * @method scroll_to_bottom
312 */
312 */
313 Notebook.prototype.scroll_to_bottom = function () {
313 Notebook.prototype.scroll_to_bottom = function () {
314 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
314 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
315 };
315 };
316
316
317 /**
317 /**
318 * Scroll to the top of the page.
318 * Scroll to the top of the page.
319 *
319 *
320 * @method scroll_to_top
320 * @method scroll_to_top
321 */
321 */
322 Notebook.prototype.scroll_to_top = function () {
322 Notebook.prototype.scroll_to_top = function () {
323 this.element.animate({scrollTop:0}, 0);
323 this.element.animate({scrollTop:0}, 0);
324 };
324 };
325
325
326 // Edit Notebook metadata
326 // Edit Notebook metadata
327
327
328 Notebook.prototype.edit_metadata = function () {
328 Notebook.prototype.edit_metadata = function () {
329 var that = this;
329 var that = this;
330 dialog.edit_metadata({
330 dialog.edit_metadata({
331 md: this.metadata,
331 md: this.metadata,
332 callback: function (md) {
332 callback: function (md) {
333 that.metadata = md;
333 that.metadata = md;
334 },
334 },
335 name: 'Notebook',
335 name: 'Notebook',
336 notebook: this,
336 notebook: this,
337 keyboard_manager: this.keyboard_manager});
337 keyboard_manager: this.keyboard_manager});
338 };
338 };
339
339
340 // Cell indexing, retrieval, etc.
340 // Cell indexing, retrieval, etc.
341
341
342 /**
342 /**
343 * Get all cell elements in the notebook.
343 * Get all cell elements in the notebook.
344 *
344 *
345 * @method get_cell_elements
345 * @method get_cell_elements
346 * @return {jQuery} A selector of all cell elements
346 * @return {jQuery} A selector of all cell elements
347 */
347 */
348 Notebook.prototype.get_cell_elements = function () {
348 Notebook.prototype.get_cell_elements = function () {
349 return this.container.children("div.cell");
349 return this.container.children("div.cell");
350 };
350 };
351
351
352 /**
352 /**
353 * Get a particular cell element.
353 * Get a particular cell element.
354 *
354 *
355 * @method get_cell_element
355 * @method get_cell_element
356 * @param {Number} index An index of a cell to select
356 * @param {Number} index An index of a cell to select
357 * @return {jQuery} A selector of the given cell.
357 * @return {jQuery} A selector of the given cell.
358 */
358 */
359 Notebook.prototype.get_cell_element = function (index) {
359 Notebook.prototype.get_cell_element = function (index) {
360 var result = null;
360 var result = null;
361 var e = this.get_cell_elements().eq(index);
361 var e = this.get_cell_elements().eq(index);
362 if (e.length !== 0) {
362 if (e.length !== 0) {
363 result = e;
363 result = e;
364 }
364 }
365 return result;
365 return result;
366 };
366 };
367
367
368 /**
368 /**
369 * Try to get a particular cell by msg_id.
369 * Try to get a particular cell by msg_id.
370 *
370 *
371 * @method get_msg_cell
371 * @method get_msg_cell
372 * @param {String} msg_id A message UUID
372 * @param {String} msg_id A message UUID
373 * @return {Cell} Cell or null if no cell was found.
373 * @return {Cell} Cell or null if no cell was found.
374 */
374 */
375 Notebook.prototype.get_msg_cell = function (msg_id) {
375 Notebook.prototype.get_msg_cell = function (msg_id) {
376 return codecell.CodeCell.msg_cells[msg_id] || null;
376 return codecell.CodeCell.msg_cells[msg_id] || null;
377 };
377 };
378
378
379 /**
379 /**
380 * Count the cells in this notebook.
380 * Count the cells in this notebook.
381 *
381 *
382 * @method ncells
382 * @method ncells
383 * @return {Number} The number of cells in this notebook
383 * @return {Number} The number of cells in this notebook
384 */
384 */
385 Notebook.prototype.ncells = function () {
385 Notebook.prototype.ncells = function () {
386 return this.get_cell_elements().length;
386 return this.get_cell_elements().length;
387 };
387 };
388
388
389 /**
389 /**
390 * Get all Cell objects in this notebook.
390 * Get all Cell objects in this notebook.
391 *
391 *
392 * @method get_cells
392 * @method get_cells
393 * @return {Array} This notebook's Cell objects
393 * @return {Array} This notebook's Cell objects
394 */
394 */
395 // TODO: we are often calling cells as cells()[i], which we should optimize
395 // TODO: we are often calling cells as cells()[i], which we should optimize
396 // to cells(i) or a new method.
396 // to cells(i) or a new method.
397 Notebook.prototype.get_cells = function () {
397 Notebook.prototype.get_cells = function () {
398 return this.get_cell_elements().toArray().map(function (e) {
398 return this.get_cell_elements().toArray().map(function (e) {
399 return $(e).data("cell");
399 return $(e).data("cell");
400 });
400 });
401 };
401 };
402
402
403 /**
403 /**
404 * Get a Cell object from this notebook.
404 * Get a Cell object from this notebook.
405 *
405 *
406 * @method get_cell
406 * @method get_cell
407 * @param {Number} index An index of a cell to retrieve
407 * @param {Number} index An index of a cell to retrieve
408 * @return {Cell} A particular cell
408 * @return {Cell} A particular cell
409 */
409 */
410 Notebook.prototype.get_cell = function (index) {
410 Notebook.prototype.get_cell = function (index) {
411 var result = null;
411 var result = null;
412 var ce = this.get_cell_element(index);
412 var ce = this.get_cell_element(index);
413 if (ce !== null) {
413 if (ce !== null) {
414 result = ce.data('cell');
414 result = ce.data('cell');
415 }
415 }
416 return result;
416 return result;
417 };
417 };
418
418
419 /**
419 /**
420 * Get the cell below a given cell.
420 * Get the cell below a given cell.
421 *
421 *
422 * @method get_next_cell
422 * @method get_next_cell
423 * @param {Cell} cell The provided cell
423 * @param {Cell} cell The provided cell
424 * @return {Cell} The next cell
424 * @return {Cell} The next cell
425 */
425 */
426 Notebook.prototype.get_next_cell = function (cell) {
426 Notebook.prototype.get_next_cell = function (cell) {
427 var result = null;
427 var result = null;
428 var index = this.find_cell_index(cell);
428 var index = this.find_cell_index(cell);
429 if (this.is_valid_cell_index(index+1)) {
429 if (this.is_valid_cell_index(index+1)) {
430 result = this.get_cell(index+1);
430 result = this.get_cell(index+1);
431 }
431 }
432 return result;
432 return result;
433 };
433 };
434
434
435 /**
435 /**
436 * Get the cell above a given cell.
436 * Get the cell above a given cell.
437 *
437 *
438 * @method get_prev_cell
438 * @method get_prev_cell
439 * @param {Cell} cell The provided cell
439 * @param {Cell} cell The provided cell
440 * @return {Cell} The previous cell
440 * @return {Cell} The previous cell
441 */
441 */
442 Notebook.prototype.get_prev_cell = function (cell) {
442 Notebook.prototype.get_prev_cell = function (cell) {
443 // TODO: off-by-one
443 // TODO: off-by-one
444 // nb.get_prev_cell(nb.get_cell(1)) is null
444 // nb.get_prev_cell(nb.get_cell(1)) is null
445 var result = null;
445 var result = null;
446 var index = this.find_cell_index(cell);
446 var index = this.find_cell_index(cell);
447 if (index !== null && index > 1) {
447 if (index !== null && index > 1) {
448 result = this.get_cell(index-1);
448 result = this.get_cell(index-1);
449 }
449 }
450 return result;
450 return result;
451 };
451 };
452
452
453 /**
453 /**
454 * Get the numeric index of a given cell.
454 * Get the numeric index of a given cell.
455 *
455 *
456 * @method find_cell_index
456 * @method find_cell_index
457 * @param {Cell} cell The provided cell
457 * @param {Cell} cell The provided cell
458 * @return {Number} The cell's numeric index
458 * @return {Number} The cell's numeric index
459 */
459 */
460 Notebook.prototype.find_cell_index = function (cell) {
460 Notebook.prototype.find_cell_index = function (cell) {
461 var result = null;
461 var result = null;
462 this.get_cell_elements().filter(function (index) {
462 this.get_cell_elements().filter(function (index) {
463 if ($(this).data("cell") === cell) {
463 if ($(this).data("cell") === cell) {
464 result = index;
464 result = index;
465 }
465 }
466 });
466 });
467 return result;
467 return result;
468 };
468 };
469
469
470 /**
470 /**
471 * Get a given index , or the selected index if none is provided.
471 * Get a given index , or the selected index if none is provided.
472 *
472 *
473 * @method index_or_selected
473 * @method index_or_selected
474 * @param {Number} index A cell's index
474 * @param {Number} index A cell's index
475 * @return {Number} The given index, or selected index if none is provided.
475 * @return {Number} The given index, or selected index if none is provided.
476 */
476 */
477 Notebook.prototype.index_or_selected = function (index) {
477 Notebook.prototype.index_or_selected = function (index) {
478 var i;
478 var i;
479 if (index === undefined || index === null) {
479 if (index === undefined || index === null) {
480 i = this.get_selected_index();
480 i = this.get_selected_index();
481 if (i === null) {
481 if (i === null) {
482 i = 0;
482 i = 0;
483 }
483 }
484 } else {
484 } else {
485 i = index;
485 i = index;
486 }
486 }
487 return i;
487 return i;
488 };
488 };
489
489
490 /**
490 /**
491 * Get the currently selected cell.
491 * Get the currently selected cell.
492 * @method get_selected_cell
492 * @method get_selected_cell
493 * @return {Cell} The selected cell
493 * @return {Cell} The selected cell
494 */
494 */
495 Notebook.prototype.get_selected_cell = function () {
495 Notebook.prototype.get_selected_cell = function () {
496 var index = this.get_selected_index();
496 var index = this.get_selected_index();
497 return this.get_cell(index);
497 return this.get_cell(index);
498 };
498 };
499
499
500 /**
500 /**
501 * Check whether a cell index is valid.
501 * Check whether a cell index is valid.
502 *
502 *
503 * @method is_valid_cell_index
503 * @method is_valid_cell_index
504 * @param {Number} index A cell index
504 * @param {Number} index A cell index
505 * @return True if the index is valid, false otherwise
505 * @return True if the index is valid, false otherwise
506 */
506 */
507 Notebook.prototype.is_valid_cell_index = function (index) {
507 Notebook.prototype.is_valid_cell_index = function (index) {
508 if (index !== null && index >= 0 && index < this.ncells()) {
508 if (index !== null && index >= 0 && index < this.ncells()) {
509 return true;
509 return true;
510 } else {
510 } else {
511 return false;
511 return false;
512 }
512 }
513 };
513 };
514
514
515 /**
515 /**
516 * Get the index of the currently selected cell.
516 * Get the index of the currently selected cell.
517
517
518 * @method get_selected_index
518 * @method get_selected_index
519 * @return {Number} The selected cell's numeric index
519 * @return {Number} The selected cell's numeric index
520 */
520 */
521 Notebook.prototype.get_selected_index = function () {
521 Notebook.prototype.get_selected_index = function () {
522 var result = null;
522 var result = null;
523 this.get_cell_elements().filter(function (index) {
523 this.get_cell_elements().filter(function (index) {
524 if ($(this).data("cell").selected === true) {
524 if ($(this).data("cell").selected === true) {
525 result = index;
525 result = index;
526 }
526 }
527 });
527 });
528 return result;
528 return result;
529 };
529 };
530
530
531
531
532 // Cell selection.
532 // Cell selection.
533
533
534 /**
534 /**
535 * Programmatically select a cell.
535 * Programmatically select a cell.
536 *
536 *
537 * @method select
537 * @method select
538 * @param {Number} index A cell's index
538 * @param {Number} index A cell's index
539 * @return {Notebook} This notebook
539 * @return {Notebook} This notebook
540 */
540 */
541 Notebook.prototype.select = function (index) {
541 Notebook.prototype.select = function (index) {
542 if (this.is_valid_cell_index(index)) {
542 if (this.is_valid_cell_index(index)) {
543 var sindex = this.get_selected_index();
543 var sindex = this.get_selected_index();
544 if (sindex !== null && index !== sindex) {
544 if (sindex !== null && index !== sindex) {
545 // If we are about to select a different cell, make sure we are
545 // If we are about to select a different cell, make sure we are
546 // first in command mode.
546 // first in command mode.
547 if (this.mode !== 'command') {
547 if (this.mode !== 'command') {
548 this.command_mode();
548 this.command_mode();
549 }
549 }
550 this.get_cell(sindex).unselect();
550 this.get_cell(sindex).unselect();
551 }
551 }
552 var cell = this.get_cell(index);
552 var cell = this.get_cell(index);
553 cell.select();
553 cell.select();
554 if (cell.cell_type === 'heading') {
554 if (cell.cell_type === 'heading') {
555 this.events.trigger('selected_cell_type_changed.Notebook',
555 this.events.trigger('selected_cell_type_changed.Notebook',
556 {'cell_type':cell.cell_type,level:cell.level}
556 {'cell_type':cell.cell_type,level:cell.level}
557 );
557 );
558 } else {
558 } else {
559 this.events.trigger('selected_cell_type_changed.Notebook',
559 this.events.trigger('selected_cell_type_changed.Notebook',
560 {'cell_type':cell.cell_type}
560 {'cell_type':cell.cell_type}
561 );
561 );
562 }
562 }
563 }
563 }
564 return this;
564 return this;
565 };
565 };
566
566
567 /**
567 /**
568 * Programmatically select the next cell.
568 * Programmatically select the next cell.
569 *
569 *
570 * @method select_next
570 * @method select_next
571 * @return {Notebook} This notebook
571 * @return {Notebook} This notebook
572 */
572 */
573 Notebook.prototype.select_next = function () {
573 Notebook.prototype.select_next = function () {
574 var index = this.get_selected_index();
574 var index = this.get_selected_index();
575 this.select(index+1);
575 this.select(index+1);
576 return this;
576 return this;
577 };
577 };
578
578
579 /**
579 /**
580 * Programmatically select the previous cell.
580 * Programmatically select the previous cell.
581 *
581 *
582 * @method select_prev
582 * @method select_prev
583 * @return {Notebook} This notebook
583 * @return {Notebook} This notebook
584 */
584 */
585 Notebook.prototype.select_prev = function () {
585 Notebook.prototype.select_prev = function () {
586 var index = this.get_selected_index();
586 var index = this.get_selected_index();
587 this.select(index-1);
587 this.select(index-1);
588 return this;
588 return this;
589 };
589 };
590
590
591
591
592 // Edit/Command mode
592 // Edit/Command mode
593
593
594 /**
594 /**
595 * Gets the index of the cell that is in edit mode.
595 * Gets the index of the cell that is in edit mode.
596 *
596 *
597 * @method get_edit_index
597 * @method get_edit_index
598 *
598 *
599 * @return index {int}
599 * @return index {int}
600 **/
600 **/
601 Notebook.prototype.get_edit_index = function () {
601 Notebook.prototype.get_edit_index = function () {
602 var result = null;
602 var result = null;
603 this.get_cell_elements().filter(function (index) {
603 this.get_cell_elements().filter(function (index) {
604 if ($(this).data("cell").mode === 'edit') {
604 if ($(this).data("cell").mode === 'edit') {
605 result = index;
605 result = index;
606 }
606 }
607 });
607 });
608 return result;
608 return result;
609 };
609 };
610
610
611 /**
611 /**
612 * Handle when a a cell blurs and the notebook should enter command mode.
612 * Handle when a a cell blurs and the notebook should enter command mode.
613 *
613 *
614 * @method handle_command_mode
614 * @method handle_command_mode
615 * @param [cell] {Cell} Cell to enter command mode on.
615 * @param [cell] {Cell} Cell to enter command mode on.
616 **/
616 **/
617 Notebook.prototype.handle_command_mode = function (cell) {
617 Notebook.prototype.handle_command_mode = function (cell) {
618 if (this.mode !== 'command') {
618 if (this.mode !== 'command') {
619 cell.command_mode();
619 cell.command_mode();
620 this.mode = 'command';
620 this.mode = 'command';
621 this.events.trigger('command_mode.Notebook');
621 this.events.trigger('command_mode.Notebook');
622 this.keyboard_manager.command_mode();
622 this.keyboard_manager.command_mode();
623 }
623 }
624 };
624 };
625
625
626 /**
626 /**
627 * Make the notebook enter command mode.
627 * Make the notebook enter command mode.
628 *
628 *
629 * @method command_mode
629 * @method command_mode
630 **/
630 **/
631 Notebook.prototype.command_mode = function () {
631 Notebook.prototype.command_mode = function () {
632 var cell = this.get_cell(this.get_edit_index());
632 var cell = this.get_cell(this.get_edit_index());
633 if (cell && this.mode !== 'command') {
633 if (cell && this.mode !== 'command') {
634 // We don't call cell.command_mode, but rather call cell.focus_cell()
634 // We don't call cell.command_mode, but rather call cell.focus_cell()
635 // which will blur and CM editor and trigger the call to
635 // which will blur and CM editor and trigger the call to
636 // handle_command_mode.
636 // handle_command_mode.
637 cell.focus_cell();
637 cell.focus_cell();
638 }
638 }
639 };
639 };
640
640
641 /**
641 /**
642 * Handle when a cell fires it's edit_mode event.
642 * Handle when a cell fires it's edit_mode event.
643 *
643 *
644 * @method handle_edit_mode
644 * @method handle_edit_mode
645 * @param [cell] {Cell} Cell to enter edit mode on.
645 * @param [cell] {Cell} Cell to enter edit mode on.
646 **/
646 **/
647 Notebook.prototype.handle_edit_mode = function (cell) {
647 Notebook.prototype.handle_edit_mode = function (cell) {
648 if (cell && this.mode !== 'edit') {
648 if (cell && this.mode !== 'edit') {
649 cell.edit_mode();
649 cell.edit_mode();
650 this.mode = 'edit';
650 this.mode = 'edit';
651 this.events.trigger('edit_mode.Notebook');
651 this.events.trigger('edit_mode.Notebook');
652 this.keyboard_manager.edit_mode();
652 this.keyboard_manager.edit_mode();
653 }
653 }
654 };
654 };
655
655
656 /**
656 /**
657 * Make a cell enter edit mode.
657 * Make a cell enter edit mode.
658 *
658 *
659 * @method edit_mode
659 * @method edit_mode
660 **/
660 **/
661 Notebook.prototype.edit_mode = function () {
661 Notebook.prototype.edit_mode = function () {
662 var cell = this.get_selected_cell();
662 var cell = this.get_selected_cell();
663 if (cell && this.mode !== 'edit') {
663 if (cell && this.mode !== 'edit') {
664 cell.unrender();
664 cell.unrender();
665 cell.focus_editor();
665 cell.focus_editor();
666 }
666 }
667 };
667 };
668
668
669 /**
669 /**
670 * Focus the currently selected cell.
670 * Focus the currently selected cell.
671 *
671 *
672 * @method focus_cell
672 * @method focus_cell
673 **/
673 **/
674 Notebook.prototype.focus_cell = function () {
674 Notebook.prototype.focus_cell = function () {
675 var cell = this.get_selected_cell();
675 var cell = this.get_selected_cell();
676 if (cell === null) {return;} // No cell is selected
676 if (cell === null) {return;} // No cell is selected
677 cell.focus_cell();
677 cell.focus_cell();
678 };
678 };
679
679
680 // Cell movement
680 // Cell movement
681
681
682 /**
682 /**
683 * Move given (or selected) cell up and select it.
683 * Move given (or selected) cell up and select it.
684 *
684 *
685 * @method move_cell_up
685 * @method move_cell_up
686 * @param [index] {integer} cell index
686 * @param [index] {integer} cell index
687 * @return {Notebook} This notebook
687 * @return {Notebook} This notebook
688 **/
688 **/
689 Notebook.prototype.move_cell_up = function (index) {
689 Notebook.prototype.move_cell_up = function (index) {
690 var i = this.index_or_selected(index);
690 var i = this.index_or_selected(index);
691 if (this.is_valid_cell_index(i) && i > 0) {
691 if (this.is_valid_cell_index(i) && i > 0) {
692 var pivot = this.get_cell_element(i-1);
692 var pivot = this.get_cell_element(i-1);
693 var tomove = this.get_cell_element(i);
693 var tomove = this.get_cell_element(i);
694 if (pivot !== null && tomove !== null) {
694 if (pivot !== null && tomove !== null) {
695 tomove.detach();
695 tomove.detach();
696 pivot.before(tomove);
696 pivot.before(tomove);
697 this.select(i-1);
697 this.select(i-1);
698 var cell = this.get_selected_cell();
698 var cell = this.get_selected_cell();
699 cell.focus_cell();
699 cell.focus_cell();
700 }
700 }
701 this.set_dirty(true);
701 this.set_dirty(true);
702 }
702 }
703 return this;
703 return this;
704 };
704 };
705
705
706
706
707 /**
707 /**
708 * Move given (or selected) cell down and select it
708 * Move given (or selected) cell down and select it
709 *
709 *
710 * @method move_cell_down
710 * @method move_cell_down
711 * @param [index] {integer} cell index
711 * @param [index] {integer} cell index
712 * @return {Notebook} This notebook
712 * @return {Notebook} This notebook
713 **/
713 **/
714 Notebook.prototype.move_cell_down = function (index) {
714 Notebook.prototype.move_cell_down = function (index) {
715 var i = this.index_or_selected(index);
715 var i = this.index_or_selected(index);
716 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
716 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
717 var pivot = this.get_cell_element(i+1);
717 var pivot = this.get_cell_element(i+1);
718 var tomove = this.get_cell_element(i);
718 var tomove = this.get_cell_element(i);
719 if (pivot !== null && tomove !== null) {
719 if (pivot !== null && tomove !== null) {
720 tomove.detach();
720 tomove.detach();
721 pivot.after(tomove);
721 pivot.after(tomove);
722 this.select(i+1);
722 this.select(i+1);
723 var cell = this.get_selected_cell();
723 var cell = this.get_selected_cell();
724 cell.focus_cell();
724 cell.focus_cell();
725 }
725 }
726 }
726 }
727 this.set_dirty();
727 this.set_dirty();
728 return this;
728 return this;
729 };
729 };
730
730
731
731
732 // Insertion, deletion.
732 // Insertion, deletion.
733
733
734 /**
734 /**
735 * Delete a cell from the notebook.
735 * Delete a cell from the notebook.
736 *
736 *
737 * @method delete_cell
737 * @method delete_cell
738 * @param [index] A cell's numeric index
738 * @param [index] A cell's numeric index
739 * @return {Notebook} This notebook
739 * @return {Notebook} This notebook
740 */
740 */
741 Notebook.prototype.delete_cell = function (index) {
741 Notebook.prototype.delete_cell = function (index) {
742 var i = this.index_or_selected(index);
742 var i = this.index_or_selected(index);
743 var cell = this.get_selected_cell();
743 var cell = this.get_selected_cell();
744 this.undelete_backup = cell.toJSON();
744 this.undelete_backup = cell.toJSON();
745 $('#undelete_cell').removeClass('disabled');
745 $('#undelete_cell').removeClass('disabled');
746 if (this.is_valid_cell_index(i)) {
746 if (this.is_valid_cell_index(i)) {
747 var old_ncells = this.ncells();
747 var old_ncells = this.ncells();
748 var ce = this.get_cell_element(i);
748 var ce = this.get_cell_element(i);
749 ce.remove();
749 ce.remove();
750 if (i === 0) {
750 if (i === 0) {
751 // Always make sure we have at least one cell.
751 // Always make sure we have at least one cell.
752 if (old_ncells === 1) {
752 if (old_ncells === 1) {
753 this.insert_cell_below('code');
753 this.insert_cell_below('code');
754 }
754 }
755 this.select(0);
755 this.select(0);
756 this.undelete_index = 0;
756 this.undelete_index = 0;
757 this.undelete_below = false;
757 this.undelete_below = false;
758 } else if (i === old_ncells-1 && i !== 0) {
758 } else if (i === old_ncells-1 && i !== 0) {
759 this.select(i-1);
759 this.select(i-1);
760 this.undelete_index = i - 1;
760 this.undelete_index = i - 1;
761 this.undelete_below = true;
761 this.undelete_below = true;
762 } else {
762 } else {
763 this.select(i);
763 this.select(i);
764 this.undelete_index = i;
764 this.undelete_index = i;
765 this.undelete_below = false;
765 this.undelete_below = false;
766 }
766 }
767 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
767 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
768 this.set_dirty(true);
768 this.set_dirty(true);
769 }
769 }
770 return this;
770 return this;
771 };
771 };
772
772
773 /**
773 /**
774 * Restore the most recently deleted cell.
774 * Restore the most recently deleted cell.
775 *
775 *
776 * @method undelete
776 * @method undelete
777 */
777 */
778 Notebook.prototype.undelete_cell = function() {
778 Notebook.prototype.undelete_cell = function() {
779 if (this.undelete_backup !== null && this.undelete_index !== null) {
779 if (this.undelete_backup !== null && this.undelete_index !== null) {
780 var current_index = this.get_selected_index();
780 var current_index = this.get_selected_index();
781 if (this.undelete_index < current_index) {
781 if (this.undelete_index < current_index) {
782 current_index = current_index + 1;
782 current_index = current_index + 1;
783 }
783 }
784 if (this.undelete_index >= this.ncells()) {
784 if (this.undelete_index >= this.ncells()) {
785 this.select(this.ncells() - 1);
785 this.select(this.ncells() - 1);
786 }
786 }
787 else {
787 else {
788 this.select(this.undelete_index);
788 this.select(this.undelete_index);
789 }
789 }
790 var cell_data = this.undelete_backup;
790 var cell_data = this.undelete_backup;
791 var new_cell = null;
791 var new_cell = null;
792 if (this.undelete_below) {
792 if (this.undelete_below) {
793 new_cell = this.insert_cell_below(cell_data.cell_type);
793 new_cell = this.insert_cell_below(cell_data.cell_type);
794 } else {
794 } else {
795 new_cell = this.insert_cell_above(cell_data.cell_type);
795 new_cell = this.insert_cell_above(cell_data.cell_type);
796 }
796 }
797 new_cell.fromJSON(cell_data);
797 new_cell.fromJSON(cell_data);
798 if (this.undelete_below) {
798 if (this.undelete_below) {
799 this.select(current_index+1);
799 this.select(current_index+1);
800 } else {
800 } else {
801 this.select(current_index);
801 this.select(current_index);
802 }
802 }
803 this.undelete_backup = null;
803 this.undelete_backup = null;
804 this.undelete_index = null;
804 this.undelete_index = null;
805 }
805 }
806 $('#undelete_cell').addClass('disabled');
806 $('#undelete_cell').addClass('disabled');
807 };
807 };
808
808
809 /**
809 /**
810 * Insert a cell so that after insertion the cell is at given index.
810 * Insert a cell so that after insertion the cell is at given index.
811 *
811 *
812 * If cell type is not provided, it will default to the type of the
812 * If cell type is not provided, it will default to the type of the
813 * currently active cell.
813 * currently active cell.
814 *
814 *
815 * Similar to insert_above, but index parameter is mandatory
815 * Similar to insert_above, but index parameter is mandatory
816 *
816 *
817 * Index will be brought back into the accessible range [0,n]
817 * Index will be brought back into the accessible range [0,n]
818 *
818 *
819 * @method insert_cell_at_index
819 * @method insert_cell_at_index
820 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
820 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
821 * @param [index] {int} a valid index where to insert cell
821 * @param [index] {int} a valid index where to insert cell
822 *
822 *
823 * @return cell {cell|null} created cell or null
823 * @return cell {cell|null} created cell or null
824 **/
824 **/
825 Notebook.prototype.insert_cell_at_index = function(type, index){
825 Notebook.prototype.insert_cell_at_index = function(type, index){
826
826
827 var ncells = this.ncells();
827 var ncells = this.ncells();
828 index = Math.min(index,ncells);
828 index = Math.min(index,ncells);
829 index = Math.max(index,0);
829 index = Math.max(index,0);
830 var cell = null;
830 var cell = null;
831 type = type || this.get_selected_cell().cell_type;
831 type = type || this.get_selected_cell().cell_type;
832
832
833 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
833 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
834 var cell_options = {
834 var cell_options = {
835 events: this.events,
835 events: this.events,
836 config: this.config,
836 config: this.config,
837 keyboard_manager: this.keyboard_manager,
837 keyboard_manager: this.keyboard_manager,
838 notebook: this,
838 notebook: this,
839 tooltip: this.tooltip,
839 tooltip: this.tooltip,
840 };
840 };
841 if (type === 'code') {
841 if (type === 'code') {
842 cell = new codecell.CodeCell(this.kernel, cell_options);
842 cell = new codecell.CodeCell(this.kernel, cell_options);
843 cell.set_input_prompt();
843 cell.set_input_prompt();
844 } else if (type === 'markdown') {
844 } else if (type === 'markdown') {
845 cell = new textcell.MarkdownCell(cell_options);
845 cell = new textcell.MarkdownCell(cell_options);
846 } else if (type === 'raw') {
846 } else if (type === 'raw') {
847 cell = new textcell.RawCell(cell_options);
847 cell = new textcell.RawCell(cell_options);
848 } else if (type === 'heading') {
848 } else if (type === 'heading') {
849 cell = new textcell.HeadingCell(cell_options);
849 cell = new textcell.HeadingCell(cell_options);
850 }
850 }
851
851
852 if(this._insert_element_at_index(cell.element,index)) {
852 if(this._insert_element_at_index(cell.element,index)) {
853 cell.render();
853 cell.render();
854 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
854 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
855 cell.refresh();
855 cell.refresh();
856 // We used to select the cell after we refresh it, but there
856 // We used to select the cell after we refresh it, but there
857 // are now cases were this method is called where select is
857 // are now cases were this method is called where select is
858 // not appropriate. The selection logic should be handled by the
858 // not appropriate. The selection logic should be handled by the
859 // caller of the the top level insert_cell methods.
859 // caller of the the top level insert_cell methods.
860 this.set_dirty(true);
860 this.set_dirty(true);
861 }
861 }
862 }
862 }
863 return cell;
863 return cell;
864
864
865 };
865 };
866
866
867 /**
867 /**
868 * Insert an element at given cell index.
868 * Insert an element at given cell index.
869 *
869 *
870 * @method _insert_element_at_index
870 * @method _insert_element_at_index
871 * @param element {dom element} a cell element
871 * @param element {dom element} a cell element
872 * @param [index] {int} a valid index where to inser cell
872 * @param [index] {int} a valid index where to inser cell
873 * @private
873 * @private
874 *
874 *
875 * return true if everything whent fine.
875 * return true if everything whent fine.
876 **/
876 **/
877 Notebook.prototype._insert_element_at_index = function(element, index){
877 Notebook.prototype._insert_element_at_index = function(element, index){
878 if (element === undefined){
878 if (element === undefined){
879 return false;
879 return false;
880 }
880 }
881
881
882 var ncells = this.ncells();
882 var ncells = this.ncells();
883
883
884 if (ncells === 0) {
884 if (ncells === 0) {
885 // special case append if empty
885 // special case append if empty
886 this.element.find('div.end_space').before(element);
886 this.element.find('div.end_space').before(element);
887 } else if ( ncells === index ) {
887 } else if ( ncells === index ) {
888 // special case append it the end, but not empty
888 // special case append it the end, but not empty
889 this.get_cell_element(index-1).after(element);
889 this.get_cell_element(index-1).after(element);
890 } else if (this.is_valid_cell_index(index)) {
890 } else if (this.is_valid_cell_index(index)) {
891 // otherwise always somewhere to append to
891 // otherwise always somewhere to append to
892 this.get_cell_element(index).before(element);
892 this.get_cell_element(index).before(element);
893 } else {
893 } else {
894 return false;
894 return false;
895 }
895 }
896
896
897 if (this.undelete_index !== null && index <= this.undelete_index) {
897 if (this.undelete_index !== null && index <= this.undelete_index) {
898 this.undelete_index = this.undelete_index + 1;
898 this.undelete_index = this.undelete_index + 1;
899 this.set_dirty(true);
899 this.set_dirty(true);
900 }
900 }
901 return true;
901 return true;
902 };
902 };
903
903
904 /**
904 /**
905 * Insert a cell of given type above given index, or at top
905 * Insert a cell of given type above given index, or at top
906 * of notebook if index smaller than 0.
906 * of notebook if index smaller than 0.
907 *
907 *
908 * default index value is the one of currently selected cell
908 * default index value is the one of currently selected cell
909 *
909 *
910 * @method insert_cell_above
910 * @method insert_cell_above
911 * @param [type] {string} cell type
911 * @param [type] {string} cell type
912 * @param [index] {integer}
912 * @param [index] {integer}
913 *
913 *
914 * @return handle to created cell or null
914 * @return handle to created cell or null
915 **/
915 **/
916 Notebook.prototype.insert_cell_above = function (type, index) {
916 Notebook.prototype.insert_cell_above = function (type, index) {
917 index = this.index_or_selected(index);
917 index = this.index_or_selected(index);
918 return this.insert_cell_at_index(type, index);
918 return this.insert_cell_at_index(type, index);
919 };
919 };
920
920
921 /**
921 /**
922 * Insert a cell of given type below given index, or at bottom
922 * Insert a cell of given type below given index, or at bottom
923 * of notebook if index greater than number of cells
923 * of notebook if index greater than number of cells
924 *
924 *
925 * default index value is the one of currently selected cell
925 * default index value is the one of currently selected cell
926 *
926 *
927 * @method insert_cell_below
927 * @method insert_cell_below
928 * @param [type] {string} cell type
928 * @param [type] {string} cell type
929 * @param [index] {integer}
929 * @param [index] {integer}
930 *
930 *
931 * @return handle to created cell or null
931 * @return handle to created cell or null
932 *
932 *
933 **/
933 **/
934 Notebook.prototype.insert_cell_below = function (type, index) {
934 Notebook.prototype.insert_cell_below = function (type, index) {
935 index = this.index_or_selected(index);
935 index = this.index_or_selected(index);
936 return this.insert_cell_at_index(type, index+1);
936 return this.insert_cell_at_index(type, index+1);
937 };
937 };
938
938
939
939
940 /**
940 /**
941 * Insert cell at end of notebook
941 * Insert cell at end of notebook
942 *
942 *
943 * @method insert_cell_at_bottom
943 * @method insert_cell_at_bottom
944 * @param {String} type cell type
944 * @param {String} type cell type
945 *
945 *
946 * @return the added cell; or null
946 * @return the added cell; or null
947 **/
947 **/
948 Notebook.prototype.insert_cell_at_bottom = function (type){
948 Notebook.prototype.insert_cell_at_bottom = function (type){
949 var len = this.ncells();
949 var len = this.ncells();
950 return this.insert_cell_below(type,len-1);
950 return this.insert_cell_below(type,len-1);
951 };
951 };
952
952
953 /**
953 /**
954 * Turn a cell into a code cell.
954 * Turn a cell into a code cell.
955 *
955 *
956 * @method to_code
956 * @method to_code
957 * @param {Number} [index] A cell's index
957 * @param {Number} [index] A cell's index
958 */
958 */
959 Notebook.prototype.to_code = function (index) {
959 Notebook.prototype.to_code = function (index) {
960 var i = this.index_or_selected(index);
960 var i = this.index_or_selected(index);
961 if (this.is_valid_cell_index(i)) {
961 if (this.is_valid_cell_index(i)) {
962 var source_element = this.get_cell_element(i);
962 var source_element = this.get_cell_element(i);
963 var source_cell = source_element.data("cell");
963 var source_cell = source_element.data("cell");
964 if (!(source_cell instanceof codecell.CodeCell)) {
964 if (!(source_cell instanceof codecell.CodeCell)) {
965 var target_cell = this.insert_cell_below('code',i);
965 var target_cell = this.insert_cell_below('code',i);
966 var text = source_cell.get_text();
966 var text = source_cell.get_text();
967 if (text === source_cell.placeholder) {
967 if (text === source_cell.placeholder) {
968 text = '';
968 text = '';
969 }
969 }
970 target_cell.set_text(text);
970 target_cell.set_text(text);
971 // make this value the starting point, so that we can only undo
971 // make this value the starting point, so that we can only undo
972 // to this state, instead of a blank cell
972 // to this state, instead of a blank cell
973 target_cell.code_mirror.clearHistory();
973 target_cell.code_mirror.clearHistory();
974 source_element.remove();
974 source_element.remove();
975 this.select(i);
975 this.select(i);
976 var cursor = source_cell.code_mirror.getCursor();
976 var cursor = source_cell.code_mirror.getCursor();
977 target_cell.code_mirror.setCursor(cursor);
977 target_cell.code_mirror.setCursor(cursor);
978 this.set_dirty(true);
978 this.set_dirty(true);
979 }
979 }
980 }
980 }
981 };
981 };
982
982
983 /**
983 /**
984 * Turn a cell into a Markdown cell.
984 * Turn a cell into a Markdown cell.
985 *
985 *
986 * @method to_markdown
986 * @method to_markdown
987 * @param {Number} [index] A cell's index
987 * @param {Number} [index] A cell's index
988 */
988 */
989 Notebook.prototype.to_markdown = function (index) {
989 Notebook.prototype.to_markdown = function (index) {
990 var i = this.index_or_selected(index);
990 var i = this.index_or_selected(index);
991 if (this.is_valid_cell_index(i)) {
991 if (this.is_valid_cell_index(i)) {
992 var source_element = this.get_cell_element(i);
992 var source_element = this.get_cell_element(i);
993 var source_cell = source_element.data("cell");
993 var source_cell = source_element.data("cell");
994 if (!(source_cell instanceof textcell.MarkdownCell)) {
994 if (!(source_cell instanceof textcell.MarkdownCell)) {
995 var target_cell = this.insert_cell_below('markdown',i);
995 var target_cell = this.insert_cell_below('markdown',i);
996 var text = source_cell.get_text();
996 var text = source_cell.get_text();
997 if (text === source_cell.placeholder) {
997 if (text === source_cell.placeholder) {
998 text = '';
998 text = '';
999 }
999 }
1000 // We must show the editor before setting its contents
1000 // We must show the editor before setting its contents
1001 target_cell.unrender();
1001 target_cell.unrender();
1002 target_cell.set_text(text);
1002 target_cell.set_text(text);
1003 // make this value the starting point, so that we can only undo
1003 // make this value the starting point, so that we can only undo
1004 // to this state, instead of a blank cell
1004 // to this state, instead of a blank cell
1005 target_cell.code_mirror.clearHistory();
1005 target_cell.code_mirror.clearHistory();
1006 source_element.remove();
1006 source_element.remove();
1007 this.select(i);
1007 this.select(i);
1008 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1008 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1009 target_cell.render();
1009 target_cell.render();
1010 }
1010 }
1011 var cursor = source_cell.code_mirror.getCursor();
1011 var cursor = source_cell.code_mirror.getCursor();
1012 target_cell.code_mirror.setCursor(cursor);
1012 target_cell.code_mirror.setCursor(cursor);
1013 this.set_dirty(true);
1013 this.set_dirty(true);
1014 }
1014 }
1015 }
1015 }
1016 };
1016 };
1017
1017
1018 /**
1018 /**
1019 * Turn a cell into a raw text cell.
1019 * Turn a cell into a raw text cell.
1020 *
1020 *
1021 * @method to_raw
1021 * @method to_raw
1022 * @param {Number} [index] A cell's index
1022 * @param {Number} [index] A cell's index
1023 */
1023 */
1024 Notebook.prototype.to_raw = function (index) {
1024 Notebook.prototype.to_raw = function (index) {
1025 var i = this.index_or_selected(index);
1025 var i = this.index_or_selected(index);
1026 if (this.is_valid_cell_index(i)) {
1026 if (this.is_valid_cell_index(i)) {
1027 var source_element = this.get_cell_element(i);
1027 var source_element = this.get_cell_element(i);
1028 var source_cell = source_element.data("cell");
1028 var source_cell = source_element.data("cell");
1029 var target_cell = null;
1029 var target_cell = null;
1030 if (!(source_cell instanceof textcell.RawCell)) {
1030 if (!(source_cell instanceof textcell.RawCell)) {
1031 target_cell = this.insert_cell_below('raw',i);
1031 target_cell = this.insert_cell_below('raw',i);
1032 var text = source_cell.get_text();
1032 var text = source_cell.get_text();
1033 if (text === source_cell.placeholder) {
1033 if (text === source_cell.placeholder) {
1034 text = '';
1034 text = '';
1035 }
1035 }
1036 // We must show the editor before setting its contents
1036 // We must show the editor before setting its contents
1037 target_cell.unrender();
1037 target_cell.unrender();
1038 target_cell.set_text(text);
1038 target_cell.set_text(text);
1039 // make this value the starting point, so that we can only undo
1039 // make this value the starting point, so that we can only undo
1040 // to this state, instead of a blank cell
1040 // to this state, instead of a blank cell
1041 target_cell.code_mirror.clearHistory();
1041 target_cell.code_mirror.clearHistory();
1042 source_element.remove();
1042 source_element.remove();
1043 this.select(i);
1043 this.select(i);
1044 var cursor = source_cell.code_mirror.getCursor();
1044 var cursor = source_cell.code_mirror.getCursor();
1045 target_cell.code_mirror.setCursor(cursor);
1045 target_cell.code_mirror.setCursor(cursor);
1046 this.set_dirty(true);
1046 this.set_dirty(true);
1047 }
1047 }
1048 }
1048 }
1049 };
1049 };
1050
1050
1051 /**
1051 /**
1052 * Turn a cell into a heading cell.
1052 * Turn a cell into a heading cell.
1053 *
1053 *
1054 * @method to_heading
1054 * @method to_heading
1055 * @param {Number} [index] A cell's index
1055 * @param {Number} [index] A cell's index
1056 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
1056 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
1057 */
1057 */
1058 Notebook.prototype.to_heading = function (index, level) {
1058 Notebook.prototype.to_heading = function (index, level) {
1059 level = level || 1;
1059 level = level || 1;
1060 var i = this.index_or_selected(index);
1060 var i = this.index_or_selected(index);
1061 if (this.is_valid_cell_index(i)) {
1061 if (this.is_valid_cell_index(i)) {
1062 var source_element = this.get_cell_element(i);
1062 var source_element = this.get_cell_element(i);
1063 var source_cell = source_element.data("cell");
1063 var source_cell = source_element.data("cell");
1064 var target_cell = null;
1064 var target_cell = null;
1065 if (source_cell instanceof textcell.HeadingCell) {
1065 if (source_cell instanceof textcell.HeadingCell) {
1066 source_cell.set_level(level);
1066 source_cell.set_level(level);
1067 } else {
1067 } else {
1068 target_cell = this.insert_cell_below('heading',i);
1068 target_cell = this.insert_cell_below('heading',i);
1069 var text = source_cell.get_text();
1069 var text = source_cell.get_text();
1070 if (text === source_cell.placeholder) {
1070 if (text === source_cell.placeholder) {
1071 text = '';
1071 text = '';
1072 }
1072 }
1073 // We must show the editor before setting its contents
1073 // We must show the editor before setting its contents
1074 target_cell.set_level(level);
1074 target_cell.set_level(level);
1075 target_cell.unrender();
1075 target_cell.unrender();
1076 target_cell.set_text(text);
1076 target_cell.set_text(text);
1077 // make this value the starting point, so that we can only undo
1077 // make this value the starting point, so that we can only undo
1078 // to this state, instead of a blank cell
1078 // to this state, instead of a blank cell
1079 target_cell.code_mirror.clearHistory();
1079 target_cell.code_mirror.clearHistory();
1080 source_element.remove();
1080 source_element.remove();
1081 this.select(i);
1081 this.select(i);
1082 var cursor = source_cell.code_mirror.getCursor();
1082 var cursor = source_cell.code_mirror.getCursor();
1083 target_cell.code_mirror.setCursor(cursor);
1083 target_cell.code_mirror.setCursor(cursor);
1084 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1084 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1085 target_cell.render();
1085 target_cell.render();
1086 }
1086 }
1087 }
1087 }
1088 this.set_dirty(true);
1088 this.set_dirty(true);
1089 this.events.trigger('selected_cell_type_changed.Notebook',
1089 this.events.trigger('selected_cell_type_changed.Notebook',
1090 {'cell_type':'heading',level:level}
1090 {'cell_type':'heading',level:level}
1091 );
1091 );
1092 }
1092 }
1093 };
1093 };
1094
1094
1095
1095
1096 // Cut/Copy/Paste
1096 // Cut/Copy/Paste
1097
1097
1098 /**
1098 /**
1099 * Enable UI elements for pasting cells.
1099 * Enable UI elements for pasting cells.
1100 *
1100 *
1101 * @method enable_paste
1101 * @method enable_paste
1102 */
1102 */
1103 Notebook.prototype.enable_paste = function () {
1103 Notebook.prototype.enable_paste = function () {
1104 var that = this;
1104 var that = this;
1105 if (!this.paste_enabled) {
1105 if (!this.paste_enabled) {
1106 $('#paste_cell_replace').removeClass('disabled')
1106 $('#paste_cell_replace').removeClass('disabled')
1107 .on('click', function () {that.paste_cell_replace();});
1107 .on('click', function () {that.paste_cell_replace();});
1108 $('#paste_cell_above').removeClass('disabled')
1108 $('#paste_cell_above').removeClass('disabled')
1109 .on('click', function () {that.paste_cell_above();});
1109 .on('click', function () {that.paste_cell_above();});
1110 $('#paste_cell_below').removeClass('disabled')
1110 $('#paste_cell_below').removeClass('disabled')
1111 .on('click', function () {that.paste_cell_below();});
1111 .on('click', function () {that.paste_cell_below();});
1112 this.paste_enabled = true;
1112 this.paste_enabled = true;
1113 }
1113 }
1114 };
1114 };
1115
1115
1116 /**
1116 /**
1117 * Disable UI elements for pasting cells.
1117 * Disable UI elements for pasting cells.
1118 *
1118 *
1119 * @method disable_paste
1119 * @method disable_paste
1120 */
1120 */
1121 Notebook.prototype.disable_paste = function () {
1121 Notebook.prototype.disable_paste = function () {
1122 if (this.paste_enabled) {
1122 if (this.paste_enabled) {
1123 $('#paste_cell_replace').addClass('disabled').off('click');
1123 $('#paste_cell_replace').addClass('disabled').off('click');
1124 $('#paste_cell_above').addClass('disabled').off('click');
1124 $('#paste_cell_above').addClass('disabled').off('click');
1125 $('#paste_cell_below').addClass('disabled').off('click');
1125 $('#paste_cell_below').addClass('disabled').off('click');
1126 this.paste_enabled = false;
1126 this.paste_enabled = false;
1127 }
1127 }
1128 };
1128 };
1129
1129
1130 /**
1130 /**
1131 * Cut a cell.
1131 * Cut a cell.
1132 *
1132 *
1133 * @method cut_cell
1133 * @method cut_cell
1134 */
1134 */
1135 Notebook.prototype.cut_cell = function () {
1135 Notebook.prototype.cut_cell = function () {
1136 this.copy_cell();
1136 this.copy_cell();
1137 this.delete_cell();
1137 this.delete_cell();
1138 };
1138 };
1139
1139
1140 /**
1140 /**
1141 * Copy a cell.
1141 * Copy a cell.
1142 *
1142 *
1143 * @method copy_cell
1143 * @method copy_cell
1144 */
1144 */
1145 Notebook.prototype.copy_cell = function () {
1145 Notebook.prototype.copy_cell = function () {
1146 var cell = this.get_selected_cell();
1146 var cell = this.get_selected_cell();
1147 this.clipboard = cell.toJSON();
1147 this.clipboard = cell.toJSON();
1148 this.enable_paste();
1148 this.enable_paste();
1149 };
1149 };
1150
1150
1151 /**
1151 /**
1152 * Replace the selected cell with a cell in the clipboard.
1152 * Replace the selected cell with a cell in the clipboard.
1153 *
1153 *
1154 * @method paste_cell_replace
1154 * @method paste_cell_replace
1155 */
1155 */
1156 Notebook.prototype.paste_cell_replace = function () {
1156 Notebook.prototype.paste_cell_replace = function () {
1157 if (this.clipboard !== null && this.paste_enabled) {
1157 if (this.clipboard !== null && this.paste_enabled) {
1158 var cell_data = this.clipboard;
1158 var cell_data = this.clipboard;
1159 var new_cell = this.insert_cell_above(cell_data.cell_type);
1159 var new_cell = this.insert_cell_above(cell_data.cell_type);
1160 new_cell.fromJSON(cell_data);
1160 new_cell.fromJSON(cell_data);
1161 var old_cell = this.get_next_cell(new_cell);
1161 var old_cell = this.get_next_cell(new_cell);
1162 this.delete_cell(this.find_cell_index(old_cell));
1162 this.delete_cell(this.find_cell_index(old_cell));
1163 this.select(this.find_cell_index(new_cell));
1163 this.select(this.find_cell_index(new_cell));
1164 }
1164 }
1165 };
1165 };
1166
1166
1167 /**
1167 /**
1168 * Paste a cell from the clipboard above the selected cell.
1168 * Paste a cell from the clipboard above the selected cell.
1169 *
1169 *
1170 * @method paste_cell_above
1170 * @method paste_cell_above
1171 */
1171 */
1172 Notebook.prototype.paste_cell_above = function () {
1172 Notebook.prototype.paste_cell_above = function () {
1173 if (this.clipboard !== null && this.paste_enabled) {
1173 if (this.clipboard !== null && this.paste_enabled) {
1174 var cell_data = this.clipboard;
1174 var cell_data = this.clipboard;
1175 var new_cell = this.insert_cell_above(cell_data.cell_type);
1175 var new_cell = this.insert_cell_above(cell_data.cell_type);
1176 new_cell.fromJSON(cell_data);
1176 new_cell.fromJSON(cell_data);
1177 new_cell.focus_cell();
1177 new_cell.focus_cell();
1178 }
1178 }
1179 };
1179 };
1180
1180
1181 /**
1181 /**
1182 * Paste a cell from the clipboard below the selected cell.
1182 * Paste a cell from the clipboard below the selected cell.
1183 *
1183 *
1184 * @method paste_cell_below
1184 * @method paste_cell_below
1185 */
1185 */
1186 Notebook.prototype.paste_cell_below = function () {
1186 Notebook.prototype.paste_cell_below = function () {
1187 if (this.clipboard !== null && this.paste_enabled) {
1187 if (this.clipboard !== null && this.paste_enabled) {
1188 var cell_data = this.clipboard;
1188 var cell_data = this.clipboard;
1189 var new_cell = this.insert_cell_below(cell_data.cell_type);
1189 var new_cell = this.insert_cell_below(cell_data.cell_type);
1190 new_cell.fromJSON(cell_data);
1190 new_cell.fromJSON(cell_data);
1191 new_cell.focus_cell();
1191 new_cell.focus_cell();
1192 }
1192 }
1193 };
1193 };
1194
1194
1195 // Split/merge
1195 // Split/merge
1196
1196
1197 /**
1197 /**
1198 * Split the selected cell into two, at the cursor.
1198 * Split the selected cell into two, at the cursor.
1199 *
1199 *
1200 * @method split_cell
1200 * @method split_cell
1201 */
1201 */
1202 Notebook.prototype.split_cell = function () {
1202 Notebook.prototype.split_cell = function () {
1203 var mdc = textcell.MarkdownCell;
1203 var mdc = textcell.MarkdownCell;
1204 var rc = textcell.RawCell;
1204 var rc = textcell.RawCell;
1205 var cell = this.get_selected_cell();
1205 var cell = this.get_selected_cell();
1206 if (cell.is_splittable()) {
1206 if (cell.is_splittable()) {
1207 var texta = cell.get_pre_cursor();
1207 var texta = cell.get_pre_cursor();
1208 var textb = cell.get_post_cursor();
1208 var textb = cell.get_post_cursor();
1209 if (cell instanceof codecell.CodeCell) {
1209 if (cell instanceof codecell.CodeCell) {
1210 // In this case the operations keep the notebook in its existing mode
1210 // In this case the operations keep the notebook in its existing mode
1211 // so we don't need to do any post-op mode changes.
1211 // so we don't need to do any post-op mode changes.
1212 cell.set_text(textb);
1212 cell.set_text(textb);
1213 var new_cell = this.insert_cell_above('code');
1213 var new_cell = this.insert_cell_above('code');
1214 new_cell.set_text(texta);
1214 new_cell.set_text(texta);
1215 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1215 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1216 // We know cell is !rendered so we can use set_text.
1216 // We know cell is !rendered so we can use set_text.
1217 cell.set_text(textb);
1217 cell.set_text(textb);
1218 var new_cell = this.insert_cell_above(cell.cell_type);
1218 var new_cell = this.insert_cell_above(cell.cell_type);
1219 // Unrender the new cell so we can call set_text.
1219 // Unrender the new cell so we can call set_text.
1220 new_cell.unrender();
1220 new_cell.unrender();
1221 new_cell.set_text(texta);
1221 new_cell.set_text(texta);
1222 }
1222 }
1223 }
1223 }
1224 };
1224 };
1225
1225
1226 /**
1226 /**
1227 * Combine the selected cell into the cell above it.
1227 * Combine the selected cell into the cell above it.
1228 *
1228 *
1229 * @method merge_cell_above
1229 * @method merge_cell_above
1230 */
1230 */
1231 Notebook.prototype.merge_cell_above = function () {
1231 Notebook.prototype.merge_cell_above = function () {
1232 var mdc = textcell.MarkdownCell;
1232 var mdc = textcell.MarkdownCell;
1233 var rc = textcell.RawCell;
1233 var rc = textcell.RawCell;
1234 var index = this.get_selected_index();
1234 var index = this.get_selected_index();
1235 var cell = this.get_cell(index);
1235 var cell = this.get_cell(index);
1236 var render = cell.rendered;
1236 var render = cell.rendered;
1237 if (!cell.is_mergeable()) {
1237 if (!cell.is_mergeable()) {
1238 return;
1238 return;
1239 }
1239 }
1240 if (index > 0) {
1240 if (index > 0) {
1241 var upper_cell = this.get_cell(index-1);
1241 var upper_cell = this.get_cell(index-1);
1242 if (!upper_cell.is_mergeable()) {
1242 if (!upper_cell.is_mergeable()) {
1243 return;
1243 return;
1244 }
1244 }
1245 var upper_text = upper_cell.get_text();
1245 var upper_text = upper_cell.get_text();
1246 var text = cell.get_text();
1246 var text = cell.get_text();
1247 if (cell instanceof codecell.CodeCell) {
1247 if (cell instanceof codecell.CodeCell) {
1248 cell.set_text(upper_text+'\n'+text);
1248 cell.set_text(upper_text+'\n'+text);
1249 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1249 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1250 cell.unrender(); // Must unrender before we set_text.
1250 cell.unrender(); // Must unrender before we set_text.
1251 cell.set_text(upper_text+'\n\n'+text);
1251 cell.set_text(upper_text+'\n\n'+text);
1252 if (render) {
1252 if (render) {
1253 // The rendered state of the final cell should match
1253 // The rendered state of the final cell should match
1254 // that of the original selected cell;
1254 // that of the original selected cell;
1255 cell.render();
1255 cell.render();
1256 }
1256 }
1257 }
1257 }
1258 this.delete_cell(index-1);
1258 this.delete_cell(index-1);
1259 this.select(this.find_cell_index(cell));
1259 this.select(this.find_cell_index(cell));
1260 }
1260 }
1261 };
1261 };
1262
1262
1263 /**
1263 /**
1264 * Combine the selected cell into the cell below it.
1264 * Combine the selected cell into the cell below it.
1265 *
1265 *
1266 * @method merge_cell_below
1266 * @method merge_cell_below
1267 */
1267 */
1268 Notebook.prototype.merge_cell_below = function () {
1268 Notebook.prototype.merge_cell_below = function () {
1269 var mdc = textcell.MarkdownCell;
1269 var mdc = textcell.MarkdownCell;
1270 var rc = textcell.RawCell;
1270 var rc = textcell.RawCell;
1271 var index = this.get_selected_index();
1271 var index = this.get_selected_index();
1272 var cell = this.get_cell(index);
1272 var cell = this.get_cell(index);
1273 var render = cell.rendered;
1273 var render = cell.rendered;
1274 if (!cell.is_mergeable()) {
1274 if (!cell.is_mergeable()) {
1275 return;
1275 return;
1276 }
1276 }
1277 if (index < this.ncells()-1) {
1277 if (index < this.ncells()-1) {
1278 var lower_cell = this.get_cell(index+1);
1278 var lower_cell = this.get_cell(index+1);
1279 if (!lower_cell.is_mergeable()) {
1279 if (!lower_cell.is_mergeable()) {
1280 return;
1280 return;
1281 }
1281 }
1282 var lower_text = lower_cell.get_text();
1282 var lower_text = lower_cell.get_text();
1283 var text = cell.get_text();
1283 var text = cell.get_text();
1284 if (cell instanceof codecell.CodeCell) {
1284 if (cell instanceof codecell.CodeCell) {
1285 cell.set_text(text+'\n'+lower_text);
1285 cell.set_text(text+'\n'+lower_text);
1286 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1286 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1287 cell.unrender(); // Must unrender before we set_text.
1287 cell.unrender(); // Must unrender before we set_text.
1288 cell.set_text(text+'\n\n'+lower_text);
1288 cell.set_text(text+'\n\n'+lower_text);
1289 if (render) {
1289 if (render) {
1290 // The rendered state of the final cell should match
1290 // The rendered state of the final cell should match
1291 // that of the original selected cell;
1291 // that of the original selected cell;
1292 cell.render();
1292 cell.render();
1293 }
1293 }
1294 }
1294 }
1295 this.delete_cell(index+1);
1295 this.delete_cell(index+1);
1296 this.select(this.find_cell_index(cell));
1296 this.select(this.find_cell_index(cell));
1297 }
1297 }
1298 };
1298 };
1299
1299
1300
1300
1301 // Cell collapsing and output clearing
1301 // Cell collapsing and output clearing
1302
1302
1303 /**
1303 /**
1304 * Hide a cell's output.
1304 * Hide a cell's output.
1305 *
1305 *
1306 * @method collapse_output
1306 * @method collapse_output
1307 * @param {Number} index A cell's numeric index
1307 * @param {Number} index A cell's numeric index
1308 */
1308 */
1309 Notebook.prototype.collapse_output = function (index) {
1309 Notebook.prototype.collapse_output = function (index) {
1310 var i = this.index_or_selected(index);
1310 var i = this.index_or_selected(index);
1311 var cell = this.get_cell(i);
1311 var cell = this.get_cell(i);
1312 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1312 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1313 cell.collapse_output();
1313 cell.collapse_output();
1314 this.set_dirty(true);
1314 this.set_dirty(true);
1315 }
1315 }
1316 };
1316 };
1317
1317
1318 /**
1318 /**
1319 * Hide each code cell's output area.
1319 * Hide each code cell's output area.
1320 *
1320 *
1321 * @method collapse_all_output
1321 * @method collapse_all_output
1322 */
1322 */
1323 Notebook.prototype.collapse_all_output = function () {
1323 Notebook.prototype.collapse_all_output = function () {
1324 $.map(this.get_cells(), function (cell, i) {
1324 $.map(this.get_cells(), function (cell, i) {
1325 if (cell instanceof codecell.CodeCell) {
1325 if (cell instanceof codecell.CodeCell) {
1326 cell.collapse_output();
1326 cell.collapse_output();
1327 }
1327 }
1328 });
1328 });
1329 // this should not be set if the `collapse` key is removed from nbformat
1329 // this should not be set if the `collapse` key is removed from nbformat
1330 this.set_dirty(true);
1330 this.set_dirty(true);
1331 };
1331 };
1332
1332
1333 /**
1333 /**
1334 * Show a cell's output.
1334 * Show a cell's output.
1335 *
1335 *
1336 * @method expand_output
1336 * @method expand_output
1337 * @param {Number} index A cell's numeric index
1337 * @param {Number} index A cell's numeric index
1338 */
1338 */
1339 Notebook.prototype.expand_output = function (index) {
1339 Notebook.prototype.expand_output = function (index) {
1340 var i = this.index_or_selected(index);
1340 var i = this.index_or_selected(index);
1341 var cell = this.get_cell(i);
1341 var cell = this.get_cell(i);
1342 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1342 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1343 cell.expand_output();
1343 cell.expand_output();
1344 this.set_dirty(true);
1344 this.set_dirty(true);
1345 }
1345 }
1346 };
1346 };
1347
1347
1348 /**
1348 /**
1349 * Expand each code cell's output area, and remove scrollbars.
1349 * Expand each code cell's output area, and remove scrollbars.
1350 *
1350 *
1351 * @method expand_all_output
1351 * @method expand_all_output
1352 */
1352 */
1353 Notebook.prototype.expand_all_output = function () {
1353 Notebook.prototype.expand_all_output = function () {
1354 $.map(this.get_cells(), function (cell, i) {
1354 $.map(this.get_cells(), function (cell, i) {
1355 if (cell instanceof codecell.CodeCell) {
1355 if (cell instanceof codecell.CodeCell) {
1356 cell.expand_output();
1356 cell.expand_output();
1357 }
1357 }
1358 });
1358 });
1359 // this should not be set if the `collapse` key is removed from nbformat
1359 // this should not be set if the `collapse` key is removed from nbformat
1360 this.set_dirty(true);
1360 this.set_dirty(true);
1361 };
1361 };
1362
1362
1363 /**
1363 /**
1364 * Clear the selected CodeCell's output area.
1364 * Clear the selected CodeCell's output area.
1365 *
1365 *
1366 * @method clear_output
1366 * @method clear_output
1367 * @param {Number} index A cell's numeric index
1367 * @param {Number} index A cell's numeric index
1368 */
1368 */
1369 Notebook.prototype.clear_output = function (index) {
1369 Notebook.prototype.clear_output = function (index) {
1370 var i = this.index_or_selected(index);
1370 var i = this.index_or_selected(index);
1371 var cell = this.get_cell(i);
1371 var cell = this.get_cell(i);
1372 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1372 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1373 cell.clear_output();
1373 cell.clear_output();
1374 this.set_dirty(true);
1374 this.set_dirty(true);
1375 }
1375 }
1376 };
1376 };
1377
1377
1378 /**
1378 /**
1379 * Clear each code cell's output area.
1379 * Clear each code cell's output area.
1380 *
1380 *
1381 * @method clear_all_output
1381 * @method clear_all_output
1382 */
1382 */
1383 Notebook.prototype.clear_all_output = function () {
1383 Notebook.prototype.clear_all_output = function () {
1384 $.map(this.get_cells(), function (cell, i) {
1384 $.map(this.get_cells(), function (cell, i) {
1385 if (cell instanceof codecell.CodeCell) {
1385 if (cell instanceof codecell.CodeCell) {
1386 cell.clear_output();
1386 cell.clear_output();
1387 }
1387 }
1388 });
1388 });
1389 this.set_dirty(true);
1389 this.set_dirty(true);
1390 };
1390 };
1391
1391
1392 /**
1392 /**
1393 * Scroll the selected CodeCell's output area.
1393 * Scroll the selected CodeCell's output area.
1394 *
1394 *
1395 * @method scroll_output
1395 * @method scroll_output
1396 * @param {Number} index A cell's numeric index
1396 * @param {Number} index A cell's numeric index
1397 */
1397 */
1398 Notebook.prototype.scroll_output = function (index) {
1398 Notebook.prototype.scroll_output = function (index) {
1399 var i = this.index_or_selected(index);
1399 var i = this.index_or_selected(index);
1400 var cell = this.get_cell(i);
1400 var cell = this.get_cell(i);
1401 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1401 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1402 cell.scroll_output();
1402 cell.scroll_output();
1403 this.set_dirty(true);
1403 this.set_dirty(true);
1404 }
1404 }
1405 };
1405 };
1406
1406
1407 /**
1407 /**
1408 * Expand each code cell's output area, and add a scrollbar for long output.
1408 * Expand each code cell's output area, and add a scrollbar for long output.
1409 *
1409 *
1410 * @method scroll_all_output
1410 * @method scroll_all_output
1411 */
1411 */
1412 Notebook.prototype.scroll_all_output = function () {
1412 Notebook.prototype.scroll_all_output = function () {
1413 $.map(this.get_cells(), function (cell, i) {
1413 $.map(this.get_cells(), function (cell, i) {
1414 if (cell instanceof codecell.CodeCell) {
1414 if (cell instanceof codecell.CodeCell) {
1415 cell.scroll_output();
1415 cell.scroll_output();
1416 }
1416 }
1417 });
1417 });
1418 // this should not be set if the `collapse` key is removed from nbformat
1418 // this should not be set if the `collapse` key is removed from nbformat
1419 this.set_dirty(true);
1419 this.set_dirty(true);
1420 };
1420 };
1421
1421
1422 /** Toggle whether a cell's output is collapsed or expanded.
1422 /** Toggle whether a cell's output is collapsed or expanded.
1423 *
1423 *
1424 * @method toggle_output
1424 * @method toggle_output
1425 * @param {Number} index A cell's numeric index
1425 * @param {Number} index A cell's numeric index
1426 */
1426 */
1427 Notebook.prototype.toggle_output = function (index) {
1427 Notebook.prototype.toggle_output = function (index) {
1428 var i = this.index_or_selected(index);
1428 var i = this.index_or_selected(index);
1429 var cell = this.get_cell(i);
1429 var cell = this.get_cell(i);
1430 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1430 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1431 cell.toggle_output();
1431 cell.toggle_output();
1432 this.set_dirty(true);
1432 this.set_dirty(true);
1433 }
1433 }
1434 };
1434 };
1435
1435
1436 /**
1436 /**
1437 * Hide/show the output of all cells.
1437 * Hide/show the output of all cells.
1438 *
1438 *
1439 * @method toggle_all_output
1439 * @method toggle_all_output
1440 */
1440 */
1441 Notebook.prototype.toggle_all_output = function () {
1441 Notebook.prototype.toggle_all_output = function () {
1442 $.map(this.get_cells(), function (cell, i) {
1442 $.map(this.get_cells(), function (cell, i) {
1443 if (cell instanceof codecell.CodeCell) {
1443 if (cell instanceof codecell.CodeCell) {
1444 cell.toggle_output();
1444 cell.toggle_output();
1445 }
1445 }
1446 });
1446 });
1447 // this should not be set if the `collapse` key is removed from nbformat
1447 // this should not be set if the `collapse` key is removed from nbformat
1448 this.set_dirty(true);
1448 this.set_dirty(true);
1449 };
1449 };
1450
1450
1451 /**
1451 /**
1452 * Toggle a scrollbar for long cell outputs.
1452 * Toggle a scrollbar for long cell outputs.
1453 *
1453 *
1454 * @method toggle_output_scroll
1454 * @method toggle_output_scroll
1455 * @param {Number} index A cell's numeric index
1455 * @param {Number} index A cell's numeric index
1456 */
1456 */
1457 Notebook.prototype.toggle_output_scroll = function (index) {
1457 Notebook.prototype.toggle_output_scroll = function (index) {
1458 var i = this.index_or_selected(index);
1458 var i = this.index_or_selected(index);
1459 var cell = this.get_cell(i);
1459 var cell = this.get_cell(i);
1460 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1460 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1461 cell.toggle_output_scroll();
1461 cell.toggle_output_scroll();
1462 this.set_dirty(true);
1462 this.set_dirty(true);
1463 }
1463 }
1464 };
1464 };
1465
1465
1466 /**
1466 /**
1467 * Toggle the scrolling of long output on all cells.
1467 * Toggle the scrolling of long output on all cells.
1468 *
1468 *
1469 * @method toggle_all_output_scrolling
1469 * @method toggle_all_output_scrolling
1470 */
1470 */
1471 Notebook.prototype.toggle_all_output_scroll = function () {
1471 Notebook.prototype.toggle_all_output_scroll = function () {
1472 $.map(this.get_cells(), function (cell, i) {
1472 $.map(this.get_cells(), function (cell, i) {
1473 if (cell instanceof codecell.CodeCell) {
1473 if (cell instanceof codecell.CodeCell) {
1474 cell.toggle_output_scroll();
1474 cell.toggle_output_scroll();
1475 }
1475 }
1476 });
1476 });
1477 // this should not be set if the `collapse` key is removed from nbformat
1477 // this should not be set if the `collapse` key is removed from nbformat
1478 this.set_dirty(true);
1478 this.set_dirty(true);
1479 };
1479 };
1480
1480
1481 // Other cell functions: line numbers, ...
1481 // Other cell functions: line numbers, ...
1482
1482
1483 /**
1483 /**
1484 * Toggle line numbers in the selected cell's input area.
1484 * Toggle line numbers in the selected cell's input area.
1485 *
1485 *
1486 * @method cell_toggle_line_numbers
1486 * @method cell_toggle_line_numbers
1487 */
1487 */
1488 Notebook.prototype.cell_toggle_line_numbers = function() {
1488 Notebook.prototype.cell_toggle_line_numbers = function() {
1489 this.get_selected_cell().toggle_line_numbers();
1489 this.get_selected_cell().toggle_line_numbers();
1490 };
1490 };
1491
1491
1492 // Session related things
1492 // Session related things
1493
1493
1494 /**
1494 /**
1495 * Start a new session and set it on each code cell.
1495 * Start a new session and set it on each code cell.
1496 *
1496 *
1497 * @method start_session
1497 * @method start_session
1498 */
1498 */
1499 Notebook.prototype.start_session = function () {
1499 Notebook.prototype.start_session = function (kernel_name) {
1500 if (kernel_name === undefined) {
1501 kernel_name = this.default_kernel_name;
1502 }
1503 console.log("start_session", kernel_name);
1500 this.session = new session.Session({
1504 this.session = new session.Session({
1501 base_url: this.base_url,
1505 base_url: this.base_url,
1502 ws_url: this.ws_url,
1506 ws_url: this.ws_url,
1503 notebook_path: this.notebook_path,
1507 notebook_path: this.notebook_path,
1504 notebook_name: this.notebook_name,
1508 notebook_name: this.notebook_name,
1505 // For now, create all sessions with the 'python' kernel, which is the
1509 // For now, create all sessions with the 'python' kernel, which is the
1506 // default. Later, the user will be able to select kernels. This is
1510 // default. Later, the user will be able to select kernels. This is
1507 // overridden if KernelManager.kernel_cmd is specified for the server.
1511 // overridden if KernelManager.kernel_cmd is specified for the server.
1508 kernel_name: this.default_kernel_name,
1512 kernel_name: kernel_name,
1509 notebook: this});
1513 notebook: this});
1510
1514
1511 this.session.start($.proxy(this._session_started, this));
1515 this.session.start($.proxy(this._session_started, this));
1512 };
1516 };
1513
1517
1514
1518
1515 /**
1519 /**
1516 * Once a session is started, link the code cells to the kernel and pass the
1520 * Once a session is started, link the code cells to the kernel and pass the
1517 * comm manager to the widget manager
1521 * comm manager to the widget manager
1518 *
1522 *
1519 */
1523 */
1520 Notebook.prototype._session_started = function(){
1524 Notebook.prototype._session_started = function(){
1521 this.kernel = this.session.kernel;
1525 this.kernel = this.session.kernel;
1522 var ncells = this.ncells();
1526 var ncells = this.ncells();
1523 for (var i=0; i<ncells; i++) {
1527 for (var i=0; i<ncells; i++) {
1524 var cell = this.get_cell(i);
1528 var cell = this.get_cell(i);
1525 if (cell instanceof codecell.CodeCell) {
1529 if (cell instanceof codecell.CodeCell) {
1526 cell.set_kernel(this.session.kernel);
1530 cell.set_kernel(this.session.kernel);
1527 }
1531 }
1528 }
1532 }
1529 };
1533 };
1530
1534
1531 /**
1535 /**
1532 * Prompt the user to restart the IPython kernel.
1536 * Prompt the user to restart the IPython kernel.
1533 *
1537 *
1534 * @method restart_kernel
1538 * @method restart_kernel
1535 */
1539 */
1536 Notebook.prototype.restart_kernel = function () {
1540 Notebook.prototype.restart_kernel = function () {
1537 var that = this;
1541 var that = this;
1538 dialog.modal({
1542 dialog.modal({
1539 notebook: this,
1543 notebook: this,
1540 keyboard_manager: this.keyboard_manager,
1544 keyboard_manager: this.keyboard_manager,
1541 title : "Restart kernel or continue running?",
1545 title : "Restart kernel or continue running?",
1542 body : $("<p/>").text(
1546 body : $("<p/>").text(
1543 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1547 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1544 ),
1548 ),
1545 buttons : {
1549 buttons : {
1546 "Continue running" : {},
1550 "Continue running" : {},
1547 "Restart" : {
1551 "Restart" : {
1548 "class" : "btn-danger",
1552 "class" : "btn-danger",
1549 "click" : function() {
1553 "click" : function() {
1550 that.session.restart_kernel();
1554 that.session.restart_kernel();
1551 }
1555 }
1552 }
1556 }
1553 }
1557 }
1554 });
1558 });
1555 };
1559 };
1556
1560
1557 /**
1561 /**
1558 * Execute or render cell outputs and go into command mode.
1562 * Execute or render cell outputs and go into command mode.
1559 *
1563 *
1560 * @method execute_cell
1564 * @method execute_cell
1561 */
1565 */
1562 Notebook.prototype.execute_cell = function () {
1566 Notebook.prototype.execute_cell = function () {
1563 // mode = shift, ctrl, alt
1567 // mode = shift, ctrl, alt
1564 var cell = this.get_selected_cell();
1568 var cell = this.get_selected_cell();
1565 var cell_index = this.find_cell_index(cell);
1569 var cell_index = this.find_cell_index(cell);
1566
1570
1567 cell.execute();
1571 cell.execute();
1568 this.command_mode();
1572 this.command_mode();
1569 this.set_dirty(true);
1573 this.set_dirty(true);
1570 };
1574 };
1571
1575
1572 /**
1576 /**
1573 * Execute or render cell outputs and insert a new cell below.
1577 * Execute or render cell outputs and insert a new cell below.
1574 *
1578 *
1575 * @method execute_cell_and_insert_below
1579 * @method execute_cell_and_insert_below
1576 */
1580 */
1577 Notebook.prototype.execute_cell_and_insert_below = function () {
1581 Notebook.prototype.execute_cell_and_insert_below = function () {
1578 var cell = this.get_selected_cell();
1582 var cell = this.get_selected_cell();
1579 var cell_index = this.find_cell_index(cell);
1583 var cell_index = this.find_cell_index(cell);
1580
1584
1581 cell.execute();
1585 cell.execute();
1582
1586
1583 // If we are at the end always insert a new cell and return
1587 // If we are at the end always insert a new cell and return
1584 if (cell_index === (this.ncells()-1)) {
1588 if (cell_index === (this.ncells()-1)) {
1585 this.command_mode();
1589 this.command_mode();
1586 this.insert_cell_below();
1590 this.insert_cell_below();
1587 this.select(cell_index+1);
1591 this.select(cell_index+1);
1588 this.edit_mode();
1592 this.edit_mode();
1589 this.scroll_to_bottom();
1593 this.scroll_to_bottom();
1590 this.set_dirty(true);
1594 this.set_dirty(true);
1591 return;
1595 return;
1592 }
1596 }
1593
1597
1594 this.command_mode();
1598 this.command_mode();
1595 this.insert_cell_below();
1599 this.insert_cell_below();
1596 this.select(cell_index+1);
1600 this.select(cell_index+1);
1597 this.edit_mode();
1601 this.edit_mode();
1598 this.set_dirty(true);
1602 this.set_dirty(true);
1599 };
1603 };
1600
1604
1601 /**
1605 /**
1602 * Execute or render cell outputs and select the next cell.
1606 * Execute or render cell outputs and select the next cell.
1603 *
1607 *
1604 * @method execute_cell_and_select_below
1608 * @method execute_cell_and_select_below
1605 */
1609 */
1606 Notebook.prototype.execute_cell_and_select_below = function () {
1610 Notebook.prototype.execute_cell_and_select_below = function () {
1607
1611
1608 var cell = this.get_selected_cell();
1612 var cell = this.get_selected_cell();
1609 var cell_index = this.find_cell_index(cell);
1613 var cell_index = this.find_cell_index(cell);
1610
1614
1611 cell.execute();
1615 cell.execute();
1612
1616
1613 // If we are at the end always insert a new cell and return
1617 // If we are at the end always insert a new cell and return
1614 if (cell_index === (this.ncells()-1)) {
1618 if (cell_index === (this.ncells()-1)) {
1615 this.command_mode();
1619 this.command_mode();
1616 this.insert_cell_below();
1620 this.insert_cell_below();
1617 this.select(cell_index+1);
1621 this.select(cell_index+1);
1618 this.edit_mode();
1622 this.edit_mode();
1619 this.scroll_to_bottom();
1623 this.scroll_to_bottom();
1620 this.set_dirty(true);
1624 this.set_dirty(true);
1621 return;
1625 return;
1622 }
1626 }
1623
1627
1624 this.command_mode();
1628 this.command_mode();
1625 this.select(cell_index+1);
1629 this.select(cell_index+1);
1626 this.focus_cell();
1630 this.focus_cell();
1627 this.set_dirty(true);
1631 this.set_dirty(true);
1628 };
1632 };
1629
1633
1630 /**
1634 /**
1631 * Execute all cells below the selected cell.
1635 * Execute all cells below the selected cell.
1632 *
1636 *
1633 * @method execute_cells_below
1637 * @method execute_cells_below
1634 */
1638 */
1635 Notebook.prototype.execute_cells_below = function () {
1639 Notebook.prototype.execute_cells_below = function () {
1636 this.execute_cell_range(this.get_selected_index(), this.ncells());
1640 this.execute_cell_range(this.get_selected_index(), this.ncells());
1637 this.scroll_to_bottom();
1641 this.scroll_to_bottom();
1638 };
1642 };
1639
1643
1640 /**
1644 /**
1641 * Execute all cells above the selected cell.
1645 * Execute all cells above the selected cell.
1642 *
1646 *
1643 * @method execute_cells_above
1647 * @method execute_cells_above
1644 */
1648 */
1645 Notebook.prototype.execute_cells_above = function () {
1649 Notebook.prototype.execute_cells_above = function () {
1646 this.execute_cell_range(0, this.get_selected_index());
1650 this.execute_cell_range(0, this.get_selected_index());
1647 };
1651 };
1648
1652
1649 /**
1653 /**
1650 * Execute all cells.
1654 * Execute all cells.
1651 *
1655 *
1652 * @method execute_all_cells
1656 * @method execute_all_cells
1653 */
1657 */
1654 Notebook.prototype.execute_all_cells = function () {
1658 Notebook.prototype.execute_all_cells = function () {
1655 this.execute_cell_range(0, this.ncells());
1659 this.execute_cell_range(0, this.ncells());
1656 this.scroll_to_bottom();
1660 this.scroll_to_bottom();
1657 };
1661 };
1658
1662
1659 /**
1663 /**
1660 * Execute a contiguous range of cells.
1664 * Execute a contiguous range of cells.
1661 *
1665 *
1662 * @method execute_cell_range
1666 * @method execute_cell_range
1663 * @param {Number} start Index of the first cell to execute (inclusive)
1667 * @param {Number} start Index of the first cell to execute (inclusive)
1664 * @param {Number} end Index of the last cell to execute (exclusive)
1668 * @param {Number} end Index of the last cell to execute (exclusive)
1665 */
1669 */
1666 Notebook.prototype.execute_cell_range = function (start, end) {
1670 Notebook.prototype.execute_cell_range = function (start, end) {
1667 this.command_mode();
1671 this.command_mode();
1668 for (var i=start; i<end; i++) {
1672 for (var i=start; i<end; i++) {
1669 this.select(i);
1673 this.select(i);
1670 this.execute_cell();
1674 this.execute_cell();
1671 }
1675 }
1672 };
1676 };
1673
1677
1674 // Persistance and loading
1678 // Persistance and loading
1675
1679
1676 /**
1680 /**
1677 * Getter method for this notebook's name.
1681 * Getter method for this notebook's name.
1678 *
1682 *
1679 * @method get_notebook_name
1683 * @method get_notebook_name
1680 * @return {String} This notebook's name (excluding file extension)
1684 * @return {String} This notebook's name (excluding file extension)
1681 */
1685 */
1682 Notebook.prototype.get_notebook_name = function () {
1686 Notebook.prototype.get_notebook_name = function () {
1683 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1687 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1684 return nbname;
1688 return nbname;
1685 };
1689 };
1686
1690
1687 /**
1691 /**
1688 * Setter method for this notebook's name.
1692 * Setter method for this notebook's name.
1689 *
1693 *
1690 * @method set_notebook_name
1694 * @method set_notebook_name
1691 * @param {String} name A new name for this notebook
1695 * @param {String} name A new name for this notebook
1692 */
1696 */
1693 Notebook.prototype.set_notebook_name = function (name) {
1697 Notebook.prototype.set_notebook_name = function (name) {
1694 this.notebook_name = name;
1698 this.notebook_name = name;
1695 };
1699 };
1696
1700
1697 /**
1701 /**
1698 * Check that a notebook's name is valid.
1702 * Check that a notebook's name is valid.
1699 *
1703 *
1700 * @method test_notebook_name
1704 * @method test_notebook_name
1701 * @param {String} nbname A name for this notebook
1705 * @param {String} nbname A name for this notebook
1702 * @return {Boolean} True if the name is valid, false if invalid
1706 * @return {Boolean} True if the name is valid, false if invalid
1703 */
1707 */
1704 Notebook.prototype.test_notebook_name = function (nbname) {
1708 Notebook.prototype.test_notebook_name = function (nbname) {
1705 nbname = nbname || '';
1709 nbname = nbname || '';
1706 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1710 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1707 return true;
1711 return true;
1708 } else {
1712 } else {
1709 return false;
1713 return false;
1710 }
1714 }
1711 };
1715 };
1712
1716
1713 /**
1717 /**
1714 * Load a notebook from JSON (.ipynb).
1718 * Load a notebook from JSON (.ipynb).
1715 *
1719 *
1716 * This currently handles one worksheet: others are deleted.
1720 * This currently handles one worksheet: others are deleted.
1717 *
1721 *
1718 * @method fromJSON
1722 * @method fromJSON
1719 * @param {Object} data JSON representation of a notebook
1723 * @param {Object} data JSON representation of a notebook
1720 */
1724 */
1721 Notebook.prototype.fromJSON = function (data) {
1725 Notebook.prototype.fromJSON = function (data) {
1722 var content = data.content;
1726 var content = data.content;
1723 var ncells = this.ncells();
1727 var ncells = this.ncells();
1724 var i;
1728 var i;
1725 for (i=0; i<ncells; i++) {
1729 for (i=0; i<ncells; i++) {
1726 // Always delete cell 0 as they get renumbered as they are deleted.
1730 // Always delete cell 0 as they get renumbered as they are deleted.
1727 this.delete_cell(0);
1731 this.delete_cell(0);
1728 }
1732 }
1729 // Save the metadata and name.
1733 // Save the metadata and name.
1730 this.metadata = content.metadata;
1734 this.metadata = content.metadata;
1731 this.notebook_name = data.name;
1735 this.notebook_name = data.name;
1732 var trusted = true;
1736 var trusted = true;
1733 // Only handle 1 worksheet for now.
1737 // Only handle 1 worksheet for now.
1734 var worksheet = content.worksheets[0];
1738 var worksheet = content.worksheets[0];
1735 if (worksheet !== undefined) {
1739 if (worksheet !== undefined) {
1736 if (worksheet.metadata) {
1740 if (worksheet.metadata) {
1737 this.worksheet_metadata = worksheet.metadata;
1741 this.worksheet_metadata = worksheet.metadata;
1738 }
1742 }
1739 var new_cells = worksheet.cells;
1743 var new_cells = worksheet.cells;
1740 ncells = new_cells.length;
1744 ncells = new_cells.length;
1741 var cell_data = null;
1745 var cell_data = null;
1742 var new_cell = null;
1746 var new_cell = null;
1743 for (i=0; i<ncells; i++) {
1747 for (i=0; i<ncells; i++) {
1744 cell_data = new_cells[i];
1748 cell_data = new_cells[i];
1745 // VERSIONHACK: plaintext -> raw
1749 // VERSIONHACK: plaintext -> raw
1746 // handle never-released plaintext name for raw cells
1750 // handle never-released plaintext name for raw cells
1747 if (cell_data.cell_type === 'plaintext'){
1751 if (cell_data.cell_type === 'plaintext'){
1748 cell_data.cell_type = 'raw';
1752 cell_data.cell_type = 'raw';
1749 }
1753 }
1750
1754
1751 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1755 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1752 new_cell.fromJSON(cell_data);
1756 new_cell.fromJSON(cell_data);
1753 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1757 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1754 trusted = false;
1758 trusted = false;
1755 }
1759 }
1756 }
1760 }
1757 }
1761 }
1758 if (trusted != this.trusted) {
1762 if (trusted != this.trusted) {
1759 this.trusted = trusted;
1763 this.trusted = trusted;
1760 this.events.trigger("trust_changed.Notebook", trusted);
1764 this.events.trigger("trust_changed.Notebook", trusted);
1761 }
1765 }
1762 if (content.worksheets.length > 1) {
1766 if (content.worksheets.length > 1) {
1763 dialog.modal({
1767 dialog.modal({
1764 notebook: this,
1768 notebook: this,
1765 keyboard_manager: this.keyboard_manager,
1769 keyboard_manager: this.keyboard_manager,
1766 title : "Multiple worksheets",
1770 title : "Multiple worksheets",
1767 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1771 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1768 "but this version of IPython can only handle the first. " +
1772 "but this version of IPython can only handle the first. " +
1769 "If you save this notebook, worksheets after the first will be lost.",
1773 "If you save this notebook, worksheets after the first will be lost.",
1770 buttons : {
1774 buttons : {
1771 OK : {
1775 OK : {
1772 class : "btn-danger"
1776 class : "btn-danger"
1773 }
1777 }
1774 }
1778 }
1775 });
1779 });
1776 }
1780 }
1777 };
1781 };
1778
1782
1779 /**
1783 /**
1780 * Dump this notebook into a JSON-friendly object.
1784 * Dump this notebook into a JSON-friendly object.
1781 *
1785 *
1782 * @method toJSON
1786 * @method toJSON
1783 * @return {Object} A JSON-friendly representation of this notebook.
1787 * @return {Object} A JSON-friendly representation of this notebook.
1784 */
1788 */
1785 Notebook.prototype.toJSON = function () {
1789 Notebook.prototype.toJSON = function () {
1786 var cells = this.get_cells();
1790 var cells = this.get_cells();
1787 var ncells = cells.length;
1791 var ncells = cells.length;
1788 var cell_array = new Array(ncells);
1792 var cell_array = new Array(ncells);
1789 var trusted = true;
1793 var trusted = true;
1790 for (var i=0; i<ncells; i++) {
1794 for (var i=0; i<ncells; i++) {
1791 var cell = cells[i];
1795 var cell = cells[i];
1792 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1796 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1793 trusted = false;
1797 trusted = false;
1794 }
1798 }
1795 cell_array[i] = cell.toJSON();
1799 cell_array[i] = cell.toJSON();
1796 }
1800 }
1797 var data = {
1801 var data = {
1798 // Only handle 1 worksheet for now.
1802 // Only handle 1 worksheet for now.
1799 worksheets : [{
1803 worksheets : [{
1800 cells: cell_array,
1804 cells: cell_array,
1801 metadata: this.worksheet_metadata
1805 metadata: this.worksheet_metadata
1802 }],
1806 }],
1803 metadata : this.metadata
1807 metadata : this.metadata
1804 };
1808 };
1805 if (trusted != this.trusted) {
1809 if (trusted != this.trusted) {
1806 this.trusted = trusted;
1810 this.trusted = trusted;
1807 this.events.trigger("trust_changed.Notebook", trusted);
1811 this.events.trigger("trust_changed.Notebook", trusted);
1808 }
1812 }
1809 return data;
1813 return data;
1810 };
1814 };
1811
1815
1812 /**
1816 /**
1813 * Start an autosave timer, for periodically saving the notebook.
1817 * Start an autosave timer, for periodically saving the notebook.
1814 *
1818 *
1815 * @method set_autosave_interval
1819 * @method set_autosave_interval
1816 * @param {Integer} interval the autosave interval in milliseconds
1820 * @param {Integer} interval the autosave interval in milliseconds
1817 */
1821 */
1818 Notebook.prototype.set_autosave_interval = function (interval) {
1822 Notebook.prototype.set_autosave_interval = function (interval) {
1819 var that = this;
1823 var that = this;
1820 // clear previous interval, so we don't get simultaneous timers
1824 // clear previous interval, so we don't get simultaneous timers
1821 if (this.autosave_timer) {
1825 if (this.autosave_timer) {
1822 clearInterval(this.autosave_timer);
1826 clearInterval(this.autosave_timer);
1823 }
1827 }
1824
1828
1825 this.autosave_interval = this.minimum_autosave_interval = interval;
1829 this.autosave_interval = this.minimum_autosave_interval = interval;
1826 if (interval) {
1830 if (interval) {
1827 this.autosave_timer = setInterval(function() {
1831 this.autosave_timer = setInterval(function() {
1828 if (that.dirty) {
1832 if (that.dirty) {
1829 that.save_notebook();
1833 that.save_notebook();
1830 }
1834 }
1831 }, interval);
1835 }, interval);
1832 this.events.trigger("autosave_enabled.Notebook", interval);
1836 this.events.trigger("autosave_enabled.Notebook", interval);
1833 } else {
1837 } else {
1834 this.autosave_timer = null;
1838 this.autosave_timer = null;
1835 this.events.trigger("autosave_disabled.Notebook");
1839 this.events.trigger("autosave_disabled.Notebook");
1836 }
1840 }
1837 };
1841 };
1838
1842
1839 /**
1843 /**
1840 * Save this notebook on the server. This becomes a notebook instance's
1844 * Save this notebook on the server. This becomes a notebook instance's
1841 * .save_notebook method *after* the entire notebook has been loaded.
1845 * .save_notebook method *after* the entire notebook has been loaded.
1842 *
1846 *
1843 * @method save_notebook
1847 * @method save_notebook
1844 */
1848 */
1845 Notebook.prototype.save_notebook = function (extra_settings) {
1849 Notebook.prototype.save_notebook = function (extra_settings) {
1846 // Create a JSON model to be sent to the server.
1850 // Create a JSON model to be sent to the server.
1847 var model = {};
1851 var model = {};
1848 model.name = this.notebook_name;
1852 model.name = this.notebook_name;
1849 model.path = this.notebook_path;
1853 model.path = this.notebook_path;
1850 model.content = this.toJSON();
1854 model.content = this.toJSON();
1851 model.content.nbformat = this.nbformat;
1855 model.content.nbformat = this.nbformat;
1852 model.content.nbformat_minor = this.nbformat_minor;
1856 model.content.nbformat_minor = this.nbformat_minor;
1853 // time the ajax call for autosave tuning purposes.
1857 // time the ajax call for autosave tuning purposes.
1854 var start = new Date().getTime();
1858 var start = new Date().getTime();
1855 // We do the call with settings so we can set cache to false.
1859 // We do the call with settings so we can set cache to false.
1856 var settings = {
1860 var settings = {
1857 processData : false,
1861 processData : false,
1858 cache : false,
1862 cache : false,
1859 type : "PUT",
1863 type : "PUT",
1860 data : JSON.stringify(model),
1864 data : JSON.stringify(model),
1861 headers : {'Content-Type': 'application/json'},
1865 headers : {'Content-Type': 'application/json'},
1862 success : $.proxy(this.save_notebook_success, this, start),
1866 success : $.proxy(this.save_notebook_success, this, start),
1863 error : $.proxy(this.save_notebook_error, this)
1867 error : $.proxy(this.save_notebook_error, this)
1864 };
1868 };
1865 if (extra_settings) {
1869 if (extra_settings) {
1866 for (var key in extra_settings) {
1870 for (var key in extra_settings) {
1867 settings[key] = extra_settings[key];
1871 settings[key] = extra_settings[key];
1868 }
1872 }
1869 }
1873 }
1870 this.events.trigger('notebook_saving.Notebook');
1874 this.events.trigger('notebook_saving.Notebook');
1871 var url = utils.url_join_encode(
1875 var url = utils.url_join_encode(
1872 this.base_url,
1876 this.base_url,
1873 'api/notebooks',
1877 'api/notebooks',
1874 this.notebook_path,
1878 this.notebook_path,
1875 this.notebook_name
1879 this.notebook_name
1876 );
1880 );
1877 $.ajax(url, settings);
1881 $.ajax(url, settings);
1878 };
1882 };
1879
1883
1880 /**
1884 /**
1881 * Success callback for saving a notebook.
1885 * Success callback for saving a notebook.
1882 *
1886 *
1883 * @method save_notebook_success
1887 * @method save_notebook_success
1884 * @param {Integer} start the time when the save request started
1888 * @param {Integer} start the time when the save request started
1885 * @param {Object} data JSON representation of a notebook
1889 * @param {Object} data JSON representation of a notebook
1886 * @param {String} status Description of response status
1890 * @param {String} status Description of response status
1887 * @param {jqXHR} xhr jQuery Ajax object
1891 * @param {jqXHR} xhr jQuery Ajax object
1888 */
1892 */
1889 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1893 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1890 this.set_dirty(false);
1894 this.set_dirty(false);
1891 this.events.trigger('notebook_saved.Notebook');
1895 this.events.trigger('notebook_saved.Notebook');
1892 this._update_autosave_interval(start);
1896 this._update_autosave_interval(start);
1893 if (this._checkpoint_after_save) {
1897 if (this._checkpoint_after_save) {
1894 this.create_checkpoint();
1898 this.create_checkpoint();
1895 this._checkpoint_after_save = false;
1899 this._checkpoint_after_save = false;
1896 }
1900 }
1897 };
1901 };
1898
1902
1899 /**
1903 /**
1900 * update the autosave interval based on how long the last save took
1904 * update the autosave interval based on how long the last save took
1901 *
1905 *
1902 * @method _update_autosave_interval
1906 * @method _update_autosave_interval
1903 * @param {Integer} timestamp when the save request started
1907 * @param {Integer} timestamp when the save request started
1904 */
1908 */
1905 Notebook.prototype._update_autosave_interval = function (start) {
1909 Notebook.prototype._update_autosave_interval = function (start) {
1906 var duration = (new Date().getTime() - start);
1910 var duration = (new Date().getTime() - start);
1907 if (this.autosave_interval) {
1911 if (this.autosave_interval) {
1908 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1912 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1909 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1913 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1910 // round to 10 seconds, otherwise we will be setting a new interval too often
1914 // round to 10 seconds, otherwise we will be setting a new interval too often
1911 interval = 10000 * Math.round(interval / 10000);
1915 interval = 10000 * Math.round(interval / 10000);
1912 // set new interval, if it's changed
1916 // set new interval, if it's changed
1913 if (interval != this.autosave_interval) {
1917 if (interval != this.autosave_interval) {
1914 this.set_autosave_interval(interval);
1918 this.set_autosave_interval(interval);
1915 }
1919 }
1916 }
1920 }
1917 };
1921 };
1918
1922
1919 /**
1923 /**
1920 * Failure callback for saving a notebook.
1924 * Failure callback for saving a notebook.
1921 *
1925 *
1922 * @method save_notebook_error
1926 * @method save_notebook_error
1923 * @param {jqXHR} xhr jQuery Ajax object
1927 * @param {jqXHR} xhr jQuery Ajax object
1924 * @param {String} status Description of response status
1928 * @param {String} status Description of response status
1925 * @param {String} error HTTP error message
1929 * @param {String} error HTTP error message
1926 */
1930 */
1927 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1931 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1928 this.events.trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1932 this.events.trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1929 };
1933 };
1930
1934
1931 /**
1935 /**
1932 * Explicitly trust the output of this notebook.
1936 * Explicitly trust the output of this notebook.
1933 *
1937 *
1934 * @method trust_notebook
1938 * @method trust_notebook
1935 */
1939 */
1936 Notebook.prototype.trust_notebook = function (extra_settings) {
1940 Notebook.prototype.trust_notebook = function (extra_settings) {
1937 var body = $("<div>").append($("<p>")
1941 var body = $("<div>").append($("<p>")
1938 .text("A trusted IPython notebook may execute hidden malicious code ")
1942 .text("A trusted IPython notebook may execute hidden malicious code ")
1939 .append($("<strong>")
1943 .append($("<strong>")
1940 .append(
1944 .append(
1941 $("<em>").text("when you open it")
1945 $("<em>").text("when you open it")
1942 )
1946 )
1943 ).append(".").append(
1947 ).append(".").append(
1944 " Selecting trust will immediately reload this notebook in a trusted state."
1948 " Selecting trust will immediately reload this notebook in a trusted state."
1945 ).append(
1949 ).append(
1946 " For more information, see the "
1950 " For more information, see the "
1947 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
1951 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
1948 .text("IPython security documentation")
1952 .text("IPython security documentation")
1949 ).append(".")
1953 ).append(".")
1950 );
1954 );
1951
1955
1952 var nb = this;
1956 var nb = this;
1953 dialog.modal({
1957 dialog.modal({
1954 notebook: this,
1958 notebook: this,
1955 keyboard_manager: this.keyboard_manager,
1959 keyboard_manager: this.keyboard_manager,
1956 title: "Trust this notebook?",
1960 title: "Trust this notebook?",
1957 body: body,
1961 body: body,
1958
1962
1959 buttons: {
1963 buttons: {
1960 Cancel : {},
1964 Cancel : {},
1961 Trust : {
1965 Trust : {
1962 class : "btn-danger",
1966 class : "btn-danger",
1963 click : function () {
1967 click : function () {
1964 var cells = nb.get_cells();
1968 var cells = nb.get_cells();
1965 for (var i = 0; i < cells.length; i++) {
1969 for (var i = 0; i < cells.length; i++) {
1966 var cell = cells[i];
1970 var cell = cells[i];
1967 if (cell.cell_type == 'code') {
1971 if (cell.cell_type == 'code') {
1968 cell.output_area.trusted = true;
1972 cell.output_area.trusted = true;
1969 }
1973 }
1970 }
1974 }
1971 this.events.on('notebook_saved.Notebook', function () {
1975 this.events.on('notebook_saved.Notebook', function () {
1972 window.location.reload();
1976 window.location.reload();
1973 });
1977 });
1974 nb.save_notebook();
1978 nb.save_notebook();
1975 }
1979 }
1976 }
1980 }
1977 }
1981 }
1978 });
1982 });
1979 };
1983 };
1980
1984
1981 Notebook.prototype.new_notebook = function(){
1985 Notebook.prototype.new_notebook = function(){
1982 var path = this.notebook_path;
1986 var path = this.notebook_path;
1983 var base_url = this.base_url;
1987 var base_url = this.base_url;
1984 var settings = {
1988 var settings = {
1985 processData : false,
1989 processData : false,
1986 cache : false,
1990 cache : false,
1987 type : "POST",
1991 type : "POST",
1988 dataType : "json",
1992 dataType : "json",
1989 async : false,
1993 async : false,
1990 success : function (data, status, xhr){
1994 success : function (data, status, xhr){
1991 var notebook_name = data.name;
1995 var notebook_name = data.name;
1992 window.open(
1996 window.open(
1993 utils.url_join_encode(
1997 utils.url_join_encode(
1994 base_url,
1998 base_url,
1995 'notebooks',
1999 'notebooks',
1996 path,
2000 path,
1997 notebook_name
2001 notebook_name
1998 ),
2002 ),
1999 '_blank'
2003 '_blank'
2000 );
2004 );
2001 },
2005 },
2002 error : utils.log_ajax_error,
2006 error : utils.log_ajax_error,
2003 };
2007 };
2004 var url = utils.url_join_encode(
2008 var url = utils.url_join_encode(
2005 base_url,
2009 base_url,
2006 'api/notebooks',
2010 'api/notebooks',
2007 path
2011 path
2008 );
2012 );
2009 $.ajax(url,settings);
2013 $.ajax(url,settings);
2010 };
2014 };
2011
2015
2012
2016
2013 Notebook.prototype.copy_notebook = function(){
2017 Notebook.prototype.copy_notebook = function(){
2014 var path = this.notebook_path;
2018 var path = this.notebook_path;
2015 var base_url = this.base_url;
2019 var base_url = this.base_url;
2016 var settings = {
2020 var settings = {
2017 processData : false,
2021 processData : false,
2018 cache : false,
2022 cache : false,
2019 type : "POST",
2023 type : "POST",
2020 dataType : "json",
2024 dataType : "json",
2021 data : JSON.stringify({copy_from : this.notebook_name}),
2025 data : JSON.stringify({copy_from : this.notebook_name}),
2022 async : false,
2026 async : false,
2023 success : function (data, status, xhr) {
2027 success : function (data, status, xhr) {
2024 window.open(utils.url_join_encode(
2028 window.open(utils.url_join_encode(
2025 base_url,
2029 base_url,
2026 'notebooks',
2030 'notebooks',
2027 data.path,
2031 data.path,
2028 data.name
2032 data.name
2029 ), '_blank');
2033 ), '_blank');
2030 },
2034 },
2031 error : utils.log_ajax_error,
2035 error : utils.log_ajax_error,
2032 };
2036 };
2033 var url = utils.url_join_encode(
2037 var url = utils.url_join_encode(
2034 base_url,
2038 base_url,
2035 'api/notebooks',
2039 'api/notebooks',
2036 path
2040 path
2037 );
2041 );
2038 $.ajax(url,settings);
2042 $.ajax(url,settings);
2039 };
2043 };
2040
2044
2041 Notebook.prototype.rename = function (nbname) {
2045 Notebook.prototype.rename = function (nbname) {
2042 var that = this;
2046 var that = this;
2043 if (!nbname.match(/\.ipynb$/)) {
2047 if (!nbname.match(/\.ipynb$/)) {
2044 nbname = nbname + ".ipynb";
2048 nbname = nbname + ".ipynb";
2045 }
2049 }
2046 var data = {name: nbname};
2050 var data = {name: nbname};
2047 var settings = {
2051 var settings = {
2048 processData : false,
2052 processData : false,
2049 cache : false,
2053 cache : false,
2050 type : "PATCH",
2054 type : "PATCH",
2051 data : JSON.stringify(data),
2055 data : JSON.stringify(data),
2052 dataType: "json",
2056 dataType: "json",
2053 headers : {'Content-Type': 'application/json'},
2057 headers : {'Content-Type': 'application/json'},
2054 success : $.proxy(that.rename_success, this),
2058 success : $.proxy(that.rename_success, this),
2055 error : $.proxy(that.rename_error, this)
2059 error : $.proxy(that.rename_error, this)
2056 };
2060 };
2057 this.events.trigger('rename_notebook.Notebook', data);
2061 this.events.trigger('rename_notebook.Notebook', data);
2058 var url = utils.url_join_encode(
2062 var url = utils.url_join_encode(
2059 this.base_url,
2063 this.base_url,
2060 'api/notebooks',
2064 'api/notebooks',
2061 this.notebook_path,
2065 this.notebook_path,
2062 this.notebook_name
2066 this.notebook_name
2063 );
2067 );
2064 $.ajax(url, settings);
2068 $.ajax(url, settings);
2065 };
2069 };
2066
2070
2067 Notebook.prototype.delete = function () {
2071 Notebook.prototype.delete = function () {
2068 var that = this;
2072 var that = this;
2069 var settings = {
2073 var settings = {
2070 processData : false,
2074 processData : false,
2071 cache : false,
2075 cache : false,
2072 type : "DELETE",
2076 type : "DELETE",
2073 dataType: "json",
2077 dataType: "json",
2074 error : utils.log_ajax_error,
2078 error : utils.log_ajax_error,
2075 };
2079 };
2076 var url = utils.url_join_encode(
2080 var url = utils.url_join_encode(
2077 this.base_url,
2081 this.base_url,
2078 'api/notebooks',
2082 'api/notebooks',
2079 this.notebook_path,
2083 this.notebook_path,
2080 this.notebook_name
2084 this.notebook_name
2081 );
2085 );
2082 $.ajax(url, settings);
2086 $.ajax(url, settings);
2083 };
2087 };
2084
2088
2085
2089
2086 Notebook.prototype.rename_success = function (json, status, xhr) {
2090 Notebook.prototype.rename_success = function (json, status, xhr) {
2087 var name = this.notebook_name = json.name;
2091 var name = this.notebook_name = json.name;
2088 var path = json.path;
2092 var path = json.path;
2089 this.session.rename_notebook(name, path);
2093 this.session.rename_notebook(name, path);
2090 this.events.trigger('notebook_renamed.Notebook', json);
2094 this.events.trigger('notebook_renamed.Notebook', json);
2091 };
2095 };
2092
2096
2093 Notebook.prototype.rename_error = function (xhr, status, error) {
2097 Notebook.prototype.rename_error = function (xhr, status, error) {
2094 var that = this;
2098 var that = this;
2095 var dialog_body = $('<div/>').append(
2099 var dialog_body = $('<div/>').append(
2096 $("<p/>").addClass("rename-message")
2100 $("<p/>").addClass("rename-message")
2097 .text('This notebook name already exists.')
2101 .text('This notebook name already exists.')
2098 );
2102 );
2099 this.events.trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
2103 this.events.trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
2100 dialog.modal({
2104 dialog.modal({
2101 notebook: this,
2105 notebook: this,
2102 keyboard_manager: this.keyboard_manager,
2106 keyboard_manager: this.keyboard_manager,
2103 title: "Notebook Rename Error!",
2107 title: "Notebook Rename Error!",
2104 body: dialog_body,
2108 body: dialog_body,
2105 buttons : {
2109 buttons : {
2106 "Cancel": {},
2110 "Cancel": {},
2107 "OK": {
2111 "OK": {
2108 class: "btn-primary",
2112 class: "btn-primary",
2109 click: function () {
2113 click: function () {
2110 this.save_widget.rename_notebook({notebook:that});
2114 this.save_widget.rename_notebook({notebook:that});
2111 }}
2115 }}
2112 },
2116 },
2113 open : function (event, ui) {
2117 open : function (event, ui) {
2114 var that = $(this);
2118 var that = $(this);
2115 // Upon ENTER, click the OK button.
2119 // Upon ENTER, click the OK button.
2116 that.find('input[type="text"]').keydown(function (event, ui) {
2120 that.find('input[type="text"]').keydown(function (event, ui) {
2117 if (event.which === this.keyboard.keycodes.enter) {
2121 if (event.which === this.keyboard.keycodes.enter) {
2118 that.find('.btn-primary').first().click();
2122 that.find('.btn-primary').first().click();
2119 }
2123 }
2120 });
2124 });
2121 that.find('input[type="text"]').focus();
2125 that.find('input[type="text"]').focus();
2122 }
2126 }
2123 });
2127 });
2124 };
2128 };
2125
2129
2126 /**
2130 /**
2127 * Request a notebook's data from the server.
2131 * Request a notebook's data from the server.
2128 *
2132 *
2129 * @method load_notebook
2133 * @method load_notebook
2130 * @param {String} notebook_name and path A notebook to load
2134 * @param {String} notebook_name and path A notebook to load
2131 */
2135 */
2132 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2136 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2133 var that = this;
2137 var that = this;
2134 this.notebook_name = notebook_name;
2138 this.notebook_name = notebook_name;
2135 this.notebook_path = notebook_path;
2139 this.notebook_path = notebook_path;
2136 // We do the call with settings so we can set cache to false.
2140 // We do the call with settings so we can set cache to false.
2137 var settings = {
2141 var settings = {
2138 processData : false,
2142 processData : false,
2139 cache : false,
2143 cache : false,
2140 type : "GET",
2144 type : "GET",
2141 dataType : "json",
2145 dataType : "json",
2142 success : $.proxy(this.load_notebook_success,this),
2146 success : $.proxy(this.load_notebook_success,this),
2143 error : $.proxy(this.load_notebook_error,this),
2147 error : $.proxy(this.load_notebook_error,this),
2144 };
2148 };
2145 this.events.trigger('notebook_loading.Notebook');
2149 this.events.trigger('notebook_loading.Notebook');
2146 var url = utils.url_join_encode(
2150 var url = utils.url_join_encode(
2147 this.base_url,
2151 this.base_url,
2148 'api/notebooks',
2152 'api/notebooks',
2149 this.notebook_path,
2153 this.notebook_path,
2150 this.notebook_name
2154 this.notebook_name
2151 );
2155 );
2152 $.ajax(url, settings);
2156 $.ajax(url, settings);
2153 };
2157 };
2154
2158
2155 /**
2159 /**
2156 * Success callback for loading a notebook from the server.
2160 * Success callback for loading a notebook from the server.
2157 *
2161 *
2158 * Load notebook data from the JSON response.
2162 * Load notebook data from the JSON response.
2159 *
2163 *
2160 * @method load_notebook_success
2164 * @method load_notebook_success
2161 * @param {Object} data JSON representation of a notebook
2165 * @param {Object} data JSON representation of a notebook
2162 * @param {String} status Description of response status
2166 * @param {String} status Description of response status
2163 * @param {jqXHR} xhr jQuery Ajax object
2167 * @param {jqXHR} xhr jQuery Ajax object
2164 */
2168 */
2165 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2169 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2166 this.fromJSON(data);
2170 this.fromJSON(data);
2167 if (this.ncells() === 0) {
2171 if (this.ncells() === 0) {
2168 this.insert_cell_below('code');
2172 this.insert_cell_below('code');
2169 this.edit_mode(0);
2173 this.edit_mode(0);
2170 } else {
2174 } else {
2171 this.select(0);
2175 this.select(0);
2172 this.handle_command_mode(this.get_cell(0));
2176 this.handle_command_mode(this.get_cell(0));
2173 }
2177 }
2174 this.set_dirty(false);
2178 this.set_dirty(false);
2175 this.scroll_to_top();
2179 this.scroll_to_top();
2176 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
2180 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
2177 var msg = "This notebook has been converted from an older " +
2181 var msg = "This notebook has been converted from an older " +
2178 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
2182 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
2179 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
2183 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
2180 "newer notebook format will be used and older versions of IPython " +
2184 "newer notebook format will be used and older versions of IPython " +
2181 "may not be able to read it. To keep the older version, close the " +
2185 "may not be able to read it. To keep the older version, close the " +
2182 "notebook without saving it.";
2186 "notebook without saving it.";
2183 dialog.modal({
2187 dialog.modal({
2184 notebook: this,
2188 notebook: this,
2185 keyboard_manager: this.keyboard_manager,
2189 keyboard_manager: this.keyboard_manager,
2186 title : "Notebook converted",
2190 title : "Notebook converted",
2187 body : msg,
2191 body : msg,
2188 buttons : {
2192 buttons : {
2189 OK : {
2193 OK : {
2190 class : "btn-primary"
2194 class : "btn-primary"
2191 }
2195 }
2192 }
2196 }
2193 });
2197 });
2194 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
2198 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
2195 var that = this;
2199 var that = this;
2196 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
2200 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
2197 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
2201 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
2198 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2202 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2199 this_vs + ". You can still work with this notebook, but some features " +
2203 this_vs + ". You can still work with this notebook, but some features " +
2200 "introduced in later notebook versions may not be available.";
2204 "introduced in later notebook versions may not be available.";
2201
2205
2202 dialog.modal({
2206 dialog.modal({
2203 notebook: this,
2207 notebook: this,
2204 keyboard_manager: this.keyboard_manager,
2208 keyboard_manager: this.keyboard_manager,
2205 title : "Newer Notebook",
2209 title : "Newer Notebook",
2206 body : msg,
2210 body : msg,
2207 buttons : {
2211 buttons : {
2208 OK : {
2212 OK : {
2209 class : "btn-danger"
2213 class : "btn-danger"
2210 }
2214 }
2211 }
2215 }
2212 });
2216 });
2213
2217
2214 }
2218 }
2215
2219
2216 // Create the session after the notebook is completely loaded to prevent
2220 // Create the session after the notebook is completely loaded to prevent
2217 // code execution upon loading, which is a security risk.
2221 // code execution upon loading, which is a security risk.
2218 if (this.session === null) {
2222 if (this.session === null) {
2219 this.start_session();
2223 this.start_session();
2220 }
2224 }
2221 // load our checkpoint list
2225 // load our checkpoint list
2222 this.list_checkpoints();
2226 this.list_checkpoints();
2223
2227
2224 // load toolbar state
2228 // load toolbar state
2225 if (this.metadata.celltoolbar) {
2229 if (this.metadata.celltoolbar) {
2226 celltoolbar.CellToolbar.global_show();
2230 celltoolbar.CellToolbar.global_show();
2227 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar, this.events);
2231 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar, this.events);
2228 } else {
2232 } else {
2229 celltoolbar.CellToolbar.global_hide();
2233 celltoolbar.CellToolbar.global_hide();
2230 }
2234 }
2231
2235
2232 // now that we're fully loaded, it is safe to restore save functionality
2236 // now that we're fully loaded, it is safe to restore save functionality
2233 delete(this.save_notebook);
2237 delete(this.save_notebook);
2234 this.events.trigger('notebook_loaded.Notebook');
2238 this.events.trigger('notebook_loaded.Notebook');
2235 };
2239 };
2236
2240
2237 /**
2241 /**
2238 * Failure callback for loading a notebook from the server.
2242 * Failure callback for loading a notebook from the server.
2239 *
2243 *
2240 * @method load_notebook_error
2244 * @method load_notebook_error
2241 * @param {jqXHR} xhr jQuery Ajax object
2245 * @param {jqXHR} xhr jQuery Ajax object
2242 * @param {String} status Description of response status
2246 * @param {String} status Description of response status
2243 * @param {String} error HTTP error message
2247 * @param {String} error HTTP error message
2244 */
2248 */
2245 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2249 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2246 this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2250 this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2247 var msg;
2251 var msg;
2248 if (xhr.status === 400) {
2252 if (xhr.status === 400) {
2249 msg = error;
2253 msg = error;
2250 } else if (xhr.status === 500) {
2254 } else if (xhr.status === 500) {
2251 msg = "An unknown error occurred while loading this notebook. " +
2255 msg = "An unknown error occurred while loading this notebook. " +
2252 "This version can load notebook formats " +
2256 "This version can load notebook formats " +
2253 "v" + this.nbformat + " or earlier.";
2257 "v" + this.nbformat + " or earlier.";
2254 }
2258 }
2255 dialog.modal({
2259 dialog.modal({
2256 notebook: this,
2260 notebook: this,
2257 keyboard_manager: this.keyboard_manager,
2261 keyboard_manager: this.keyboard_manager,
2258 title: "Error loading notebook",
2262 title: "Error loading notebook",
2259 body : msg,
2263 body : msg,
2260 buttons : {
2264 buttons : {
2261 "OK": {}
2265 "OK": {}
2262 }
2266 }
2263 });
2267 });
2264 };
2268 };
2265
2269
2266 /********************* checkpoint-related *********************/
2270 /********************* checkpoint-related *********************/
2267
2271
2268 /**
2272 /**
2269 * Save the notebook then immediately create a checkpoint.
2273 * Save the notebook then immediately create a checkpoint.
2270 *
2274 *
2271 * @method save_checkpoint
2275 * @method save_checkpoint
2272 */
2276 */
2273 Notebook.prototype.save_checkpoint = function () {
2277 Notebook.prototype.save_checkpoint = function () {
2274 this._checkpoint_after_save = true;
2278 this._checkpoint_after_save = true;
2275 this.save_notebook();
2279 this.save_notebook();
2276 };
2280 };
2277
2281
2278 /**
2282 /**
2279 * Add a checkpoint for this notebook.
2283 * Add a checkpoint for this notebook.
2280 * for use as a callback from checkpoint creation.
2284 * for use as a callback from checkpoint creation.
2281 *
2285 *
2282 * @method add_checkpoint
2286 * @method add_checkpoint
2283 */
2287 */
2284 Notebook.prototype.add_checkpoint = function (checkpoint) {
2288 Notebook.prototype.add_checkpoint = function (checkpoint) {
2285 var found = false;
2289 var found = false;
2286 for (var i = 0; i < this.checkpoints.length; i++) {
2290 for (var i = 0; i < this.checkpoints.length; i++) {
2287 var existing = this.checkpoints[i];
2291 var existing = this.checkpoints[i];
2288 if (existing.id == checkpoint.id) {
2292 if (existing.id == checkpoint.id) {
2289 found = true;
2293 found = true;
2290 this.checkpoints[i] = checkpoint;
2294 this.checkpoints[i] = checkpoint;
2291 break;
2295 break;
2292 }
2296 }
2293 }
2297 }
2294 if (!found) {
2298 if (!found) {
2295 this.checkpoints.push(checkpoint);
2299 this.checkpoints.push(checkpoint);
2296 }
2300 }
2297 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2301 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2298 };
2302 };
2299
2303
2300 /**
2304 /**
2301 * List checkpoints for this notebook.
2305 * List checkpoints for this notebook.
2302 *
2306 *
2303 * @method list_checkpoints
2307 * @method list_checkpoints
2304 */
2308 */
2305 Notebook.prototype.list_checkpoints = function () {
2309 Notebook.prototype.list_checkpoints = function () {
2306 var url = utils.url_join_encode(
2310 var url = utils.url_join_encode(
2307 this.base_url,
2311 this.base_url,
2308 'api/notebooks',
2312 'api/notebooks',
2309 this.notebook_path,
2313 this.notebook_path,
2310 this.notebook_name,
2314 this.notebook_name,
2311 'checkpoints'
2315 'checkpoints'
2312 );
2316 );
2313 $.get(url).done(
2317 $.get(url).done(
2314 $.proxy(this.list_checkpoints_success, this)
2318 $.proxy(this.list_checkpoints_success, this)
2315 ).fail(
2319 ).fail(
2316 $.proxy(this.list_checkpoints_error, this)
2320 $.proxy(this.list_checkpoints_error, this)
2317 );
2321 );
2318 };
2322 };
2319
2323
2320 /**
2324 /**
2321 * Success callback for listing checkpoints.
2325 * Success callback for listing checkpoints.
2322 *
2326 *
2323 * @method list_checkpoint_success
2327 * @method list_checkpoint_success
2324 * @param {Object} data JSON representation of a checkpoint
2328 * @param {Object} data JSON representation of a checkpoint
2325 * @param {String} status Description of response status
2329 * @param {String} status Description of response status
2326 * @param {jqXHR} xhr jQuery Ajax object
2330 * @param {jqXHR} xhr jQuery Ajax object
2327 */
2331 */
2328 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2332 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2329 data = $.parseJSON(data);
2333 data = $.parseJSON(data);
2330 this.checkpoints = data;
2334 this.checkpoints = data;
2331 if (data.length) {
2335 if (data.length) {
2332 this.last_checkpoint = data[data.length - 1];
2336 this.last_checkpoint = data[data.length - 1];
2333 } else {
2337 } else {
2334 this.last_checkpoint = null;
2338 this.last_checkpoint = null;
2335 }
2339 }
2336 this.events.trigger('checkpoints_listed.Notebook', [data]);
2340 this.events.trigger('checkpoints_listed.Notebook', [data]);
2337 };
2341 };
2338
2342
2339 /**
2343 /**
2340 * Failure callback for listing a checkpoint.
2344 * Failure callback for listing a checkpoint.
2341 *
2345 *
2342 * @method list_checkpoint_error
2346 * @method list_checkpoint_error
2343 * @param {jqXHR} xhr jQuery Ajax object
2347 * @param {jqXHR} xhr jQuery Ajax object
2344 * @param {String} status Description of response status
2348 * @param {String} status Description of response status
2345 * @param {String} error_msg HTTP error message
2349 * @param {String} error_msg HTTP error message
2346 */
2350 */
2347 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2351 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2348 this.events.trigger('list_checkpoints_failed.Notebook');
2352 this.events.trigger('list_checkpoints_failed.Notebook');
2349 };
2353 };
2350
2354
2351 /**
2355 /**
2352 * Create a checkpoint of this notebook on the server from the most recent save.
2356 * Create a checkpoint of this notebook on the server from the most recent save.
2353 *
2357 *
2354 * @method create_checkpoint
2358 * @method create_checkpoint
2355 */
2359 */
2356 Notebook.prototype.create_checkpoint = function () {
2360 Notebook.prototype.create_checkpoint = function () {
2357 var url = utils.url_join_encode(
2361 var url = utils.url_join_encode(
2358 this.base_url,
2362 this.base_url,
2359 'api/notebooks',
2363 'api/notebooks',
2360 this.notebook_path,
2364 this.notebook_path,
2361 this.notebook_name,
2365 this.notebook_name,
2362 'checkpoints'
2366 'checkpoints'
2363 );
2367 );
2364 $.post(url).done(
2368 $.post(url).done(
2365 $.proxy(this.create_checkpoint_success, this)
2369 $.proxy(this.create_checkpoint_success, this)
2366 ).fail(
2370 ).fail(
2367 $.proxy(this.create_checkpoint_error, this)
2371 $.proxy(this.create_checkpoint_error, this)
2368 );
2372 );
2369 };
2373 };
2370
2374
2371 /**
2375 /**
2372 * Success callback for creating a checkpoint.
2376 * Success callback for creating a checkpoint.
2373 *
2377 *
2374 * @method create_checkpoint_success
2378 * @method create_checkpoint_success
2375 * @param {Object} data JSON representation of a checkpoint
2379 * @param {Object} data JSON representation of a checkpoint
2376 * @param {String} status Description of response status
2380 * @param {String} status Description of response status
2377 * @param {jqXHR} xhr jQuery Ajax object
2381 * @param {jqXHR} xhr jQuery Ajax object
2378 */
2382 */
2379 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2383 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2380 data = $.parseJSON(data);
2384 data = $.parseJSON(data);
2381 this.add_checkpoint(data);
2385 this.add_checkpoint(data);
2382 this.events.trigger('checkpoint_created.Notebook', data);
2386 this.events.trigger('checkpoint_created.Notebook', data);
2383 };
2387 };
2384
2388
2385 /**
2389 /**
2386 * Failure callback for creating a checkpoint.
2390 * Failure callback for creating a checkpoint.
2387 *
2391 *
2388 * @method create_checkpoint_error
2392 * @method create_checkpoint_error
2389 * @param {jqXHR} xhr jQuery Ajax object
2393 * @param {jqXHR} xhr jQuery Ajax object
2390 * @param {String} status Description of response status
2394 * @param {String} status Description of response status
2391 * @param {String} error_msg HTTP error message
2395 * @param {String} error_msg HTTP error message
2392 */
2396 */
2393 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2397 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2394 this.events.trigger('checkpoint_failed.Notebook');
2398 this.events.trigger('checkpoint_failed.Notebook');
2395 };
2399 };
2396
2400
2397 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2401 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2398 var that = this;
2402 var that = this;
2399 checkpoint = checkpoint || this.last_checkpoint;
2403 checkpoint = checkpoint || this.last_checkpoint;
2400 if ( ! checkpoint ) {
2404 if ( ! checkpoint ) {
2401 console.log("restore dialog, but no checkpoint to restore to!");
2405 console.log("restore dialog, but no checkpoint to restore to!");
2402 return;
2406 return;
2403 }
2407 }
2404 var body = $('<div/>').append(
2408 var body = $('<div/>').append(
2405 $('<p/>').addClass("p-space").text(
2409 $('<p/>').addClass("p-space").text(
2406 "Are you sure you want to revert the notebook to " +
2410 "Are you sure you want to revert the notebook to " +
2407 "the latest checkpoint?"
2411 "the latest checkpoint?"
2408 ).append(
2412 ).append(
2409 $("<strong/>").text(
2413 $("<strong/>").text(
2410 " This cannot be undone."
2414 " This cannot be undone."
2411 )
2415 )
2412 )
2416 )
2413 ).append(
2417 ).append(
2414 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2418 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2415 ).append(
2419 ).append(
2416 $('<p/>').addClass("p-space").text(
2420 $('<p/>').addClass("p-space").text(
2417 Date(checkpoint.last_modified)
2421 Date(checkpoint.last_modified)
2418 ).css("text-align", "center")
2422 ).css("text-align", "center")
2419 );
2423 );
2420
2424
2421 dialog.modal({
2425 dialog.modal({
2422 notebook: this,
2426 notebook: this,
2423 keyboard_manager: this.keyboard_manager,
2427 keyboard_manager: this.keyboard_manager,
2424 title : "Revert notebook to checkpoint",
2428 title : "Revert notebook to checkpoint",
2425 body : body,
2429 body : body,
2426 buttons : {
2430 buttons : {
2427 Revert : {
2431 Revert : {
2428 class : "btn-danger",
2432 class : "btn-danger",
2429 click : function () {
2433 click : function () {
2430 that.restore_checkpoint(checkpoint.id);
2434 that.restore_checkpoint(checkpoint.id);
2431 }
2435 }
2432 },
2436 },
2433 Cancel : {}
2437 Cancel : {}
2434 }
2438 }
2435 });
2439 });
2436 };
2440 };
2437
2441
2438 /**
2442 /**
2439 * Restore the notebook to a checkpoint state.
2443 * Restore the notebook to a checkpoint state.
2440 *
2444 *
2441 * @method restore_checkpoint
2445 * @method restore_checkpoint
2442 * @param {String} checkpoint ID
2446 * @param {String} checkpoint ID
2443 */
2447 */
2444 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2448 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2445 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2449 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2446 var url = utils.url_join_encode(
2450 var url = utils.url_join_encode(
2447 this.base_url,
2451 this.base_url,
2448 'api/notebooks',
2452 'api/notebooks',
2449 this.notebook_path,
2453 this.notebook_path,
2450 this.notebook_name,
2454 this.notebook_name,
2451 'checkpoints',
2455 'checkpoints',
2452 checkpoint
2456 checkpoint
2453 );
2457 );
2454 $.post(url).done(
2458 $.post(url).done(
2455 $.proxy(this.restore_checkpoint_success, this)
2459 $.proxy(this.restore_checkpoint_success, this)
2456 ).fail(
2460 ).fail(
2457 $.proxy(this.restore_checkpoint_error, this)
2461 $.proxy(this.restore_checkpoint_error, this)
2458 );
2462 );
2459 };
2463 };
2460
2464
2461 /**
2465 /**
2462 * Success callback for restoring a notebook to a checkpoint.
2466 * Success callback for restoring a notebook to a checkpoint.
2463 *
2467 *
2464 * @method restore_checkpoint_success
2468 * @method restore_checkpoint_success
2465 * @param {Object} data (ignored, should be empty)
2469 * @param {Object} data (ignored, should be empty)
2466 * @param {String} status Description of response status
2470 * @param {String} status Description of response status
2467 * @param {jqXHR} xhr jQuery Ajax object
2471 * @param {jqXHR} xhr jQuery Ajax object
2468 */
2472 */
2469 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2473 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2470 this.events.trigger('checkpoint_restored.Notebook');
2474 this.events.trigger('checkpoint_restored.Notebook');
2471 this.load_notebook(this.notebook_name, this.notebook_path);
2475 this.load_notebook(this.notebook_name, this.notebook_path);
2472 };
2476 };
2473
2477
2474 /**
2478 /**
2475 * Failure callback for restoring a notebook to a checkpoint.
2479 * Failure callback for restoring a notebook to a checkpoint.
2476 *
2480 *
2477 * @method restore_checkpoint_error
2481 * @method restore_checkpoint_error
2478 * @param {jqXHR} xhr jQuery Ajax object
2482 * @param {jqXHR} xhr jQuery Ajax object
2479 * @param {String} status Description of response status
2483 * @param {String} status Description of response status
2480 * @param {String} error_msg HTTP error message
2484 * @param {String} error_msg HTTP error message
2481 */
2485 */
2482 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2486 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2483 this.events.trigger('checkpoint_restore_failed.Notebook');
2487 this.events.trigger('checkpoint_restore_failed.Notebook');
2484 };
2488 };
2485
2489
2486 /**
2490 /**
2487 * Delete a notebook checkpoint.
2491 * Delete a notebook checkpoint.
2488 *
2492 *
2489 * @method delete_checkpoint
2493 * @method delete_checkpoint
2490 * @param {String} checkpoint ID
2494 * @param {String} checkpoint ID
2491 */
2495 */
2492 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2496 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2493 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2497 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2494 var url = utils.url_join_encode(
2498 var url = utils.url_join_encode(
2495 this.base_url,
2499 this.base_url,
2496 'api/notebooks',
2500 'api/notebooks',
2497 this.notebook_path,
2501 this.notebook_path,
2498 this.notebook_name,
2502 this.notebook_name,
2499 'checkpoints',
2503 'checkpoints',
2500 checkpoint
2504 checkpoint
2501 );
2505 );
2502 $.ajax(url, {
2506 $.ajax(url, {
2503 type: 'DELETE',
2507 type: 'DELETE',
2504 success: $.proxy(this.delete_checkpoint_success, this),
2508 success: $.proxy(this.delete_checkpoint_success, this),
2505 error: $.proxy(this.delete_checkpoint_error, this)
2509 error: $.proxy(this.delete_checkpoint_error, this)
2506 });
2510 });
2507 };
2511 };
2508
2512
2509 /**
2513 /**
2510 * Success callback for deleting a notebook checkpoint
2514 * Success callback for deleting a notebook checkpoint
2511 *
2515 *
2512 * @method delete_checkpoint_success
2516 * @method delete_checkpoint_success
2513 * @param {Object} data (ignored, should be empty)
2517 * @param {Object} data (ignored, should be empty)
2514 * @param {String} status Description of response status
2518 * @param {String} status Description of response status
2515 * @param {jqXHR} xhr jQuery Ajax object
2519 * @param {jqXHR} xhr jQuery Ajax object
2516 */
2520 */
2517 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2521 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2518 this.events.trigger('checkpoint_deleted.Notebook', data);
2522 this.events.trigger('checkpoint_deleted.Notebook', data);
2519 this.load_notebook(this.notebook_name, this.notebook_path);
2523 this.load_notebook(this.notebook_name, this.notebook_path);
2520 };
2524 };
2521
2525
2522 /**
2526 /**
2523 * Failure callback for deleting a notebook checkpoint.
2527 * Failure callback for deleting a notebook checkpoint.
2524 *
2528 *
2525 * @method delete_checkpoint_error
2529 * @method delete_checkpoint_error
2526 * @param {jqXHR} xhr jQuery Ajax object
2530 * @param {jqXHR} xhr jQuery Ajax object
2527 * @param {String} status Description of response status
2531 * @param {String} status Description of response status
2528 * @param {String} error_msg HTTP error message
2532 * @param {String} error_msg HTTP error message
2529 */
2533 */
2530 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2534 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2531 this.events.trigger('checkpoint_delete_failed.Notebook');
2535 this.events.trigger('checkpoint_delete_failed.Notebook');
2532 };
2536 };
2533
2537
2534
2538
2535 // For backwards compatability.
2539 // For backwards compatability.
2536 IPython.Notebook = Notebook;
2540 IPython.Notebook = Notebook;
2537
2541
2538 return {'Notebook': Notebook};
2542 return {'Notebook': Notebook};
2539 });
2543 });
@@ -1,321 +1,327 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% endif %}
7 {% endif %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16
16
17 {{super()}}
17 {{super()}}
18
18
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20
20
21 {% endblock %}
21 {% endblock %}
22
22
23 {% block params %}
23 {% block params %}
24
24
25 data-project="{{project}}"
25 data-project="{{project}}"
26 data-base-url="{{base_url}}"
26 data-base-url="{{base_url}}"
27 data-ws-url="{{ws_url}}"
27 data-ws-url="{{ws_url}}"
28 data-notebook-name="{{notebook_name}}"
28 data-notebook-name="{{notebook_name}}"
29 data-notebook-path="{{notebook_path}}"
29 data-notebook-path="{{notebook_path}}"
30 class="notebook_app"
30 class="notebook_app"
31
31
32 {% endblock %}
32 {% endblock %}
33
33
34
34
35 {% block header %}
35 {% block header %}
36
36
37 <span id="save_widget" class="nav pull-left">
37 <span id="save_widget" class="nav pull-left">
38 <span id="notebook_name"></span>
38 <span id="notebook_name"></span>
39 <span id="checkpoint_status"></span>
39 <span id="checkpoint_status"></span>
40 <span id="autosave_status"></span>
40 <span id="autosave_status"></span>
41 </span>
41 </span>
42
42
43 <span id="kernel_selector_widget" class="nav pull-right">
44 <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_kernel_spec">Python</a>
45 <ul id="kernel_selector" class="dropdown-menu">
46 </ul>
47 </span>
48
43 {% endblock %}
49 {% endblock %}
44
50
45
51
46 {% block site %}
52 {% block site %}
47
53
48 <div id="menubar-container" class="container">
54 <div id="menubar-container" class="container">
49 <div id="menubar">
55 <div id="menubar">
50 <div id="menus" class="navbar navbar-default" role="navigation">
56 <div id="menus" class="navbar navbar-default" role="navigation">
51 <div class="container-fluid">
57 <div class="container-fluid">
52 <ul class="nav navbar-nav">
58 <ul class="nav navbar-nav">
53 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
59 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
54 <ul id="file_menu" class="dropdown-menu">
60 <ul id="file_menu" class="dropdown-menu">
55 <li id="new_notebook"
61 <li id="new_notebook"
56 title="Make a new notebook (Opens a new window)">
62 title="Make a new notebook (Opens a new window)">
57 <a href="#">New</a></li>
63 <a href="#">New</a></li>
58 <li id="open_notebook"
64 <li id="open_notebook"
59 title="Opens a new window with the Dashboard view">
65 title="Opens a new window with the Dashboard view">
60 <a href="#">Open...</a></li>
66 <a href="#">Open...</a></li>
61 <!-- <hr/> -->
67 <!-- <hr/> -->
62 <li class="divider"></li>
68 <li class="divider"></li>
63 <li id="copy_notebook"
69 <li id="copy_notebook"
64 title="Open a copy of this notebook's contents and start a new kernel">
70 title="Open a copy of this notebook's contents and start a new kernel">
65 <a href="#">Make a Copy...</a></li>
71 <a href="#">Make a Copy...</a></li>
66 <li id="rename_notebook"><a href="#">Rename...</a></li>
72 <li id="rename_notebook"><a href="#">Rename...</a></li>
67 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
73 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
68 <!-- <hr/> -->
74 <!-- <hr/> -->
69 <li class="divider"></li>
75 <li class="divider"></li>
70 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
76 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
71 <ul class="dropdown-menu">
77 <ul class="dropdown-menu">
72 <li><a href="#"></a></li>
78 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
79 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
80 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
81 <li><a href="#"></a></li>
76 <li><a href="#"></a></li>
82 <li><a href="#"></a></li>
77 </ul>
83 </ul>
78 </li>
84 </li>
79 <li class="divider"></li>
85 <li class="divider"></li>
80 <li id="print_preview"><a href="#">Print Preview</a></li>
86 <li id="print_preview"><a href="#">Print Preview</a></li>
81 <li class="dropdown-submenu"><a href="#">Download as</a>
87 <li class="dropdown-submenu"><a href="#">Download as</a>
82 <ul class="dropdown-menu">
88 <ul class="dropdown-menu">
83 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
89 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
84 <li id="download_py"><a href="#">Python (.py)</a></li>
90 <li id="download_py"><a href="#">Python (.py)</a></li>
85 <li id="download_html"><a href="#">HTML (.html)</a></li>
91 <li id="download_html"><a href="#">HTML (.html)</a></li>
86 <li id="download_rst"><a href="#">reST (.rst)</a></li>
92 <li id="download_rst"><a href="#">reST (.rst)</a></li>
87 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
93 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
88 </ul>
94 </ul>
89 </li>
95 </li>
90 <li class="divider"></li>
96 <li class="divider"></li>
91 <li id="trust_notebook"
97 <li id="trust_notebook"
92 title="Trust the output of this notebook">
98 title="Trust the output of this notebook">
93 <a href="#" >Trust Notebook</a></li>
99 <a href="#" >Trust Notebook</a></li>
94 <li class="divider"></li>
100 <li class="divider"></li>
95 <li id="kill_and_exit"
101 <li id="kill_and_exit"
96 title="Shutdown this notebook's kernel, and close this window">
102 title="Shutdown this notebook's kernel, and close this window">
97 <a href="#" >Close and halt</a></li>
103 <a href="#" >Close and halt</a></li>
98 </ul>
104 </ul>
99 </li>
105 </li>
100 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
106 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
101 <ul id="edit_menu" class="dropdown-menu">
107 <ul id="edit_menu" class="dropdown-menu">
102 <li id="cut_cell"><a href="#">Cut Cell</a></li>
108 <li id="cut_cell"><a href="#">Cut Cell</a></li>
103 <li id="copy_cell"><a href="#">Copy Cell</a></li>
109 <li id="copy_cell"><a href="#">Copy Cell</a></li>
104 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
110 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
105 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
111 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
106 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
112 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
107 <li id="delete_cell"><a href="#">Delete Cell</a></li>
113 <li id="delete_cell"><a href="#">Delete Cell</a></li>
108 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
114 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
109 <li class="divider"></li>
115 <li class="divider"></li>
110 <li id="split_cell"><a href="#">Split Cell</a></li>
116 <li id="split_cell"><a href="#">Split Cell</a></li>
111 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
117 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
112 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
118 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
113 <li class="divider"></li>
119 <li class="divider"></li>
114 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
120 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
115 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
121 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
116 <li class="divider"></li>
122 <li class="divider"></li>
117 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
123 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
118 </ul>
124 </ul>
119 </li>
125 </li>
120 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
126 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
121 <ul id="view_menu" class="dropdown-menu">
127 <ul id="view_menu" class="dropdown-menu">
122 <li id="toggle_header"
128 <li id="toggle_header"
123 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
129 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
124 <a href="#">Toggle Header</a></li>
130 <a href="#">Toggle Header</a></li>
125 <li id="toggle_toolbar"
131 <li id="toggle_toolbar"
126 title="Show/Hide the action icons (below menu bar)">
132 title="Show/Hide the action icons (below menu bar)">
127 <a href="#">Toggle Toolbar</a></li>
133 <a href="#">Toggle Toolbar</a></li>
128 </ul>
134 </ul>
129 </li>
135 </li>
130 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
136 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
131 <ul id="insert_menu" class="dropdown-menu">
137 <ul id="insert_menu" class="dropdown-menu">
132 <li id="insert_cell_above"
138 <li id="insert_cell_above"
133 title="Insert an empty Code cell above the currently active cell">
139 title="Insert an empty Code cell above the currently active cell">
134 <a href="#">Insert Cell Above</a></li>
140 <a href="#">Insert Cell Above</a></li>
135 <li id="insert_cell_below"
141 <li id="insert_cell_below"
136 title="Insert an empty Code cell below the currently active cell">
142 title="Insert an empty Code cell below the currently active cell">
137 <a href="#">Insert Cell Below</a></li>
143 <a href="#">Insert Cell Below</a></li>
138 </ul>
144 </ul>
139 </li>
145 </li>
140 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
146 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
141 <ul id="cell_menu" class="dropdown-menu">
147 <ul id="cell_menu" class="dropdown-menu">
142 <li id="run_cell" title="Run this cell, and move cursor to the next one">
148 <li id="run_cell" title="Run this cell, and move cursor to the next one">
143 <a href="#">Run</a></li>
149 <a href="#">Run</a></li>
144 <li id="run_cell_select_below" title="Run this cell, select below">
150 <li id="run_cell_select_below" title="Run this cell, select below">
145 <a href="#">Run and Select Below</a></li>
151 <a href="#">Run and Select Below</a></li>
146 <li id="run_cell_insert_below" title="Run this cell, insert below">
152 <li id="run_cell_insert_below" title="Run this cell, insert below">
147 <a href="#">Run and Insert Below</a></li>
153 <a href="#">Run and Insert Below</a></li>
148 <li id="run_all_cells" title="Run all cells in the notebook">
154 <li id="run_all_cells" title="Run all cells in the notebook">
149 <a href="#">Run All</a></li>
155 <a href="#">Run All</a></li>
150 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
156 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
151 <a href="#">Run All Above</a></li>
157 <a href="#">Run All Above</a></li>
152 <li id="run_all_cells_below" title="Run this cell and all cells below it">
158 <li id="run_all_cells_below" title="Run this cell and all cells below it">
153 <a href="#">Run All Below</a></li>
159 <a href="#">Run All Below</a></li>
154 <li class="divider"></li>
160 <li class="divider"></li>
155 <li id="change_cell_type" class="dropdown-submenu"
161 <li id="change_cell_type" class="dropdown-submenu"
156 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
162 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
157 <a href="#">Cell Type</a>
163 <a href="#">Cell Type</a>
158 <ul class="dropdown-menu">
164 <ul class="dropdown-menu">
159 <li id="to_code"
165 <li id="to_code"
160 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
166 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
161 <a href="#">Code</a></li>
167 <a href="#">Code</a></li>
162 <li id="to_markdown"
168 <li id="to_markdown"
163 title="Contents will be rendered as HTML and serve as explanatory text">
169 title="Contents will be rendered as HTML and serve as explanatory text">
164 <a href="#">Markdown</a></li>
170 <a href="#">Markdown</a></li>
165 <li id="to_raw"
171 <li id="to_raw"
166 title="Contents will pass through nbconvert unmodified">
172 title="Contents will pass through nbconvert unmodified">
167 <a href="#">Raw NBConvert</a></li>
173 <a href="#">Raw NBConvert</a></li>
168 <li id="to_heading1"><a href="#">Heading 1</a></li>
174 <li id="to_heading1"><a href="#">Heading 1</a></li>
169 <li id="to_heading2"><a href="#">Heading 2</a></li>
175 <li id="to_heading2"><a href="#">Heading 2</a></li>
170 <li id="to_heading3"><a href="#">Heading 3</a></li>
176 <li id="to_heading3"><a href="#">Heading 3</a></li>
171 <li id="to_heading4"><a href="#">Heading 4</a></li>
177 <li id="to_heading4"><a href="#">Heading 4</a></li>
172 <li id="to_heading5"><a href="#">Heading 5</a></li>
178 <li id="to_heading5"><a href="#">Heading 5</a></li>
173 <li id="to_heading6"><a href="#">Heading 6</a></li>
179 <li id="to_heading6"><a href="#">Heading 6</a></li>
174 </ul>
180 </ul>
175 </li>
181 </li>
176 <li class="divider"></li>
182 <li class="divider"></li>
177 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
183 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
178 <ul class="dropdown-menu">
184 <ul class="dropdown-menu">
179 <li id="toggle_current_output"
185 <li id="toggle_current_output"
180 title="Hide/Show the output of the current cell">
186 title="Hide/Show the output of the current cell">
181 <a href="#">Toggle</a>
187 <a href="#">Toggle</a>
182 </li>
188 </li>
183 <li id="toggle_current_output_scroll"
189 <li id="toggle_current_output_scroll"
184 title="Scroll the output of the current cell">
190 title="Scroll the output of the current cell">
185 <a href="#">Toggle Scrolling</a>
191 <a href="#">Toggle Scrolling</a>
186 </li>
192 </li>
187 <li id="clear_current_output"
193 <li id="clear_current_output"
188 title="Clear the output of the current cell">
194 title="Clear the output of the current cell">
189 <a href="#">Clear</a>
195 <a href="#">Clear</a>
190 </li>
196 </li>
191 </ul>
197 </ul>
192 </li>
198 </li>
193 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
199 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
194 <ul class="dropdown-menu">
200 <ul class="dropdown-menu">
195 <li id="toggle_all_output"
201 <li id="toggle_all_output"
196 title="Hide/Show the output of all cells">
202 title="Hide/Show the output of all cells">
197 <a href="#">Toggle</a>
203 <a href="#">Toggle</a>
198 </li>
204 </li>
199 <li id="toggle_all_output_scroll"
205 <li id="toggle_all_output_scroll"
200 title="Scroll the output of all cells">
206 title="Scroll the output of all cells">
201 <a href="#">Toggle Scrolling</a>
207 <a href="#">Toggle Scrolling</a>
202 </li>
208 </li>
203 <li id="clear_all_output"
209 <li id="clear_all_output"
204 title="Clear the output of all cells">
210 title="Clear the output of all cells">
205 <a href="#">Clear</a>
211 <a href="#">Clear</a>
206 </li>
212 </li>
207 </ul>
213 </ul>
208 </li>
214 </li>
209 </ul>
215 </ul>
210 </li>
216 </li>
211 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
217 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
212 <ul id="kernel_menu" class="dropdown-menu">
218 <ul id="kernel_menu" class="dropdown-menu">
213 <li id="int_kernel"
219 <li id="int_kernel"
214 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
220 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
215 <a href="#">Interrupt</a></li>
221 <a href="#">Interrupt</a></li>
216 <li id="restart_kernel"
222 <li id="restart_kernel"
217 title="Restart the Kernel">
223 title="Restart the Kernel">
218 <a href="#">Restart</a></li>
224 <a href="#">Restart</a></li>
219 </ul>
225 </ul>
220 </li>
226 </li>
221 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
227 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
222 <ul id="help_menu" class="dropdown-menu">
228 <ul id="help_menu" class="dropdown-menu">
223 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
229 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
224 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
230 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
225 <li class="divider"></li>
231 <li class="divider"></li>
226 {% set
232 {% set
227 sections = (
233 sections = (
228 (
234 (
229 ("http://ipython.org/documentation.html","IPython Help",True),
235 ("http://ipython.org/documentation.html","IPython Help",True),
230 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
236 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
231 ),(
237 ),(
232 ("http://docs.python.org","Python",True),
238 ("http://docs.python.org","Python",True),
233 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
239 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
234 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
240 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
235 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
241 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
236 ("http://matplotlib.org/contents.html","Matplotlib",True),
242 ("http://matplotlib.org/contents.html","Matplotlib",True),
237 ("http://docs.sympy.org/latest/index.html","SymPy",True),
243 ("http://docs.sympy.org/latest/index.html","SymPy",True),
238 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
244 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
239 )
245 )
240 )
246 )
241 %}
247 %}
242
248
243 {% for helplinks in sections %}
249 {% for helplinks in sections %}
244 {% for link in helplinks %}
250 {% for link in helplinks %}
245 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
251 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
246 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
252 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
247 {{link[1]}}
253 {{link[1]}}
248 </a></li>
254 </a></li>
249 {% endfor %}
255 {% endfor %}
250 {% if not loop.last %}
256 {% if not loop.last %}
251 <li class="divider"></li>
257 <li class="divider"></li>
252 {% endif %}
258 {% endif %}
253 {% endfor %}
259 {% endfor %}
254 </li>
260 </li>
255 </ul>
261 </ul>
256 </li>
262 </li>
257 </ul>
263 </ul>
258 <ul class="nav navbar-nav navbar-right">
264 <ul class="nav navbar-nav navbar-right">
259 <div id="kernel_indicator">
265 <div id="kernel_indicator">
260 <i id="kernel_indicator_icon"></i>
266 <i id="kernel_indicator_icon"></i>
261 </div>
267 </div>
262 <div id="modal_indicator">
268 <div id="modal_indicator">
263 <i id="modal_indicator_icon"></i>
269 <i id="modal_indicator_icon"></i>
264 </div>
270 </div>
265 <div id="notification_area"></div>
271 <div id="notification_area"></div>
266 </ul>
272 </ul>
267 </div>
273 </div>
268 </div>
274 </div>
269 </div>
275 </div>
270 <div id="maintoolbar" class="navbar">
276 <div id="maintoolbar" class="navbar">
271 <div class="toolbar-inner navbar-inner navbar-nobg">
277 <div class="toolbar-inner navbar-inner navbar-nobg">
272 <div id="maintoolbar-container" class="container"></div>
278 <div id="maintoolbar-container" class="container"></div>
273 </div>
279 </div>
274 </div>
280 </div>
275 </div>
281 </div>
276
282
277 <div id="ipython-main-app">
283 <div id="ipython-main-app">
278
284
279 <div id="notebook_panel">
285 <div id="notebook_panel">
280 <div id="notebook"></div>
286 <div id="notebook"></div>
281 <div id="pager_splitter"></div>
287 <div id="pager_splitter"></div>
282 <div id="pager">
288 <div id="pager">
283 <div id='pager_button_area'>
289 <div id='pager_button_area'>
284 </div>
290 </div>
285 <div id="pager-container" class="container"></div>
291 <div id="pager-container" class="container"></div>
286 </div>
292 </div>
287 </div>
293 </div>
288
294
289 </div>
295 </div>
290 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
296 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
291
297
292
298
293 {% endblock %}
299 {% endblock %}
294
300
295
301
296 {% block script %}
302 {% block script %}
297 {{super()}}
303 {{super()}}
298
304
299 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
300 <script type="text/javascript">
306 <script type="text/javascript">
301 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
307 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
302 </script>
308 </script>
303 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
304 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
306 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
307 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
308 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
317 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
318 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
319 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
320 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
321 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
322 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
317 <script src="{{ static_url("notebook/js/codemirror-ipythongfm.js") }}" charset="utf-8"></script>
323 <script src="{{ static_url("notebook/js/codemirror-ipythongfm.js") }}" charset="utf-8"></script>
318
324
319 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
325 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
320
326
321 {% endblock %}
327 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now