##// END OF EJS Templates
Refactoring WebSocket connection failure logic....
Brian E. Granger -
Show More
@@ -1,460 +1,439
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Kernel
9 // Kernel
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule Kernel
15 * @submodule Kernel
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19
19
20 var utils = IPython.utils;
20 var utils = IPython.utils;
21
21
22 // Initialization and connection.
22 // Initialization and connection.
23 /**
23 /**
24 * A Kernel Class to communicate with the Python kernel
24 * A Kernel Class to communicate with the Python kernel
25 * @Class Kernel
25 * @Class Kernel
26 */
26 */
27 var Kernel = function (base_url) {
27 var Kernel = function (base_url) {
28 this.kernel_id = null;
28 this.kernel_id = null;
29 this.shell_channel = null;
29 this.shell_channel = null;
30 this.iopub_channel = null;
30 this.iopub_channel = null;
31 this.base_url = base_url;
31 this.base_url = base_url;
32 this.running = false;
32 this.running = false;
33 this.username = "username";
33 this.username = "username";
34 this.session_id = utils.uuid();
34 this.session_id = utils.uuid();
35 this._msg_callbacks = {};
35 this._msg_callbacks = {};
36
36
37 if (typeof(WebSocket) !== 'undefined') {
37 if (typeof(WebSocket) !== 'undefined') {
38 this.WebSocket = WebSocket;
38 this.WebSocket = WebSocket;
39 } else if (typeof(MozWebSocket) !== 'undefined') {
39 } else if (typeof(MozWebSocket) !== 'undefined') {
40 this.WebSocket = MozWebSocket;
40 this.WebSocket = MozWebSocket;
41 } else {
41 } else {
42 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.');
42 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.');
43 };
43 };
44 };
44 };
45
45
46
46
47 Kernel.prototype._get_msg = function (msg_type, content) {
47 Kernel.prototype._get_msg = function (msg_type, content) {
48 var msg = {
48 var msg = {
49 header : {
49 header : {
50 msg_id : utils.uuid(),
50 msg_id : utils.uuid(),
51 username : this.username,
51 username : this.username,
52 session : this.session_id,
52 session : this.session_id,
53 msg_type : msg_type
53 msg_type : msg_type
54 },
54 },
55 metadata : {},
55 metadata : {},
56 content : content,
56 content : content,
57 parent_header : {}
57 parent_header : {}
58 };
58 };
59 return msg;
59 return msg;
60 };
60 };
61
61
62 /**
62 /**
63 * Start the Python kernel
63 * Start the Python kernel
64 * @method start
64 * @method start
65 */
65 */
66 Kernel.prototype.start = function (notebook_id) {
66 Kernel.prototype.start = function (notebook_id) {
67 var that = this;
67 var that = this;
68 if (!this.running) {
68 if (!this.running) {
69 var qs = $.param({notebook:notebook_id});
69 var qs = $.param({notebook:notebook_id});
70 var url = this.base_url + '?' + qs;
70 var url = this.base_url + '?' + qs;
71 $.post(url,
71 $.post(url,
72 $.proxy(that._kernel_started,that),
72 $.proxy(that._kernel_started,that),
73 'json'
73 'json'
74 );
74 );
75 };
75 };
76 };
76 };
77
77
78 /**
78 /**
79 * Restart the python kernel.
79 * Restart the python kernel.
80 *
80 *
81 * Emit a 'status_restarting.Kernel' event with
81 * Emit a 'status_restarting.Kernel' event with
82 * the current object as parameter
82 * the current object as parameter
83 *
83 *
84 * @method restart
84 * @method restart
85 */
85 */
86 Kernel.prototype.restart = function () {
86 Kernel.prototype.restart = function () {
87 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
87 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
88 var that = this;
88 var that = this;
89 if (this.running) {
89 if (this.running) {
90 this.stop_channels();
90 this.stop_channels();
91 var url = this.kernel_url + "/restart";
91 var url = this.kernel_url + "/restart";
92 $.post(url,
92 $.post(url,
93 $.proxy(that._kernel_started, that),
93 $.proxy(that._kernel_started, that),
94 'json'
94 'json'
95 );
95 );
96 };
96 };
97 };
97 };
98
98
99
99
100 Kernel.prototype._kernel_started = function (json) {
100 Kernel.prototype._kernel_started = function (json) {
101 console.log("Kernel started: ", json.kernel_id);
101 console.log("Kernel started: ", json.kernel_id);
102 this.running = true;
102 this.running = true;
103 this.kernel_id = json.kernel_id;
103 this.kernel_id = json.kernel_id;
104 this.ws_url = json.ws_url;
104 this.ws_url = json.ws_url;
105 this.kernel_url = this.base_url + "/" + this.kernel_id;
105 this.kernel_url = this.base_url + "/" + this.kernel_id;
106 this.start_channels();
106 this.start_channels();
107 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
107 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
108 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
108 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
109 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
109 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
110 };
110 };
111
111
112
112
113 Kernel.prototype._websocket_closed = function(ws_url, early){
113 Kernel.prototype._websocket_closed = function(ws_url, early) {
114 var msg;
114 this.stop_channels();
115 var parent_item = $('body');
115 $([IPython.events]).trigger('websocket_closed.Kernel',
116 if (early) {
116 {ws_url: ws_url, kernel: this, early: early}
117 msg = "Websocket connection to " + ws_url + " could not be established." +
117 );
118 " You will NOT be able to run code." +
119 " Your browser may not be compatible with the websocket version in the server," +
120 " or if the url does not look right, there could be an error in the" +
121 " server's configuration.";
122 } else {
123 IPython.notification_area.widget('kernel').set_message('Reconnecting Websockets', 1000);
124 this.start_channels();
125 return;
126 }
127 var dialog = $('<div/>');
128 dialog.html(msg);
129 parent_item.append(dialog);
130 dialog.dialog({
131 resizable: false,
132 modal: true,
133 title: "Websocket closed",
134 closeText: "",
135 close: function(event, ui) {$(this).dialog('destroy').remove();},
136 buttons : {
137 "OK": function () {
138 $(this).dialog('close');
139 }
140 }
141 });
142
143 };
118 };
144
119
145 /**
120 /**
146 * Start the `shell`and `iopub` channels.
121 * Start the `shell`and `iopub` channels.
147 * Will stop and restart them if they already exist.
122 * Will stop and restart them if they already exist.
148 *
123 *
149 * @method start_channels
124 * @method start_channels
150 */
125 */
151 Kernel.prototype.start_channels = function () {
126 Kernel.prototype.start_channels = function () {
152 var that = this;
127 var that = this;
153 this.stop_channels();
128 this.stop_channels();
154 var ws_url = this.ws_url + this.kernel_url;
129 var ws_url = this.ws_url + this.kernel_url;
155 console.log("Starting WS:", ws_url);
130 console.log("Starting WebSockets:", ws_url);
156 this.shell_channel = new this.WebSocket(ws_url + "/shell");
131 this.shell_channel = new this.WebSocket(ws_url + "/shell");
157 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
132 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
158 send_cookie = function(){
133 send_cookie = function(){
159 this.send(document.cookie);
134 this.send(document.cookie);
160 };
135 };
161 var already_called_onclose = false; // only alert once
136 var already_called_onclose = false; // only alert once
162 var ws_closed_early = function(evt){
137 var ws_closed_early = function(evt){
163 if (already_called_onclose){
138 if (already_called_onclose){
164 return;
139 return;
165 }
140 }
166 already_called_onclose = true;
141 already_called_onclose = true;
167 if ( ! evt.wasClean ){
142 if ( ! evt.wasClean ){
168 that._websocket_closed(ws_url, true);
143 that._websocket_closed(ws_url, true);
169 }
144 }
170 };
145 };
171 var ws_closed_late = function(evt){
146 var ws_closed_late = function(evt){
172 if (already_called_onclose){
147 if (already_called_onclose){
173 return;
148 return;
174 }
149 }
175 already_called_onclose = true;
150 already_called_onclose = true;
176 if ( ! evt.wasClean ){
151 if ( ! evt.wasClean ){
177 that._websocket_closed(ws_url, false);
152 that._websocket_closed(ws_url, false);
178 }
153 }
179 };
154 };
180 this.shell_channel.onopen = send_cookie;
155 this.shell_channel.onopen = send_cookie;
181 this.shell_channel.onclose = ws_closed_early;
156 this.shell_channel.onclose = ws_closed_early;
182 this.iopub_channel.onopen = send_cookie;
157 this.iopub_channel.onopen = send_cookie;
183 this.iopub_channel.onclose = ws_closed_early;
158 this.iopub_channel.onclose = ws_closed_early;
184 // switch from early-close to late-close message after 1s
159 // switch from early-close to late-close message after 1s
185 setTimeout(function(){
160 setTimeout(function() {
186 that.shell_channel.onclose = ws_closed_late;
161 if (that.shell_channel !== null) {
187 that.iopub_channel.onclose = ws_closed_late;
162 that.shell_channel.onclose = ws_closed_late;
163 }
164 if (that.iopub_channel !== null) {
165 that.iopub_channel.onclose = ws_closed_late;
166 }
188 }, 1000);
167 }, 1000);
189 };
168 };
190
169
191 /**
170 /**
192 * Start the `shell`and `iopub` channels.
171 * Start the `shell`and `iopub` channels.
193 * @method stop_channels
172 * @method stop_channels
194 */
173 */
195 Kernel.prototype.stop_channels = function () {
174 Kernel.prototype.stop_channels = function () {
196 if (this.shell_channel !== null) {
175 if (this.shell_channel !== null) {
197 this.shell_channel.onclose = function (evt) {};
176 this.shell_channel.onclose = function (evt) {};
198 this.shell_channel.close();
177 this.shell_channel.close();
199 this.shell_channel = null;
178 this.shell_channel = null;
200 };
179 };
201 if (this.iopub_channel !== null) {
180 if (this.iopub_channel !== null) {
202 this.iopub_channel.onclose = function (evt) {};
181 this.iopub_channel.onclose = function (evt) {};
203 this.iopub_channel.close();
182 this.iopub_channel.close();
204 this.iopub_channel = null;
183 this.iopub_channel = null;
205 };
184 };
206 };
185 };
207
186
208 // Main public methods.
187 // Main public methods.
209
188
210 /**
189 /**
211 * Get info on object asynchronoulsy
190 * Get info on object asynchronoulsy
212 *
191 *
213 * @async
192 * @async
214 * @param objname {string}
193 * @param objname {string}
215 * @param callback {dict}
194 * @param callback {dict}
216 * @method object_info_request
195 * @method object_info_request
217 *
196 *
218 * @example
197 * @example
219 *
198 *
220 * When calling this method pass a callbacks structure of the form:
199 * When calling this method pass a callbacks structure of the form:
221 *
200 *
222 * callbacks = {
201 * callbacks = {
223 * 'object_info_reply': object_info_reply_callback
202 * 'object_info_reply': object_info_reply_callback
224 * }
203 * }
225 *
204 *
226 * The `object_info_reply_callback` will be passed the content object of the
205 * The `object_info_reply_callback` will be passed the content object of the
227 *
206 *
228 * `object_into_reply` message documented in
207 * `object_into_reply` message documented in
229 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
208 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
230 */
209 */
231 Kernel.prototype.object_info_request = function (objname, callbacks) {
210 Kernel.prototype.object_info_request = function (objname, callbacks) {
232 if(typeof(objname)!=null && objname!=null)
211 if(typeof(objname)!=null && objname!=null)
233 {
212 {
234 var content = {
213 var content = {
235 oname : objname.toString(),
214 oname : objname.toString(),
236 };
215 };
237 var msg = this._get_msg("object_info_request", content);
216 var msg = this._get_msg("object_info_request", content);
238 this.shell_channel.send(JSON.stringify(msg));
217 this.shell_channel.send(JSON.stringify(msg));
239 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
218 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
240 return msg.header.msg_id;
219 return msg.header.msg_id;
241 }
220 }
242 return;
221 return;
243 }
222 }
244
223
245 /**
224 /**
246 * Execute given code into kernel, and pass result to callback.
225 * Execute given code into kernel, and pass result to callback.
247 *
226 *
248 * @async
227 * @async
249 * @method execute
228 * @method execute
250 * @param {string} code
229 * @param {string} code
251 * @param callback {Object} With the following keys
230 * @param callback {Object} With the following keys
252 * @param callback.'execute_reply' {function}
231 * @param callback.'execute_reply' {function}
253 * @param callback.'output' {function}
232 * @param callback.'output' {function}
254 * @param callback.'clear_output' {function}
233 * @param callback.'clear_output' {function}
255 * @param callback.'set_next_input' {function}
234 * @param callback.'set_next_input' {function}
256 * @param {object} [options]
235 * @param {object} [options]
257 * @param [options.silent=false] {Boolean}
236 * @param [options.silent=false] {Boolean}
258 * @param [options.user_expressions=empty_dict] {Dict}
237 * @param [options.user_expressions=empty_dict] {Dict}
259 * @param [options.user_variables=empty_list] {List od Strings}
238 * @param [options.user_variables=empty_list] {List od Strings}
260 * @param [options.allow_stdin=false] {Boolean} true|false
239 * @param [options.allow_stdin=false] {Boolean} true|false
261 *
240 *
262 * @example
241 * @example
263 *
242 *
264 * The options object should contain the options for the execute call. Its default
243 * The options object should contain the options for the execute call. Its default
265 * values are:
244 * values are:
266 *
245 *
267 * options = {
246 * options = {
268 * silent : true,
247 * silent : true,
269 * user_variables : [],
248 * user_variables : [],
270 * user_expressions : {},
249 * user_expressions : {},
271 * allow_stdin : false
250 * allow_stdin : false
272 * }
251 * }
273 *
252 *
274 * When calling this method pass a callbacks structure of the form:
253 * When calling this method pass a callbacks structure of the form:
275 *
254 *
276 * callbacks = {
255 * callbacks = {
277 * 'execute_reply': execute_reply_callback,
256 * 'execute_reply': execute_reply_callback,
278 * 'output': output_callback,
257 * 'output': output_callback,
279 * 'clear_output': clear_output_callback,
258 * 'clear_output': clear_output_callback,
280 * 'set_next_input': set_next_input_callback
259 * 'set_next_input': set_next_input_callback
281 * }
260 * }
282 *
261 *
283 * The `execute_reply_callback` will be passed the content and metadata
262 * The `execute_reply_callback` will be passed the content and metadata
284 * objects of the `execute_reply` message documented
263 * objects of the `execute_reply` message documented
285 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
264 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
286 *
265 *
287 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
266 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
288 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
267 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
289 * output:
268 * output:
290 *
269 *
291 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
270 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
292 *
271 *
293 * The `clear_output_callback` will be passed a content object that contains
272 * The `clear_output_callback` will be passed a content object that contains
294 * stdout, stderr and other fields that are booleans, as well as the metadata object.
273 * stdout, stderr and other fields that are booleans, as well as the metadata object.
295 *
274 *
296 * The `set_next_input_callback` will be passed the text that should become the next
275 * The `set_next_input_callback` will be passed the text that should become the next
297 * input cell.
276 * input cell.
298 */
277 */
299 Kernel.prototype.execute = function (code, callbacks, options) {
278 Kernel.prototype.execute = function (code, callbacks, options) {
300
279
301 var content = {
280 var content = {
302 code : code,
281 code : code,
303 silent : true,
282 silent : true,
304 user_variables : [],
283 user_variables : [],
305 user_expressions : {},
284 user_expressions : {},
306 allow_stdin : false
285 allow_stdin : false
307 };
286 };
308 $.extend(true, content, options)
287 $.extend(true, content, options)
309 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
288 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
310 var msg = this._get_msg("execute_request", content);
289 var msg = this._get_msg("execute_request", content);
311 this.shell_channel.send(JSON.stringify(msg));
290 this.shell_channel.send(JSON.stringify(msg));
312 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
291 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
313 return msg.header.msg_id;
292 return msg.header.msg_id;
314 };
293 };
315
294
316 /**
295 /**
317 * When calling this method pass a callbacks structure of the form:
296 * When calling this method pass a callbacks structure of the form:
318 *
297 *
319 * callbacks = {
298 * callbacks = {
320 * 'complete_reply': complete_reply_callback
299 * 'complete_reply': complete_reply_callback
321 * }
300 * }
322 *
301 *
323 * The `complete_reply_callback` will be passed the content object of the
302 * The `complete_reply_callback` will be passed the content object of the
324 * `complete_reply` message documented
303 * `complete_reply` message documented
325 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
304 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
326 *
305 *
327 * @method complete
306 * @method complete
328 * @param line {integer}
307 * @param line {integer}
329 * @param cursor_pos {integer}
308 * @param cursor_pos {integer}
330 * @param {dict} callbacks
309 * @param {dict} callbacks
331 * @param callbacks.complete_reply {function} `complete_reply_callback`
310 * @param callbacks.complete_reply {function} `complete_reply_callback`
332 *
311 *
333 */
312 */
334 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
313 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
335 callbacks = callbacks || {};
314 callbacks = callbacks || {};
336 var content = {
315 var content = {
337 text : '',
316 text : '',
338 line : line,
317 line : line,
339 cursor_pos : cursor_pos
318 cursor_pos : cursor_pos
340 };
319 };
341 var msg = this._get_msg("complete_request", content);
320 var msg = this._get_msg("complete_request", content);
342 this.shell_channel.send(JSON.stringify(msg));
321 this.shell_channel.send(JSON.stringify(msg));
343 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
322 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
344 return msg.header.msg_id;
323 return msg.header.msg_id;
345 };
324 };
346
325
347
326
348 Kernel.prototype.interrupt = function () {
327 Kernel.prototype.interrupt = function () {
349 if (this.running) {
328 if (this.running) {
350 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
329 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
351 $.post(this.kernel_url + "/interrupt");
330 $.post(this.kernel_url + "/interrupt");
352 };
331 };
353 };
332 };
354
333
355
334
356 Kernel.prototype.kill = function () {
335 Kernel.prototype.kill = function () {
357 if (this.running) {
336 if (this.running) {
358 this.running = false;
337 this.running = false;
359 var settings = {
338 var settings = {
360 cache : false,
339 cache : false,
361 type : "DELETE"
340 type : "DELETE"
362 };
341 };
363 $.ajax(this.kernel_url, settings);
342 $.ajax(this.kernel_url, settings);
364 };
343 };
365 };
344 };
366
345
367
346
368 // Reply handlers.
347 // Reply handlers.
369
348
370 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
349 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
371 var callbacks = this._msg_callbacks[msg_id];
350 var callbacks = this._msg_callbacks[msg_id];
372 return callbacks;
351 return callbacks;
373 };
352 };
374
353
375
354
376 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
355 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
377 this._msg_callbacks[msg_id] = callbacks || {};
356 this._msg_callbacks[msg_id] = callbacks || {};
378 }
357 }
379
358
380
359
381 Kernel.prototype._handle_shell_reply = function (e) {
360 Kernel.prototype._handle_shell_reply = function (e) {
382 var reply = $.parseJSON(e.data);
361 var reply = $.parseJSON(e.data);
383 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
362 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
384 var header = reply.header;
363 var header = reply.header;
385 var content = reply.content;
364 var content = reply.content;
386 var metadata = reply.metadata;
365 var metadata = reply.metadata;
387 var msg_type = header.msg_type;
366 var msg_type = header.msg_type;
388 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
367 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
389 if (callbacks !== undefined) {
368 if (callbacks !== undefined) {
390 var cb = callbacks[msg_type];
369 var cb = callbacks[msg_type];
391 if (cb !== undefined) {
370 if (cb !== undefined) {
392 cb(content, metadata);
371 cb(content, metadata);
393 }
372 }
394 };
373 };
395
374
396 if (content.payload !== undefined) {
375 if (content.payload !== undefined) {
397 var payload = content.payload || [];
376 var payload = content.payload || [];
398 this._handle_payload(callbacks, payload);
377 this._handle_payload(callbacks, payload);
399 }
378 }
400 };
379 };
401
380
402
381
403 Kernel.prototype._handle_payload = function (callbacks, payload) {
382 Kernel.prototype._handle_payload = function (callbacks, payload) {
404 var l = payload.length;
383 var l = payload.length;
405 // Payloads are handled by triggering events because we don't want the Kernel
384 // Payloads are handled by triggering events because we don't want the Kernel
406 // to depend on the Notebook or Pager classes.
385 // to depend on the Notebook or Pager classes.
407 for (var i=0; i<l; i++) {
386 for (var i=0; i<l; i++) {
408 if (payload[i].source === 'IPython.zmq.page.page') {
387 if (payload[i].source === 'IPython.zmq.page.page') {
409 var data = {'text':payload[i].text}
388 var data = {'text':payload[i].text}
410 $([IPython.events]).trigger('open_with_text.Pager', data);
389 $([IPython.events]).trigger('open_with_text.Pager', data);
411 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
390 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
412 if (callbacks.set_next_input !== undefined) {
391 if (callbacks.set_next_input !== undefined) {
413 callbacks.set_next_input(payload[i].text)
392 callbacks.set_next_input(payload[i].text)
414 }
393 }
415 }
394 }
416 };
395 };
417 };
396 };
418
397
419
398
420 Kernel.prototype._handle_iopub_reply = function (e) {
399 Kernel.prototype._handle_iopub_reply = function (e) {
421 var reply = $.parseJSON(e.data);
400 var reply = $.parseJSON(e.data);
422 var content = reply.content;
401 var content = reply.content;
423 var msg_type = reply.header.msg_type;
402 var msg_type = reply.header.msg_type;
424 var metadata = reply.metadata;
403 var metadata = reply.metadata;
425 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
404 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
426 if (msg_type !== 'status' && callbacks === undefined) {
405 if (msg_type !== 'status' && callbacks === undefined) {
427 // Message not from one of this notebook's cells and there are no
406 // Message not from one of this notebook's cells and there are no
428 // callbacks to handle it.
407 // callbacks to handle it.
429 return;
408 return;
430 }
409 }
431 var output_types = ['stream','display_data','pyout','pyerr'];
410 var output_types = ['stream','display_data','pyout','pyerr'];
432 if (output_types.indexOf(msg_type) >= 0) {
411 if (output_types.indexOf(msg_type) >= 0) {
433 var cb = callbacks['output'];
412 var cb = callbacks['output'];
434 if (cb !== undefined) {
413 if (cb !== undefined) {
435 cb(msg_type, content, metadata);
414 cb(msg_type, content, metadata);
436 }
415 }
437 } else if (msg_type === 'status') {
416 } else if (msg_type === 'status') {
438 if (content.execution_state === 'busy') {
417 if (content.execution_state === 'busy') {
439 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
418 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
440 } else if (content.execution_state === 'idle') {
419 } else if (content.execution_state === 'idle') {
441 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
420 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
442 } else if (content.execution_state === 'dead') {
421 } else if (content.execution_state === 'dead') {
443 this.stop_channels();
422 this.stop_channels();
444 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
423 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
445 };
424 };
446 } else if (msg_type === 'clear_output') {
425 } else if (msg_type === 'clear_output') {
447 var cb = callbacks['clear_output'];
426 var cb = callbacks['clear_output'];
448 if (cb !== undefined) {
427 if (cb !== undefined) {
449 cb(content, metadata);
428 cb(content, metadata);
450 }
429 }
451 };
430 };
452 };
431 };
453
432
454
433
455 IPython.Kernel = Kernel;
434 IPython.Kernel = Kernel;
456
435
457 return IPython;
436 return IPython;
458
437
459 }(IPython));
438 }(IPython));
460
439
@@ -1,141 +1,188
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2012 The IPython Development Team
2 // Copyright (C) 2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notification widget
9 // Notification widget
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16
16
17 var NotificationArea = function (selector) {
17 var NotificationArea = function (selector) {
18 this.selector = selector;
18 this.selector = selector;
19 if (this.selector !== undefined) {
19 if (this.selector !== undefined) {
20 this.element = $(selector);
20 this.element = $(selector);
21 }
21 }
22 this.widget_dict = {};
22 this.widget_dict = {};
23 };
23 };
24
24
25 NotificationArea.prototype.temp_message = function (msg, timeout, css_class) {
25 NotificationArea.prototype.temp_message = function (msg, timeout, css_class) {
26 var uuid = utils.uuid();
26 var uuid = utils.uuid();
27 if( css_class == 'danger') {css_class = 'ui-state-error';}
27 if( css_class == 'danger') {css_class = 'ui-state-error';}
28 if( css_class == 'warning') {css_class = 'ui-state-highlight';}
28 if( css_class == 'warning') {css_class = 'ui-state-highlight';}
29 var tdiv = $('<div>')
29 var tdiv = $('<div>')
30 .attr('id',uuid)
30 .attr('id',uuid)
31 .addClass('notification_widget ui-widget ui-widget-content ui-corner-all')
31 .addClass('notification_widget ui-widget ui-widget-content ui-corner-all')
32 .addClass('border-box-sizing')
32 .addClass('border-box-sizing')
33 .addClass(css_class)
33 .addClass(css_class)
34 .hide()
34 .hide()
35 .text(msg);
35 .text(msg);
36
36
37 $(this.selector).append(tdiv);
37 $(this.selector).append(tdiv);
38 var tmout = Math.max(1500,(timeout||1500));
38 var tmout = Math.max(1500,(timeout||1500));
39 tdiv.fadeIn(100);
39 tdiv.fadeIn(100);
40
40
41 setTimeout(function () {
41 setTimeout(function () {
42 tdiv.fadeOut(100, function () {tdiv.remove();});
42 tdiv.fadeOut(100, function () {tdiv.remove();});
43 }, tmout);
43 }, tmout);
44 };
44 };
45
45
46 NotificationArea.prototype.widget = function(name) {
46 NotificationArea.prototype.widget = function(name) {
47 if(this.widget_dict[name] == undefined) {
47 if(this.widget_dict[name] == undefined) {
48 return this.new_notification_widget(name);
48 return this.new_notification_widget(name);
49 }
49 }
50 return this.get_widget(name);
50 return this.get_widget(name);
51 };
51 };
52
52
53 NotificationArea.prototype.get_widget = function(name) {
53 NotificationArea.prototype.get_widget = function(name) {
54 if(this.widget_dict[name] == undefined) {
54 if(this.widget_dict[name] == undefined) {
55 throw('no widgets with this name');
55 throw('no widgets with this name');
56 }
56 }
57 return this.widget_dict[name];
57 return this.widget_dict[name];
58 };
58 };
59
59
60 NotificationArea.prototype.new_notification_widget = function(name) {
60 NotificationArea.prototype.new_notification_widget = function(name) {
61 if(this.widget_dict[name] != undefined) {
61 if(this.widget_dict[name] != undefined) {
62 throw('widget with that name already exists ! ');
62 throw('widget with that name already exists ! ');
63 }
63 }
64 var div = $('<div/>').attr('id','notification_'+name);
64 var div = $('<div/>').attr('id','notification_'+name);
65 $(this.selector).append(div);
65 $(this.selector).append(div);
66 this.widget_dict[name] = new IPython.NotificationWidget('#notification_'+name);
66 this.widget_dict[name] = new IPython.NotificationWidget('#notification_'+name);
67 return this.widget_dict[name];
67 return this.widget_dict[name];
68 };
68 };
69
69
70 NotificationArea.prototype.init_notification_widgets = function() {
70 NotificationArea.prototype.init_notification_widgets = function() {
71 var knw = this.new_notification_widget('kernel');
71 var knw = this.new_notification_widget('kernel');
72
72
73 // Kernel events
73 // Kernel events
74 $([IPython.events]).on('status_idle.Kernel',function () {
74 $([IPython.events]).on('status_idle.Kernel',function () {
75 IPython.save_widget.update_document_title();
75 IPython.save_widget.update_document_title();
76 knw.set_message('Kernel Idle',200);
76 knw.set_message('Kernel Idle',200);
77 }
77 }
78 );
78 );
79
79
80 $([IPython.events]).on('status_busy.Kernel',function () {
80 $([IPython.events]).on('status_busy.Kernel',function () {
81 window.document.title='(Busy) '+window.document.title;
81 window.document.title='(Busy) '+window.document.title;
82 knw.set_message("Kernel busy");
82 knw.set_message("Kernel busy");
83 });
83 });
84
84
85 $([IPython.events]).on('status_restarting.Kernel',function () {
85 $([IPython.events]).on('status_restarting.Kernel',function () {
86 IPython.save_widget.update_document_title();
86 IPython.save_widget.update_document_title();
87 knw.set_message("Restarting kernel",1000);
87 knw.set_message("Restarting kernel",1000);
88 });
88 });
89
89
90 $([IPython.events]).on('status_interrupting.Kernel',function () {
90 $([IPython.events]).on('status_interrupting.Kernel',function () {
91 knw.set_message("Interrupting kernel");
91 knw.set_message("Interrupting kernel");
92 });
92 });
93
93
94 $([IPython.events]).on('status_dead.Kernel',function () {
94 $([IPython.events]).on('status_dead.Kernel',function () {
95 var dialog = $('<div/>');
95 var dialog = $('<div/>');
96 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
96 dialog.html('The kernel has died, would you like to restart it?' +
97 ' If you do not restart the kernel, you will be able to save' +
98 ' the notebook, but running code will not work until the notebook' +
99 ' is reopened.'
100 );
97 $(document).append(dialog);
101 $(document).append(dialog);
98 dialog.dialog({
102 dialog.dialog({
99 resizable: false,
103 resizable: false,
100 modal: true,
104 modal: true,
101 title: "Dead kernel",
105 title: "Dead kernel",
106 close: function(event, ui) {$(this).dialog('destroy').remove();},
102 buttons : {
107 buttons : {
103 "Restart": function () {
108 "Restart": function () {
104 $([IPython.events]).trigger('status_restarting.Kernel');
109 $([IPython.events]).trigger('status_restarting.Kernel');
105 IPython.notebook.start_kernel();
110 IPython.notebook.start_kernel();
106 $(this).dialog('close');
111 $(this).dialog('close');
107 },
112 },
108 "Continue running": function () {
113 "Don't restart": function () {
109 $(this).dialog('close');
114 $(this).dialog('close');
110 }
115 }
111 }
116 }
112 });
117 });
113 });
118 });
114
119
120 $([IPython.events]).on('websocket_closed.Kernel', function (event, data) {
121 var kernel = data.kernel;
122 var ws_url = data.ws_url;
123 var early = data.early;
124 var msg;
125 console.log(early);
126 if (!early) {
127 knw.set_message('Reconnecting WebSockets', 1000);
128 setTimeout(function () {
129 kernel.start_channels();
130 }, 5000);
131 return;
132 }
133 console.log('WebSocket connection failed: ', ws_url)
134 msg = "A WebSocket connection to could not be established." +
135 " You will NOT be able to run code. Check your" +
136 " network connection or notebook server configuration.";
137 var dialog = $('<div/>');
138 dialog.html(msg);
139 $(document).append(dialog);
140 dialog.dialog({
141 resizable: false,
142 modal: true,
143 title: "WebSocket connection failed",
144 closeText: "",
145 close: function(event, ui) {$(this).dialog('destroy').remove();},
146 buttons : {
147 "OK": function () {
148 $(this).dialog('close');
149 },
150 "Reconnect": function () {
151 knw.set_message('Reconnecting WebSockets', 1000);
152 setTimeout(function () {
153 kernel.start_channels();
154 }, 5000);
155 $(this).dialog('close');
156 }
157 }
158 });
159 });
160
161
115 var nnw = this.new_notification_widget('notebook');
162 var nnw = this.new_notification_widget('notebook');
116
163
117 // Notebook events
164 // Notebook events
118 $([IPython.events]).on('notebook_loading.Notebook', function () {
165 $([IPython.events]).on('notebook_loading.Notebook', function () {
119 nnw.set_message("Loading notebook",500);
166 nnw.set_message("Loading notebook",500);
120 });
167 });
121 $([IPython.events]).on('notebook_loaded.Notebook', function () {
168 $([IPython.events]).on('notebook_loaded.Notebook', function () {
122 nnw.set_message("Notebook loaded",500);
169 nnw.set_message("Notebook loaded",500);
123 });
170 });
124 $([IPython.events]).on('notebook_saving.Notebook', function () {
171 $([IPython.events]).on('notebook_saving.Notebook', function () {
125 nnw.set_message("Saving notebook",500);
172 nnw.set_message("Saving notebook",500);
126 });
173 });
127 $([IPython.events]).on('notebook_saved.Notebook', function () {
174 $([IPython.events]).on('notebook_saved.Notebook', function () {
128 nnw.set_message("Notebook saved",2000);
175 nnw.set_message("Notebook saved",2000);
129 });
176 });
130 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
177 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
131 nnw.set_message("Notebook save failed");
178 nnw.set_message("Notebook save failed");
132 });
179 });
133
180
134 };
181 };
135
182
136 IPython.NotificationArea = NotificationArea;
183 IPython.NotificationArea = NotificationArea;
137
184
138 return IPython;
185 return IPython;
139
186
140 }(IPython));
187 }(IPython));
141
188
General Comments 0
You need to be logged in to leave comments. Login now