##// END OF EJS Templates
use one-off notification widget for match notification...
Min RK -
Show More
@@ -1,318 +1,324 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'jquery',
6 6 'base/js/namespace',
7 7 'base/js/dialog',
8 8 'base/js/utils',
9 9 ], function($, IPython, dialog, utils) {
10 10 "use strict";
11 11
12 12 var KernelSelector = function(selector, notebook) {
13 13 var that = this;
14 14 this.selector = selector;
15 15 this.notebook = notebook;
16 16 this.notebook.set_kernelselector(this);
17 17 this.events = notebook.events;
18 18 this.current_selection = null;
19 19 this.kernelspecs = {};
20 20 if (this.selector !== undefined) {
21 21 this.element = $(selector);
22 22 this.request_kernelspecs();
23 23 }
24 24 this.bind_events();
25 25 // Make the object globally available for user convenience & inspection
26 26 IPython.kernelselector = this;
27 27 this._finish_load = null;
28 28 this._loaded = false;
29 29 this.loaded = new Promise(function(resolve) {
30 30 that._finish_load = resolve;
31 31 });
32 32
33 33 Object.seal(this);
34 34 };
35 35
36 36 KernelSelector.prototype.request_kernelspecs = function() {
37 37 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
38 38 utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
39 39 };
40 40
41 41 var _sorted_names = function(kernelspecs) {
42 42 // sort kernel names
43 43 return Object.keys(kernelspecs).sort(function (a, b) {
44 44 // sort by display_name
45 45 var da = kernelspecs[a].spec.display_name;
46 46 var db = kernelspecs[b].spec.display_name;
47 47 if (da === db) {
48 48 return 0;
49 49 } else if (da > db) {
50 50 return 1;
51 51 } else {
52 52 return -1;
53 53 }
54 54 });
55 55 };
56 56
57 57 KernelSelector.prototype._got_kernelspecs = function(data) {
58 58 var that = this;
59 59 this.kernelspecs = data.kernelspecs;
60 60 var change_kernel_submenu = $("#menu-change-kernel-submenu");
61 61 var new_notebook_submenu = $("#menu-new-notebook-submenu");
62 62 var keys = _sorted_names(data.kernelspecs);
63 63
64 64 keys.map(function (key) {
65 65 // Create the Kernel > Change kernel submenu
66 66 var ks = data.kernelspecs[key];
67 67 change_kernel_submenu.append(
68 68 $("<li>").attr("id", "kernel-submenu-"+ks.name).append(
69 69 $('<a>')
70 70 .attr('href', '#')
71 71 .click( function () {
72 72 that.set_kernel(ks.name);
73 73 })
74 74 .text(ks.spec.display_name)
75 75 )
76 76 );
77 77 // Create the File > New Notebook submenu
78 78 new_notebook_submenu.append(
79 79 $("<li>").attr("id", "new-notebook-submenu-"+ks.name).append(
80 80 $('<a>')
81 81 .attr('href', '#')
82 82 .click( function () {
83 83 that.new_notebook(ks.name);
84 84 })
85 85 .text(ks.spec.display_name)
86 86 )
87 87 );
88 88
89 89 });
90 90 // trigger loaded promise
91 91 this._loaded = true;
92 92 this._finish_load();
93 93 };
94 94
95 95 KernelSelector.prototype._spec_changed = function (event, ks) {
96 96 /** event handler for spec_changed */
97 97
98 98 // update selection
99 99 this.current_selection = ks.name;
100 100
101 101 // put the current kernel at the top of File > New Notebook
102 102 var cur_kernel_entry = $("#new-notebook-submenu-" + ks.name);
103 103 var parent = cur_kernel_entry.parent();
104 104 // do something only if there is more than one kernel
105 105 if (parent.children().length > 1) {
106 106 // first, sort back the submenu
107 107 parent.append(
108 108 parent.children("li[class!='divider']").sort(
109 109 function (a,b) {
110 110 var da = $("a",a).text();
111 111 var db = $("a",b).text();
112 112 if (da === db) {
113 113 return 0;
114 114 } else if (da > db) {
115 115 return 1;
116 116 } else {
117 117 return -1;
118 118 }}));
119 119 // then, if there is no divider yet, add one
120 120 if (!parent.children("li[class='divider']").length) {
121 121 parent.prepend($("<li>").attr("class","divider"));
122 122 }
123 123 // finally, put the current kernel at the top
124 124 parent.prepend(cur_kernel_entry);
125 125 }
126 126
127 127 // load logo
128 128 var logo_img = this.element.find("img.current_kernel_logo");
129 129 $("#kernel_indicator").find('.kernel_indicator_name').text(ks.spec.display_name);
130 130 if (ks.resources['logo-64x64']) {
131 131 logo_img.attr("src", ks.resources['logo-64x64']);
132 132 logo_img.show();
133 133 } else {
134 134 logo_img.hide();
135 135 }
136 136
137 137 // load kernel css
138 138 var css_url = ks.resources['kernel.css'];
139 139 if (css_url) {
140 140 $('#kernel-css').attr('href', css_url);
141 141 } else {
142 142 $('#kernel-css').attr('href', '');
143 143 }
144 144
145 145 // load kernel js
146 146 if (ks.resources['kernel.js']) {
147 147 require([ks.resources['kernel.js']],
148 148 function (kernel_mod) {
149 149 if (kernel_mod && kernel_mod.onload) {
150 150 kernel_mod.onload();
151 151 } else {
152 152 console.warn("Kernel " + ks.name + " has a kernel.js file that does not contain "+
153 153 "any asynchronous module definition. This is undefined behavior "+
154 154 "and not recommended.");
155 155 }
156 156 }, function (err) {
157 157 console.warn("Failed to load kernel.js from ", ks.resources['kernel.js'], err);
158 158 }
159 159 );
160 160 }
161 161 };
162 162
163 163 KernelSelector.prototype.set_kernel = function (selected) {
164 164 /** set the kernel by name, ensuring kernelspecs have been loaded, first
165 165
166 166 kernel can be just a kernel name, or a notebook kernelspec metadata
167 167 (name, language, display_name).
168 168 */
169 169 var that = this;
170 170 if (typeof selected === 'string') {
171 171 selected = {
172 172 name: selected
173 173 };
174 174 }
175 175 if (this._loaded) {
176 176 this._set_kernel(selected);
177 177 } else {
178 178 return this.loaded.then(function () {
179 179 that._set_kernel(selected);
180 180 });
181 181 }
182 182 };
183 183
184 184 KernelSelector.prototype._set_kernel = function (selected) {
185 185 /** Actually set the kernel (kernelspecs have been loaded) */
186 186 if (selected.name === this.current_selection) {
187 187 // only trigger event if value changed
188 188 return;
189 189 }
190 190 var kernelspecs = this.kernelspecs;
191 191 var ks = kernelspecs[selected.name];
192 192 if (ks === undefined) {
193 193 var available = _sorted_names(kernelspecs);
194 194 var matches = [];
195 195 if (selected.language && selected.language.length > 0) {
196 196 available.map(function (name) {
197 197 if (kernelspecs[name].spec.language.toLowerCase() === selected.language.toLowerCase()) {
198 198 matches.push(name);
199 199 }
200 200 });
201 201 }
202 202 if (matches.length === 1) {
203 203 ks = kernelspecs[matches[0]];
204 console.log("No exact match found for " + selected.name +
205 ", using only kernel that matches language=" + selected.language, ks);
206 this.events.trigger("spec_match_found.Kernel", {
207 selected: selected,
208 found: ks,
209 });
204 210 }
205 211 // if still undefined, trigger failure event
206 212 if (ks === undefined) {
207 213 this.events.trigger("spec_not_found.Kernel", {
208 214 selected: selected,
209 215 matches: matches,
210 216 available: available,
211 217 });
212 218 return;
213 219 }
214 220 }
215 221 if (this.notebook._session_starting) {
216 222 console.error("Cannot change kernel while waiting for pending session start.");
217 223 return;
218 224 }
219 225 this.current_selection = ks.name;
220 226 this.events.trigger('spec_changed.Kernel', ks);
221 227 };
222 228
223 229 KernelSelector.prototype._spec_not_found = function (event, data) {
224 230 var that = this;
225 231 var select = $("<select>").addClass('form-control');
226 232 console.warn("Kernelspec not found:", data);
227 233 var names;
228 234 if (data.matches.length > 1) {
229 235 names = data.matches;
230 236 } else {
231 237 names = data.available;
232 238 }
233 239 names.map(function (name) {
234 240 var ks = that.kernelspecs[name];
235 241 select.append(
236 242 $('<option/>').attr('value', ks.name).text(ks.spec.display_name || ks.name)
237 243 );
238 244 });
239 245
240 246 var body = $("<form>").addClass("form-inline").append(
241 247 $("<span>").text(
242 248 "I couldn't find a kernel matching " + (data.selected.display_name || data.name) + "." +
243 249 " Please select a kernel:"
244 250 )
245 251 ).append(select);
246 252
247 253 dialog.modal({
248 254 title : 'Kernel not found',
249 255 body : body,
250 256 buttons : {
251 257 'Continue without kernel' : {
252 258 class : 'btn-danger',
253 259 click : function () {
254 260 that.events.trigger('no_kernel.Kernel');
255 261 }
256 262 },
257 263 OK : {
258 264 class : 'btn-primary',
259 265 click : function () {
260 266 that.set_kernel(select.val());
261 267 }
262 268 }
263 269 }
264 270 });
265 271 };
266 272
267 273 KernelSelector.prototype.new_notebook = function (kernel_name) {
268 274
269 275 var w = window.open();
270 276 // Create a new notebook in the same path as the current
271 277 // notebook's path.
272 278 var that = this;
273 279 var parent = utils.url_path_split(that.notebook.notebook_path)[0];
274 280 that.notebook.contents.new_untitled(parent, {type: "notebook"}).then(
275 281 function (data) {
276 282 var url = utils.url_join_encode(
277 283 that.notebook.base_url, 'notebooks', data.path
278 284 );
279 285 url += "?kernel_name=" + kernel_name;
280 286 w.location = url;
281 287 },
282 288 function(error) {
283 289 w.close();
284 290 dialog.modal({
285 291 title : 'Creating Notebook Failed',
286 292 body : "The error was: " + error.message,
287 293 buttons : {'OK' : {'class' : 'btn-primary'}}
288 294 });
289 295 }
290 296 );
291 297 };
292 298
293 299 KernelSelector.prototype.lock_switch = function() {
294 300 // should set a flag and display warning+reload if user want to
295 301 // re-change kernel. As UI discussion never finish
296 302 // making that a separate PR.
297 303 console.warn('switching kernel is not guaranteed to work !');
298 304 };
299 305
300 306 KernelSelector.prototype.bind_events = function() {
301 307 var that = this;
302 308 this.events.on('spec_changed.Kernel', $.proxy(this._spec_changed, this));
303 309 this.events.on('spec_not_found.Kernel', $.proxy(this._spec_not_found, this));
304 310 this.events.on('kernel_created.Session', function (event, data) {
305 311 that.set_kernel(data.kernel.name);
306 312 });
307 313
308 314 var logo_img = this.element.find("img.current_kernel_logo");
309 315 logo_img.on("load", function() {
310 316 logo_img.show();
311 317 });
312 318 logo_img.on("error", function() {
313 319 logo_img.hide();
314 320 });
315 321 };
316 322
317 323 return {'KernelSelector': KernelSelector};
318 324 });
@@ -1,330 +1,337 b''
1 1 define([
2 2 'base/js/namespace',
3 3 'jquery',
4 4 'base/js/utils',
5 5 'base/js/dialog',
6 6 'base/js/notificationarea',
7 7 'moment'
8 8 ], function(IPython, $, utils, dialog, notificationarea, moment) {
9 9 "use strict";
10 10 var NotificationArea = notificationarea.NotificationArea;
11 11
12 12 var NotebookNotificationArea = function(selector, options) {
13 13 NotificationArea.apply(this, [selector, options]);
14 14 this.save_widget = options.save_widget;
15 15 this.notebook = options.notebook;
16 16 this.keyboard_manager = options.keyboard_manager;
17 }
17 };
18 18
19 19 NotebookNotificationArea.prototype = Object.create(NotificationArea.prototype);
20 20
21 21 /**
22 22 * Initialize the default set of notification widgets.
23 23 *
24 24 * @method init_notification_widgets
25 25 */
26 26 NotebookNotificationArea.prototype.init_notification_widgets = function () {
27 27 this.init_kernel_notification_widget();
28 28 this.init_notebook_notification_widget();
29 29 };
30 30
31 31 /**
32 32 * Initialize the notification widget for kernel status messages.
33 33 *
34 34 * @method init_kernel_notification_widget
35 35 */
36 36 NotebookNotificationArea.prototype.init_kernel_notification_widget = function () {
37 37 var that = this;
38 38 var knw = this.new_notification_widget('kernel');
39 39 var $kernel_ind_icon = $("#kernel_indicator_icon");
40 40 var $modal_ind_icon = $("#modal_indicator");
41 var $body = $('body')
41 var $body = $('body');
42 42
43 43 // Command/Edit mode
44 44 this.events.on('edit_mode.Notebook', function () {
45 45 that.save_widget.update_document_title();
46 46 $body.addClass('edit_mode');
47 47 $body.removeClass('command_mode');
48 48 $modal_ind_icon.attr('title','Edit Mode');
49 49 });
50 50
51 51 this.events.on('command_mode.Notebook', function () {
52 52 that.save_widget.update_document_title();
53 53 $body.removeClass('edit_mode');
54 54 $body.addClass('command_mode');
55 55 $modal_ind_icon.attr('title','Command Mode');
56 56 });
57 57
58 58 // Implicitly start off in Command mode, switching to Edit mode will trigger event
59 59 $modal_ind_icon.addClass('modal_indicator').attr('title','Command Mode');
60 $body.addClass('command_mode')
60 $body.addClass('command_mode');
61 61
62 62 // Kernel events
63 63
64 64 // this can be either kernel_created.Kernel or kernel_created.Session
65 65 this.events.on('kernel_created.Kernel kernel_created.Session', function () {
66 66 knw.info("Kernel Created", 500);
67 67 });
68 68
69 69 this.events.on('kernel_reconnecting.Kernel', function () {
70 70 knw.warning("Connecting to kernel");
71 71 });
72 72
73 73 this.events.on('kernel_connection_dead.Kernel', function (evt, info) {
74 74 knw.danger("Not Connected", undefined, function () {
75 75 // schedule reconnect a short time in the future, don't reconnect immediately
76 76 setTimeout($.proxy(info.kernel.reconnect, info.kernel), 500);
77 77 }, {title: 'click to reconnect'});
78 78 });
79 79
80 80 this.events.on('kernel_connected.Kernel', function () {
81 81 knw.info("Connected", 500);
82 82 });
83 83
84 84 this.events.on('kernel_restarting.Kernel', function () {
85 85 that.save_widget.update_document_title();
86 86 knw.set_message("Restarting kernel", 2000);
87 87 });
88 88
89 89 this.events.on('kernel_autorestarting.Kernel', function (evt, info) {
90 90 // Only show the dialog on the first restart attempt. This
91 91 // number gets tracked by the `Kernel` object and passed
92 92 // along here, because we don't want to show the user 5
93 93 // dialogs saying the same thing (which is the number of
94 94 // times it tries restarting).
95 95 if (info.attempt === 1) {
96 96
97 97 dialog.kernel_modal({
98 98 notebook: that.notebook,
99 99 keyboard_manager: that.keyboard_manager,
100 100 title: "Kernel Restarting",
101 101 body: "The kernel appears to have died. It will restart automatically.",
102 102 buttons: {
103 103 OK : {
104 104 class : "btn-primary"
105 105 }
106 106 }
107 107 });
108 };
108 }
109 109
110 110 that.save_widget.update_document_title();
111 111 knw.danger("Dead kernel");
112 112 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
113 113 });
114 114
115 115 this.events.on('kernel_interrupting.Kernel', function () {
116 116 knw.set_message("Interrupting kernel", 2000);
117 117 });
118 118
119 119 this.events.on('kernel_disconnected.Kernel', function () {
120 120 $kernel_ind_icon
121 121 .attr('class', 'kernel_disconnected_icon')
122 122 .attr('title', 'No Connection to Kernel');
123 123 });
124 124
125 125 this.events.on('kernel_connection_failed.Kernel', function (evt, info) {
126 126 // only show the dialog if this is the first failed
127 127 // connect attempt, because the kernel will continue
128 128 // trying to reconnect and we don't want to spam the user
129 129 // with messages
130 130 if (info.attempt === 1) {
131 131
132 132 var msg = "A connection to the notebook server could not be established." +
133 133 " The notebook will continue trying to reconnect, but" +
134 134 " until it does, you will NOT be able to run code. Check your" +
135 135 " network connection or notebook server configuration.";
136 136
137 137 dialog.kernel_modal({
138 138 title: "Connection failed",
139 139 body: msg,
140 140 keyboard_manager: that.keyboard_manager,
141 141 notebook: that.notebook,
142 142 buttons : {
143 143 "OK": {}
144 144 }
145 145 });
146 146 }
147 147 });
148 148
149 149 this.events.on('kernel_killed.Kernel kernel_killed.Session', function () {
150 150 that.save_widget.update_document_title();
151 151 knw.warning("No kernel");
152 152 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel is not running');
153 153 });
154 154
155 155 this.events.on('kernel_dead.Kernel', function () {
156 156
157 157 var showMsg = function () {
158 158
159 159 var msg = 'The kernel has died, and the automatic restart has failed.' +
160 160 ' It is possible the kernel cannot be restarted.' +
161 161 ' If you are not able to restart the kernel, you will still be able to save' +
162 162 ' the notebook, but running code will no longer work until the notebook' +
163 163 ' is reopened.';
164 164
165 165 dialog.kernel_modal({
166 166 title: "Dead kernel",
167 167 body : msg,
168 168 keyboard_manager: that.keyboard_manager,
169 169 notebook: that.notebook,
170 170 buttons : {
171 171 "Manual Restart": {
172 172 class: "btn-danger",
173 173 click: function () {
174 174 that.notebook.start_session();
175 175 }
176 176 },
177 177 "Don't restart": {}
178 178 }
179 179 });
180 180
181 181 return false;
182 182 };
183 183
184 184 that.save_widget.update_document_title();
185 185 knw.danger("Dead kernel", undefined, showMsg);
186 186 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
187 187
188 188 showMsg();
189 189 });
190 190
191 191 this.events.on("no_kernel.Kernel", function (evt, data) {
192 192 $("#kernel_indicator").find('.kernel_indicator_name').text("No Kernel");
193 193 });
194 194
195 195 this.events.on('kernel_dead.Session', function (evt, info) {
196 196 var full = info.xhr.responseJSON.message;
197 197 var short = info.xhr.responseJSON.short_message || 'Kernel error';
198 198 var traceback = info.xhr.responseJSON.traceback;
199 199
200 200 var showMsg = function () {
201 201 var msg = $('<div/>').append($('<p/>').text(full));
202 202 var cm, cm_elem, cm_open;
203 203
204 204 if (traceback) {
205 205 cm_elem = $('<div/>')
206 206 .css('margin-top', '1em')
207 207 .css('padding', '1em')
208 208 .addClass('output_scroll');
209 209 msg.append(cm_elem);
210 210 cm = CodeMirror(cm_elem.get(0), {
211 211 mode: "python",
212 212 readOnly : true
213 213 });
214 214 cm.setValue(traceback);
215 215 cm_open = $.proxy(cm.refresh, cm);
216 216 }
217 217
218 218 dialog.kernel_modal({
219 219 title: "Failed to start the kernel",
220 220 body : msg,
221 221 keyboard_manager: that.keyboard_manager,
222 222 notebook: that.notebook,
223 223 open: cm_open,
224 224 buttons : {
225 225 "Ok": { class: 'btn-primary' }
226 226 }
227 227 });
228 228
229 229 return false;
230 230 };
231 231
232 232 that.save_widget.update_document_title();
233 233 $kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
234 234 knw.danger(short, undefined, showMsg);
235 235 });
236 236
237 237 this.events.on('kernel_starting.Kernel kernel_created.Session', function () {
238 238 window.document.title='(Starting) '+window.document.title;
239 239 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
240 240 knw.set_message("Kernel starting, please wait...");
241 241 });
242 242
243 243 this.events.on('kernel_ready.Kernel', function () {
244 244 that.save_widget.update_document_title();
245 245 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
246 246 knw.info("Kernel ready", 500);
247 247 });
248 248
249 249 this.events.on('kernel_idle.Kernel', function () {
250 250 that.save_widget.update_document_title();
251 251 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
252 252 });
253 253
254 254 this.events.on('kernel_busy.Kernel', function () {
255 255 window.document.title='(Busy) '+window.document.title;
256 256 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
257 257 });
258 258
259 this.events.on('spec_match_found.Kernel', function (evt, data) {
260 that.widget('kernelspec').info("Using kernel: " + data.found.spec.display_name, 3000, undefined, {
261 title: "Only candidate for language: " + data.selected.language + " was " + data.found.spec.display_name
262 });
263 });
264
265
259 266 // Start the kernel indicator in the busy state, and send a kernel_info request.
260 267 // When the kernel_info reply arrives, the kernel is idle.
261 268 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
262 269 };
263 270
264 271 /**
265 272 * Initialize the notification widget for notebook status messages.
266 273 *
267 274 * @method init_notebook_notification_widget
268 275 */
269 276 NotebookNotificationArea.prototype.init_notebook_notification_widget = function () {
270 277 var nnw = this.new_notification_widget('notebook');
271 278
272 279 // Notebook events
273 280 this.events.on('notebook_loading.Notebook', function () {
274 281 nnw.set_message("Loading notebook",500);
275 282 });
276 283 this.events.on('notebook_loaded.Notebook', function () {
277 284 nnw.set_message("Notebook loaded",500);
278 285 });
279 286 this.events.on('notebook_saving.Notebook', function () {
280 287 nnw.set_message("Saving notebook",500);
281 288 });
282 289 this.events.on('notebook_saved.Notebook', function () {
283 290 nnw.set_message("Notebook saved",2000);
284 291 });
285 292 this.events.on('notebook_save_failed.Notebook', function (evt, error) {
286 293 nnw.warning(error.message || "Notebook save failed");
287 294 });
288 295 this.events.on('notebook_copy_failed.Notebook', function (evt, error) {
289 296 nnw.warning(error.message || "Notebook copy failed");
290 297 });
291 298
292 299 // Checkpoint events
293 300 this.events.on('checkpoint_created.Notebook', function (evt, data) {
294 301 var msg = "Checkpoint created";
295 302 if (data.last_modified) {
296 303 var d = new Date(data.last_modified);
297 304 msg = msg + ": " + moment(d).format("HH:mm:ss");
298 305 }
299 306 nnw.set_message(msg, 2000);
300 307 });
301 308 this.events.on('checkpoint_failed.Notebook', function () {
302 309 nnw.warning("Checkpoint failed");
303 310 });
304 311 this.events.on('checkpoint_deleted.Notebook', function () {
305 312 nnw.set_message("Checkpoint deleted", 500);
306 313 });
307 314 this.events.on('checkpoint_delete_failed.Notebook', function () {
308 315 nnw.warning("Checkpoint delete failed");
309 316 });
310 317 this.events.on('checkpoint_restoring.Notebook', function () {
311 318 nnw.set_message("Restoring to checkpoint...", 500);
312 319 });
313 320 this.events.on('checkpoint_restore_failed.Notebook', function () {
314 321 nnw.warning("Checkpoint restore failed");
315 322 });
316 323
317 324 // Autosave events
318 325 this.events.on('autosave_disabled.Notebook', function () {
319 326 nnw.set_message("Autosave disabled", 2000);
320 327 });
321 328 this.events.on('autosave_enabled.Notebook', function (evt, interval) {
322 329 nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
323 330 });
324 331 };
325 332
326 333 // Backwards compatibility.
327 334 IPython.NotificationArea = NotebookNotificationArea;
328 335
329 336 return {'NotebookNotificationArea': NotebookNotificationArea};
330 337 });
General Comments 0
You need to be logged in to leave comments. Login now