##// END OF EJS Templates
Merge pull request #6876 from minrk/falloff-reconnect...
Kyle Kelley -
r18747:b13ab53f merge
parent child Browse files
Show More
@@ -1,356 +1,359
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 'notebook/js/tour',
8 'notebook/js/tour',
9 'bootstrap',
9 'bootstrap',
10 'moment',
10 'moment',
11 ], function(IPython, $, utils, tour, bootstrap, moment) {
11 ], function(IPython, $, utils, tour, bootstrap, moment) {
12 "use strict";
12 "use strict";
13
13
14 var MenuBar = function (selector, options) {
14 var MenuBar = function (selector, options) {
15 // Constructor
15 // Constructor
16 //
16 //
17 // A MenuBar Class to generate the menubar of IPython notebook
17 // A MenuBar Class to generate the menubar of IPython notebook
18 //
18 //
19 // Parameters:
19 // Parameters:
20 // selector: string
20 // selector: string
21 // options: dictionary
21 // options: dictionary
22 // Dictionary of keyword arguments.
22 // Dictionary of keyword arguments.
23 // notebook: Notebook instance
23 // notebook: Notebook instance
24 // contents: ContentManager instance
24 // contents: ContentManager instance
25 // layout_manager: LayoutManager instance
25 // layout_manager: LayoutManager instance
26 // events: $(Events) instance
26 // events: $(Events) instance
27 // save_widget: SaveWidget instance
27 // save_widget: SaveWidget instance
28 // quick_help: QuickHelp instance
28 // quick_help: QuickHelp instance
29 // base_url : string
29 // base_url : string
30 // notebook_path : string
30 // notebook_path : string
31 // notebook_name : string
31 // notebook_name : string
32 options = options || {};
32 options = options || {};
33 this.base_url = options.base_url || utils.get_body_data("baseUrl");
33 this.base_url = options.base_url || utils.get_body_data("baseUrl");
34 this.selector = selector;
34 this.selector = selector;
35 this.notebook = options.notebook;
35 this.notebook = options.notebook;
36 this.contents = options.contents;
36 this.contents = options.contents;
37 this.layout_manager = options.layout_manager;
37 this.layout_manager = options.layout_manager;
38 this.events = options.events;
38 this.events = options.events;
39 this.save_widget = options.save_widget;
39 this.save_widget = options.save_widget;
40 this.quick_help = options.quick_help;
40 this.quick_help = options.quick_help;
41
41
42 try {
42 try {
43 this.tour = new tour.Tour(this.notebook, this.events);
43 this.tour = new tour.Tour(this.notebook, this.events);
44 } catch (e) {
44 } catch (e) {
45 this.tour = undefined;
45 this.tour = undefined;
46 console.log("Failed to instantiate Notebook Tour", e);
46 console.log("Failed to instantiate Notebook Tour", e);
47 }
47 }
48
48
49 if (this.selector !== undefined) {
49 if (this.selector !== undefined) {
50 this.element = $(selector);
50 this.element = $(selector);
51 this.style();
51 this.style();
52 this.bind_events();
52 this.bind_events();
53 }
53 }
54 };
54 };
55
55
56 // TODO: This has definitively nothing to do with style ...
56 // TODO: This has definitively nothing to do with style ...
57 MenuBar.prototype.style = function () {
57 MenuBar.prototype.style = function () {
58 var that = this;
58 var that = this;
59 this.element.find("li").click(function (event, ui) {
59 this.element.find("li").click(function (event, ui) {
60 // The selected cell loses focus when the menu is entered, so we
60 // The selected cell loses focus when the menu is entered, so we
61 // re-select it upon selection.
61 // re-select it upon selection.
62 var i = that.notebook.get_selected_index();
62 var i = that.notebook.get_selected_index();
63 that.notebook.select(i);
63 that.notebook.select(i);
64 }
64 }
65 );
65 );
66 };
66 };
67
67
68 MenuBar.prototype._nbconvert = function (format, download) {
68 MenuBar.prototype._nbconvert = function (format, download) {
69 download = download || false;
69 download = download || false;
70 var notebook_path = this.notebook.notebook_path;
70 var notebook_path = this.notebook.notebook_path;
71 var notebook_name = this.notebook.notebook_name;
71 var notebook_name = this.notebook.notebook_name;
72 if (this.notebook.dirty) {
72 if (this.notebook.dirty) {
73 this.notebook.save_notebook({async : false});
73 this.notebook.save_notebook({async : false});
74 }
74 }
75 var url = utils.url_join_encode(
75 var url = utils.url_join_encode(
76 this.base_url,
76 this.base_url,
77 'nbconvert',
77 'nbconvert',
78 format,
78 format,
79 notebook_path,
79 notebook_path,
80 notebook_name
80 notebook_name
81 ) + "?download=" + download.toString();
81 ) + "?download=" + download.toString();
82
82
83 window.open(url);
83 window.open(url);
84 };
84 };
85
85
86 MenuBar.prototype.bind_events = function () {
86 MenuBar.prototype.bind_events = function () {
87 // File
87 // File
88 var that = this;
88 var that = this;
89 this.element.find('#new_notebook').click(function () {
89 this.element.find('#new_notebook').click(function () {
90 // Create a new notebook in the same path as the current
90 // Create a new notebook in the same path as the current
91 // notebook's path.
91 // notebook's path.
92 that.contents.new(that.notebook.notebook_path, null, {
92 that.contents.new(that.notebook.notebook_path, null, {
93 ext: ".ipynb",
93 ext: ".ipynb",
94 extra_settings: {async: false}, // So we can open a new window afterwards
94 extra_settings: {async: false}, // So we can open a new window afterwards
95 success: function (data) {
95 success: function (data) {
96 window.open(
96 window.open(
97 utils.url_join_encode(
97 utils.url_join_encode(
98 that.base_url, 'notebooks',
98 that.base_url, 'notebooks',
99 data.path, data.name
99 data.path, data.name
100 ), '_blank');
100 ), '_blank');
101 },
101 },
102 error: function(error) {
102 error: function(error) {
103 dialog.modal({
103 dialog.modal({
104 title : 'Creating Notebook Failed',
104 title : 'Creating Notebook Failed',
105 body : "The error was: " + error.message,
105 body : "The error was: " + error.message,
106 buttons : {'OK' : {'class' : 'btn-primary'}}
106 buttons : {'OK' : {'class' : 'btn-primary'}}
107 });
107 });
108 }
108 }
109 });
109 });
110 });
110 });
111 this.element.find('#open_notebook').click(function () {
111 this.element.find('#open_notebook').click(function () {
112 window.open(utils.url_join_encode(
112 window.open(utils.url_join_encode(
113 that.notebook.base_url,
113 that.notebook.base_url,
114 'tree',
114 'tree',
115 that.notebook.notebook_path
115 that.notebook.notebook_path
116 ));
116 ));
117 });
117 });
118 this.element.find('#copy_notebook').click(function () {
118 this.element.find('#copy_notebook').click(function () {
119 that.notebook.copy_notebook();
119 that.notebook.copy_notebook();
120 return false;
120 return false;
121 });
121 });
122 this.element.find('#download_ipynb').click(function () {
122 this.element.find('#download_ipynb').click(function () {
123 var base_url = that.notebook.base_url;
123 var base_url = that.notebook.base_url;
124 var notebook_path = that.notebook.notebook_path;
124 var notebook_path = that.notebook.notebook_path;
125 var notebook_name = that.notebook.notebook_name;
125 var notebook_name = that.notebook.notebook_name;
126 if (that.notebook.dirty) {
126 if (that.notebook.dirty) {
127 that.notebook.save_notebook({async : false});
127 that.notebook.save_notebook({async : false});
128 }
128 }
129
129
130 var url = utils.url_join_encode(
130 var url = utils.url_join_encode(
131 base_url,
131 base_url,
132 'files',
132 'files',
133 notebook_path,
133 notebook_path,
134 notebook_name
134 notebook_name
135 );
135 );
136 window.open(url + '?download=1');
136 window.open(url + '?download=1');
137 });
137 });
138
138
139 this.element.find('#print_preview').click(function () {
139 this.element.find('#print_preview').click(function () {
140 that._nbconvert('html', false);
140 that._nbconvert('html', false);
141 });
141 });
142
142
143 this.element.find('#download_py').click(function () {
143 this.element.find('#download_py').click(function () {
144 that._nbconvert('python', true);
144 that._nbconvert('python', true);
145 });
145 });
146
146
147 this.element.find('#download_html').click(function () {
147 this.element.find('#download_html').click(function () {
148 that._nbconvert('html', true);
148 that._nbconvert('html', true);
149 });
149 });
150
150
151 this.element.find('#download_rst').click(function () {
151 this.element.find('#download_rst').click(function () {
152 that._nbconvert('rst', true);
152 that._nbconvert('rst', true);
153 });
153 });
154
154
155 this.element.find('#download_pdf').click(function () {
155 this.element.find('#download_pdf').click(function () {
156 that._nbconvert('pdf', true);
156 that._nbconvert('pdf', true);
157 });
157 });
158
158
159 this.element.find('#rename_notebook').click(function () {
159 this.element.find('#rename_notebook').click(function () {
160 that.save_widget.rename_notebook({notebook: that.notebook});
160 that.save_widget.rename_notebook({notebook: that.notebook});
161 });
161 });
162 this.element.find('#save_checkpoint').click(function () {
162 this.element.find('#save_checkpoint').click(function () {
163 that.notebook.save_checkpoint();
163 that.notebook.save_checkpoint();
164 });
164 });
165 this.element.find('#restore_checkpoint').click(function () {
165 this.element.find('#restore_checkpoint').click(function () {
166 });
166 });
167 this.element.find('#trust_notebook').click(function () {
167 this.element.find('#trust_notebook').click(function () {
168 that.notebook.trust_notebook();
168 that.notebook.trust_notebook();
169 });
169 });
170 this.events.on('trust_changed.Notebook', function (event, trusted) {
170 this.events.on('trust_changed.Notebook', function (event, trusted) {
171 if (trusted) {
171 if (trusted) {
172 that.element.find('#trust_notebook')
172 that.element.find('#trust_notebook')
173 .addClass("disabled")
173 .addClass("disabled")
174 .find("a").text("Trusted Notebook");
174 .find("a").text("Trusted Notebook");
175 } else {
175 } else {
176 that.element.find('#trust_notebook')
176 that.element.find('#trust_notebook')
177 .removeClass("disabled")
177 .removeClass("disabled")
178 .find("a").text("Trust Notebook");
178 .find("a").text("Trust Notebook");
179 }
179 }
180 });
180 });
181 this.element.find('#kill_and_exit').click(function () {
181 this.element.find('#kill_and_exit').click(function () {
182 var close_window = function () {
182 var close_window = function () {
183 // allow closing of new tabs in Chromium, impossible in FF
183 // allow closing of new tabs in Chromium, impossible in FF
184 window.open('', '_self', '');
184 window.open('', '_self', '');
185 window.close();
185 window.close();
186 };
186 };
187 // finish with close on success or failure
187 // finish with close on success or failure
188 that.notebook.session.delete(close_window, close_window);
188 that.notebook.session.delete(close_window, close_window);
189 });
189 });
190 // Edit
190 // Edit
191 this.element.find('#cut_cell').click(function () {
191 this.element.find('#cut_cell').click(function () {
192 that.notebook.cut_cell();
192 that.notebook.cut_cell();
193 });
193 });
194 this.element.find('#copy_cell').click(function () {
194 this.element.find('#copy_cell').click(function () {
195 that.notebook.copy_cell();
195 that.notebook.copy_cell();
196 });
196 });
197 this.element.find('#delete_cell').click(function () {
197 this.element.find('#delete_cell').click(function () {
198 that.notebook.delete_cell();
198 that.notebook.delete_cell();
199 });
199 });
200 this.element.find('#undelete_cell').click(function () {
200 this.element.find('#undelete_cell').click(function () {
201 that.notebook.undelete_cell();
201 that.notebook.undelete_cell();
202 });
202 });
203 this.element.find('#split_cell').click(function () {
203 this.element.find('#split_cell').click(function () {
204 that.notebook.split_cell();
204 that.notebook.split_cell();
205 });
205 });
206 this.element.find('#merge_cell_above').click(function () {
206 this.element.find('#merge_cell_above').click(function () {
207 that.notebook.merge_cell_above();
207 that.notebook.merge_cell_above();
208 });
208 });
209 this.element.find('#merge_cell_below').click(function () {
209 this.element.find('#merge_cell_below').click(function () {
210 that.notebook.merge_cell_below();
210 that.notebook.merge_cell_below();
211 });
211 });
212 this.element.find('#move_cell_up').click(function () {
212 this.element.find('#move_cell_up').click(function () {
213 that.notebook.move_cell_up();
213 that.notebook.move_cell_up();
214 });
214 });
215 this.element.find('#move_cell_down').click(function () {
215 this.element.find('#move_cell_down').click(function () {
216 that.notebook.move_cell_down();
216 that.notebook.move_cell_down();
217 });
217 });
218 this.element.find('#edit_nb_metadata').click(function () {
218 this.element.find('#edit_nb_metadata').click(function () {
219 that.notebook.edit_metadata({
219 that.notebook.edit_metadata({
220 notebook: that.notebook,
220 notebook: that.notebook,
221 keyboard_manager: that.notebook.keyboard_manager});
221 keyboard_manager: that.notebook.keyboard_manager});
222 });
222 });
223
223
224 // View
224 // View
225 this.element.find('#toggle_header').click(function () {
225 this.element.find('#toggle_header').click(function () {
226 $('div#header').toggle();
226 $('div#header').toggle();
227 that.layout_manager.do_resize();
227 that.layout_manager.do_resize();
228 });
228 });
229 this.element.find('#toggle_toolbar').click(function () {
229 this.element.find('#toggle_toolbar').click(function () {
230 $('div#maintoolbar').toggle();
230 $('div#maintoolbar').toggle();
231 that.layout_manager.do_resize();
231 that.layout_manager.do_resize();
232 });
232 });
233 // Insert
233 // Insert
234 this.element.find('#insert_cell_above').click(function () {
234 this.element.find('#insert_cell_above').click(function () {
235 that.notebook.insert_cell_above('code');
235 that.notebook.insert_cell_above('code');
236 that.notebook.select_prev();
236 that.notebook.select_prev();
237 });
237 });
238 this.element.find('#insert_cell_below').click(function () {
238 this.element.find('#insert_cell_below').click(function () {
239 that.notebook.insert_cell_below('code');
239 that.notebook.insert_cell_below('code');
240 that.notebook.select_next();
240 that.notebook.select_next();
241 });
241 });
242 // Cell
242 // Cell
243 this.element.find('#run_cell').click(function () {
243 this.element.find('#run_cell').click(function () {
244 that.notebook.execute_cell();
244 that.notebook.execute_cell();
245 });
245 });
246 this.element.find('#run_cell_select_below').click(function () {
246 this.element.find('#run_cell_select_below').click(function () {
247 that.notebook.execute_cell_and_select_below();
247 that.notebook.execute_cell_and_select_below();
248 });
248 });
249 this.element.find('#run_cell_insert_below').click(function () {
249 this.element.find('#run_cell_insert_below').click(function () {
250 that.notebook.execute_cell_and_insert_below();
250 that.notebook.execute_cell_and_insert_below();
251 });
251 });
252 this.element.find('#run_all_cells').click(function () {
252 this.element.find('#run_all_cells').click(function () {
253 that.notebook.execute_all_cells();
253 that.notebook.execute_all_cells();
254 });
254 });
255 this.element.find('#run_all_cells_above').click(function () {
255 this.element.find('#run_all_cells_above').click(function () {
256 that.notebook.execute_cells_above();
256 that.notebook.execute_cells_above();
257 });
257 });
258 this.element.find('#run_all_cells_below').click(function () {
258 this.element.find('#run_all_cells_below').click(function () {
259 that.notebook.execute_cells_below();
259 that.notebook.execute_cells_below();
260 });
260 });
261 this.element.find('#to_code').click(function () {
261 this.element.find('#to_code').click(function () {
262 that.notebook.to_code();
262 that.notebook.to_code();
263 });
263 });
264 this.element.find('#to_markdown').click(function () {
264 this.element.find('#to_markdown').click(function () {
265 that.notebook.to_markdown();
265 that.notebook.to_markdown();
266 });
266 });
267 this.element.find('#to_raw').click(function () {
267 this.element.find('#to_raw').click(function () {
268 that.notebook.to_raw();
268 that.notebook.to_raw();
269 });
269 });
270
270
271 this.element.find('#toggle_current_output').click(function () {
271 this.element.find('#toggle_current_output').click(function () {
272 that.notebook.toggle_output();
272 that.notebook.toggle_output();
273 });
273 });
274 this.element.find('#toggle_current_output_scroll').click(function () {
274 this.element.find('#toggle_current_output_scroll').click(function () {
275 that.notebook.toggle_output_scroll();
275 that.notebook.toggle_output_scroll();
276 });
276 });
277 this.element.find('#clear_current_output').click(function () {
277 this.element.find('#clear_current_output').click(function () {
278 that.notebook.clear_output();
278 that.notebook.clear_output();
279 });
279 });
280
280
281 this.element.find('#toggle_all_output').click(function () {
281 this.element.find('#toggle_all_output').click(function () {
282 that.notebook.toggle_all_output();
282 that.notebook.toggle_all_output();
283 });
283 });
284 this.element.find('#toggle_all_output_scroll').click(function () {
284 this.element.find('#toggle_all_output_scroll').click(function () {
285 that.notebook.toggle_all_output_scroll();
285 that.notebook.toggle_all_output_scroll();
286 });
286 });
287 this.element.find('#clear_all_output').click(function () {
287 this.element.find('#clear_all_output').click(function () {
288 that.notebook.clear_all_output();
288 that.notebook.clear_all_output();
289 });
289 });
290
290
291 // Kernel
291 // Kernel
292 this.element.find('#int_kernel').click(function () {
292 this.element.find('#int_kernel').click(function () {
293 that.notebook.kernel.interrupt();
293 that.notebook.kernel.interrupt();
294 });
294 });
295 this.element.find('#restart_kernel').click(function () {
295 this.element.find('#restart_kernel').click(function () {
296 that.notebook.restart_kernel();
296 that.notebook.restart_kernel();
297 });
297 });
298 this.element.find('#reconnect_kernel').click(function () {
299 that.notebook.kernel.reconnect();
300 });
298 // Help
301 // Help
299 if (this.tour) {
302 if (this.tour) {
300 this.element.find('#notebook_tour').click(function () {
303 this.element.find('#notebook_tour').click(function () {
301 that.tour.start();
304 that.tour.start();
302 });
305 });
303 } else {
306 } else {
304 this.element.find('#notebook_tour').addClass("disabled");
307 this.element.find('#notebook_tour').addClass("disabled");
305 }
308 }
306 this.element.find('#keyboard_shortcuts').click(function () {
309 this.element.find('#keyboard_shortcuts').click(function () {
307 that.quick_help.show_keyboard_shortcuts();
310 that.quick_help.show_keyboard_shortcuts();
308 });
311 });
309
312
310 this.update_restore_checkpoint(null);
313 this.update_restore_checkpoint(null);
311
314
312 this.events.on('checkpoints_listed.Notebook', function (event, data) {
315 this.events.on('checkpoints_listed.Notebook', function (event, data) {
313 that.update_restore_checkpoint(that.notebook.checkpoints);
316 that.update_restore_checkpoint(that.notebook.checkpoints);
314 });
317 });
315
318
316 this.events.on('checkpoint_created.Notebook', function (event, data) {
319 this.events.on('checkpoint_created.Notebook', function (event, data) {
317 that.update_restore_checkpoint(that.notebook.checkpoints);
320 that.update_restore_checkpoint(that.notebook.checkpoints);
318 });
321 });
319 };
322 };
320
323
321 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
324 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
322 var ul = this.element.find("#restore_checkpoint").find("ul");
325 var ul = this.element.find("#restore_checkpoint").find("ul");
323 ul.empty();
326 ul.empty();
324 if (!checkpoints || checkpoints.length === 0) {
327 if (!checkpoints || checkpoints.length === 0) {
325 ul.append(
328 ul.append(
326 $("<li/>")
329 $("<li/>")
327 .addClass("disabled")
330 .addClass("disabled")
328 .append(
331 .append(
329 $("<a/>")
332 $("<a/>")
330 .text("No checkpoints")
333 .text("No checkpoints")
331 )
334 )
332 );
335 );
333 return;
336 return;
334 }
337 }
335
338
336 var that = this;
339 var that = this;
337 checkpoints.map(function (checkpoint) {
340 checkpoints.map(function (checkpoint) {
338 var d = new Date(checkpoint.last_modified);
341 var d = new Date(checkpoint.last_modified);
339 ul.append(
342 ul.append(
340 $("<li/>").append(
343 $("<li/>").append(
341 $("<a/>")
344 $("<a/>")
342 .attr("href", "#")
345 .attr("href", "#")
343 .text(moment(d).format("LLLL"))
346 .text(moment(d).format("LLLL"))
344 .click(function () {
347 .click(function () {
345 that.notebook.restore_checkpoint_dialog(checkpoint);
348 that.notebook.restore_checkpoint_dialog(checkpoint);
346 })
349 })
347 )
350 )
348 );
351 );
349 });
352 });
350 };
353 };
351
354
352 // Backwards compatability.
355 // Backwards compatability.
353 IPython.MenuBar = MenuBar;
356 IPython.MenuBar = MenuBar;
354
357
355 return {'MenuBar': MenuBar};
358 return {'MenuBar': MenuBar};
356 });
359 });
@@ -1,377 +1,384
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/notificationwidget',
9 'notebook/js/notificationwidget',
10 'moment'
10 'moment'
11 ], function(IPython, $, utils, dialog, notificationwidget, moment) {
11 ], function(IPython, $, utils, dialog, notificationwidget, moment) {
12 "use strict";
12 "use strict";
13
13
14 // store reference to the NotificationWidget class
14 // store reference to the NotificationWidget class
15 var NotificationWidget = notificationwidget.NotificationWidget;
15 var NotificationWidget = notificationwidget.NotificationWidget;
16
16
17 /**
17 /**
18 * Construct the NotificationArea object. Options are:
18 * Construct the NotificationArea object. Options are:
19 * events: $(Events) instance
19 * events: $(Events) instance
20 * save_widget: SaveWidget instance
20 * save_widget: SaveWidget instance
21 * notebook: Notebook instance
21 * notebook: Notebook instance
22 * keyboard_manager: KeyboardManager instance
22 * keyboard_manager: KeyboardManager instance
23 *
23 *
24 * @constructor
24 * @constructor
25 * @param {string} selector - a jQuery selector string for the
25 * @param {string} selector - a jQuery selector string for the
26 * notification area element
26 * notification area element
27 * @param {Object} [options] - a dictionary of keyword arguments.
27 * @param {Object} [options] - a dictionary of keyword arguments.
28 */
28 */
29 var NotificationArea = function (selector, options) {
29 var NotificationArea = function (selector, options) {
30 this.selector = selector;
30 this.selector = selector;
31 this.events = options.events;
31 this.events = options.events;
32 this.save_widget = options.save_widget;
32 this.save_widget = options.save_widget;
33 this.notebook = options.notebook;
33 this.notebook = options.notebook;
34 this.keyboard_manager = options.keyboard_manager;
34 this.keyboard_manager = options.keyboard_manager;
35 if (this.selector !== undefined) {
35 if (this.selector !== undefined) {
36 this.element = $(selector);
36 this.element = $(selector);
37 }
37 }
38 this.widget_dict = {};
38 this.widget_dict = {};
39 };
39 };
40
40
41 /**
41 /**
42 * Get a widget by name, creating it if it doesn't exist.
42 * Get a widget by name, creating it if it doesn't exist.
43 *
43 *
44 * @method widget
44 * @method widget
45 * @param {string} name - the widget name
45 * @param {string} name - the widget name
46 */
46 */
47 NotificationArea.prototype.widget = function (name) {
47 NotificationArea.prototype.widget = function (name) {
48 if (this.widget_dict[name] === undefined) {
48 if (this.widget_dict[name] === undefined) {
49 return this.new_notification_widget(name);
49 return this.new_notification_widget(name);
50 }
50 }
51 return this.get_widget(name);
51 return this.get_widget(name);
52 };
52 };
53
53
54 /**
54 /**
55 * Get a widget by name, throwing an error if it doesn't exist.
55 * Get a widget by name, throwing an error if it doesn't exist.
56 *
56 *
57 * @method get_widget
57 * @method get_widget
58 * @param {string} name - the widget name
58 * @param {string} name - the widget name
59 */
59 */
60 NotificationArea.prototype.get_widget = function (name) {
60 NotificationArea.prototype.get_widget = function (name) {
61 if(this.widget_dict[name] === undefined) {
61 if(this.widget_dict[name] === undefined) {
62 throw('no widgets with this name');
62 throw('no widgets with this name');
63 }
63 }
64 return this.widget_dict[name];
64 return this.widget_dict[name];
65 };
65 };
66
66
67 /**
67 /**
68 * Create a new notification widget with the given name. The
68 * Create a new notification widget with the given name. The
69 * widget must not already exist.
69 * widget must not already exist.
70 *
70 *
71 * @method new_notification_widget
71 * @method new_notification_widget
72 * @param {string} name - the widget name
72 * @param {string} name - the widget name
73 */
73 */
74 NotificationArea.prototype.new_notification_widget = function (name) {
74 NotificationArea.prototype.new_notification_widget = function (name) {
75 if (this.widget_dict[name] !== undefined) {
75 if (this.widget_dict[name] !== undefined) {
76 throw('widget with that name already exists!');
76 throw('widget with that name already exists!');
77 }
77 }
78
78
79 // create the element for the notification widget and add it
79 // create the element for the notification widget and add it
80 // to the notification aread element
80 // to the notification aread element
81 var div = $('<div/>').attr('id', 'notification_' + name);
81 var div = $('<div/>').attr('id', 'notification_' + name);
82 $(this.selector).append(div);
82 $(this.selector).append(div);
83
83
84 // create the widget object and return it
84 // create the widget object and return it
85 this.widget_dict[name] = new NotificationWidget('#notification_' + name);
85 this.widget_dict[name] = new NotificationWidget('#notification_' + name);
86 return this.widget_dict[name];
86 return this.widget_dict[name];
87 };
87 };
88
88
89 /**
89 /**
90 * Initialize the default set of notification widgets.
90 * Initialize the default set of notification widgets.
91 *
91 *
92 * @method init_notification_widgets
92 * @method init_notification_widgets
93 */
93 */
94 NotificationArea.prototype.init_notification_widgets = function () {
94 NotificationArea.prototype.init_notification_widgets = function () {
95 this.init_kernel_notification_widget();
95 this.init_kernel_notification_widget();
96 this.init_notebook_notification_widget();
96 this.init_notebook_notification_widget();
97 };
97 };
98
98
99 /**
99 /**
100 * Initialize the notification widget for kernel status messages.
100 * Initialize the notification widget for kernel status messages.
101 *
101 *
102 * @method init_kernel_notification_widget
102 * @method init_kernel_notification_widget
103 */
103 */
104 NotificationArea.prototype.init_kernel_notification_widget = function () {
104 NotificationArea.prototype.init_kernel_notification_widget = function () {
105 var that = this;
105 var that = this;
106 var knw = this.new_notification_widget('kernel');
106 var knw = this.new_notification_widget('kernel');
107 var $kernel_ind_icon = $("#kernel_indicator_icon");
107 var $kernel_ind_icon = $("#kernel_indicator_icon");
108 var $modal_ind_icon = $("#modal_indicator_icon");
108 var $modal_ind_icon = $("#modal_indicator_icon");
109
109
110 // Command/Edit mode
110 // Command/Edit mode
111 this.events.on('edit_mode.Notebook', function () {
111 this.events.on('edit_mode.Notebook', function () {
112 that.save_widget.update_document_title();
112 that.save_widget.update_document_title();
113 $modal_ind_icon.attr('class','edit_mode_icon').attr('title','Edit Mode');
113 $modal_ind_icon.attr('class','edit_mode_icon').attr('title','Edit Mode');
114 });
114 });
115
115
116 this.events.on('command_mode.Notebook', function () {
116 this.events.on('command_mode.Notebook', function () {
117 that.save_widget.update_document_title();
117 that.save_widget.update_document_title();
118 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
118 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
119 });
119 });
120
120
121 // Implicitly start off in Command mode, switching to Edit mode will trigger event
121 // Implicitly start off in Command mode, switching to Edit mode will trigger event
122 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
122 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
123
123
124 // Kernel events
124 // Kernel events
125
125
126 // this can be either kernel_created.Kernel or kernel_created.Session
126 // this can be either kernel_created.Kernel or kernel_created.Session
127 this.events.on('kernel_created.Kernel kernel_created.Session', function () {
127 this.events.on('kernel_created.Kernel kernel_created.Session', function () {
128 knw.info("Kernel Created", 500);
128 knw.info("Kernel Created", 500);
129 });
129 });
130
130
131 this.events.on('kernel_reconnecting.Kernel', function () {
131 this.events.on('kernel_reconnecting.Kernel', function () {
132 knw.warning("Connecting to kernel");
132 knw.warning("Connecting to kernel");
133 });
133 });
134
134
135 this.events.on('kernel_connection_dead.Kernel', function (evt, info) {
136 knw.danger("Not Connected", undefined, function () {
137 // schedule reconnect a short time in the future, don't reconnect immediately
138 setTimeout($.proxy(info.kernel.reconnect, info.kernel), 500);
139 }, {title: 'click to reconnect'});
140 });
141
135 this.events.on('kernel_connected.Kernel', function () {
142 this.events.on('kernel_connected.Kernel', function () {
136 knw.info("Connected", 500);
143 knw.info("Connected", 500);
137 });
144 });
138
145
139 this.events.on('kernel_restarting.Kernel', function () {
146 this.events.on('kernel_restarting.Kernel', function () {
140 that.save_widget.update_document_title();
147 that.save_widget.update_document_title();
141 knw.set_message("Restarting kernel", 2000);
148 knw.set_message("Restarting kernel", 2000);
142 });
149 });
143
150
144 this.events.on('kernel_autorestarting.Kernel', function (evt, info) {
151 this.events.on('kernel_autorestarting.Kernel', function (evt, info) {
145 // Only show the dialog on the first restart attempt. This
152 // Only show the dialog on the first restart attempt. This
146 // number gets tracked by the `Kernel` object and passed
153 // number gets tracked by the `Kernel` object and passed
147 // along here, because we don't want to show the user 5
154 // along here, because we don't want to show the user 5
148 // dialogs saying the same thing (which is the number of
155 // dialogs saying the same thing (which is the number of
149 // times it tries restarting).
156 // times it tries restarting).
150 if (info.attempt === 1) {
157 if (info.attempt === 1) {
151
158
152 dialog.kernel_modal({
159 dialog.kernel_modal({
153 notebook: that.notebook,
160 notebook: that.notebook,
154 keyboard_manager: that.keyboard_manager,
161 keyboard_manager: that.keyboard_manager,
155 title: "Kernel Restarting",
162 title: "Kernel Restarting",
156 body: "The kernel appears to have died. It will restart automatically.",
163 body: "The kernel appears to have died. It will restart automatically.",
157 buttons: {
164 buttons: {
158 OK : {
165 OK : {
159 class : "btn-primary"
166 class : "btn-primary"
160 }
167 }
161 }
168 }
162 });
169 });
163 };
170 };
164
171
165 that.save_widget.update_document_title();
172 that.save_widget.update_document_title();
166 knw.danger("Dead kernel");
173 knw.danger("Dead kernel");
167 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
174 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
168 });
175 });
169
176
170 this.events.on('kernel_interrupting.Kernel', function () {
177 this.events.on('kernel_interrupting.Kernel', function () {
171 knw.set_message("Interrupting kernel", 2000);
178 knw.set_message("Interrupting kernel", 2000);
172 });
179 });
173
180
174 this.events.on('kernel_disconnected.Kernel', function () {
181 this.events.on('kernel_disconnected.Kernel', function () {
175 $kernel_ind_icon
182 $kernel_ind_icon
176 .attr('class', 'kernel_disconnected_icon')
183 .attr('class', 'kernel_disconnected_icon')
177 .attr('title', 'No Connection to Kernel');
184 .attr('title', 'No Connection to Kernel');
178 });
185 });
179
186
180 this.events.on('kernel_connection_failed.Kernel', function (evt, info) {
187 this.events.on('kernel_connection_failed.Kernel', function (evt, info) {
181 // only show the dialog if this is the first failed
188 // only show the dialog if this is the first failed
182 // connect attempt, because the kernel will continue
189 // connect attempt, because the kernel will continue
183 // trying to reconnect and we don't want to spam the user
190 // trying to reconnect and we don't want to spam the user
184 // with messages
191 // with messages
185 if (info.attempt === 1) {
192 if (info.attempt === 1) {
186
193
187 var msg = "A connection to the notebook server could not be established." +
194 var msg = "A connection to the notebook server could not be established." +
188 " The notebook will continue trying to reconnect, but" +
195 " The notebook will continue trying to reconnect, but" +
189 " until it does, you will NOT be able to run code. Check your" +
196 " until it does, you will NOT be able to run code. Check your" +
190 " network connection or notebook server configuration.";
197 " network connection or notebook server configuration.";
191
198
192 dialog.kernel_modal({
199 dialog.kernel_modal({
193 title: "Connection failed",
200 title: "Connection failed",
194 body: msg,
201 body: msg,
195 keyboard_manager: that.keyboard_manager,
202 keyboard_manager: that.keyboard_manager,
196 notebook: that.notebook,
203 notebook: that.notebook,
197 buttons : {
204 buttons : {
198 "OK": {}
205 "OK": {}
199 }
206 }
200 });
207 });
201 }
208 }
202 });
209 });
203
210
204 this.events.on('kernel_killed.Kernel kernel_killed.Session', function () {
211 this.events.on('kernel_killed.Kernel kernel_killed.Session', function () {
205 that.save_widget.update_document_title();
212 that.save_widget.update_document_title();
206 knw.danger("Dead kernel");
213 knw.danger("Dead kernel");
207 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
214 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
208 });
215 });
209
216
210 this.events.on('kernel_dead.Kernel', function () {
217 this.events.on('kernel_dead.Kernel', function () {
211
218
212 var showMsg = function () {
219 var showMsg = function () {
213
220
214 var msg = 'The kernel has died, and the automatic restart has failed.' +
221 var msg = 'The kernel has died, and the automatic restart has failed.' +
215 ' It is possible the kernel cannot be restarted.' +
222 ' It is possible the kernel cannot be restarted.' +
216 ' If you are not able to restart the kernel, you will still be able to save' +
223 ' If you are not able to restart the kernel, you will still be able to save' +
217 ' the notebook, but running code will no longer work until the notebook' +
224 ' the notebook, but running code will no longer work until the notebook' +
218 ' is reopened.';
225 ' is reopened.';
219
226
220 dialog.kernel_modal({
227 dialog.kernel_modal({
221 title: "Dead kernel",
228 title: "Dead kernel",
222 body : msg,
229 body : msg,
223 keyboard_manager: that.keyboard_manager,
230 keyboard_manager: that.keyboard_manager,
224 notebook: that.notebook,
231 notebook: that.notebook,
225 buttons : {
232 buttons : {
226 "Manual Restart": {
233 "Manual Restart": {
227 class: "btn-danger",
234 class: "btn-danger",
228 click: function () {
235 click: function () {
229 that.notebook.start_session();
236 that.notebook.start_session();
230 }
237 }
231 },
238 },
232 "Don't restart": {}
239 "Don't restart": {}
233 }
240 }
234 });
241 });
235
242
236 return false;
243 return false;
237 };
244 };
238
245
239 that.save_widget.update_document_title();
246 that.save_widget.update_document_title();
240 knw.danger("Dead kernel", undefined, showMsg);
247 knw.danger("Dead kernel", undefined, showMsg);
241 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
248 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
242
249
243 showMsg();
250 showMsg();
244 });
251 });
245
252
246 this.events.on('kernel_dead.Session', function (evt, info) {
253 this.events.on('kernel_dead.Session', function (evt, info) {
247 var full = info.xhr.responseJSON.message;
254 var full = info.xhr.responseJSON.message;
248 var short = info.xhr.responseJSON.short_message || 'Kernel error';
255 var short = info.xhr.responseJSON.short_message || 'Kernel error';
249 var traceback = info.xhr.responseJSON.traceback;
256 var traceback = info.xhr.responseJSON.traceback;
250
257
251 var showMsg = function () {
258 var showMsg = function () {
252 var msg = $('<div/>').append($('<p/>').text(full));
259 var msg = $('<div/>').append($('<p/>').text(full));
253 var cm, cm_elem, cm_open;
260 var cm, cm_elem, cm_open;
254
261
255 if (traceback) {
262 if (traceback) {
256 cm_elem = $('<div/>')
263 cm_elem = $('<div/>')
257 .css('margin-top', '1em')
264 .css('margin-top', '1em')
258 .css('padding', '1em')
265 .css('padding', '1em')
259 .addClass('output_scroll');
266 .addClass('output_scroll');
260 msg.append(cm_elem);
267 msg.append(cm_elem);
261 cm = CodeMirror(cm_elem.get(0), {
268 cm = CodeMirror(cm_elem.get(0), {
262 mode: "python",
269 mode: "python",
263 readOnly : true
270 readOnly : true
264 });
271 });
265 cm.setValue(traceback);
272 cm.setValue(traceback);
266 cm_open = $.proxy(cm.refresh, cm);
273 cm_open = $.proxy(cm.refresh, cm);
267 }
274 }
268
275
269 dialog.kernel_modal({
276 dialog.kernel_modal({
270 title: "Failed to start the kernel",
277 title: "Failed to start the kernel",
271 body : msg,
278 body : msg,
272 keyboard_manager: that.keyboard_manager,
279 keyboard_manager: that.keyboard_manager,
273 notebook: that.notebook,
280 notebook: that.notebook,
274 open: cm_open,
281 open: cm_open,
275 buttons : {
282 buttons : {
276 "Ok": { class: 'btn-primary' }
283 "Ok": { class: 'btn-primary' }
277 }
284 }
278 });
285 });
279
286
280 return false;
287 return false;
281 };
288 };
282
289
283 that.save_widget.update_document_title();
290 that.save_widget.update_document_title();
284 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
291 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
285 knw.danger(short, undefined, showMsg);
292 knw.danger(short, undefined, showMsg);
286 });
293 });
287
294
288 this.events.on('kernel_starting.Kernel', function () {
295 this.events.on('kernel_starting.Kernel', function () {
289 window.document.title='(Starting) '+window.document.title;
296 window.document.title='(Starting) '+window.document.title;
290 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
297 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
291 knw.set_message("Kernel starting, please wait...");
298 knw.set_message("Kernel starting, please wait...");
292 });
299 });
293
300
294 this.events.on('kernel_ready.Kernel', function () {
301 this.events.on('kernel_ready.Kernel', function () {
295 that.save_widget.update_document_title();
302 that.save_widget.update_document_title();
296 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
303 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
297 knw.info("Kernel ready", 500);
304 knw.info("Kernel ready", 500);
298 });
305 });
299
306
300 this.events.on('kernel_idle.Kernel', function () {
307 this.events.on('kernel_idle.Kernel', function () {
301 that.save_widget.update_document_title();
308 that.save_widget.update_document_title();
302 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
309 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
303 });
310 });
304
311
305 this.events.on('kernel_busy.Kernel', function () {
312 this.events.on('kernel_busy.Kernel', function () {
306 window.document.title='(Busy) '+window.document.title;
313 window.document.title='(Busy) '+window.document.title;
307 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
314 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
308 });
315 });
309
316
310 // Start the kernel indicator in the busy state, and send a kernel_info request.
317 // Start the kernel indicator in the busy state, and send a kernel_info request.
311 // When the kernel_info reply arrives, the kernel is idle.
318 // When the kernel_info reply arrives, the kernel is idle.
312 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
319 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
313 };
320 };
314
321
315 /**
322 /**
316 * Initialize the notification widget for notebook status messages.
323 * Initialize the notification widget for notebook status messages.
317 *
324 *
318 * @method init_notebook_notification_widget
325 * @method init_notebook_notification_widget
319 */
326 */
320 NotificationArea.prototype.init_notebook_notification_widget = function () {
327 NotificationArea.prototype.init_notebook_notification_widget = function () {
321 var nnw = this.new_notification_widget('notebook');
328 var nnw = this.new_notification_widget('notebook');
322
329
323 // Notebook events
330 // Notebook events
324 this.events.on('notebook_loading.Notebook', function () {
331 this.events.on('notebook_loading.Notebook', function () {
325 nnw.set_message("Loading notebook",500);
332 nnw.set_message("Loading notebook",500);
326 });
333 });
327 this.events.on('notebook_loaded.Notebook', function () {
334 this.events.on('notebook_loaded.Notebook', function () {
328 nnw.set_message("Notebook loaded",500);
335 nnw.set_message("Notebook loaded",500);
329 });
336 });
330 this.events.on('notebook_saving.Notebook', function () {
337 this.events.on('notebook_saving.Notebook', function () {
331 nnw.set_message("Saving notebook",500);
338 nnw.set_message("Saving notebook",500);
332 });
339 });
333 this.events.on('notebook_saved.Notebook', function () {
340 this.events.on('notebook_saved.Notebook', function () {
334 nnw.set_message("Notebook saved",2000);
341 nnw.set_message("Notebook saved",2000);
335 });
342 });
336 this.events.on('notebook_save_failed.Notebook', function (evt, xhr, status, data) {
343 this.events.on('notebook_save_failed.Notebook', function (evt, xhr, status, data) {
337 nnw.warning(data || "Notebook save failed");
344 nnw.warning(data || "Notebook save failed");
338 });
345 });
339
346
340 // Checkpoint events
347 // Checkpoint events
341 this.events.on('checkpoint_created.Notebook', function (evt, data) {
348 this.events.on('checkpoint_created.Notebook', function (evt, data) {
342 var msg = "Checkpoint created";
349 var msg = "Checkpoint created";
343 if (data.last_modified) {
350 if (data.last_modified) {
344 var d = new Date(data.last_modified);
351 var d = new Date(data.last_modified);
345 msg = msg + ": " + moment(d).format("HH:mm:ss");
352 msg = msg + ": " + moment(d).format("HH:mm:ss");
346 }
353 }
347 nnw.set_message(msg, 2000);
354 nnw.set_message(msg, 2000);
348 });
355 });
349 this.events.on('checkpoint_failed.Notebook', function () {
356 this.events.on('checkpoint_failed.Notebook', function () {
350 nnw.warning("Checkpoint failed");
357 nnw.warning("Checkpoint failed");
351 });
358 });
352 this.events.on('checkpoint_deleted.Notebook', function () {
359 this.events.on('checkpoint_deleted.Notebook', function () {
353 nnw.set_message("Checkpoint deleted", 500);
360 nnw.set_message("Checkpoint deleted", 500);
354 });
361 });
355 this.events.on('checkpoint_delete_failed.Notebook', function () {
362 this.events.on('checkpoint_delete_failed.Notebook', function () {
356 nnw.warning("Checkpoint delete failed");
363 nnw.warning("Checkpoint delete failed");
357 });
364 });
358 this.events.on('checkpoint_restoring.Notebook', function () {
365 this.events.on('checkpoint_restoring.Notebook', function () {
359 nnw.set_message("Restoring to checkpoint...", 500);
366 nnw.set_message("Restoring to checkpoint...", 500);
360 });
367 });
361 this.events.on('checkpoint_restore_failed.Notebook', function () {
368 this.events.on('checkpoint_restore_failed.Notebook', function () {
362 nnw.warning("Checkpoint restore failed");
369 nnw.warning("Checkpoint restore failed");
363 });
370 });
364
371
365 // Autosave events
372 // Autosave events
366 this.events.on('autosave_disabled.Notebook', function () {
373 this.events.on('autosave_disabled.Notebook', function () {
367 nnw.set_message("Autosave disabled", 2000);
374 nnw.set_message("Autosave disabled", 2000);
368 });
375 });
369 this.events.on('autosave_enabled.Notebook', function (evt, interval) {
376 this.events.on('autosave_enabled.Notebook', function (evt, interval) {
370 nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
377 nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
371 });
378 });
372 };
379 };
373
380
374 IPython.NotificationArea = NotificationArea;
381 IPython.NotificationArea = NotificationArea;
375
382
376 return {'NotificationArea': NotificationArea};
383 return {'NotificationArea': NotificationArea};
377 });
384 });
@@ -1,1029 +1,1052
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 './comm',
8 './comm',
9 './serialize',
9 './serialize',
10 'widgets/js/init'
10 'widgets/js/init'
11 ], function(IPython, $, utils, comm, serialize, widgetmanager) {
11 ], function(IPython, $, utils, comm, serialize, widgetmanager) {
12 "use strict";
12 "use strict";
13
13
14 /**
14 /**
15 * A Kernel class to communicate with the Python kernel. This
15 * A Kernel class to communicate with the Python kernel. This
16 * should generally not be constructed directly, but be created
16 * should generally not be constructed directly, but be created
17 * by. the `Session` object. Once created, this object should be
17 * by. the `Session` object. Once created, this object should be
18 * used to communicate with the kernel.
18 * used to communicate with the kernel.
19 *
19 *
20 * @class Kernel
20 * @class Kernel
21 * @param {string} kernel_service_url - the URL to access the kernel REST api
21 * @param {string} kernel_service_url - the URL to access the kernel REST api
22 * @param {string} ws_url - the websockets URL
22 * @param {string} ws_url - the websockets URL
23 * @param {Notebook} notebook - notebook object
23 * @param {Notebook} notebook - notebook object
24 * @param {string} name - the kernel type (e.g. python3)
24 * @param {string} name - the kernel type (e.g. python3)
25 */
25 */
26 var Kernel = function (kernel_service_url, ws_url, notebook, name) {
26 var Kernel = function (kernel_service_url, ws_url, notebook, name) {
27 this.events = notebook.events;
27 this.events = notebook.events;
28
28
29 this.id = null;
29 this.id = null;
30 this.name = name;
30 this.name = name;
31
31
32 this.channels = {
32 this.channels = {
33 'shell': null,
33 'shell': null,
34 'iopub': null,
34 'iopub': null,
35 'stdin': null
35 'stdin': null
36 };
36 };
37
37
38 this.kernel_service_url = kernel_service_url;
38 this.kernel_service_url = kernel_service_url;
39 this.kernel_url = null;
39 this.kernel_url = null;
40 this.ws_url = ws_url || IPython.utils.get_body_data("wsUrl");
40 this.ws_url = ws_url || IPython.utils.get_body_data("wsUrl");
41 if (!this.ws_url) {
41 if (!this.ws_url) {
42 // trailing 's' in https will become wss for secure web sockets
42 // trailing 's' in https will become wss for secure web sockets
43 this.ws_url = location.protocol.replace('http', 'ws') + "//" + location.host;
43 this.ws_url = location.protocol.replace('http', 'ws') + "//" + location.host;
44 }
44 }
45
45
46 this.username = "username";
46 this.username = "username";
47 this.session_id = utils.uuid();
47 this.session_id = utils.uuid();
48 this._msg_callbacks = {};
48 this._msg_callbacks = {};
49 this.info_reply = {}; // kernel_info_reply stored here after starting
49 this.info_reply = {}; // kernel_info_reply stored here after starting
50
50
51 if (typeof(WebSocket) !== 'undefined') {
51 if (typeof(WebSocket) !== 'undefined') {
52 this.WebSocket = WebSocket;
52 this.WebSocket = WebSocket;
53 } else if (typeof(MozWebSocket) !== 'undefined') {
53 } else if (typeof(MozWebSocket) !== 'undefined') {
54 this.WebSocket = MozWebSocket;
54 this.WebSocket = MozWebSocket;
55 } else {
55 } else {
56 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
56 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
57 }
57 }
58
58
59 this.bind_events();
59 this.bind_events();
60 this.init_iopub_handlers();
60 this.init_iopub_handlers();
61 this.comm_manager = new comm.CommManager(this);
61 this.comm_manager = new comm.CommManager(this);
62 this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook);
62 this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook);
63
63
64 this.last_msg_id = null;
64 this.last_msg_id = null;
65 this.last_msg_callbacks = {};
65 this.last_msg_callbacks = {};
66
66
67 this._autorestart_attempt = 0;
67 this._autorestart_attempt = 0;
68 this._reconnect_attempt = 0;
68 this._reconnect_attempt = 0;
69 this.reconnect_limit = 7;
69 };
70 };
70
71
71 /**
72 /**
72 * @function _get_msg
73 * @function _get_msg
73 */
74 */
74 Kernel.prototype._get_msg = function (msg_type, content, metadata, buffers) {
75 Kernel.prototype._get_msg = function (msg_type, content, metadata, buffers) {
75 var msg = {
76 var msg = {
76 header : {
77 header : {
77 msg_id : utils.uuid(),
78 msg_id : utils.uuid(),
78 username : this.username,
79 username : this.username,
79 session : this.session_id,
80 session : this.session_id,
80 msg_type : msg_type,
81 msg_type : msg_type,
81 version : "5.0"
82 version : "5.0"
82 },
83 },
83 metadata : metadata || {},
84 metadata : metadata || {},
84 content : content,
85 content : content,
85 buffers : buffers || [],
86 buffers : buffers || [],
86 parent_header : {}
87 parent_header : {}
87 };
88 };
88 return msg;
89 return msg;
89 };
90 };
90
91
91 /**
92 /**
92 * @function bind_events
93 * @function bind_events
93 */
94 */
94 Kernel.prototype.bind_events = function () {
95 Kernel.prototype.bind_events = function () {
95 var that = this;
96 var that = this;
96 this.events.on('send_input_reply.Kernel', function(evt, data) {
97 this.events.on('send_input_reply.Kernel', function(evt, data) {
97 that.send_input_reply(data);
98 that.send_input_reply(data);
98 });
99 });
99
100
100 var record_status = function (evt, info) {
101 var record_status = function (evt, info) {
101 console.log('Kernel: ' + evt.type + ' (' + info.kernel.id + ')');
102 console.log('Kernel: ' + evt.type + ' (' + info.kernel.id + ')');
102 };
103 };
103
104
104 this.events.on('kernel_created.Kernel', record_status);
105 this.events.on('kernel_created.Kernel', record_status);
105 this.events.on('kernel_reconnecting.Kernel', record_status);
106 this.events.on('kernel_reconnecting.Kernel', record_status);
106 this.events.on('kernel_connected.Kernel', record_status);
107 this.events.on('kernel_connected.Kernel', record_status);
107 this.events.on('kernel_starting.Kernel', record_status);
108 this.events.on('kernel_starting.Kernel', record_status);
108 this.events.on('kernel_restarting.Kernel', record_status);
109 this.events.on('kernel_restarting.Kernel', record_status);
109 this.events.on('kernel_autorestarting.Kernel', record_status);
110 this.events.on('kernel_autorestarting.Kernel', record_status);
110 this.events.on('kernel_interrupting.Kernel', record_status);
111 this.events.on('kernel_interrupting.Kernel', record_status);
111 this.events.on('kernel_disconnected.Kernel', record_status);
112 this.events.on('kernel_disconnected.Kernel', record_status);
112 // these are commented out because they are triggered a lot, but can
113 // these are commented out because they are triggered a lot, but can
113 // be uncommented for debugging purposes
114 // be uncommented for debugging purposes
114 //this.events.on('kernel_idle.Kernel', record_status);
115 //this.events.on('kernel_idle.Kernel', record_status);
115 //this.events.on('kernel_busy.Kernel', record_status);
116 //this.events.on('kernel_busy.Kernel', record_status);
116 this.events.on('kernel_ready.Kernel', record_status);
117 this.events.on('kernel_ready.Kernel', record_status);
117 this.events.on('kernel_killed.Kernel', record_status);
118 this.events.on('kernel_killed.Kernel', record_status);
118 this.events.on('kernel_dead.Kernel', record_status);
119 this.events.on('kernel_dead.Kernel', record_status);
119
120
120 this.events.on('kernel_ready.Kernel', function () {
121 this.events.on('kernel_ready.Kernel', function () {
121 that._autorestart_attempt = 0;
122 that._autorestart_attempt = 0;
122 });
123 });
123 this.events.on('kernel_connected.Kernel', function () {
124 this.events.on('kernel_connected.Kernel', function () {
124 that._reconnect_attempt = 0;
125 that._reconnect_attempt = 0;
125 });
126 });
126 };
127 };
127
128
128 /**
129 /**
129 * Initialize the iopub handlers.
130 * Initialize the iopub handlers.
130 *
131 *
131 * @function init_iopub_handlers
132 * @function init_iopub_handlers
132 */
133 */
133 Kernel.prototype.init_iopub_handlers = function () {
134 Kernel.prototype.init_iopub_handlers = function () {
134 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
135 var output_msg_types = ['stream', 'display_data', 'execute_result', 'error'];
135 this._iopub_handlers = {};
136 this._iopub_handlers = {};
136 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
137 this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
137 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
138 this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
138
139
139 for (var i=0; i < output_msg_types.length; i++) {
140 for (var i=0; i < output_msg_types.length; i++) {
140 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
141 this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
141 }
142 }
142 };
143 };
143
144
144 /**
145 /**
145 * GET /api/kernels
146 * GET /api/kernels
146 *
147 *
147 * Get the list of running kernels.
148 * Get the list of running kernels.
148 *
149 *
149 * @function list
150 * @function list
150 * @param {function} [success] - function executed on ajax success
151 * @param {function} [success] - function executed on ajax success
151 * @param {function} [error] - functon executed on ajax error
152 * @param {function} [error] - functon executed on ajax error
152 */
153 */
153 Kernel.prototype.list = function (success, error) {
154 Kernel.prototype.list = function (success, error) {
154 $.ajax(this.kernel_service_url, {
155 $.ajax(this.kernel_service_url, {
155 processData: false,
156 processData: false,
156 cache: false,
157 cache: false,
157 type: "GET",
158 type: "GET",
158 dataType: "json",
159 dataType: "json",
159 success: success,
160 success: success,
160 error: this._on_error(error)
161 error: this._on_error(error)
161 });
162 });
162 };
163 };
163
164
164 /**
165 /**
165 * POST /api/kernels
166 * POST /api/kernels
166 *
167 *
167 * Start a new kernel.
168 * Start a new kernel.
168 *
169 *
169 * In general this shouldn't be used -- the kernel should be
170 * In general this shouldn't be used -- the kernel should be
170 * started through the session API. If you use this function and
171 * started through the session API. If you use this function and
171 * are also using the session API then your session and kernel
172 * are also using the session API then your session and kernel
172 * WILL be out of sync!
173 * WILL be out of sync!
173 *
174 *
174 * @function start
175 * @function start
175 * @param {params} [Object] - parameters to include in the query string
176 * @param {params} [Object] - parameters to include in the query string
176 * @param {function} [success] - function executed on ajax success
177 * @param {function} [success] - function executed on ajax success
177 * @param {function} [error] - functon executed on ajax error
178 * @param {function} [error] - functon executed on ajax error
178 */
179 */
179 Kernel.prototype.start = function (params, success, error) {
180 Kernel.prototype.start = function (params, success, error) {
180 var url = this.kernel_service_url;
181 var url = this.kernel_service_url;
181 var qs = $.param(params || {}); // query string for sage math stuff
182 var qs = $.param(params || {}); // query string for sage math stuff
182 if (qs !== "") {
183 if (qs !== "") {
183 url = url + "?" + qs;
184 url = url + "?" + qs;
184 }
185 }
185
186
186 var that = this;
187 var that = this;
187 var on_success = function (data, status, xhr) {
188 var on_success = function (data, status, xhr) {
188 that.events.trigger('kernel_created.Kernel', {kernel: that});
189 that.events.trigger('kernel_created.Kernel', {kernel: that});
189 that._kernel_created(data);
190 that._kernel_created(data);
190 if (success) {
191 if (success) {
191 success(data, status, xhr);
192 success(data, status, xhr);
192 }
193 }
193 };
194 };
194
195
195 $.ajax(url, {
196 $.ajax(url, {
196 processData: false,
197 processData: false,
197 cache: false,
198 cache: false,
198 type: "POST",
199 type: "POST",
199 data: JSON.stringify({name: this.name}),
200 data: JSON.stringify({name: this.name}),
200 dataType: "json",
201 dataType: "json",
201 success: this._on_success(on_success),
202 success: this._on_success(on_success),
202 error: this._on_error(error)
203 error: this._on_error(error)
203 });
204 });
204
205
205 return url;
206 return url;
206 };
207 };
207
208
208 /**
209 /**
209 * GET /api/kernels/[:kernel_id]
210 * GET /api/kernels/[:kernel_id]
210 *
211 *
211 * Get information about the kernel.
212 * Get information about the kernel.
212 *
213 *
213 * @function get_info
214 * @function get_info
214 * @param {function} [success] - function executed on ajax success
215 * @param {function} [success] - function executed on ajax success
215 * @param {function} [error] - functon executed on ajax error
216 * @param {function} [error] - functon executed on ajax error
216 */
217 */
217 Kernel.prototype.get_info = function (success, error) {
218 Kernel.prototype.get_info = function (success, error) {
218 $.ajax(this.kernel_url, {
219 $.ajax(this.kernel_url, {
219 processData: false,
220 processData: false,
220 cache: false,
221 cache: false,
221 type: "GET",
222 type: "GET",
222 dataType: "json",
223 dataType: "json",
223 success: this._on_success(success),
224 success: this._on_success(success),
224 error: this._on_error(error)
225 error: this._on_error(error)
225 });
226 });
226 };
227 };
227
228
228 /**
229 /**
229 * DELETE /api/kernels/[:kernel_id]
230 * DELETE /api/kernels/[:kernel_id]
230 *
231 *
231 * Shutdown the kernel.
232 * Shutdown the kernel.
232 *
233 *
233 * If you are also using sessions, then this function shoul NOT be
234 * If you are also using sessions, then this function shoul NOT be
234 * used. Instead, use Session.delete. Otherwise, the session and
235 * used. Instead, use Session.delete. Otherwise, the session and
235 * kernel WILL be out of sync.
236 * kernel WILL be out of sync.
236 *
237 *
237 * @function kill
238 * @function kill
238 * @param {function} [success] - function executed on ajax success
239 * @param {function} [success] - function executed on ajax success
239 * @param {function} [error] - functon executed on ajax error
240 * @param {function} [error] - functon executed on ajax error
240 */
241 */
241 Kernel.prototype.kill = function (success, error) {
242 Kernel.prototype.kill = function (success, error) {
242 this.events.trigger('kernel_killed.Kernel', {kernel: this});
243 this.events.trigger('kernel_killed.Kernel', {kernel: this});
243 this._kernel_dead();
244 this._kernel_dead();
244 $.ajax(this.kernel_url, {
245 $.ajax(this.kernel_url, {
245 processData: false,
246 processData: false,
246 cache: false,
247 cache: false,
247 type: "DELETE",
248 type: "DELETE",
248 dataType: "json",
249 dataType: "json",
249 success: this._on_success(success),
250 success: this._on_success(success),
250 error: this._on_error(error)
251 error: this._on_error(error)
251 });
252 });
252 };
253 };
253
254
254 /**
255 /**
255 * POST /api/kernels/[:kernel_id]/interrupt
256 * POST /api/kernels/[:kernel_id]/interrupt
256 *
257 *
257 * Interrupt the kernel.
258 * Interrupt the kernel.
258 *
259 *
259 * @function interrupt
260 * @function interrupt
260 * @param {function} [success] - function executed on ajax success
261 * @param {function} [success] - function executed on ajax success
261 * @param {function} [error] - functon executed on ajax error
262 * @param {function} [error] - functon executed on ajax error
262 */
263 */
263 Kernel.prototype.interrupt = function (success, error) {
264 Kernel.prototype.interrupt = function (success, error) {
264 this.events.trigger('kernel_interrupting.Kernel', {kernel: this});
265 this.events.trigger('kernel_interrupting.Kernel', {kernel: this});
265
266
266 var that = this;
267 var that = this;
267 var on_success = function (data, status, xhr) {
268 var on_success = function (data, status, xhr) {
268 // get kernel info so we know what state the kernel is in
269 // get kernel info so we know what state the kernel is in
269 that.kernel_info();
270 that.kernel_info();
270 if (success) {
271 if (success) {
271 success(data, status, xhr);
272 success(data, status, xhr);
272 }
273 }
273 };
274 };
274
275
275 var url = utils.url_join_encode(this.kernel_url, 'interrupt');
276 var url = utils.url_join_encode(this.kernel_url, 'interrupt');
276 $.ajax(url, {
277 $.ajax(url, {
277 processData: false,
278 processData: false,
278 cache: false,
279 cache: false,
279 type: "POST",
280 type: "POST",
280 dataType: "json",
281 dataType: "json",
281 success: this._on_success(on_success),
282 success: this._on_success(on_success),
282 error: this._on_error(error)
283 error: this._on_error(error)
283 });
284 });
284 };
285 };
285
286
286 /**
287 /**
287 * POST /api/kernels/[:kernel_id]/restart
288 * POST /api/kernels/[:kernel_id]/restart
288 *
289 *
289 * Restart the kernel.
290 * Restart the kernel.
290 *
291 *
291 * @function interrupt
292 * @function interrupt
292 * @param {function} [success] - function executed on ajax success
293 * @param {function} [success] - function executed on ajax success
293 * @param {function} [error] - functon executed on ajax error
294 * @param {function} [error] - functon executed on ajax error
294 */
295 */
295 Kernel.prototype.restart = function (success, error) {
296 Kernel.prototype.restart = function (success, error) {
296 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
297 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
297 this.stop_channels();
298 this.stop_channels();
298
299
299 var that = this;
300 var that = this;
300 var on_success = function (data, status, xhr) {
301 var on_success = function (data, status, xhr) {
301 that.events.trigger('kernel_created.Kernel', {kernel: that});
302 that.events.trigger('kernel_created.Kernel', {kernel: that});
302 that._kernel_created(data);
303 that._kernel_created(data);
303 if (success) {
304 if (success) {
304 success(data, status, xhr);
305 success(data, status, xhr);
305 }
306 }
306 };
307 };
307
308
308 var on_error = function (xhr, status, err) {
309 var on_error = function (xhr, status, err) {
309 that.events.trigger('kernel_dead.Kernel', {kernel: that});
310 that.events.trigger('kernel_dead.Kernel', {kernel: that});
310 that._kernel_dead();
311 that._kernel_dead();
311 if (error) {
312 if (error) {
312 error(xhr, status, err);
313 error(xhr, status, err);
313 }
314 }
314 };
315 };
315
316
316 var url = utils.url_join_encode(this.kernel_url, 'restart');
317 var url = utils.url_join_encode(this.kernel_url, 'restart');
317 $.ajax(url, {
318 $.ajax(url, {
318 processData: false,
319 processData: false,
319 cache: false,
320 cache: false,
320 type: "POST",
321 type: "POST",
321 dataType: "json",
322 dataType: "json",
322 success: this._on_success(on_success),
323 success: this._on_success(on_success),
323 error: this._on_error(on_error)
324 error: this._on_error(on_error)
324 });
325 });
325 };
326 };
326
327
327 /**
328 /**
328 * Reconnect to a disconnected kernel. This is not actually a
329 * Reconnect to a disconnected kernel. This is not actually a
329 * standard HTTP request, but useful function nonetheless for
330 * standard HTTP request, but useful function nonetheless for
330 * reconnecting to the kernel if the connection is somehow lost.
331 * reconnecting to the kernel if the connection is somehow lost.
331 *
332 *
332 * @function reconnect
333 * @function reconnect
333 */
334 */
334 Kernel.prototype.reconnect = function () {
335 Kernel.prototype.reconnect = function () {
335 this.events.trigger('kernel_reconnecting.Kernel', {kernel: this});
336 if (this.is_connected()) {
336 setTimeout($.proxy(this.start_channels, this), 3000);
337 return;
338 }
339 this._reconnect_attempt = this._reconnect_attempt + 1;
340 this.events.trigger('kernel_reconnecting.Kernel', {
341 kernel: this,
342 attempt: this._reconnect_attempt,
343 });
344 this.start_channels();
337 };
345 };
338
346
339 /**
347 /**
340 * Handle a successful AJAX request by updating the kernel id and
348 * Handle a successful AJAX request by updating the kernel id and
341 * name from the response, and then optionally calling a provided
349 * name from the response, and then optionally calling a provided
342 * callback.
350 * callback.
343 *
351 *
344 * @function _on_success
352 * @function _on_success
345 * @param {function} success - callback
353 * @param {function} success - callback
346 */
354 */
347 Kernel.prototype._on_success = function (success) {
355 Kernel.prototype._on_success = function (success) {
348 var that = this;
356 var that = this;
349 return function (data, status, xhr) {
357 return function (data, status, xhr) {
350 if (data) {
358 if (data) {
351 that.id = data.id;
359 that.id = data.id;
352 that.name = data.name;
360 that.name = data.name;
353 }
361 }
354 that.kernel_url = utils.url_join_encode(that.kernel_service_url, that.id);
362 that.kernel_url = utils.url_join_encode(that.kernel_service_url, that.id);
355 if (success) {
363 if (success) {
356 success(data, status, xhr);
364 success(data, status, xhr);
357 }
365 }
358 };
366 };
359 };
367 };
360
368
361 /**
369 /**
362 * Handle a failed AJAX request by logging the error message, and
370 * Handle a failed AJAX request by logging the error message, and
363 * then optionally calling a provided callback.
371 * then optionally calling a provided callback.
364 *
372 *
365 * @function _on_error
373 * @function _on_error
366 * @param {function} error - callback
374 * @param {function} error - callback
367 */
375 */
368 Kernel.prototype._on_error = function (error) {
376 Kernel.prototype._on_error = function (error) {
369 return function (xhr, status, err) {
377 return function (xhr, status, err) {
370 utils.log_ajax_error(xhr, status, err);
378 utils.log_ajax_error(xhr, status, err);
371 if (error) {
379 if (error) {
372 error(xhr, status, err);
380 error(xhr, status, err);
373 }
381 }
374 };
382 };
375 };
383 };
376
384
377 /**
385 /**
378 * Perform necessary tasks once the kernel has been started,
386 * Perform necessary tasks once the kernel has been started,
379 * including actually connecting to the kernel.
387 * including actually connecting to the kernel.
380 *
388 *
381 * @function _kernel_created
389 * @function _kernel_created
382 * @param {Object} data - information about the kernel including id
390 * @param {Object} data - information about the kernel including id
383 */
391 */
384 Kernel.prototype._kernel_created = function (data) {
392 Kernel.prototype._kernel_created = function (data) {
385 this.id = data.id;
393 this.id = data.id;
386 this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id);
394 this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id);
387 this.start_channels();
395 this.start_channels();
388 };
396 };
389
397
390 /**
398 /**
391 * Perform necessary tasks once the connection to the kernel has
399 * Perform necessary tasks once the connection to the kernel has
392 * been established. This includes requesting information about
400 * been established. This includes requesting information about
393 * the kernel.
401 * the kernel.
394 *
402 *
395 * @function _kernel_connected
403 * @function _kernel_connected
396 */
404 */
397 Kernel.prototype._kernel_connected = function () {
405 Kernel.prototype._kernel_connected = function () {
398 this.events.trigger('kernel_connected.Kernel', {kernel: this});
406 this.events.trigger('kernel_connected.Kernel', {kernel: this});
399 this.events.trigger('kernel_starting.Kernel', {kernel: this});
407 this.events.trigger('kernel_starting.Kernel', {kernel: this});
400 // get kernel info so we know what state the kernel is in
408 // get kernel info so we know what state the kernel is in
401 var that = this;
409 var that = this;
402 this.kernel_info(function (reply) {
410 this.kernel_info(function (reply) {
403 that.info_reply = reply.content;
411 that.info_reply = reply.content;
404 that.events.trigger('kernel_ready.Kernel', {kernel: that});
412 that.events.trigger('kernel_ready.Kernel', {kernel: that});
405 });
413 });
406 };
414 };
407
415
408 /**
416 /**
409 * Perform necessary tasks after the kernel has died. This closing
417 * Perform necessary tasks after the kernel has died. This closing
410 * communication channels to the kernel if they are still somehow
418 * communication channels to the kernel if they are still somehow
411 * open.
419 * open.
412 *
420 *
413 * @function _kernel_dead
421 * @function _kernel_dead
414 */
422 */
415 Kernel.prototype._kernel_dead = function () {
423 Kernel.prototype._kernel_dead = function () {
416 this.stop_channels();
424 this.stop_channels();
417 };
425 };
418
426
419 /**
427 /**
420 * Start the `shell`and `iopub` channels.
428 * Start the `shell`and `iopub` channels.
421 * Will stop and restart them if they already exist.
429 * Will stop and restart them if they already exist.
422 *
430 *
423 * @function start_channels
431 * @function start_channels
424 */
432 */
425 Kernel.prototype.start_channels = function () {
433 Kernel.prototype.start_channels = function () {
426 var that = this;
434 var that = this;
427 this.stop_channels();
435 this.stop_channels();
428 var ws_host_url = this.ws_url + this.kernel_url;
436 var ws_host_url = this.ws_url + this.kernel_url;
429
437
430 console.log("Starting WebSockets:", ws_host_url);
438 console.log("Starting WebSockets:", ws_host_url);
431
439
432 var channel_url = function(channel) {
440 var channel_url = function(channel) {
433 return [
441 return [
434 that.ws_url,
442 that.ws_url,
435 utils.url_join_encode(that.kernel_url, channel),
443 utils.url_join_encode(that.kernel_url, channel),
436 "?session_id=" + that.session_id
444 "?session_id=" + that.session_id
437 ].join('');
445 ].join('');
438 };
446 };
439 this.channels.shell = new this.WebSocket(channel_url("shell"));
447 this.channels.shell = new this.WebSocket(channel_url("shell"));
440 this.channels.stdin = new this.WebSocket(channel_url("stdin"));
448 this.channels.stdin = new this.WebSocket(channel_url("stdin"));
441 this.channels.iopub = new this.WebSocket(channel_url("iopub"));
449 this.channels.iopub = new this.WebSocket(channel_url("iopub"));
442
450
443 var already_called_onclose = false; // only alert once
451 var already_called_onclose = false; // only alert once
444 var ws_closed_early = function(evt){
452 var ws_closed_early = function(evt){
445 if (already_called_onclose){
453 if (already_called_onclose){
446 return;
454 return;
447 }
455 }
448 already_called_onclose = true;
456 already_called_onclose = true;
449 if ( ! evt.wasClean ){
457 if ( ! evt.wasClean ){
450 // If the websocket was closed early, that could mean
458 // If the websocket was closed early, that could mean
451 // that the kernel is actually dead. Try getting
459 // that the kernel is actually dead. Try getting
452 // information about the kernel from the API call --
460 // information about the kernel from the API call --
453 // if that fails, then assume the kernel is dead,
461 // if that fails, then assume the kernel is dead,
454 // otherwise just follow the typical websocket closed
462 // otherwise just follow the typical websocket closed
455 // protocol.
463 // protocol.
456 that.get_info(function () {
464 that.get_info(function () {
457 that._ws_closed(ws_host_url, false);
465 that._ws_closed(ws_host_url, false);
458 }, function () {
466 }, function () {
459 that.events.trigger('kernel_dead.Kernel', {kernel: that});
467 that.events.trigger('kernel_dead.Kernel', {kernel: that});
460 that._kernel_dead();
468 that._kernel_dead();
461 });
469 });
462 }
470 }
463 };
471 };
464 var ws_closed_late = function(evt){
472 var ws_closed_late = function(evt){
465 if (already_called_onclose){
473 if (already_called_onclose){
466 return;
474 return;
467 }
475 }
468 already_called_onclose = true;
476 already_called_onclose = true;
469 if ( ! evt.wasClean ){
477 if ( ! evt.wasClean ){
470 that._ws_closed(ws_host_url, false);
478 that._ws_closed(ws_host_url, false);
471 }
479 }
472 };
480 };
473 var ws_error = function(evt){
481 var ws_error = function(evt){
474 if (already_called_onclose){
482 if (already_called_onclose){
475 return;
483 return;
476 }
484 }
477 already_called_onclose = true;
485 already_called_onclose = true;
478 that._ws_closed(ws_host_url, true);
486 that._ws_closed(ws_host_url, true);
479 };
487 };
480
488
481 for (var c in this.channels) {
489 for (var c in this.channels) {
482 this.channels[c].onopen = $.proxy(this._ws_opened, this);
490 this.channels[c].onopen = $.proxy(this._ws_opened, this);
483 this.channels[c].onclose = ws_closed_early;
491 this.channels[c].onclose = ws_closed_early;
484 this.channels[c].onerror = ws_error;
492 this.channels[c].onerror = ws_error;
485 }
493 }
486 // switch from early-close to late-close message after 1s
494 // switch from early-close to late-close message after 1s
487 setTimeout(function() {
495 setTimeout(function() {
488 for (var c in that.channels) {
496 for (var c in that.channels) {
489 if (that.channels[c] !== null) {
497 if (that.channels[c] !== null) {
490 that.channels[c].onclose = ws_closed_late;
498 that.channels[c].onclose = ws_closed_late;
491 }
499 }
492 }
500 }
493 }, 1000);
501 }, 1000);
494 this.channels.shell.onmessage = $.proxy(this._handle_shell_reply, this);
502 this.channels.shell.onmessage = $.proxy(this._handle_shell_reply, this);
495 this.channels.iopub.onmessage = $.proxy(this._handle_iopub_message, this);
503 this.channels.iopub.onmessage = $.proxy(this._handle_iopub_message, this);
496 this.channels.stdin.onmessage = $.proxy(this._handle_input_request, this);
504 this.channels.stdin.onmessage = $.proxy(this._handle_input_request, this);
497 };
505 };
498
506
499 /**
507 /**
500 * Handle a websocket entering the open state,
508 * Handle a websocket entering the open state,
501 * signaling that the kernel is connected when all channels are open.
509 * signaling that the kernel is connected when all channels are open.
502 *
510 *
503 * @function _ws_opened
511 * @function _ws_opened
504 */
512 */
505 Kernel.prototype._ws_opened = function (evt) {
513 Kernel.prototype._ws_opened = function (evt) {
506 if (this.is_connected()) {
514 if (this.is_connected()) {
507 // all events ready, trigger started event.
515 // all events ready, trigger started event.
508 this._kernel_connected();
516 this._kernel_connected();
509 }
517 }
510 };
518 };
511
519
512 /**
520 /**
513 * Handle a websocket entering the closed state. This closes the
521 * Handle a websocket entering the closed state. This closes the
514 * other communication channels if they are open. If the websocket
522 * other communication channels if they are open. If the websocket
515 * was not closed due to an error, try to reconnect to the kernel.
523 * was not closed due to an error, try to reconnect to the kernel.
516 *
524 *
517 * @function _ws_closed
525 * @function _ws_closed
518 * @param {string} ws_url - the websocket url
526 * @param {string} ws_url - the websocket url
519 * @param {bool} error - whether the connection was closed due to an error
527 * @param {bool} error - whether the connection was closed due to an error
520 */
528 */
521 Kernel.prototype._ws_closed = function(ws_url, error) {
529 Kernel.prototype._ws_closed = function(ws_url, error) {
522 this.stop_channels();
530 this.stop_channels();
523
531
524 this.events.trigger('kernel_disconnected.Kernel', {kernel: this});
532 this.events.trigger('kernel_disconnected.Kernel', {kernel: this});
525 if (error) {
533 if (error) {
526 console.log('WebSocket connection failed: ', ws_url);
534 console.log('WebSocket connection failed: ', ws_url);
527 this._reconnect_attempt = this._reconnect_attempt + 1;
528 this.events.trigger('kernel_connection_failed.Kernel', {kernel: this, ws_url: ws_url, attempt: this._reconnect_attempt});
535 this.events.trigger('kernel_connection_failed.Kernel', {kernel: this, ws_url: ws_url, attempt: this._reconnect_attempt});
529 }
536 }
530 this.reconnect();
537 this._schedule_reconnect();
531 };
538 };
532
539
540 Kernel.prototype._schedule_reconnect = function () {
541 // function to call when kernel connection is lost
542 // schedules reconnect, or fires 'connection_dead' if reconnect limit is hit
543 if (this._reconnect_attempt < this.reconnect_limit) {
544 var timeout = Math.pow(2, this._reconnect_attempt);
545 console.log("Connection lost, reconnecting in " + timeout + " seconds.");
546 setTimeout($.proxy(this.reconnect, this), 1e3 * timeout);
547 } else {
548 this.events.trigger('kernel_connection_dead.Kernel', {
549 kernel: this,
550 reconnect_attempt: this._reconnect_attempt,
551 });
552 console.log("Failed to reconnect, giving up.");
553 }
554 };
555
533 /**
556 /**
534 * Close the websocket channels. After successful close, the value
557 * Close the websocket channels. After successful close, the value
535 * in `this.channels[channel_name]` will be null.
558 * in `this.channels[channel_name]` will be null.
536 *
559 *
537 * @function stop_channels
560 * @function stop_channels
538 */
561 */
539 Kernel.prototype.stop_channels = function () {
562 Kernel.prototype.stop_channels = function () {
540 var that = this;
563 var that = this;
541 var close = function (c) {
564 var close = function (c) {
542 return function () {
565 return function () {
543 if (that.channels[c] && that.channels[c].readyState === WebSocket.CLOSED) {
566 if (that.channels[c] && that.channels[c].readyState === WebSocket.CLOSED) {
544 that.channels[c] = null;
567 that.channels[c] = null;
545 }
568 }
546 };
569 };
547 };
570 };
548 for (var c in this.channels) {
571 for (var c in this.channels) {
549 if ( this.channels[c] !== null ) {
572 if ( this.channels[c] !== null ) {
550 if (this.channels[c].readyState === WebSocket.OPEN) {
573 if (this.channels[c].readyState === WebSocket.OPEN) {
551 this.channels[c].onclose = close(c);
574 this.channels[c].onclose = close(c);
552 this.channels[c].close();
575 this.channels[c].close();
553 } else {
576 } else {
554 close(c)();
577 close(c)();
555 }
578 }
556 }
579 }
557 }
580 }
558 };
581 };
559
582
560 /**
583 /**
561 * Check whether there is a connection to the kernel. This
584 * Check whether there is a connection to the kernel. This
562 * function only returns true if all channel objects have been
585 * function only returns true if all channel objects have been
563 * created and have a state of WebSocket.OPEN.
586 * created and have a state of WebSocket.OPEN.
564 *
587 *
565 * @function is_connected
588 * @function is_connected
566 * @returns {bool} - whether there is a connection
589 * @returns {bool} - whether there is a connection
567 */
590 */
568 Kernel.prototype.is_connected = function () {
591 Kernel.prototype.is_connected = function () {
569 for (var c in this.channels) {
592 for (var c in this.channels) {
570 // if any channel is not ready, then we're not connected
593 // if any channel is not ready, then we're not connected
571 if (this.channels[c] === null) {
594 if (this.channels[c] === null) {
572 return false;
595 return false;
573 }
596 }
574 if (this.channels[c].readyState !== WebSocket.OPEN) {
597 if (this.channels[c].readyState !== WebSocket.OPEN) {
575 return false;
598 return false;
576 }
599 }
577 }
600 }
578 return true;
601 return true;
579 };
602 };
580
603
581 /**
604 /**
582 * Check whether the connection to the kernel has been completely
605 * Check whether the connection to the kernel has been completely
583 * severed. This function only returns true if all channel objects
606 * severed. This function only returns true if all channel objects
584 * are null.
607 * are null.
585 *
608 *
586 * @function is_fully_disconnected
609 * @function is_fully_disconnected
587 * @returns {bool} - whether the kernel is fully disconnected
610 * @returns {bool} - whether the kernel is fully disconnected
588 */
611 */
589 Kernel.prototype.is_fully_disconnected = function () {
612 Kernel.prototype.is_fully_disconnected = function () {
590 for (var c in this.channels) {
613 for (var c in this.channels) {
591 if (this.channels[c] === null) {
614 if (this.channels[c] === null) {
592 return true;
615 return true;
593 }
616 }
594 }
617 }
595 return false;
618 return false;
596 };
619 };
597
620
598 /**
621 /**
599 * Send a message on the Kernel's shell channel
622 * Send a message on the Kernel's shell channel
600 *
623 *
601 * @function send_shell_message
624 * @function send_shell_message
602 */
625 */
603 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
626 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
604 if (!this.is_connected()) {
627 if (!this.is_connected()) {
605 throw new Error("kernel is not connected");
628 throw new Error("kernel is not connected");
606 }
629 }
607 var msg = this._get_msg(msg_type, content, metadata, buffers);
630 var msg = this._get_msg(msg_type, content, metadata, buffers);
608 this.channels.shell.send(serialize.serialize(msg));
631 this.channels.shell.send(serialize.serialize(msg));
609 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
632 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
610 return msg.header.msg_id;
633 return msg.header.msg_id;
611 };
634 };
612
635
613 /**
636 /**
614 * Get kernel info
637 * Get kernel info
615 *
638 *
616 * @function kernel_info
639 * @function kernel_info
617 * @param callback {function}
640 * @param callback {function}
618 *
641 *
619 * When calling this method, pass a callback function that expects one argument.
642 * When calling this method, pass a callback function that expects one argument.
620 * The callback will be passed the complete `kernel_info_reply` message documented
643 * The callback will be passed the complete `kernel_info_reply` message documented
621 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
644 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
622 */
645 */
623 Kernel.prototype.kernel_info = function (callback) {
646 Kernel.prototype.kernel_info = function (callback) {
624 var callbacks;
647 var callbacks;
625 if (callback) {
648 if (callback) {
626 callbacks = { shell : { reply : callback } };
649 callbacks = { shell : { reply : callback } };
627 }
650 }
628 return this.send_shell_message("kernel_info_request", {}, callbacks);
651 return this.send_shell_message("kernel_info_request", {}, callbacks);
629 };
652 };
630
653
631 /**
654 /**
632 * Get info on an object
655 * Get info on an object
633 *
656 *
634 * When calling this method, pass a callback function that expects one argument.
657 * When calling this method, pass a callback function that expects one argument.
635 * The callback will be passed the complete `inspect_reply` message documented
658 * The callback will be passed the complete `inspect_reply` message documented
636 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
659 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
637 *
660 *
638 * @function inspect
661 * @function inspect
639 * @param code {string}
662 * @param code {string}
640 * @param cursor_pos {integer}
663 * @param cursor_pos {integer}
641 * @param callback {function}
664 * @param callback {function}
642 */
665 */
643 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
666 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
644 var callbacks;
667 var callbacks;
645 if (callback) {
668 if (callback) {
646 callbacks = { shell : { reply : callback } };
669 callbacks = { shell : { reply : callback } };
647 }
670 }
648
671
649 var content = {
672 var content = {
650 code : code,
673 code : code,
651 cursor_pos : cursor_pos,
674 cursor_pos : cursor_pos,
652 detail_level : 0
675 detail_level : 0
653 };
676 };
654 return this.send_shell_message("inspect_request", content, callbacks);
677 return this.send_shell_message("inspect_request", content, callbacks);
655 };
678 };
656
679
657 /**
680 /**
658 * Execute given code into kernel, and pass result to callback.
681 * Execute given code into kernel, and pass result to callback.
659 *
682 *
660 * @async
683 * @async
661 * @function execute
684 * @function execute
662 * @param {string} code
685 * @param {string} code
663 * @param [callbacks] {Object} With the following keys (all optional)
686 * @param [callbacks] {Object} With the following keys (all optional)
664 * @param callbacks.shell.reply {function}
687 * @param callbacks.shell.reply {function}
665 * @param callbacks.shell.payload.[payload_name] {function}
688 * @param callbacks.shell.payload.[payload_name] {function}
666 * @param callbacks.iopub.output {function}
689 * @param callbacks.iopub.output {function}
667 * @param callbacks.iopub.clear_output {function}
690 * @param callbacks.iopub.clear_output {function}
668 * @param callbacks.input {function}
691 * @param callbacks.input {function}
669 * @param {object} [options]
692 * @param {object} [options]
670 * @param [options.silent=false] {Boolean}
693 * @param [options.silent=false] {Boolean}
671 * @param [options.user_expressions=empty_dict] {Dict}
694 * @param [options.user_expressions=empty_dict] {Dict}
672 * @param [options.allow_stdin=false] {Boolean} true|false
695 * @param [options.allow_stdin=false] {Boolean} true|false
673 *
696 *
674 * @example
697 * @example
675 *
698 *
676 * The options object should contain the options for the execute
699 * The options object should contain the options for the execute
677 * call. Its default values are:
700 * call. Its default values are:
678 *
701 *
679 * options = {
702 * options = {
680 * silent : true,
703 * silent : true,
681 * user_expressions : {},
704 * user_expressions : {},
682 * allow_stdin : false
705 * allow_stdin : false
683 * }
706 * }
684 *
707 *
685 * When calling this method pass a callbacks structure of the
708 * When calling this method pass a callbacks structure of the
686 * form:
709 * form:
687 *
710 *
688 * callbacks = {
711 * callbacks = {
689 * shell : {
712 * shell : {
690 * reply : execute_reply_callback,
713 * reply : execute_reply_callback,
691 * payload : {
714 * payload : {
692 * set_next_input : set_next_input_callback,
715 * set_next_input : set_next_input_callback,
693 * }
716 * }
694 * },
717 * },
695 * iopub : {
718 * iopub : {
696 * output : output_callback,
719 * output : output_callback,
697 * clear_output : clear_output_callback,
720 * clear_output : clear_output_callback,
698 * },
721 * },
699 * input : raw_input_callback
722 * input : raw_input_callback
700 * }
723 * }
701 *
724 *
702 * Each callback will be passed the entire message as a single
725 * Each callback will be passed the entire message as a single
703 * arugment. Payload handlers will be passed the corresponding
726 * arugment. Payload handlers will be passed the corresponding
704 * payload and the execute_reply message.
727 * payload and the execute_reply message.
705 */
728 */
706 Kernel.prototype.execute = function (code, callbacks, options) {
729 Kernel.prototype.execute = function (code, callbacks, options) {
707 var content = {
730 var content = {
708 code : code,
731 code : code,
709 silent : true,
732 silent : true,
710 store_history : false,
733 store_history : false,
711 user_expressions : {},
734 user_expressions : {},
712 allow_stdin : false
735 allow_stdin : false
713 };
736 };
714 callbacks = callbacks || {};
737 callbacks = callbacks || {};
715 if (callbacks.input !== undefined) {
738 if (callbacks.input !== undefined) {
716 content.allow_stdin = true;
739 content.allow_stdin = true;
717 }
740 }
718 $.extend(true, content, options);
741 $.extend(true, content, options);
719 this.events.trigger('execution_request.Kernel', {kernel: this, content: content});
742 this.events.trigger('execution_request.Kernel', {kernel: this, content: content});
720 return this.send_shell_message("execute_request", content, callbacks);
743 return this.send_shell_message("execute_request", content, callbacks);
721 };
744 };
722
745
723 /**
746 /**
724 * When calling this method, pass a function to be called with the
747 * When calling this method, pass a function to be called with the
725 * `complete_reply` message as its only argument when it arrives.
748 * `complete_reply` message as its only argument when it arrives.
726 *
749 *
727 * `complete_reply` is documented
750 * `complete_reply` is documented
728 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
751 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
729 *
752 *
730 * @function complete
753 * @function complete
731 * @param code {string}
754 * @param code {string}
732 * @param cursor_pos {integer}
755 * @param cursor_pos {integer}
733 * @param callback {function}
756 * @param callback {function}
734 */
757 */
735 Kernel.prototype.complete = function (code, cursor_pos, callback) {
758 Kernel.prototype.complete = function (code, cursor_pos, callback) {
736 var callbacks;
759 var callbacks;
737 if (callback) {
760 if (callback) {
738 callbacks = { shell : { reply : callback } };
761 callbacks = { shell : { reply : callback } };
739 }
762 }
740 var content = {
763 var content = {
741 code : code,
764 code : code,
742 cursor_pos : cursor_pos
765 cursor_pos : cursor_pos
743 };
766 };
744 return this.send_shell_message("complete_request", content, callbacks);
767 return this.send_shell_message("complete_request", content, callbacks);
745 };
768 };
746
769
747 /**
770 /**
748 * @function send_input_reply
771 * @function send_input_reply
749 */
772 */
750 Kernel.prototype.send_input_reply = function (input) {
773 Kernel.prototype.send_input_reply = function (input) {
751 if (!this.is_connected()) {
774 if (!this.is_connected()) {
752 throw new Error("kernel is not connected");
775 throw new Error("kernel is not connected");
753 }
776 }
754 var content = {
777 var content = {
755 value : input
778 value : input
756 };
779 };
757 this.events.trigger('input_reply.Kernel', {kernel: this, content: content});
780 this.events.trigger('input_reply.Kernel', {kernel: this, content: content});
758 var msg = this._get_msg("input_reply", content);
781 var msg = this._get_msg("input_reply", content);
759 this.channels.stdin.send(serialize.serialize(msg));
782 this.channels.stdin.send(serialize.serialize(msg));
760 return msg.header.msg_id;
783 return msg.header.msg_id;
761 };
784 };
762
785
763 /**
786 /**
764 * @function register_iopub_handler
787 * @function register_iopub_handler
765 */
788 */
766 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
789 Kernel.prototype.register_iopub_handler = function (msg_type, callback) {
767 this._iopub_handlers[msg_type] = callback;
790 this._iopub_handlers[msg_type] = callback;
768 };
791 };
769
792
770 /**
793 /**
771 * Get the iopub handler for a specific message type.
794 * Get the iopub handler for a specific message type.
772 *
795 *
773 * @function get_iopub_handler
796 * @function get_iopub_handler
774 */
797 */
775 Kernel.prototype.get_iopub_handler = function (msg_type) {
798 Kernel.prototype.get_iopub_handler = function (msg_type) {
776 return this._iopub_handlers[msg_type];
799 return this._iopub_handlers[msg_type];
777 };
800 };
778
801
779 /**
802 /**
780 * Get callbacks for a specific message.
803 * Get callbacks for a specific message.
781 *
804 *
782 * @function get_callbacks_for_msg
805 * @function get_callbacks_for_msg
783 */
806 */
784 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
807 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
785 if (msg_id == this.last_msg_id) {
808 if (msg_id == this.last_msg_id) {
786 return this.last_msg_callbacks;
809 return this.last_msg_callbacks;
787 } else {
810 } else {
788 return this._msg_callbacks[msg_id];
811 return this._msg_callbacks[msg_id];
789 }
812 }
790 };
813 };
791
814
792 /**
815 /**
793 * Clear callbacks for a specific message.
816 * Clear callbacks for a specific message.
794 *
817 *
795 * @function clear_callbacks_for_msg
818 * @function clear_callbacks_for_msg
796 */
819 */
797 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
820 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
798 if (this._msg_callbacks[msg_id] !== undefined ) {
821 if (this._msg_callbacks[msg_id] !== undefined ) {
799 delete this._msg_callbacks[msg_id];
822 delete this._msg_callbacks[msg_id];
800 }
823 }
801 };
824 };
802
825
803 /**
826 /**
804 * @function _finish_shell
827 * @function _finish_shell
805 */
828 */
806 Kernel.prototype._finish_shell = function (msg_id) {
829 Kernel.prototype._finish_shell = function (msg_id) {
807 var callbacks = this._msg_callbacks[msg_id];
830 var callbacks = this._msg_callbacks[msg_id];
808 if (callbacks !== undefined) {
831 if (callbacks !== undefined) {
809 callbacks.shell_done = true;
832 callbacks.shell_done = true;
810 if (callbacks.iopub_done) {
833 if (callbacks.iopub_done) {
811 this.clear_callbacks_for_msg(msg_id);
834 this.clear_callbacks_for_msg(msg_id);
812 }
835 }
813 }
836 }
814 };
837 };
815
838
816 /**
839 /**
817 * @function _finish_iopub
840 * @function _finish_iopub
818 */
841 */
819 Kernel.prototype._finish_iopub = function (msg_id) {
842 Kernel.prototype._finish_iopub = function (msg_id) {
820 var callbacks = this._msg_callbacks[msg_id];
843 var callbacks = this._msg_callbacks[msg_id];
821 if (callbacks !== undefined) {
844 if (callbacks !== undefined) {
822 callbacks.iopub_done = true;
845 callbacks.iopub_done = true;
823 if (callbacks.shell_done) {
846 if (callbacks.shell_done) {
824 this.clear_callbacks_for_msg(msg_id);
847 this.clear_callbacks_for_msg(msg_id);
825 }
848 }
826 }
849 }
827 };
850 };
828
851
829 /**
852 /**
830 * Set callbacks for a particular message.
853 * Set callbacks for a particular message.
831 * Callbacks should be a struct of the following form:
854 * Callbacks should be a struct of the following form:
832 * shell : {
855 * shell : {
833 *
856 *
834 * }
857 * }
835 *
858 *
836 * @function set_callbacks_for_msg
859 * @function set_callbacks_for_msg
837 */
860 */
838 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
861 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
839 this.last_msg_id = msg_id;
862 this.last_msg_id = msg_id;
840 if (callbacks) {
863 if (callbacks) {
841 // shallow-copy mapping, because we will modify it at the top level
864 // shallow-copy mapping, because we will modify it at the top level
842 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
865 var cbcopy = this._msg_callbacks[msg_id] = this.last_msg_callbacks = {};
843 cbcopy.shell = callbacks.shell;
866 cbcopy.shell = callbacks.shell;
844 cbcopy.iopub = callbacks.iopub;
867 cbcopy.iopub = callbacks.iopub;
845 cbcopy.input = callbacks.input;
868 cbcopy.input = callbacks.input;
846 cbcopy.shell_done = (!callbacks.shell);
869 cbcopy.shell_done = (!callbacks.shell);
847 cbcopy.iopub_done = (!callbacks.iopub);
870 cbcopy.iopub_done = (!callbacks.iopub);
848 } else {
871 } else {
849 this.last_msg_callbacks = {};
872 this.last_msg_callbacks = {};
850 }
873 }
851 };
874 };
852
875
853 /**
876 /**
854 * @function _handle_shell_reply
877 * @function _handle_shell_reply
855 */
878 */
856 Kernel.prototype._handle_shell_reply = function (e) {
879 Kernel.prototype._handle_shell_reply = function (e) {
857 serialize.deserialize(e.data, $.proxy(this._finish_shell_reply, this));
880 serialize.deserialize(e.data, $.proxy(this._finish_shell_reply, this));
858 };
881 };
859
882
860 Kernel.prototype._finish_shell_reply = function (reply) {
883 Kernel.prototype._finish_shell_reply = function (reply) {
861 this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
884 this.events.trigger('shell_reply.Kernel', {kernel: this, reply:reply});
862 var content = reply.content;
885 var content = reply.content;
863 var metadata = reply.metadata;
886 var metadata = reply.metadata;
864 var parent_id = reply.parent_header.msg_id;
887 var parent_id = reply.parent_header.msg_id;
865 var callbacks = this.get_callbacks_for_msg(parent_id);
888 var callbacks = this.get_callbacks_for_msg(parent_id);
866 if (!callbacks || !callbacks.shell) {
889 if (!callbacks || !callbacks.shell) {
867 return;
890 return;
868 }
891 }
869 var shell_callbacks = callbacks.shell;
892 var shell_callbacks = callbacks.shell;
870
893
871 // signal that shell callbacks are done
894 // signal that shell callbacks are done
872 this._finish_shell(parent_id);
895 this._finish_shell(parent_id);
873
896
874 if (shell_callbacks.reply !== undefined) {
897 if (shell_callbacks.reply !== undefined) {
875 shell_callbacks.reply(reply);
898 shell_callbacks.reply(reply);
876 }
899 }
877 if (content.payload && shell_callbacks.payload) {
900 if (content.payload && shell_callbacks.payload) {
878 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
901 this._handle_payloads(content.payload, shell_callbacks.payload, reply);
879 }
902 }
880 };
903 };
881
904
882 /**
905 /**
883 * @function _handle_payloads
906 * @function _handle_payloads
884 */
907 */
885 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
908 Kernel.prototype._handle_payloads = function (payloads, payload_callbacks, msg) {
886 var l = payloads.length;
909 var l = payloads.length;
887 // Payloads are handled by triggering events because we don't want the Kernel
910 // Payloads are handled by triggering events because we don't want the Kernel
888 // to depend on the Notebook or Pager classes.
911 // to depend on the Notebook or Pager classes.
889 for (var i=0; i<l; i++) {
912 for (var i=0; i<l; i++) {
890 var payload = payloads[i];
913 var payload = payloads[i];
891 var callback = payload_callbacks[payload.source];
914 var callback = payload_callbacks[payload.source];
892 if (callback) {
915 if (callback) {
893 callback(payload, msg);
916 callback(payload, msg);
894 }
917 }
895 }
918 }
896 };
919 };
897
920
898 /**
921 /**
899 * @function _handle_status_message
922 * @function _handle_status_message
900 */
923 */
901 Kernel.prototype._handle_status_message = function (msg) {
924 Kernel.prototype._handle_status_message = function (msg) {
902 var execution_state = msg.content.execution_state;
925 var execution_state = msg.content.execution_state;
903 var parent_id = msg.parent_header.msg_id;
926 var parent_id = msg.parent_header.msg_id;
904
927
905 // dispatch status msg callbacks, if any
928 // dispatch status msg callbacks, if any
906 var callbacks = this.get_callbacks_for_msg(parent_id);
929 var callbacks = this.get_callbacks_for_msg(parent_id);
907 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
930 if (callbacks && callbacks.iopub && callbacks.iopub.status) {
908 try {
931 try {
909 callbacks.iopub.status(msg);
932 callbacks.iopub.status(msg);
910 } catch (e) {
933 } catch (e) {
911 console.log("Exception in status msg handler", e, e.stack);
934 console.log("Exception in status msg handler", e, e.stack);
912 }
935 }
913 }
936 }
914
937
915 if (execution_state === 'busy') {
938 if (execution_state === 'busy') {
916 this.events.trigger('kernel_busy.Kernel', {kernel: this});
939 this.events.trigger('kernel_busy.Kernel', {kernel: this});
917
940
918 } else if (execution_state === 'idle') {
941 } else if (execution_state === 'idle') {
919 // signal that iopub callbacks are (probably) done
942 // signal that iopub callbacks are (probably) done
920 // async output may still arrive,
943 // async output may still arrive,
921 // but only for the most recent request
944 // but only for the most recent request
922 this._finish_iopub(parent_id);
945 this._finish_iopub(parent_id);
923
946
924 // trigger status_idle event
947 // trigger status_idle event
925 this.events.trigger('kernel_idle.Kernel', {kernel: this});
948 this.events.trigger('kernel_idle.Kernel', {kernel: this});
926
949
927 } else if (execution_state === 'starting') {
950 } else if (execution_state === 'starting') {
928 this.events.trigger('kernel_starting.Kernel', {kernel: this});
951 this.events.trigger('kernel_starting.Kernel', {kernel: this});
929 var that = this;
952 var that = this;
930 this.kernel_info(function (reply) {
953 this.kernel_info(function (reply) {
931 that.info_reply = reply.content;
954 that.info_reply = reply.content;
932 that.events.trigger('kernel_ready.Kernel', {kernel: that});
955 that.events.trigger('kernel_ready.Kernel', {kernel: that});
933 });
956 });
934
957
935 } else if (execution_state === 'restarting') {
958 } else if (execution_state === 'restarting') {
936 // autorestarting is distinct from restarting,
959 // autorestarting is distinct from restarting,
937 // in that it means the kernel died and the server is restarting it.
960 // in that it means the kernel died and the server is restarting it.
938 // kernel_restarting sets the notification widget,
961 // kernel_restarting sets the notification widget,
939 // autorestart shows the more prominent dialog.
962 // autorestart shows the more prominent dialog.
940 this._autorestart_attempt = this._autorestart_attempt + 1;
963 this._autorestart_attempt = this._autorestart_attempt + 1;
941 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
964 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
942 this.events.trigger('kernel_autorestarting.Kernel', {kernel: this, attempt: this._autorestart_attempt});
965 this.events.trigger('kernel_autorestarting.Kernel', {kernel: this, attempt: this._autorestart_attempt});
943
966
944 } else if (execution_state === 'dead') {
967 } else if (execution_state === 'dead') {
945 this.events.trigger('kernel_dead.Kernel', {kernel: this});
968 this.events.trigger('kernel_dead.Kernel', {kernel: this});
946 this._kernel_dead();
969 this._kernel_dead();
947 }
970 }
948 };
971 };
949
972
950 /**
973 /**
951 * Handle clear_output message
974 * Handle clear_output message
952 *
975 *
953 * @function _handle_clear_output
976 * @function _handle_clear_output
954 */
977 */
955 Kernel.prototype._handle_clear_output = function (msg) {
978 Kernel.prototype._handle_clear_output = function (msg) {
956 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
979 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
957 if (!callbacks || !callbacks.iopub) {
980 if (!callbacks || !callbacks.iopub) {
958 return;
981 return;
959 }
982 }
960 var callback = callbacks.iopub.clear_output;
983 var callback = callbacks.iopub.clear_output;
961 if (callback) {
984 if (callback) {
962 callback(msg);
985 callback(msg);
963 }
986 }
964 };
987 };
965
988
966 /**
989 /**
967 * handle an output message (execute_result, display_data, etc.)
990 * handle an output message (execute_result, display_data, etc.)
968 *
991 *
969 * @function _handle_output_message
992 * @function _handle_output_message
970 */
993 */
971 Kernel.prototype._handle_output_message = function (msg) {
994 Kernel.prototype._handle_output_message = function (msg) {
972 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
995 var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
973 if (!callbacks || !callbacks.iopub) {
996 if (!callbacks || !callbacks.iopub) {
974 return;
997 return;
975 }
998 }
976 var callback = callbacks.iopub.output;
999 var callback = callbacks.iopub.output;
977 if (callback) {
1000 if (callback) {
978 callback(msg);
1001 callback(msg);
979 }
1002 }
980 };
1003 };
981
1004
982 /**
1005 /**
983 * Dispatch IOPub messages to respective handlers. Each message
1006 * Dispatch IOPub messages to respective handlers. Each message
984 * type should have a handler.
1007 * type should have a handler.
985 *
1008 *
986 * @function _handle_iopub_message
1009 * @function _handle_iopub_message
987 */
1010 */
988 Kernel.prototype._handle_iopub_message = function (e) {
1011 Kernel.prototype._handle_iopub_message = function (e) {
989 serialize.deserialize(e.data, $.proxy(this._finish_iopub_message, this));
1012 serialize.deserialize(e.data, $.proxy(this._finish_iopub_message, this));
990 };
1013 };
991
1014
992
1015
993 Kernel.prototype._finish_iopub_message = function (msg) {
1016 Kernel.prototype._finish_iopub_message = function (msg) {
994 var handler = this.get_iopub_handler(msg.header.msg_type);
1017 var handler = this.get_iopub_handler(msg.header.msg_type);
995 if (handler !== undefined) {
1018 if (handler !== undefined) {
996 handler(msg);
1019 handler(msg);
997 }
1020 }
998 };
1021 };
999
1022
1000 /**
1023 /**
1001 * @function _handle_input_request
1024 * @function _handle_input_request
1002 */
1025 */
1003 Kernel.prototype._handle_input_request = function (e) {
1026 Kernel.prototype._handle_input_request = function (e) {
1004 serialize.deserialize(e.data, $.proxy(this._finish_input_request, this));
1027 serialize.deserialize(e.data, $.proxy(this._finish_input_request, this));
1005 };
1028 };
1006
1029
1007
1030
1008 Kernel.prototype._finish_input_request = function (request) {
1031 Kernel.prototype._finish_input_request = function (request) {
1009 var header = request.header;
1032 var header = request.header;
1010 var content = request.content;
1033 var content = request.content;
1011 var metadata = request.metadata;
1034 var metadata = request.metadata;
1012 var msg_type = header.msg_type;
1035 var msg_type = header.msg_type;
1013 if (msg_type !== 'input_request') {
1036 if (msg_type !== 'input_request') {
1014 console.log("Invalid input request!", request);
1037 console.log("Invalid input request!", request);
1015 return;
1038 return;
1016 }
1039 }
1017 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
1040 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
1018 if (callbacks) {
1041 if (callbacks) {
1019 if (callbacks.input) {
1042 if (callbacks.input) {
1020 callbacks.input(request);
1043 callbacks.input(request);
1021 }
1044 }
1022 }
1045 }
1023 };
1046 };
1024
1047
1025 // Backwards compatability.
1048 // Backwards compatability.
1026 IPython.Kernel = Kernel;
1049 IPython.Kernel = Kernel;
1027
1050
1028 return {'Kernel': Kernel};
1051 return {'Kernel': Kernel};
1029 });
1052 });
@@ -1,322 +1,328
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
37
38 <span id="save_widget" class="nav pull-left">
38 <span id="save_widget" class="nav pull-left">
39 <span id="notebook_name"></span>
39 <span id="notebook_name"></span>
40 <span id="checkpoint_status"></span>
40 <span id="checkpoint_status"></span>
41 <span id="autosave_status"></span>
41 <span id="autosave_status"></span>
42 </span>
42 </span>
43
43
44 <span id="kernel_selector_widget" class="pull-right dropdown">
44 <span id="kernel_selector_widget" class="pull-right dropdown">
45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
46 <span class='kernel_name'>Python</span>
46 <span class='kernel_name'>Python</span>
47 <span class="caret"></span>
47 <span class="caret"></span>
48 </button>
48 </button>
49 <ul id="kernel_selector" class="dropdown-menu">
49 <ul id="kernel_selector" class="dropdown-menu">
50 </ul>
50 </ul>
51 </span>
51 </span>
52
52
53 {% endblock %}
53 {% endblock %}
54
54
55
55
56 {% block site %}
56 {% block site %}
57
57
58 <div id="menubar-container" class="container">
58 <div id="menubar-container" class="container">
59 <div id="menubar">
59 <div id="menubar">
60 <div id="menus" class="navbar navbar-default" role="navigation">
60 <div id="menus" class="navbar navbar-default" role="navigation">
61 <div class="container-fluid">
61 <div class="container-fluid">
62 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
62 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
63 <i class="fa fa-bars"></i>
63 <i class="fa fa-bars"></i>
64 <span class="navbar-text">Menu</span>
64 <span class="navbar-text">Menu</span>
65 </button>
65 </button>
66 <ul class="nav navbar-nav navbar-right">
66 <ul class="nav navbar-nav navbar-right">
67 <li id="kernel_indicator">
67 <li id="kernel_indicator">
68 <i id="kernel_indicator_icon"></i>
68 <i id="kernel_indicator_icon"></i>
69 </li>
69 </li>
70 <li id="modal_indicator">
70 <li id="modal_indicator">
71 <i id="modal_indicator_icon"></i>
71 <i id="modal_indicator_icon"></i>
72 </li>
72 </li>
73 <li id="notification_area"></li>
73 <li id="notification_area"></li>
74 </ul>
74 </ul>
75 <div class="navbar-collapse collapse">
75 <div class="navbar-collapse collapse">
76 <ul class="nav navbar-nav">
76 <ul class="nav navbar-nav">
77 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
77 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
78 <ul id="file_menu" class="dropdown-menu">
78 <ul id="file_menu" class="dropdown-menu">
79 <li id="new_notebook"
79 <li id="new_notebook"
80 title="Make a new notebook (Opens a new window)">
80 title="Make a new notebook (Opens a new window)">
81 <a href="#">New</a></li>
81 <a href="#">New</a></li>
82 <li id="open_notebook"
82 <li id="open_notebook"
83 title="Opens a new window with the Dashboard view">
83 title="Opens a new window with the Dashboard view">
84 <a href="#">Open...</a></li>
84 <a href="#">Open...</a></li>
85 <!-- <hr/> -->
85 <!-- <hr/> -->
86 <li class="divider"></li>
86 <li class="divider"></li>
87 <li id="copy_notebook"
87 <li id="copy_notebook"
88 title="Open a copy of this notebook's contents and start a new kernel">
88 title="Open a copy of this notebook's contents and start a new kernel">
89 <a href="#">Make a Copy...</a></li>
89 <a href="#">Make a Copy...</a></li>
90 <li id="rename_notebook"><a href="#">Rename...</a></li>
90 <li id="rename_notebook"><a href="#">Rename...</a></li>
91 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
91 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
92 <!-- <hr/> -->
92 <!-- <hr/> -->
93 <li class="divider"></li>
93 <li class="divider"></li>
94 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
94 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
95 <ul class="dropdown-menu">
95 <ul class="dropdown-menu">
96 <li><a href="#"></a></li>
96 <li><a href="#"></a></li>
97 <li><a href="#"></a></li>
97 <li><a href="#"></a></li>
98 <li><a href="#"></a></li>
98 <li><a href="#"></a></li>
99 <li><a href="#"></a></li>
99 <li><a href="#"></a></li>
100 <li><a href="#"></a></li>
100 <li><a href="#"></a></li>
101 </ul>
101 </ul>
102 </li>
102 </li>
103 <li class="divider"></li>
103 <li class="divider"></li>
104 <li id="print_preview"><a href="#">Print Preview</a></li>
104 <li id="print_preview"><a href="#">Print Preview</a></li>
105 <li class="dropdown-submenu"><a href="#">Download as</a>
105 <li class="dropdown-submenu"><a href="#">Download as</a>
106 <ul class="dropdown-menu">
106 <ul class="dropdown-menu">
107 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
107 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
108 <li id="download_py"><a href="#">Python (.py)</a></li>
108 <li id="download_py"><a href="#">Python (.py)</a></li>
109 <li id="download_html"><a href="#">HTML (.html)</a></li>
109 <li id="download_html"><a href="#">HTML (.html)</a></li>
110 <li id="download_rst"><a href="#">reST (.rst)</a></li>
110 <li id="download_rst"><a href="#">reST (.rst)</a></li>
111 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
111 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
112 </ul>
112 </ul>
113 </li>
113 </li>
114 <li class="divider"></li>
114 <li class="divider"></li>
115 <li id="trust_notebook"
115 <li id="trust_notebook"
116 title="Trust the output of this notebook">
116 title="Trust the output of this notebook">
117 <a href="#" >Trust Notebook</a></li>
117 <a href="#" >Trust Notebook</a></li>
118 <li class="divider"></li>
118 <li class="divider"></li>
119 <li id="kill_and_exit"
119 <li id="kill_and_exit"
120 title="Shutdown this notebook's kernel, and close this window">
120 title="Shutdown this notebook's kernel, and close this window">
121 <a href="#" >Close and halt</a></li>
121 <a href="#" >Close and halt</a></li>
122 </ul>
122 </ul>
123 </li>
123 </li>
124 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
124 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
125 <ul id="edit_menu" class="dropdown-menu">
125 <ul id="edit_menu" class="dropdown-menu">
126 <li id="cut_cell"><a href="#">Cut Cell</a></li>
126 <li id="cut_cell"><a href="#">Cut Cell</a></li>
127 <li id="copy_cell"><a href="#">Copy Cell</a></li>
127 <li id="copy_cell"><a href="#">Copy Cell</a></li>
128 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
128 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
129 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
129 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
130 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
130 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
131 <li id="delete_cell"><a href="#">Delete Cell</a></li>
131 <li id="delete_cell"><a href="#">Delete Cell</a></li>
132 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
132 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
133 <li class="divider"></li>
133 <li class="divider"></li>
134 <li id="split_cell"><a href="#">Split Cell</a></li>
134 <li id="split_cell"><a href="#">Split Cell</a></li>
135 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
135 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
136 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
136 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
137 <li class="divider"></li>
137 <li class="divider"></li>
138 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
138 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
139 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
139 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
140 <li class="divider"></li>
140 <li class="divider"></li>
141 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
141 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
142 </ul>
142 </ul>
143 </li>
143 </li>
144 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
144 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
145 <ul id="view_menu" class="dropdown-menu">
145 <ul id="view_menu" class="dropdown-menu">
146 <li id="toggle_header"
146 <li id="toggle_header"
147 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
147 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
148 <a href="#">Toggle Header</a></li>
148 <a href="#">Toggle Header</a></li>
149 <li id="toggle_toolbar"
149 <li id="toggle_toolbar"
150 title="Show/Hide the action icons (below menu bar)">
150 title="Show/Hide the action icons (below menu bar)">
151 <a href="#">Toggle Toolbar</a></li>
151 <a href="#">Toggle Toolbar</a></li>
152 </ul>
152 </ul>
153 </li>
153 </li>
154 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
154 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
155 <ul id="insert_menu" class="dropdown-menu">
155 <ul id="insert_menu" class="dropdown-menu">
156 <li id="insert_cell_above"
156 <li id="insert_cell_above"
157 title="Insert an empty Code cell above the currently active cell">
157 title="Insert an empty Code cell above the currently active cell">
158 <a href="#">Insert Cell Above</a></li>
158 <a href="#">Insert Cell Above</a></li>
159 <li id="insert_cell_below"
159 <li id="insert_cell_below"
160 title="Insert an empty Code cell below the currently active cell">
160 title="Insert an empty Code cell below the currently active cell">
161 <a href="#">Insert Cell Below</a></li>
161 <a href="#">Insert Cell Below</a></li>
162 </ul>
162 </ul>
163 </li>
163 </li>
164 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
164 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
165 <ul id="cell_menu" class="dropdown-menu">
165 <ul id="cell_menu" class="dropdown-menu">
166 <li id="run_cell" title="Run this cell, and move cursor to the next one">
166 <li id="run_cell" title="Run this cell, and move cursor to the next one">
167 <a href="#">Run</a></li>
167 <a href="#">Run</a></li>
168 <li id="run_cell_select_below" title="Run this cell, select below">
168 <li id="run_cell_select_below" title="Run this cell, select below">
169 <a href="#">Run and Select Below</a></li>
169 <a href="#">Run and Select Below</a></li>
170 <li id="run_cell_insert_below" title="Run this cell, insert below">
170 <li id="run_cell_insert_below" title="Run this cell, insert below">
171 <a href="#">Run and Insert Below</a></li>
171 <a href="#">Run and Insert Below</a></li>
172 <li id="run_all_cells" title="Run all cells in the notebook">
172 <li id="run_all_cells" title="Run all cells in the notebook">
173 <a href="#">Run All</a></li>
173 <a href="#">Run All</a></li>
174 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
174 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
175 <a href="#">Run All Above</a></li>
175 <a href="#">Run All Above</a></li>
176 <li id="run_all_cells_below" title="Run this cell and all cells below it">
176 <li id="run_all_cells_below" title="Run this cell and all cells below it">
177 <a href="#">Run All Below</a></li>
177 <a href="#">Run All Below</a></li>
178 <li class="divider"></li>
178 <li class="divider"></li>
179 <li id="change_cell_type" class="dropdown-submenu"
179 <li id="change_cell_type" class="dropdown-submenu"
180 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
180 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
181 <a href="#">Cell Type</a>
181 <a href="#">Cell Type</a>
182 <ul class="dropdown-menu">
182 <ul class="dropdown-menu">
183 <li id="to_code"
183 <li id="to_code"
184 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
184 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
185 <a href="#">Code</a></li>
185 <a href="#">Code</a></li>
186 <li id="to_markdown"
186 <li id="to_markdown"
187 title="Contents will be rendered as HTML and serve as explanatory text">
187 title="Contents will be rendered as HTML and serve as explanatory text">
188 <a href="#">Markdown</a></li>
188 <a href="#">Markdown</a></li>
189 <li id="to_raw"
189 <li id="to_raw"
190 title="Contents will pass through nbconvert unmodified">
190 title="Contents will pass through nbconvert unmodified">
191 <a href="#">Raw NBConvert</a></li>
191 <a href="#">Raw NBConvert</a></li>
192 </ul>
192 </ul>
193 </li>
193 </li>
194 <li class="divider"></li>
194 <li class="divider"></li>
195 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
195 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
196 <ul class="dropdown-menu">
196 <ul class="dropdown-menu">
197 <li id="toggle_current_output"
197 <li id="toggle_current_output"
198 title="Hide/Show the output of the current cell">
198 title="Hide/Show the output of the current cell">
199 <a href="#">Toggle</a>
199 <a href="#">Toggle</a>
200 </li>
200 </li>
201 <li id="toggle_current_output_scroll"
201 <li id="toggle_current_output_scroll"
202 title="Scroll the output of the current cell">
202 title="Scroll the output of the current cell">
203 <a href="#">Toggle Scrolling</a>
203 <a href="#">Toggle Scrolling</a>
204 </li>
204 </li>
205 <li id="clear_current_output"
205 <li id="clear_current_output"
206 title="Clear the output of the current cell">
206 title="Clear the output of the current cell">
207 <a href="#">Clear</a>
207 <a href="#">Clear</a>
208 </li>
208 </li>
209 </ul>
209 </ul>
210 </li>
210 </li>
211 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
211 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
212 <ul class="dropdown-menu">
212 <ul class="dropdown-menu">
213 <li id="toggle_all_output"
213 <li id="toggle_all_output"
214 title="Hide/Show the output of all cells">
214 title="Hide/Show the output of all cells">
215 <a href="#">Toggle</a>
215 <a href="#">Toggle</a>
216 </li>
216 </li>
217 <li id="toggle_all_output_scroll"
217 <li id="toggle_all_output_scroll"
218 title="Scroll the output of all cells">
218 title="Scroll the output of all cells">
219 <a href="#">Toggle Scrolling</a>
219 <a href="#">Toggle Scrolling</a>
220 </li>
220 </li>
221 <li id="clear_all_output"
221 <li id="clear_all_output"
222 title="Clear the output of all cells">
222 title="Clear the output of all cells">
223 <a href="#">Clear</a>
223 <a href="#">Clear</a>
224 </li>
224 </li>
225 </ul>
225 </ul>
226 </li>
226 </li>
227 </ul>
227 </ul>
228 </li>
228 </li>
229 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
229 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
230 <ul id="kernel_menu" class="dropdown-menu">
230 <ul id="kernel_menu" class="dropdown-menu">
231 <li id="int_kernel"
231 <li id="int_kernel"
232 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
232 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
233 <a href="#">Interrupt</a></li>
233 <a href="#">Interrupt</a>
234 </li>
234 <li id="restart_kernel"
235 <li id="restart_kernel"
235 title="Restart the Kernel">
236 title="Restart the Kernel">
236 <a href="#">Restart</a></li>
237 <a href="#">Restart</a>
238 </li>
239 <li id="reconnect_kernel"
240 title="Reconnect to the Kernel">
241 <a href="#">Reconnect</a>
242 </li>
237 <li class="divider"></li>
243 <li class="divider"></li>
238 <li id="menu-change-kernel" class="dropdown-submenu">
244 <li id="menu-change-kernel" class="dropdown-submenu">
239 <a href="#">Change kernel</a>
245 <a href="#">Change kernel</a>
240 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
246 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
241 </li>
247 </li>
242 </ul>
248 </ul>
243 </li>
249 </li>
244 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
250 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
245 <ul id="help_menu" class="dropdown-menu">
251 <ul id="help_menu" class="dropdown-menu">
246 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
252 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
247 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
253 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
248 <li class="divider"></li>
254 <li class="divider"></li>
249 {% set
255 {% set
250 sections = (
256 sections = (
251 (
257 (
252 ("http://ipython.org/documentation.html","IPython Help",True),
258 ("http://ipython.org/documentation.html","IPython Help",True),
253 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
259 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
254 ),(
260 ),(
255 ("http://docs.python.org","Python",True),
261 ("http://docs.python.org","Python",True),
256 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
262 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
257 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
263 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
258 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
264 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
259 ("http://matplotlib.org/contents.html","Matplotlib",True),
265 ("http://matplotlib.org/contents.html","Matplotlib",True),
260 ("http://docs.sympy.org/latest/index.html","SymPy",True),
266 ("http://docs.sympy.org/latest/index.html","SymPy",True),
261 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
267 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
262 )
268 )
263 )
269 )
264 %}
270 %}
265
271
266 {% for helplinks in sections %}
272 {% for helplinks in sections %}
267 {% for link in helplinks %}
273 {% for link in helplinks %}
268 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
274 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
269 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
275 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
270 {{link[1]}}
276 {{link[1]}}
271 </a></li>
277 </a></li>
272 {% endfor %}
278 {% endfor %}
273 {% if not loop.last %}
279 {% if not loop.last %}
274 <li class="divider"></li>
280 <li class="divider"></li>
275 {% endif %}
281 {% endif %}
276 {% endfor %}
282 {% endfor %}
277 <li class="divider"></li>
283 <li class="divider"></li>
278 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
284 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
279 </ul>
285 </ul>
280 </li>
286 </li>
281 </ul>
287 </ul>
282 </div>
288 </div>
283 </div>
289 </div>
284 </div>
290 </div>
285 </div>
291 </div>
286 <div id="maintoolbar" class="navbar">
292 <div id="maintoolbar" class="navbar">
287 <div class="toolbar-inner navbar-inner navbar-nobg">
293 <div class="toolbar-inner navbar-inner navbar-nobg">
288 <div id="maintoolbar-container" class="container"></div>
294 <div id="maintoolbar-container" class="container"></div>
289 </div>
295 </div>
290 </div>
296 </div>
291 </div>
297 </div>
292
298
293 <div id="ipython-main-app">
299 <div id="ipython-main-app">
294
300
295 <div id="notebook_panel">
301 <div id="notebook_panel">
296 <div id="notebook"></div>
302 <div id="notebook"></div>
297 <div id="pager_splitter"></div>
303 <div id="pager_splitter"></div>
298 <div id="pager">
304 <div id="pager">
299 <div id='pager_button_area'>
305 <div id='pager_button_area'>
300 </div>
306 </div>
301 <div id="pager-container" class="container"></div>
307 <div id="pager-container" class="container"></div>
302 </div>
308 </div>
303 </div>
309 </div>
304
310
305 </div>
311 </div>
306 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
312 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
307
313
308
314
309 {% endblock %}
315 {% endblock %}
310
316
311
317
312 {% block script %}
318 {% block script %}
313 {{super()}}
319 {{super()}}
314 <script type="text/javascript">
320 <script type="text/javascript">
315 sys_info = {{sys_info}};
321 sys_info = {{sys_info}};
316 </script>
322 </script>
317
323
318 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
324 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
319
325
320 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
326 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
321
327
322 {% endblock %}
328 {% endblock %}
@@ -1,317 +1,323
1
1
2 //
2 //
3 // Kernel tests
3 // Kernel tests
4 //
4 //
5 casper.notebook_test(function () {
5 casper.notebook_test(function () {
6 // test that the kernel is running
6 // test that the kernel is running
7 this.then(function () {
7 this.then(function () {
8 this.test.assert(this.kernel_running(), 'kernel is running');
8 this.test.assert(this.kernel_running(), 'kernel is running');
9 });
9 });
10
10
11 // test list
11 // test list
12 this.thenEvaluate(function () {
12 this.thenEvaluate(function () {
13 IPython._kernels = null;
13 IPython._kernels = null;
14 IPython.notebook.kernel.list(function (data) {
14 IPython.notebook.kernel.list(function (data) {
15 IPython._kernels = data;
15 IPython._kernels = data;
16 });
16 });
17 });
17 });
18 this.waitFor(function () {
18 this.waitFor(function () {
19 return this.evaluate(function () {
19 return this.evaluate(function () {
20 return IPython._kernels !== null;
20 return IPython._kernels !== null;
21 });
21 });
22 });
22 });
23 this.then(function () {
23 this.then(function () {
24 var num_kernels = this.evaluate(function () {
24 var num_kernels = this.evaluate(function () {
25 return IPython._kernels.length;
25 return IPython._kernels.length;
26 });
26 });
27 this.test.assertEquals(num_kernels, 1, 'one kernel running');
27 this.test.assertEquals(num_kernels, 1, 'one kernel running');
28 });
28 });
29
29
30 // test get_info
30 // test get_info
31 var kernel_info = this.evaluate(function () {
31 var kernel_info = this.evaluate(function () {
32 return {
32 return {
33 name: IPython.notebook.kernel.name,
33 name: IPython.notebook.kernel.name,
34 id: IPython.notebook.kernel.id
34 id: IPython.notebook.kernel.id
35 };
35 };
36 });
36 });
37 this.thenEvaluate(function () {
37 this.thenEvaluate(function () {
38 IPython._kernel_info = null;
38 IPython._kernel_info = null;
39 IPython.notebook.kernel.get_info(function (data) {
39 IPython.notebook.kernel.get_info(function (data) {
40 IPython._kernel_info = data;
40 IPython._kernel_info = data;
41 });
41 });
42 });
42 });
43 this.waitFor(function () {
43 this.waitFor(function () {
44 return this.evaluate(function () {
44 return this.evaluate(function () {
45 return IPython._kernel_info !== null;
45 return IPython._kernel_info !== null;
46 });
46 });
47 });
47 });
48 this.then(function () {
48 this.then(function () {
49 var new_kernel_info = this.evaluate(function () {
49 var new_kernel_info = this.evaluate(function () {
50 return IPython._kernel_info;
50 return IPython._kernel_info;
51 });
51 });
52 this.test.assertEquals(kernel_info.name, new_kernel_info.name, 'kernel: name correct');
52 this.test.assertEquals(kernel_info.name, new_kernel_info.name, 'kernel: name correct');
53 this.test.assertEquals(kernel_info.id, new_kernel_info.id, 'kernel: id correct');
53 this.test.assertEquals(kernel_info.id, new_kernel_info.id, 'kernel: id correct');
54 });
54 });
55
55
56 // test interrupt
56 // test interrupt
57 this.thenEvaluate(function () {
57 this.thenEvaluate(function () {
58 IPython._interrupted = false;
58 IPython._interrupted = false;
59 IPython.notebook.kernel.interrupt(function () {
59 IPython.notebook.kernel.interrupt(function () {
60 IPython._interrupted = true;
60 IPython._interrupted = true;
61 });
61 });
62 });
62 });
63 this.waitFor(function () {
63 this.waitFor(function () {
64 return this.evaluate(function () {
64 return this.evaluate(function () {
65 return IPython._interrupted;
65 return IPython._interrupted;
66 });
66 });
67 });
67 });
68 this.then(function () {
68 this.then(function () {
69 var interrupted = this.evaluate(function () {
69 var interrupted = this.evaluate(function () {
70 return IPython._interrupted;
70 return IPython._interrupted;
71 });
71 });
72 this.test.assert(interrupted, 'kernel was interrupted');
72 this.test.assert(interrupted, 'kernel was interrupted');
73 });
73 });
74
74
75 // test restart
75 // test restart
76 this.thenEvaluate(function () {
76 this.thenEvaluate(function () {
77 IPython.notebook.kernel.restart();
77 IPython.notebook.kernel.restart();
78 });
78 });
79 this.waitFor(this.kernel_disconnected);
79 this.waitFor(this.kernel_disconnected);
80 this.wait_for_kernel_ready();
80 this.wait_for_kernel_ready();
81 this.then(function () {
81 this.then(function () {
82 this.test.assert(this.kernel_running(), 'kernel restarted');
82 this.test.assert(this.kernel_running(), 'kernel restarted');
83 });
83 });
84
84
85 // test reconnect
85 // test reconnect
86 this.thenEvaluate(function () {
86 this.thenEvaluate(function () {
87 IPython.notebook.kernel.stop_channels();
87 IPython.notebook.kernel.stop_channels();
88 });
88 });
89 this.waitFor(this.kernel_disconnected);
89 this.waitFor(this.kernel_disconnected);
90 this.thenEvaluate(function () {
90 this.thenEvaluate(function () {
91 IPython.notebook.kernel.reconnect();
91 IPython.notebook.kernel.reconnect();
92 });
92 });
93 this.wait_for_kernel_ready();
93 this.wait_for_kernel_ready();
94 this.then(function () {
94 this.then(function () {
95 this.test.assert(this.kernel_running(), 'kernel reconnected');
95 this.test.assert(this.kernel_running(), 'kernel reconnected');
96 });
96 });
97
97
98 // test kernel_info_request
98 // test kernel_info_request
99 this.evaluate(function () {
99 this.evaluate(function () {
100 IPython.notebook.kernel.kernel_info(
100 IPython.notebook.kernel.kernel_info(
101 function(msg){
101 function(msg){
102 IPython._kernel_info_response = msg;
102 IPython._kernel_info_response = msg;
103 });
103 });
104 });
104 });
105 this.waitFor(
105 this.waitFor(
106 function () {
106 function () {
107 return this.evaluate(function(){
107 return this.evaluate(function(){
108 return IPython._kernel_info_response;
108 return IPython._kernel_info_response;
109 });
109 });
110 });
110 });
111 this.then(function () {
111 this.then(function () {
112 var kernel_info_response = this.evaluate(function(){
112 var kernel_info_response = this.evaluate(function(){
113 return IPython._kernel_info_response;
113 return IPython._kernel_info_response;
114 });
114 });
115 this.test.assertTrue( kernel_info_response.msg_type === 'kernel_info_reply', 'Kernel info request return kernel_info_reply');
115 this.test.assertTrue( kernel_info_response.msg_type === 'kernel_info_reply', 'Kernel info request return kernel_info_reply');
116 this.test.assertTrue( kernel_info_response.content !== undefined, 'Kernel_info_reply is not undefined');
116 this.test.assertTrue( kernel_info_response.content !== undefined, 'Kernel_info_reply is not undefined');
117 });
117 });
118
118
119 // test kill
119 // test kill
120 this.thenEvaluate(function () {
120 this.thenEvaluate(function () {
121 IPython.notebook.kernel.kill();
121 IPython.notebook.kernel.kill();
122 });
122 });
123 this.waitFor(this.kernel_disconnected);
123 this.waitFor(this.kernel_disconnected);
124 this.then(function () {
124 this.then(function () {
125 this.test.assert(!this.kernel_running(), 'kernel is not running');
125 this.test.assert(!this.kernel_running(), 'kernel is not running');
126 });
126 });
127
127
128 // test start
128 // test start
129 var url;
129 var url;
130 this.then(function () {
130 this.then(function () {
131 url = this.evaluate(function () {
131 url = this.evaluate(function () {
132 return IPython.notebook.kernel.start();
132 return IPython.notebook.kernel.start();
133 });
133 });
134 });
134 });
135 this.then(function () {
135 this.then(function () {
136 this.test.assertEquals(url, "/api/kernels", "start url is correct");
136 this.test.assertEquals(url, "/api/kernels", "start url is correct");
137 });
137 });
138 this.wait_for_kernel_ready();
138 this.wait_for_kernel_ready();
139 this.then(function () {
139 this.then(function () {
140 this.test.assert(this.kernel_running(), 'kernel is running');
140 this.test.assert(this.kernel_running(), 'kernel is running');
141 });
141 });
142
142
143 // test start with parameters
143 // test start with parameters
144 this.thenEvaluate(function () {
144 this.thenEvaluate(function () {
145 IPython.notebook.kernel.kill();
145 IPython.notebook.kernel.kill();
146 });
146 });
147 this.waitFor(this.kernel_disconnected);
147 this.waitFor(this.kernel_disconnected);
148 this.then(function () {
148 this.then(function () {
149 url = this.evaluate(function () {
149 url = this.evaluate(function () {
150 return IPython.notebook.kernel.start({foo: "bar"});
150 return IPython.notebook.kernel.start({foo: "bar"});
151 });
151 });
152 });
152 });
153 this.then(function () {
153 this.then(function () {
154 this.test.assertEquals(url, "/api/kernels?foo=bar", "start url with params is correct");
154 this.test.assertEquals(url, "/api/kernels?foo=bar", "start url with params is correct");
155 });
155 });
156 this.wait_for_kernel_ready();
156 this.wait_for_kernel_ready();
157 this.then(function () {
157 this.then(function () {
158 this.test.assert(this.kernel_running(), 'kernel is running');
158 this.test.assert(this.kernel_running(), 'kernel is running');
159 });
159 });
160
160
161 // check for events in kill/start cycle
161 // check for events in kill/start cycle
162 this.event_test(
162 this.event_test(
163 'kill/start',
163 'kill/start',
164 [
164 [
165 'kernel_killed.Kernel',
165 'kernel_killed.Kernel',
166 'kernel_created.Kernel',
166 'kernel_created.Kernel',
167 'kernel_connected.Kernel',
167 'kernel_connected.Kernel',
168 'kernel_starting.Kernel',
168 'kernel_starting.Kernel',
169 'kernel_ready.Kernel'
169 'kernel_ready.Kernel'
170 ],
170 ],
171 function () {
171 function () {
172 this.thenEvaluate(function () {
172 this.thenEvaluate(function () {
173 IPython.notebook.kernel.kill();
173 IPython.notebook.kernel.kill();
174 });
174 });
175 this.waitFor(this.kernel_disconnected);
175 this.waitFor(this.kernel_disconnected);
176 this.thenEvaluate(function () {
176 this.thenEvaluate(function () {
177 IPython.notebook.kernel.start();
177 IPython.notebook.kernel.start();
178 });
178 });
179 }
179 }
180 );
180 );
181 // wait for any last idle/busy messages to be handled
181 // wait for any last idle/busy messages to be handled
182 this.wait_for_kernel_ready();
182 this.wait_for_kernel_ready();
183
183
184 // check for events in disconnect/connect cycle
184 // check for events in disconnect/connect cycle
185 this.event_test(
185 this.event_test(
186 'reconnect',
186 'reconnect',
187 [
187 [
188 'kernel_reconnecting.Kernel',
188 'kernel_reconnecting.Kernel',
189 'kernel_connected.Kernel',
189 'kernel_connected.Kernel',
190 ],
190 ],
191 function () {
191 function () {
192 this.thenEvaluate(function () {
192 this.thenEvaluate(function () {
193 IPython.notebook.kernel.stop_channels();
193 IPython.notebook.kernel.stop_channels();
194 IPython.notebook.kernel.reconnect(1);
194 IPython.notebook.kernel.reconnect(1);
195 });
195 });
196 }
196 }
197 );
197 );
198 // wait for any last idle/busy messages to be handled
198 // wait for any last idle/busy messages to be handled
199 this.wait_for_kernel_ready();
199 this.wait_for_kernel_ready();
200
200
201 // check for events in the restart cycle
201 // check for events in the restart cycle
202 this.event_test(
202 this.event_test(
203 'restart',
203 'restart',
204 [
204 [
205 'kernel_restarting.Kernel',
205 'kernel_restarting.Kernel',
206 'kernel_created.Kernel',
206 'kernel_created.Kernel',
207 'kernel_connected.Kernel',
207 'kernel_connected.Kernel',
208 'kernel_starting.Kernel',
208 'kernel_starting.Kernel',
209 'kernel_ready.Kernel'
209 'kernel_ready.Kernel'
210 ],
210 ],
211 function () {
211 function () {
212 this.thenEvaluate(function () {
212 this.thenEvaluate(function () {
213 IPython.notebook.kernel.restart();
213 IPython.notebook.kernel.restart();
214 });
214 });
215 }
215 }
216 );
216 );
217 // wait for any last idle/busy messages to be handled
217 // wait for any last idle/busy messages to be handled
218 this.wait_for_kernel_ready();
218 this.wait_for_kernel_ready();
219
219
220 // check for events in the interrupt cycle
220 // check for events in the interrupt cycle
221 this.event_test(
221 this.event_test(
222 'interrupt',
222 'interrupt',
223 [
223 [
224 'kernel_interrupting.Kernel',
224 'kernel_interrupting.Kernel',
225 'kernel_busy.Kernel',
225 'kernel_busy.Kernel',
226 'kernel_idle.Kernel'
226 'kernel_idle.Kernel'
227 ],
227 ],
228 function () {
228 function () {
229 this.thenEvaluate(function () {
229 this.thenEvaluate(function () {
230 IPython.notebook.kernel.interrupt();
230 IPython.notebook.kernel.interrupt();
231 });
231 });
232 }
232 }
233 );
233 );
234 this.wait_for_kernel_ready();
234 this.wait_for_kernel_ready();
235
235
236 // check for events after ws close
236 // check for events after ws close
237 this.event_test(
237 this.event_test(
238 'ws_closed_ok',
238 'ws_closed_ok',
239 [
239 [
240 'kernel_disconnected.Kernel',
240 'kernel_disconnected.Kernel',
241 'kernel_reconnecting.Kernel',
241 'kernel_reconnecting.Kernel',
242 'kernel_connected.Kernel',
242 'kernel_connected.Kernel',
243 'kernel_busy.Kernel',
243 'kernel_busy.Kernel',
244 'kernel_idle.Kernel'
244 'kernel_idle.Kernel'
245 ],
245 ],
246 function () {
246 function () {
247 this.thenEvaluate(function () {
247 this.thenEvaluate(function () {
248 IPython.notebook.kernel._ws_closed("", false);
248 IPython.notebook.kernel._ws_closed("", false);
249 });
249 });
250 }
250 }
251 );
251 );
252 // wait for any last idle/busy messages to be handled
252 // wait for any last idle/busy messages to be handled
253 this.wait_for_kernel_ready();
253 this.wait_for_kernel_ready();
254
254
255 // check for events after ws close (error)
255 // check for events after ws close (error)
256 this.event_test(
256 this.event_test(
257 'ws_closed_error',
257 'ws_closed_error',
258 [
258 [
259 'kernel_disconnected.Kernel',
259 'kernel_disconnected.Kernel',
260 'kernel_connection_failed.Kernel'
260 'kernel_connection_failed.Kernel',
261 'kernel_reconnecting.Kernel',
262 'kernel_connected.Kernel',
263 'kernel_busy.Kernel',
264 'kernel_idle.Kernel'
261 ],
265 ],
262 function () {
266 function () {
263 this.thenEvaluate(function () {
267 this.thenEvaluate(function () {
264 IPython.notebook.kernel._ws_closed("", true);
268 IPython.notebook.kernel._ws_closed("", true);
265 });
269 });
266 }
270 }
267 );
271 );
272 // wait for any last idle/busy messages to be handled
273 this.wait_for_kernel_ready();
268
274
269 // start the kernel back up
275 // start the kernel back up
270 this.thenEvaluate(function () {
276 this.thenEvaluate(function () {
271 IPython.notebook.kernel.restart();
277 IPython.notebook.kernel.restart();
272 });
278 });
273 this.waitFor(this.kernel_running);
279 this.waitFor(this.kernel_running);
274 this.wait_for_kernel_ready();
280 this.wait_for_kernel_ready();
275
281
276 // test handling of autorestarting messages
282 // test handling of autorestarting messages
277 this.event_test(
283 this.event_test(
278 'autorestarting',
284 'autorestarting',
279 [
285 [
280 'kernel_restarting.Kernel',
286 'kernel_restarting.Kernel',
281 'kernel_autorestarting.Kernel',
287 'kernel_autorestarting.Kernel',
282 ],
288 ],
283 function () {
289 function () {
284 this.thenEvaluate(function () {
290 this.thenEvaluate(function () {
285 var cell = IPython.notebook.get_cell(0);
291 var cell = IPython.notebook.get_cell(0);
286 cell.set_text('import os\n' + 'os._exit(1)');
292 cell.set_text('import os\n' + 'os._exit(1)');
287 cell.execute();
293 cell.execute();
288 });
294 });
289 }
295 }
290 );
296 );
291 this.wait_for_kernel_ready();
297 this.wait_for_kernel_ready();
292
298
293 // test handling of failed restart
299 // test handling of failed restart
294 this.event_test(
300 this.event_test(
295 'failed_restart',
301 'failed_restart',
296 [
302 [
297 'kernel_restarting.Kernel',
303 'kernel_restarting.Kernel',
298 'kernel_autorestarting.Kernel',
304 'kernel_autorestarting.Kernel',
299 'kernel_dead.Kernel'
305 'kernel_dead.Kernel'
300 ],
306 ],
301 function () {
307 function () {
302 this.thenEvaluate(function () {
308 this.thenEvaluate(function () {
303 var cell = IPython.notebook.get_cell(0);
309 var cell = IPython.notebook.get_cell(0);
304 cell.set_text("import os\n" +
310 cell.set_text("import os\n" +
305 "from IPython.kernel.connect import get_connection_file\n" +
311 "from IPython.kernel.connect import get_connection_file\n" +
306 "with open(get_connection_file(), 'w') as f:\n" +
312 "with open(get_connection_file(), 'w') as f:\n" +
307 " f.write('garbage')\n" +
313 " f.write('garbage')\n" +
308 "os._exit(1)");
314 "os._exit(1)");
309 cell.execute();
315 cell.execute();
310 });
316 });
311 },
317 },
312
318
313 // need an extra-long timeout, because it needs to try
319 // need an extra-long timeout, because it needs to try
314 // restarting the kernel 5 times!
320 // restarting the kernel 5 times!
315 20000
321 20000
316 );
322 );
317 });
323 });
General Comments 0
You need to be logged in to leave comments. Login now