##// END OF EJS Templates
Intercept <esc> avoid closing websocket on Firefox...
Matthias BUSSONNIER -
Show More
@@ -1,1009 +1,1014 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var Notebook = function (selector) {
17 17 this.read_only = IPython.read_only;
18 18 this.element = $(selector);
19 19 this.element.scroll();
20 20 this.element.data("notebook", this);
21 21 this.next_prompt_number = 1;
22 22 this.kernel = null;
23 23 this.dirty = false;
24 24 this.msg_cell_map = {};
25 25 this.metadata = {};
26 26 this.control_key_active = false;
27 27 this.style();
28 28 this.create_elements();
29 29 this.bind_events();
30 30 };
31 31
32 32
33 33 Notebook.prototype.style = function () {
34 34 $('div#notebook').addClass('border-box-sizing');
35 35 };
36 36
37 37
38 38 Notebook.prototype.create_elements = function () {
39 39 // We add this end_space div to the end of the notebook div to:
40 40 // i) provide a margin between the last cell and the end of the notebook
41 41 // ii) to prevent the div from scrolling up when the last cell is being
42 42 // edited, but is too low on the page, which browsers will do automatically.
43 43 var that = this;
44 44 var end_space = $('<div class="end_space"></div>').height(150);
45 45 end_space.dblclick(function (e) {
46 46 if (that.read_only) return;
47 47 var ncells = that.ncells();
48 48 that.insert_code_cell_below(ncells-1);
49 49 });
50 50 this.element.append(end_space);
51 51 $('div#notebook').addClass('border-box-sizing');
52 52 };
53 53
54 54
55 55 Notebook.prototype.bind_events = function () {
56 56 var that = this;
57 57 $(document).keydown(function (event) {
58 58 // console.log(event);
59 59 if (that.read_only) return;
60 if (event.which === 27) {
61 // Intercept escape at highest level to avoid closing
62 // websocket connection with firefox
63 event.preventDefault();
64 }
60 65 if (event.which === 38) {
61 66 var cell = that.selected_cell();
62 67 if (cell.at_top()) {
63 68 event.preventDefault();
64 69 that.select_prev();
65 70 };
66 71 } else if (event.which === 40) {
67 72 var cell = that.selected_cell();
68 73 if (cell.at_bottom()) {
69 74 event.preventDefault();
70 75 that.select_next();
71 76 };
72 77 } else if (event.which === 13 && event.shiftKey) {
73 78 that.execute_selected_cell();
74 79 return false;
75 80 } else if (event.which === 13 && event.ctrlKey) {
76 81 that.execute_selected_cell({terminal:true});
77 82 return false;
78 83 } else if (event.which === 77 && event.ctrlKey) {
79 84 that.control_key_active = true;
80 85 return false;
81 86 } else if (event.which === 68 && that.control_key_active) {
82 87 // Delete selected cell = d
83 88 that.delete_cell();
84 89 that.control_key_active = false;
85 90 return false;
86 91 } else if (event.which === 65 && that.control_key_active) {
87 92 // Insert code cell above selected = a
88 93 that.insert_code_cell_above();
89 94 that.control_key_active = false;
90 95 return false;
91 96 } else if (event.which === 66 && that.control_key_active) {
92 97 // Insert code cell below selected = b
93 98 that.insert_code_cell_below();
94 99 that.control_key_active = false;
95 100 return false;
96 101 } else if (event.which === 67 && that.control_key_active) {
97 102 // To code = c
98 103 that.to_code();
99 104 that.control_key_active = false;
100 105 return false;
101 106 } else if (event.which === 77 && that.control_key_active) {
102 107 // To markdown = m
103 108 that.to_markdown();
104 109 that.control_key_active = false;
105 110 return false;
106 111 } else if (event.which === 84 && that.control_key_active) {
107 112 // Toggle output = t
108 113 that.toggle_output();
109 114 that.control_key_active = false;
110 115 return false;
111 116 } else if (event.which === 83 && that.control_key_active) {
112 117 // Save notebook = s
113 118 IPython.save_widget.save_notebook();
114 119 that.control_key_active = false;
115 120 return false;
116 121 } else if (event.which === 74 && that.control_key_active) {
117 122 // Move cell down = j
118 123 that.move_cell_down();
119 124 that.control_key_active = false;
120 125 return false;
121 126 } else if (event.which === 75 && that.control_key_active) {
122 127 // Move cell up = k
123 128 that.move_cell_up();
124 129 that.control_key_active = false;
125 130 return false;
126 131 } else if (event.which === 80 && that.control_key_active) {
127 132 // Select previous = p
128 133 that.select_prev();
129 134 that.control_key_active = false;
130 135 return false;
131 136 } else if (event.which === 78 && that.control_key_active) {
132 137 // Select next = n
133 138 that.select_next();
134 139 that.control_key_active = false;
135 140 return false;
136 141 } else if (event.which === 76 && that.control_key_active) {
137 142 // Toggle line numbers = l
138 143 that.cell_toggle_line_numbers();
139 144 that.control_key_active = false;
140 145 return false;
141 146 } else if (event.which === 73 && that.control_key_active) {
142 147 // Interrupt kernel = i
143 148 IPython.notebook.kernel.interrupt();
144 149 that.control_key_active = false;
145 150 return false;
146 151 } else if (event.which === 190 && that.control_key_active) {
147 152 // Restart kernel = . # matches qt console
148 153 IPython.notebook.restart_kernel();
149 154 that.control_key_active = false;
150 155 return false;
151 156 } else if (event.which === 72 && that.control_key_active) {
152 157 // Show keyboard shortcuts = h
153 158 that.toggle_keyboard_shortcuts();
154 159 that.control_key_active = false;
155 160 return false;
156 161 } else if (that.control_key_active) {
157 162 that.control_key_active = false;
158 163 return true;
159 164 };
160 165 });
161 166
162 167 this.element.bind('collapse_pager', function () {
163 168 var app_height = $('div#main_app').height(); // content height
164 169 var splitter_height = $('div#pager_splitter').outerHeight(true);
165 170 var new_height = app_height - splitter_height;
166 171 that.element.animate({height : new_height + 'px'}, 'fast');
167 172 });
168 173
169 174 this.element.bind('expand_pager', function () {
170 175 var app_height = $('div#main_app').height(); // content height
171 176 var splitter_height = $('div#pager_splitter').outerHeight(true);
172 177 var pager_height = $('div#pager').outerHeight(true);
173 178 var new_height = app_height - pager_height - splitter_height;
174 179 that.element.animate({height : new_height + 'px'}, 'fast');
175 180 });
176 181
177 182 this.element.bind('collapse_left_panel', function () {
178 183 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
179 184 var new_margin = splitter_width;
180 185 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
181 186 });
182 187
183 188 this.element.bind('expand_left_panel', function () {
184 189 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
185 190 var left_panel_width = IPython.left_panel.width;
186 191 var new_margin = splitter_width + left_panel_width;
187 192 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
188 193 });
189 194
190 195 $(window).bind('beforeunload', function () {
191 196 var kill_kernel = $('#kill_kernel').prop('checked');
192 197 if (kill_kernel) {
193 198 that.kernel.kill();
194 199 }
195 200 if (that.dirty && ! that.read_only) {
196 201 return "You have unsaved changes that will be lost if you leave this page.";
197 202 };
198 203 });
199 204 };
200 205
201 206
202 207 Notebook.prototype.toggle_keyboard_shortcuts = function () {
203 208 // toggles display of keyboard shortcut dialog
204 209 var that = this;
205 210 if ( this.shortcut_dialog ){
206 211 // if dialog is already shown, close it
207 212 this.shortcut_dialog.dialog("close");
208 213 this.shortcut_dialog = null;
209 214 return;
210 215 }
211 216 var dialog = $('<div/>');
212 217 this.shortcut_dialog = dialog;
213 218 var shortcuts = [
214 219 {key: 'Shift-Enter', help: 'run cell'},
215 220 {key: 'Ctrl-Enter', help: 'run cell in-place'},
216 221 {key: 'Ctrl-m d', help: 'delete cell'},
217 222 {key: 'Ctrl-m a', help: 'insert cell above'},
218 223 {key: 'Ctrl-m b', help: 'insert cell below'},
219 224 {key: 'Ctrl-m t', help: 'toggle output'},
220 225 {key: 'Ctrl-m l', help: 'toggle line numbers'},
221 226 {key: 'Ctrl-m s', help: 'save notebook'},
222 227 {key: 'Ctrl-m j', help: 'move cell down'},
223 228 {key: 'Ctrl-m k', help: 'move cell up'},
224 229 {key: 'Ctrl-m c', help: 'code cell'},
225 230 {key: 'Ctrl-m m', help: 'markdown cell'},
226 231 {key: 'Ctrl-m p', help: 'select previous'},
227 232 {key: 'Ctrl-m n', help: 'select next'},
228 233 {key: 'Ctrl-m i', help: 'interrupt kernel'},
229 234 {key: 'Ctrl-m .', help: 'restart kernel'},
230 235 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
231 236 ];
232 237 for (var i=0; i<shortcuts.length; i++) {
233 238 dialog.append($('<div>').
234 239 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
235 240 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
236 241 );
237 242 };
238 243 dialog.bind('dialogclose', function(event) {
239 244 // dialog has been closed, allow it to be drawn again.
240 245 that.shortcut_dialog = null;
241 246 });
242 247 dialog.dialog({title: 'Keyboard shortcuts'});
243 248 };
244 249
245 250
246 251 Notebook.prototype.scroll_to_bottom = function () {
247 252 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
248 253 };
249 254
250 255
251 256 Notebook.prototype.scroll_to_top = function () {
252 257 this.element.animate({scrollTop:0}, 0);
253 258 };
254 259
255 260
256 261 // Cell indexing, retrieval, etc.
257 262
258 263
259 264 Notebook.prototype.cell_elements = function () {
260 265 return this.element.children("div.cell");
261 266 }
262 267
263 268
264 269 Notebook.prototype.ncells = function (cell) {
265 270 return this.cell_elements().length;
266 271 }
267 272
268 273
269 274 // TODO: we are often calling cells as cells()[i], which we should optimize
270 275 // to cells(i) or a new method.
271 276 Notebook.prototype.cells = function () {
272 277 return this.cell_elements().toArray().map(function (e) {
273 278 return $(e).data("cell");
274 279 });
275 280 }
276 281
277 282
278 283 Notebook.prototype.find_cell_index = function (cell) {
279 284 var result = null;
280 285 this.cell_elements().filter(function (index) {
281 286 if ($(this).data("cell") === cell) {
282 287 result = index;
283 288 };
284 289 });
285 290 return result;
286 291 };
287 292
288 293
289 294 Notebook.prototype.index_or_selected = function (index) {
290 295 return index || this.selected_index() || 0;
291 296 }
292 297
293 298
294 299 Notebook.prototype.select = function (index) {
295 300 if (index !== undefined && index >= 0 && index < this.ncells()) {
296 301 if (this.selected_index() !== null) {
297 302 this.selected_cell().unselect();
298 303 };
299 304 this.cells()[index].select();
300 305 };
301 306 return this;
302 307 };
303 308
304 309
305 310 Notebook.prototype.select_next = function () {
306 311 var index = this.selected_index();
307 312 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
308 313 this.select(index+1);
309 314 };
310 315 return this;
311 316 };
312 317
313 318
314 319 Notebook.prototype.select_prev = function () {
315 320 var index = this.selected_index();
316 321 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
317 322 this.select(index-1);
318 323 };
319 324 return this;
320 325 };
321 326
322 327
323 328 Notebook.prototype.selected_index = function () {
324 329 var result = null;
325 330 this.cell_elements().filter(function (index) {
326 331 if ($(this).data("cell").selected === true) {
327 332 result = index;
328 333 };
329 334 });
330 335 return result;
331 336 };
332 337
333 338
334 339 Notebook.prototype.cell_for_msg = function (msg_id) {
335 340 var cell_id = this.msg_cell_map[msg_id];
336 341 var result = null;
337 342 this.cell_elements().filter(function (index) {
338 343 cell = $(this).data("cell");
339 344 if (cell.cell_id === cell_id) {
340 345 result = cell;
341 346 };
342 347 });
343 348 return result;
344 349 };
345 350
346 351
347 352 Notebook.prototype.selected_cell = function () {
348 353 return this.cell_elements().eq(this.selected_index()).data("cell");
349 354 }
350 355
351 356
352 357 // Cell insertion, deletion and moving.
353 358
354 359
355 360 Notebook.prototype.delete_cell = function (index) {
356 361 var i = index || this.selected_index();
357 362 if (i !== null && i >= 0 && i < this.ncells()) {
358 363 this.cell_elements().eq(i).remove();
359 364 if (i === (this.ncells())) {
360 365 this.select(i-1);
361 366 } else {
362 367 this.select(i);
363 368 };
364 369 };
365 370 this.dirty = true;
366 371 return this;
367 372 };
368 373
369 374
370 375 Notebook.prototype.append_cell = function (cell) {
371 376 this.element.find('div.end_space').before(cell.element);
372 377 this.dirty = true;
373 378 return this;
374 379 };
375 380
376 381
377 382 Notebook.prototype.insert_cell_below = function (cell, index) {
378 383 var ncells = this.ncells();
379 384 if (ncells === 0) {
380 385 this.append_cell(cell);
381 386 return this;
382 387 };
383 388 if (index >= 0 && index < ncells) {
384 389 this.cell_elements().eq(index).after(cell.element);
385 390 };
386 391 this.dirty = true;
387 392 return this
388 393 };
389 394
390 395
391 396 Notebook.prototype.insert_cell_above = function (cell, index) {
392 397 var ncells = this.ncells();
393 398 if (ncells === 0) {
394 399 this.append_cell(cell);
395 400 return this;
396 401 };
397 402 if (index >= 0 && index < ncells) {
398 403 this.cell_elements().eq(index).before(cell.element);
399 404 };
400 405 this.dirty = true;
401 406 return this;
402 407 };
403 408
404 409
405 410 Notebook.prototype.move_cell_up = function (index) {
406 411 var i = index || this.selected_index();
407 412 if (i !== null && i < this.ncells() && i > 0) {
408 413 var pivot = this.cell_elements().eq(i-1);
409 414 var tomove = this.cell_elements().eq(i);
410 415 if (pivot !== null && tomove !== null) {
411 416 tomove.detach();
412 417 pivot.before(tomove);
413 418 this.select(i-1);
414 419 };
415 420 };
416 421 this.dirty = true;
417 422 return this;
418 423 }
419 424
420 425
421 426 Notebook.prototype.move_cell_down = function (index) {
422 427 var i = index || this.selected_index();
423 428 if (i !== null && i < (this.ncells()-1) && i >= 0) {
424 429 var pivot = this.cell_elements().eq(i+1)
425 430 var tomove = this.cell_elements().eq(i)
426 431 if (pivot !== null && tomove !== null) {
427 432 tomove.detach();
428 433 pivot.after(tomove);
429 434 this.select(i+1);
430 435 };
431 436 };
432 437 this.dirty = true;
433 438 return this;
434 439 }
435 440
436 441
437 442 Notebook.prototype.sort_cells = function () {
438 443 var ncells = this.ncells();
439 444 var sindex = this.selected_index();
440 445 var swapped;
441 446 do {
442 447 swapped = false
443 448 for (var i=1; i<ncells; i++) {
444 449 current = this.cell_elements().eq(i).data("cell");
445 450 previous = this.cell_elements().eq(i-1).data("cell");
446 451 if (previous.input_prompt_number > current.input_prompt_number) {
447 452 this.move_cell_up(i);
448 453 swapped = true;
449 454 };
450 455 };
451 456 } while (swapped);
452 457 this.select(sindex);
453 458 return this;
454 459 };
455 460
456 461
457 462 Notebook.prototype.insert_code_cell_above = function (index) {
458 463 // TODO: Bounds check for i
459 464 var i = this.index_or_selected(index);
460 465 var cell = new IPython.CodeCell(this);
461 466 cell.set_input_prompt();
462 467 this.insert_cell_above(cell, i);
463 468 this.select(this.find_cell_index(cell));
464 469 return cell;
465 470 }
466 471
467 472
468 473 Notebook.prototype.insert_code_cell_below = function (index) {
469 474 // TODO: Bounds check for i
470 475 var i = this.index_or_selected(index);
471 476 var cell = new IPython.CodeCell(this);
472 477 cell.set_input_prompt();
473 478 this.insert_cell_below(cell, i);
474 479 this.select(this.find_cell_index(cell));
475 480 return cell;
476 481 }
477 482
478 483
479 484 Notebook.prototype.insert_html_cell_above = function (index) {
480 485 // TODO: Bounds check for i
481 486 var i = this.index_or_selected(index);
482 487 var cell = new IPython.HTMLCell(this);
483 488 cell.config_mathjax();
484 489 this.insert_cell_above(cell, i);
485 490 this.select(this.find_cell_index(cell));
486 491 return cell;
487 492 }
488 493
489 494
490 495 Notebook.prototype.insert_html_cell_below = function (index) {
491 496 // TODO: Bounds check for i
492 497 var i = this.index_or_selected(index);
493 498 var cell = new IPython.HTMLCell(this);
494 499 cell.config_mathjax();
495 500 this.insert_cell_below(cell, i);
496 501 this.select(this.find_cell_index(cell));
497 502 return cell;
498 503 }
499 504
500 505
501 506 Notebook.prototype.insert_markdown_cell_above = function (index) {
502 507 // TODO: Bounds check for i
503 508 var i = this.index_or_selected(index);
504 509 var cell = new IPython.MarkdownCell(this);
505 510 cell.config_mathjax();
506 511 this.insert_cell_above(cell, i);
507 512 this.select(this.find_cell_index(cell));
508 513 return cell;
509 514 }
510 515
511 516
512 517 Notebook.prototype.insert_markdown_cell_below = function (index) {
513 518 // TODO: Bounds check for i
514 519 var i = this.index_or_selected(index);
515 520 var cell = new IPython.MarkdownCell(this);
516 521 cell.config_mathjax();
517 522 this.insert_cell_below(cell, i);
518 523 this.select(this.find_cell_index(cell));
519 524 return cell;
520 525 }
521 526
522 527
523 528 Notebook.prototype.to_code = function (index) {
524 529 // TODO: Bounds check for i
525 530 var i = this.index_or_selected(index);
526 531 var source_element = this.cell_elements().eq(i);
527 532 var source_cell = source_element.data("cell");
528 533 if (source_cell instanceof IPython.HTMLCell ||
529 534 source_cell instanceof IPython.MarkdownCell) {
530 535 this.insert_code_cell_below(i);
531 536 var target_cell = this.cells()[i+1];
532 537 target_cell.set_code(source_cell.get_source());
533 538 source_element.remove();
534 539 target_cell.select();
535 540 };
536 541 this.dirty = true;
537 542 };
538 543
539 544
540 545 Notebook.prototype.to_markdown = function (index) {
541 546 // TODO: Bounds check for i
542 547 var i = this.index_or_selected(index);
543 548 var source_element = this.cell_elements().eq(i);
544 549 var source_cell = source_element.data("cell");
545 550 var target_cell = null;
546 551 if (source_cell instanceof IPython.CodeCell) {
547 552 this.insert_markdown_cell_below(i);
548 553 var target_cell = this.cells()[i+1];
549 554 var text = source_cell.get_code();
550 555 } else if (source_cell instanceof IPython.HTMLCell) {
551 556 this.insert_markdown_cell_below(i);
552 557 var target_cell = this.cells()[i+1];
553 558 var text = source_cell.get_source();
554 559 if (text === source_cell.placeholder) {
555 560 text = target_cell.placeholder;
556 561 }
557 562 }
558 563 if (target_cell !== null) {
559 564 if (text === "") {text = target_cell.placeholder;};
560 565 target_cell.set_source(text);
561 566 source_element.remove();
562 567 target_cell.edit();
563 568 }
564 569 this.dirty = true;
565 570 };
566 571
567 572
568 573 Notebook.prototype.to_html = function (index) {
569 574 // TODO: Bounds check for i
570 575 var i = this.index_or_selected(index);
571 576 var source_element = this.cell_elements().eq(i);
572 577 var source_cell = source_element.data("cell");
573 578 var target_cell = null;
574 579 if (source_cell instanceof IPython.CodeCell) {
575 580 this.insert_html_cell_below(i);
576 581 var target_cell = this.cells()[i+1];
577 582 var text = source_cell.get_code();
578 583 } else if (source_cell instanceof IPython.MarkdownCell) {
579 584 this.insert_html_cell_below(i);
580 585 var target_cell = this.cells()[i+1];
581 586 var text = source_cell.get_source();
582 587 if (text === source_cell.placeholder) {
583 588 text = target_cell.placeholder;
584 589 }
585 590 }
586 591 if (target_cell !== null) {
587 592 if (text === "") {text = target_cell.placeholder;};
588 593 target_cell.set_source(text);
589 594 source_element.remove();
590 595 target_cell.edit();
591 596 }
592 597 this.dirty = true;
593 598 };
594 599
595 600
596 601 // Cell collapsing and output clearing
597 602
598 603 Notebook.prototype.collapse = function (index) {
599 604 var i = this.index_or_selected(index);
600 605 this.cells()[i].collapse();
601 606 this.dirty = true;
602 607 };
603 608
604 609
605 610 Notebook.prototype.expand = function (index) {
606 611 var i = this.index_or_selected(index);
607 612 this.cells()[i].expand();
608 613 this.dirty = true;
609 614 };
610 615
611 616
612 617 Notebook.prototype.toggle_output = function (index) {
613 618 var i = this.index_or_selected(index);
614 619 this.cells()[i].toggle_output();
615 620 this.dirty = true;
616 621 };
617 622
618 623
619 624 Notebook.prototype.set_autoindent = function (state) {
620 625 var cells = this.cells();
621 626 len = cells.length;
622 627 for (var i=0; i<len; i++) {
623 628 cells[i].set_autoindent(state)
624 629 };
625 630 };
626 631
627 632
628 633 Notebook.prototype.clear_all_output = function () {
629 634 var ncells = this.ncells();
630 635 var cells = this.cells();
631 636 for (var i=0; i<ncells; i++) {
632 637 if (cells[i] instanceof IPython.CodeCell) {
633 638 cells[i].clear_output(true,true,true);
634 639 }
635 640 };
636 641 this.dirty = true;
637 642 };
638 643
639 644 // Other cell functions: line numbers, ...
640 645
641 646 Notebook.prototype.cell_toggle_line_numbers = function() {
642 647 this.selected_cell().toggle_line_numbers()
643 648 };
644 649
645 650 // Kernel related things
646 651
647 652 Notebook.prototype.start_kernel = function () {
648 653 this.kernel = new IPython.Kernel();
649 654 var notebook_id = IPython.save_widget.get_notebook_id();
650 655 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
651 656 };
652 657
653 658
654 659 Notebook.prototype.restart_kernel = function () {
655 660 var that = this;
656 661 var notebook_id = IPython.save_widget.get_notebook_id();
657 662
658 663 var dialog = $('<div/>');
659 664 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
660 665 $(document).append(dialog);
661 666 dialog.dialog({
662 667 resizable: false,
663 668 modal: true,
664 669 title: "Restart kernel or continue running?",
665 670 buttons : {
666 671 "Restart": function () {
667 672 that.kernel.restart($.proxy(that.kernel_started, that));
668 673 $(this).dialog('close');
669 674 },
670 675 "Continue running": function () {
671 676 $(this).dialog('close');
672 677 }
673 678 }
674 679 });
675 680 };
676 681
677 682
678 683 Notebook.prototype.kernel_started = function () {
679 684 console.log("Kernel started: ", this.kernel.kernel_id);
680 685 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
681 686 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
682 687 };
683 688
684 689
685 690 Notebook.prototype.handle_shell_reply = function (e) {
686 691 reply = $.parseJSON(e.data);
687 692 var header = reply.header;
688 693 var content = reply.content;
689 694 var msg_type = header.msg_type;
690 695 // console.log(reply);
691 696 var cell = this.cell_for_msg(reply.parent_header.msg_id);
692 697 if (msg_type === "execute_reply") {
693 698 cell.set_input_prompt(content.execution_count);
694 699 cell.element.removeClass("running");
695 700 this.dirty = true;
696 701 } else if (msg_type === "complete_reply") {
697 702 cell.finish_completing(content.matched_text, content.matches);
698 703 };
699 704 var payload = content.payload || [];
700 705 this.handle_payload(cell, payload);
701 706 };
702 707
703 708
704 709 Notebook.prototype.handle_payload = function (cell, payload) {
705 710 var l = payload.length;
706 711 for (var i=0; i<l; i++) {
707 712 if (payload[i].source === 'IPython.zmq.page.page') {
708 713 if (payload[i].text.trim() !== '') {
709 714 IPython.pager.clear();
710 715 IPython.pager.expand();
711 716 IPython.pager.append_text(payload[i].text);
712 717 }
713 718 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
714 719 var index = this.find_cell_index(cell);
715 720 var new_cell = this.insert_code_cell_below(index);
716 721 new_cell.set_code(payload[i].text);
717 722 this.dirty = true;
718 723 }
719 724 };
720 725 };
721 726
722 727
723 728 Notebook.prototype.handle_iopub_reply = function (e) {
724 729 reply = $.parseJSON(e.data);
725 730 var content = reply.content;
726 731 // console.log(reply);
727 732 var msg_type = reply.header.msg_type;
728 733 var cell = this.cell_for_msg(reply.parent_header.msg_id);
729 734 var output_types = ['stream','display_data','pyout','pyerr'];
730 735 if (output_types.indexOf(msg_type) >= 0) {
731 736 this.handle_output(cell, msg_type, content);
732 737 } else if (msg_type === 'status') {
733 738 if (content.execution_state === 'busy') {
734 739 IPython.kernel_status_widget.status_busy();
735 740 } else if (content.execution_state === 'idle') {
736 741 IPython.kernel_status_widget.status_idle();
737 742 } else if (content.execution_state === 'dead') {
738 743 this.handle_status_dead();
739 744 };
740 745 } else if (msg_type === 'clear_output') {
741 746 cell.clear_output(content.stdout, content.stderr, content.other);
742 747 };
743 748 };
744 749
745 750
746 751 Notebook.prototype.handle_status_dead = function () {
747 752 var that = this;
748 753 this.kernel.stop_channels();
749 754 var dialog = $('<div/>');
750 755 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.');
751 756 $(document).append(dialog);
752 757 dialog.dialog({
753 758 resizable: false,
754 759 modal: true,
755 760 title: "Dead kernel",
756 761 buttons : {
757 762 "Restart": function () {
758 763 that.start_kernel();
759 764 $(this).dialog('close');
760 765 },
761 766 "Continue running": function () {
762 767 $(this).dialog('close');
763 768 }
764 769 }
765 770 });
766 771 };
767 772
768 773
769 774 Notebook.prototype.handle_output = function (cell, msg_type, content) {
770 775 var json = {};
771 776 json.output_type = msg_type;
772 777 if (msg_type === "stream") {
773 778 json.text = utils.fixConsole(content.data);
774 779 json.stream = content.name;
775 780 } else if (msg_type === "display_data") {
776 781 json = this.convert_mime_types(json, content.data);
777 782 } else if (msg_type === "pyout") {
778 783 json.prompt_number = content.execution_count;
779 784 json = this.convert_mime_types(json, content.data);
780 785 } else if (msg_type === "pyerr") {
781 786 json.ename = content.ename;
782 787 json.evalue = content.evalue;
783 788 var traceback = [];
784 789 for (var i=0; i<content.traceback.length; i++) {
785 790 traceback.push(utils.fixConsole(content.traceback[i]));
786 791 }
787 792 json.traceback = traceback;
788 793 };
789 794 cell.append_output(json);
790 795 this.dirty = true;
791 796 };
792 797
793 798
794 799 Notebook.prototype.convert_mime_types = function (json, data) {
795 800 if (data['text/plain'] !== undefined) {
796 801 json.text = utils.fixConsole(data['text/plain']);
797 802 };
798 803 if (data['text/html'] !== undefined) {
799 804 json.html = data['text/html'];
800 805 };
801 806 if (data['image/svg+xml'] !== undefined) {
802 807 json.svg = data['image/svg+xml'];
803 808 };
804 809 if (data['image/png'] !== undefined) {
805 810 json.png = data['image/png'];
806 811 };
807 812 if (data['image/jpeg'] !== undefined) {
808 813 json.jpeg = data['image/jpeg'];
809 814 };
810 815 if (data['text/latex'] !== undefined) {
811 816 json.latex = data['text/latex'];
812 817 };
813 818 if (data['application/json'] !== undefined) {
814 819 json.json = data['application/json'];
815 820 };
816 821 if (data['application/javascript'] !== undefined) {
817 822 json.javascript = data['application/javascript'];
818 823 }
819 824 return json;
820 825 };
821 826
822 827
823 828 Notebook.prototype.execute_selected_cell = function (options) {
824 829 // add_new: should a new cell be added if we are at the end of the nb
825 830 // terminal: execute in terminal mode, which stays in the current cell
826 831 default_options = {terminal: false, add_new: true}
827 832 $.extend(default_options, options)
828 833 var that = this;
829 834 var cell = that.selected_cell();
830 835 var cell_index = that.find_cell_index(cell);
831 836 if (cell instanceof IPython.CodeCell) {
832 837 cell.clear_output(true, true, true);
833 838 cell.set_input_prompt('*');
834 839 cell.element.addClass("running");
835 840 var code = cell.get_code();
836 841 var msg_id = that.kernel.execute(cell.get_code());
837 842 that.msg_cell_map[msg_id] = cell.cell_id;
838 843 } else if (cell instanceof IPython.HTMLCell) {
839 844 cell.render();
840 845 }
841 846 if (default_options.terminal) {
842 847 cell.select_all();
843 848 } else {
844 849 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
845 850 that.insert_code_cell_below();
846 851 // If we are adding a new cell at the end, scroll down to show it.
847 852 that.scroll_to_bottom();
848 853 } else {
849 854 that.select(cell_index+1);
850 855 };
851 856 };
852 857 this.dirty = true;
853 858 };
854 859
855 860
856 861 Notebook.prototype.execute_all_cells = function () {
857 862 var ncells = this.ncells();
858 863 for (var i=0; i<ncells; i++) {
859 864 this.select(i);
860 865 this.execute_selected_cell({add_new:false});
861 866 };
862 867 this.scroll_to_bottom();
863 868 };
864 869
865 870
866 871 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
867 872 var msg_id = this.kernel.complete(line, cursor_pos);
868 873 this.msg_cell_map[msg_id] = cell.cell_id;
869 874 };
870 875
871 876 // Persistance and loading
872 877
873 878
874 879 Notebook.prototype.fromJSON = function (data) {
875 880 var ncells = this.ncells();
876 881 for (var i=0; i<ncells; i++) {
877 882 // Always delete cell 0 as they get renumbered as they are deleted.
878 883 this.delete_cell(0);
879 884 };
880 885 // Save the metadata
881 886 this.metadata = data.metadata;
882 887 // Only handle 1 worksheet for now.
883 888 var worksheet = data.worksheets[0];
884 889 if (worksheet !== undefined) {
885 890 var new_cells = worksheet.cells;
886 891 ncells = new_cells.length;
887 892 var cell_data = null;
888 893 var new_cell = null;
889 894 for (var i=0; i<ncells; i++) {
890 895 cell_data = new_cells[i];
891 896 if (cell_data.cell_type == 'code') {
892 897 new_cell = this.insert_code_cell_below();
893 898 new_cell.fromJSON(cell_data);
894 899 } else if (cell_data.cell_type === 'html') {
895 900 new_cell = this.insert_html_cell_below();
896 901 new_cell.fromJSON(cell_data);
897 902 } else if (cell_data.cell_type === 'markdown') {
898 903 new_cell = this.insert_markdown_cell_below();
899 904 new_cell.fromJSON(cell_data);
900 905 };
901 906 };
902 907 };
903 908 };
904 909
905 910
906 911 Notebook.prototype.toJSON = function () {
907 912 var cells = this.cells();
908 913 var ncells = cells.length;
909 914 cell_array = new Array(ncells);
910 915 for (var i=0; i<ncells; i++) {
911 916 cell_array[i] = cells[i].toJSON();
912 917 };
913 918 data = {
914 919 // Only handle 1 worksheet for now.
915 920 worksheets : [{cells:cell_array}],
916 921 metadata : this.metadata
917 922 }
918 923 return data
919 924 };
920 925
921 926 Notebook.prototype.save_notebook = function () {
922 927 if (IPython.save_widget.test_notebook_name()) {
923 928 var notebook_id = IPython.save_widget.get_notebook_id();
924 929 var nbname = IPython.save_widget.get_notebook_name();
925 930 // We may want to move the name/id/nbformat logic inside toJSON?
926 931 var data = this.toJSON();
927 932 data.metadata.name = nbname;
928 933 data.nbformat = 2;
929 934 // We do the call with settings so we can set cache to false.
930 935 var settings = {
931 936 processData : false,
932 937 cache : false,
933 938 type : "PUT",
934 939 data : JSON.stringify(data),
935 940 headers : {'Content-Type': 'application/json'},
936 941 success : $.proxy(this.notebook_saved,this),
937 942 error : $.proxy(this.notebook_save_failed,this)
938 943 };
939 944 IPython.save_widget.status_saving();
940 945 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id
941 946 $.ajax(url, settings);
942 947 };
943 948 };
944 949
945 950
946 951 Notebook.prototype.notebook_saved = function (data, status, xhr) {
947 952 this.dirty = false;
948 953 IPython.save_widget.notebook_saved();
949 954 IPython.save_widget.status_save();
950 955 }
951 956
952 957
953 958 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
954 959 // Notify the user and reset the save button
955 960 // TODO: Handle different types of errors (timeout etc.)
956 961 alert('An unexpected error occured while saving the notebook.');
957 962 IPython.save_widget.reset_status();
958 963 }
959 964
960 965
961 966 Notebook.prototype.load_notebook = function (callback) {
962 967 var that = this;
963 968 var notebook_id = IPython.save_widget.get_notebook_id();
964 969 // We do the call with settings so we can set cache to false.
965 970 var settings = {
966 971 processData : false,
967 972 cache : false,
968 973 type : "GET",
969 974 dataType : "json",
970 975 success : function (data, status, xhr) {
971 976 that.notebook_loaded(data, status, xhr);
972 977 if (callback !== undefined) {
973 978 callback();
974 979 };
975 980 }
976 981 };
977 982 IPython.save_widget.status_loading();
978 983 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id
979 984 $.ajax(url, settings);
980 985 }
981 986
982 987
983 988 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
984 989 var allowed = xhr.getResponseHeader('Allow');
985 990 this.fromJSON(data);
986 991 if (this.ncells() === 0) {
987 992 this.insert_code_cell_below();
988 993 };
989 994 IPython.save_widget.status_save();
990 995 IPython.save_widget.set_notebook_name(data.metadata.name);
991 996 this.dirty = false;
992 997 if (! this.read_only) {
993 998 this.start_kernel();
994 999 }
995 1000 // fromJSON always selects the last cell inserted. We need to wait
996 1001 // until that is done before scrolling to the top.
997 1002 setTimeout(function () {
998 1003 IPython.notebook.select(0);
999 1004 IPython.notebook.scroll_to_top();
1000 1005 }, 50);
1001 1006 };
1002 1007
1003 1008 IPython.Notebook = Notebook;
1004 1009
1005 1010
1006 1011 return IPython;
1007 1012
1008 1013 }(IPython));
1009 1014
General Comments 0
You need to be logged in to leave comments. Login now