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