##// END OF EJS Templates
explicitly ignore iopub messages not associated with a cell in the notebook...
MinRK -
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 60 if (event.which === 38) {
61 61 var cell = that.selected_cell();
62 62 if (cell.at_top()) {
63 63 event.preventDefault();
64 64 that.select_prev();
65 65 };
66 66 } else if (event.which === 40) {
67 67 var cell = that.selected_cell();
68 68 if (cell.at_bottom()) {
69 69 event.preventDefault();
70 70 that.select_next();
71 71 };
72 72 } else if (event.which === 13 && event.shiftKey) {
73 73 that.execute_selected_cell();
74 74 return false;
75 75 } else if (event.which === 13 && event.ctrlKey) {
76 76 that.execute_selected_cell({terminal:true});
77 77 return false;
78 78 } else if (event.which === 77 && event.ctrlKey) {
79 79 that.control_key_active = true;
80 80 return false;
81 81 } else if (event.which === 68 && that.control_key_active) {
82 82 // Delete selected cell = d
83 83 that.delete_cell();
84 84 that.control_key_active = false;
85 85 return false;
86 86 } else if (event.which === 65 && that.control_key_active) {
87 87 // Insert code cell above selected = a
88 88 that.insert_code_cell_above();
89 89 that.control_key_active = false;
90 90 return false;
91 91 } else if (event.which === 66 && that.control_key_active) {
92 92 // Insert code cell below selected = b
93 93 that.insert_code_cell_below();
94 94 that.control_key_active = false;
95 95 return false;
96 96 } else if (event.which === 67 && that.control_key_active) {
97 97 // To code = c
98 98 that.to_code();
99 99 that.control_key_active = false;
100 100 return false;
101 101 } else if (event.which === 77 && that.control_key_active) {
102 102 // To markdown = m
103 103 that.to_markdown();
104 104 that.control_key_active = false;
105 105 return false;
106 106 } else if (event.which === 84 && that.control_key_active) {
107 107 // Toggle output = t
108 108 that.toggle_output();
109 109 that.control_key_active = false;
110 110 return false;
111 111 } else if (event.which === 83 && that.control_key_active) {
112 112 // Save notebook = s
113 113 IPython.save_widget.save_notebook();
114 114 that.control_key_active = false;
115 115 return false;
116 116 } else if (event.which === 74 && that.control_key_active) {
117 117 // Move cell down = j
118 118 that.move_cell_down();
119 119 that.control_key_active = false;
120 120 return false;
121 121 } else if (event.which === 75 && that.control_key_active) {
122 122 // Move cell up = k
123 123 that.move_cell_up();
124 124 that.control_key_active = false;
125 125 return false;
126 126 } else if (event.which === 80 && that.control_key_active) {
127 127 // Select previous = p
128 128 that.select_prev();
129 129 that.control_key_active = false;
130 130 return false;
131 131 } else if (event.which === 78 && that.control_key_active) {
132 132 // Select next = n
133 133 that.select_next();
134 134 that.control_key_active = false;
135 135 return false;
136 136 } else if (event.which === 76 && that.control_key_active) {
137 137 // Toggle line numbers = l
138 138 that.cell_toggle_line_numbers();
139 139 that.control_key_active = false;
140 140 return false;
141 141 } else if (event.which === 73 && that.control_key_active) {
142 142 // Interrupt kernel = i
143 143 IPython.notebook.kernel.interrupt();
144 144 that.control_key_active = false;
145 145 return false;
146 146 } else if (event.which === 190 && that.control_key_active) {
147 147 // Restart kernel = . # matches qt console
148 148 IPython.notebook.restart_kernel();
149 149 that.control_key_active = false;
150 150 return false;
151 151 } else if (event.which === 72 && that.control_key_active) {
152 152 // Show keyboard shortcuts = h
153 153 that.toggle_keyboard_shortcuts();
154 154 that.control_key_active = false;
155 155 return false;
156 156 } else if (that.control_key_active) {
157 157 that.control_key_active = false;
158 158 return true;
159 159 };
160 160 });
161 161
162 162 this.element.bind('collapse_pager', function () {
163 163 var app_height = $('div#main_app').height(); // content height
164 164 var splitter_height = $('div#pager_splitter').outerHeight(true);
165 165 var new_height = app_height - splitter_height;
166 166 that.element.animate({height : new_height + 'px'}, 'fast');
167 167 });
168 168
169 169 this.element.bind('expand_pager', function () {
170 170 var app_height = $('div#main_app').height(); // content height
171 171 var splitter_height = $('div#pager_splitter').outerHeight(true);
172 172 var pager_height = $('div#pager').outerHeight(true);
173 173 var new_height = app_height - pager_height - splitter_height;
174 174 that.element.animate({height : new_height + 'px'}, 'fast');
175 175 });
176 176
177 177 this.element.bind('collapse_left_panel', function () {
178 178 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
179 179 var new_margin = splitter_width;
180 180 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
181 181 });
182 182
183 183 this.element.bind('expand_left_panel', function () {
184 184 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
185 185 var left_panel_width = IPython.left_panel.width;
186 186 var new_margin = splitter_width + left_panel_width;
187 187 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
188 188 });
189 189
190 190 $(window).bind('beforeunload', function () {
191 191 var kill_kernel = $('#kill_kernel').prop('checked');
192 192 if (kill_kernel) {
193 193 that.kernel.kill();
194 194 }
195 195 if (that.dirty && ! that.read_only) {
196 196 return "You have unsaved changes that will be lost if you leave this page.";
197 197 };
198 198 });
199 199 };
200 200
201 201
202 202 Notebook.prototype.toggle_keyboard_shortcuts = function () {
203 203 // toggles display of keyboard shortcut dialog
204 204 var that = this;
205 205 if ( this.shortcut_dialog ){
206 206 // if dialog is already shown, close it
207 207 this.shortcut_dialog.dialog("close");
208 208 this.shortcut_dialog = null;
209 209 return;
210 210 }
211 211 var dialog = $('<div/>');
212 212 this.shortcut_dialog = dialog;
213 213 var shortcuts = [
214 214 {key: 'Shift-Enter', help: 'run cell'},
215 215 {key: 'Ctrl-Enter', help: 'run cell in-place'},
216 216 {key: 'Ctrl-m d', help: 'delete cell'},
217 217 {key: 'Ctrl-m a', help: 'insert cell above'},
218 218 {key: 'Ctrl-m b', help: 'insert cell below'},
219 219 {key: 'Ctrl-m t', help: 'toggle output'},
220 220 {key: 'Ctrl-m l', help: 'toggle line numbers'},
221 221 {key: 'Ctrl-m s', help: 'save notebook'},
222 222 {key: 'Ctrl-m j', help: 'move cell down'},
223 223 {key: 'Ctrl-m k', help: 'move cell up'},
224 224 {key: 'Ctrl-m c', help: 'code cell'},
225 225 {key: 'Ctrl-m m', help: 'markdown cell'},
226 226 {key: 'Ctrl-m p', help: 'select previous'},
227 227 {key: 'Ctrl-m n', help: 'select next'},
228 228 {key: 'Ctrl-m i', help: 'interrupt kernel'},
229 229 {key: 'Ctrl-m .', help: 'restart kernel'},
230 230 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
231 231 ];
232 232 for (var i=0; i<shortcuts.length; i++) {
233 233 dialog.append($('<div>').
234 234 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
235 235 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
236 236 );
237 237 };
238 238 dialog.bind('dialogclose', function(event) {
239 239 // dialog has been closed, allow it to be drawn again.
240 240 that.shortcut_dialog = null;
241 241 });
242 242 dialog.dialog({title: 'Keyboard shortcuts'});
243 243 };
244 244
245 245
246 246 Notebook.prototype.scroll_to_bottom = function () {
247 247 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
248 248 };
249 249
250 250
251 251 Notebook.prototype.scroll_to_top = function () {
252 252 this.element.animate({scrollTop:0}, 0);
253 253 };
254 254
255 255
256 256 // Cell indexing, retrieval, etc.
257 257
258 258
259 259 Notebook.prototype.cell_elements = function () {
260 260 return this.element.children("div.cell");
261 261 }
262 262
263 263
264 264 Notebook.prototype.ncells = function (cell) {
265 265 return this.cell_elements().length;
266 266 }
267 267
268 268
269 269 // TODO: we are often calling cells as cells()[i], which we should optimize
270 270 // to cells(i) or a new method.
271 271 Notebook.prototype.cells = function () {
272 272 return this.cell_elements().toArray().map(function (e) {
273 273 return $(e).data("cell");
274 274 });
275 275 }
276 276
277 277
278 278 Notebook.prototype.find_cell_index = function (cell) {
279 279 var result = null;
280 280 this.cell_elements().filter(function (index) {
281 281 if ($(this).data("cell") === cell) {
282 282 result = index;
283 283 };
284 284 });
285 285 return result;
286 286 };
287 287
288 288
289 289 Notebook.prototype.index_or_selected = function (index) {
290 290 return index || this.selected_index() || 0;
291 291 }
292 292
293 293
294 294 Notebook.prototype.select = function (index) {
295 295 if (index !== undefined && index >= 0 && index < this.ncells()) {
296 296 if (this.selected_index() !== null) {
297 297 this.selected_cell().unselect();
298 298 };
299 299 this.cells()[index].select();
300 300 };
301 301 return this;
302 302 };
303 303
304 304
305 305 Notebook.prototype.select_next = function () {
306 306 var index = this.selected_index();
307 307 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
308 308 this.select(index+1);
309 309 };
310 310 return this;
311 311 };
312 312
313 313
314 314 Notebook.prototype.select_prev = function () {
315 315 var index = this.selected_index();
316 316 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
317 317 this.select(index-1);
318 318 };
319 319 return this;
320 320 };
321 321
322 322
323 323 Notebook.prototype.selected_index = function () {
324 324 var result = null;
325 325 this.cell_elements().filter(function (index) {
326 326 if ($(this).data("cell").selected === true) {
327 327 result = index;
328 328 };
329 329 });
330 330 return result;
331 331 };
332 332
333 333
334 334 Notebook.prototype.cell_for_msg = function (msg_id) {
335 335 var cell_id = this.msg_cell_map[msg_id];
336 336 var result = null;
337 337 this.cell_elements().filter(function (index) {
338 338 cell = $(this).data("cell");
339 339 if (cell.cell_id === cell_id) {
340 340 result = cell;
341 341 };
342 342 });
343 343 return result;
344 344 };
345 345
346 346
347 347 Notebook.prototype.selected_cell = function () {
348 348 return this.cell_elements().eq(this.selected_index()).data("cell");
349 349 }
350 350
351 351
352 352 // Cell insertion, deletion and moving.
353 353
354 354
355 355 Notebook.prototype.delete_cell = function (index) {
356 356 var i = index || this.selected_index();
357 357 if (i !== null && i >= 0 && i < this.ncells()) {
358 358 this.cell_elements().eq(i).remove();
359 359 if (i === (this.ncells())) {
360 360 this.select(i-1);
361 361 } else {
362 362 this.select(i);
363 363 };
364 364 };
365 365 this.dirty = true;
366 366 return this;
367 367 };
368 368
369 369
370 370 Notebook.prototype.append_cell = function (cell) {
371 371 this.element.find('div.end_space').before(cell.element);
372 372 this.dirty = true;
373 373 return this;
374 374 };
375 375
376 376
377 377 Notebook.prototype.insert_cell_below = function (cell, index) {
378 378 var ncells = this.ncells();
379 379 if (ncells === 0) {
380 380 this.append_cell(cell);
381 381 return this;
382 382 };
383 383 if (index >= 0 && index < ncells) {
384 384 this.cell_elements().eq(index).after(cell.element);
385 385 };
386 386 this.dirty = true;
387 387 return this
388 388 };
389 389
390 390
391 391 Notebook.prototype.insert_cell_above = function (cell, index) {
392 392 var ncells = this.ncells();
393 393 if (ncells === 0) {
394 394 this.append_cell(cell);
395 395 return this;
396 396 };
397 397 if (index >= 0 && index < ncells) {
398 398 this.cell_elements().eq(index).before(cell.element);
399 399 };
400 400 this.dirty = true;
401 401 return this;
402 402 };
403 403
404 404
405 405 Notebook.prototype.move_cell_up = function (index) {
406 406 var i = index || this.selected_index();
407 407 if (i !== null && i < this.ncells() && i > 0) {
408 408 var pivot = this.cell_elements().eq(i-1);
409 409 var tomove = this.cell_elements().eq(i);
410 410 if (pivot !== null && tomove !== null) {
411 411 tomove.detach();
412 412 pivot.before(tomove);
413 413 this.select(i-1);
414 414 };
415 415 };
416 416 this.dirty = true;
417 417 return this;
418 418 }
419 419
420 420
421 421 Notebook.prototype.move_cell_down = function (index) {
422 422 var i = index || this.selected_index();
423 423 if (i !== null && i < (this.ncells()-1) && i >= 0) {
424 424 var pivot = this.cell_elements().eq(i+1)
425 425 var tomove = this.cell_elements().eq(i)
426 426 if (pivot !== null && tomove !== null) {
427 427 tomove.detach();
428 428 pivot.after(tomove);
429 429 this.select(i+1);
430 430 };
431 431 };
432 432 this.dirty = true;
433 433 return this;
434 434 }
435 435
436 436
437 437 Notebook.prototype.sort_cells = function () {
438 438 var ncells = this.ncells();
439 439 var sindex = this.selected_index();
440 440 var swapped;
441 441 do {
442 442 swapped = false
443 443 for (var i=1; i<ncells; i++) {
444 444 current = this.cell_elements().eq(i).data("cell");
445 445 previous = this.cell_elements().eq(i-1).data("cell");
446 446 if (previous.input_prompt_number > current.input_prompt_number) {
447 447 this.move_cell_up(i);
448 448 swapped = true;
449 449 };
450 450 };
451 451 } while (swapped);
452 452 this.select(sindex);
453 453 return this;
454 454 };
455 455
456 456
457 457 Notebook.prototype.insert_code_cell_above = function (index) {
458 458 // TODO: Bounds check for i
459 459 var i = this.index_or_selected(index);
460 460 var cell = new IPython.CodeCell(this);
461 461 cell.set_input_prompt();
462 462 this.insert_cell_above(cell, i);
463 463 this.select(this.find_cell_index(cell));
464 464 return cell;
465 465 }
466 466
467 467
468 468 Notebook.prototype.insert_code_cell_below = function (index) {
469 469 // TODO: Bounds check for i
470 470 var i = this.index_or_selected(index);
471 471 var cell = new IPython.CodeCell(this);
472 472 cell.set_input_prompt();
473 473 this.insert_cell_below(cell, i);
474 474 this.select(this.find_cell_index(cell));
475 475 return cell;
476 476 }
477 477
478 478
479 479 Notebook.prototype.insert_html_cell_above = function (index) {
480 480 // TODO: Bounds check for i
481 481 var i = this.index_or_selected(index);
482 482 var cell = new IPython.HTMLCell(this);
483 483 cell.config_mathjax();
484 484 this.insert_cell_above(cell, i);
485 485 this.select(this.find_cell_index(cell));
486 486 return cell;
487 487 }
488 488
489 489
490 490 Notebook.prototype.insert_html_cell_below = function (index) {
491 491 // TODO: Bounds check for i
492 492 var i = this.index_or_selected(index);
493 493 var cell = new IPython.HTMLCell(this);
494 494 cell.config_mathjax();
495 495 this.insert_cell_below(cell, i);
496 496 this.select(this.find_cell_index(cell));
497 497 return cell;
498 498 }
499 499
500 500
501 501 Notebook.prototype.insert_markdown_cell_above = function (index) {
502 502 // TODO: Bounds check for i
503 503 var i = this.index_or_selected(index);
504 504 var cell = new IPython.MarkdownCell(this);
505 505 cell.config_mathjax();
506 506 this.insert_cell_above(cell, i);
507 507 this.select(this.find_cell_index(cell));
508 508 return cell;
509 509 }
510 510
511 511
512 512 Notebook.prototype.insert_markdown_cell_below = function (index) {
513 513 // TODO: Bounds check for i
514 514 var i = this.index_or_selected(index);
515 515 var cell = new IPython.MarkdownCell(this);
516 516 cell.config_mathjax();
517 517 this.insert_cell_below(cell, i);
518 518 this.select(this.find_cell_index(cell));
519 519 return cell;
520 520 }
521 521
522 522
523 523 Notebook.prototype.to_code = function (index) {
524 524 // TODO: Bounds check for i
525 525 var i = this.index_or_selected(index);
526 526 var source_element = this.cell_elements().eq(i);
527 527 var source_cell = source_element.data("cell");
528 528 if (source_cell instanceof IPython.HTMLCell ||
529 529 source_cell instanceof IPython.MarkdownCell) {
530 530 this.insert_code_cell_below(i);
531 531 var target_cell = this.cells()[i+1];
532 532 target_cell.set_code(source_cell.get_source());
533 533 source_element.remove();
534 534 target_cell.select();
535 535 };
536 536 this.dirty = true;
537 537 };
538 538
539 539
540 540 Notebook.prototype.to_markdown = function (index) {
541 541 // TODO: Bounds check for i
542 542 var i = this.index_or_selected(index);
543 543 var source_element = this.cell_elements().eq(i);
544 544 var source_cell = source_element.data("cell");
545 545 var target_cell = null;
546 546 if (source_cell instanceof IPython.CodeCell) {
547 547 this.insert_markdown_cell_below(i);
548 548 var target_cell = this.cells()[i+1];
549 549 var text = source_cell.get_code();
550 550 } else if (source_cell instanceof IPython.HTMLCell) {
551 551 this.insert_markdown_cell_below(i);
552 552 var target_cell = this.cells()[i+1];
553 553 var text = source_cell.get_source();
554 554 if (text === source_cell.placeholder) {
555 555 text = target_cell.placeholder;
556 556 }
557 557 }
558 558 if (target_cell !== null) {
559 559 if (text === "") {text = target_cell.placeholder;};
560 560 target_cell.set_source(text);
561 561 source_element.remove();
562 562 target_cell.edit();
563 563 }
564 564 this.dirty = true;
565 565 };
566 566
567 567
568 568 Notebook.prototype.to_html = function (index) {
569 569 // TODO: Bounds check for i
570 570 var i = this.index_or_selected(index);
571 571 var source_element = this.cell_elements().eq(i);
572 572 var source_cell = source_element.data("cell");
573 573 var target_cell = null;
574 574 if (source_cell instanceof IPython.CodeCell) {
575 575 this.insert_html_cell_below(i);
576 576 var target_cell = this.cells()[i+1];
577 577 var text = source_cell.get_code();
578 578 } else if (source_cell instanceof IPython.MarkdownCell) {
579 579 this.insert_html_cell_below(i);
580 580 var target_cell = this.cells()[i+1];
581 581 var text = source_cell.get_source();
582 582 if (text === source_cell.placeholder) {
583 583 text = target_cell.placeholder;
584 584 }
585 585 }
586 586 if (target_cell !== null) {
587 587 if (text === "") {text = target_cell.placeholder;};
588 588 target_cell.set_source(text);
589 589 source_element.remove();
590 590 target_cell.edit();
591 591 }
592 592 this.dirty = true;
593 593 };
594 594
595 595
596 596 // Cell collapsing and output clearing
597 597
598 598 Notebook.prototype.collapse = function (index) {
599 599 var i = this.index_or_selected(index);
600 600 this.cells()[i].collapse();
601 601 this.dirty = true;
602 602 };
603 603
604 604
605 605 Notebook.prototype.expand = function (index) {
606 606 var i = this.index_or_selected(index);
607 607 this.cells()[i].expand();
608 608 this.dirty = true;
609 609 };
610 610
611 611
612 612 Notebook.prototype.toggle_output = function (index) {
613 613 var i = this.index_or_selected(index);
614 614 this.cells()[i].toggle_output();
615 615 this.dirty = true;
616 616 };
617 617
618 618
619 619 Notebook.prototype.set_autoindent = function (state) {
620 620 var cells = this.cells();
621 621 len = cells.length;
622 622 for (var i=0; i<len; i++) {
623 623 cells[i].set_autoindent(state)
624 624 };
625 625 };
626 626
627 627
628 628 Notebook.prototype.clear_all_output = function () {
629 629 var ncells = this.ncells();
630 630 var cells = this.cells();
631 631 for (var i=0; i<ncells; i++) {
632 632 if (cells[i] instanceof IPython.CodeCell) {
633 633 cells[i].clear_output(true,true,true);
634 634 }
635 635 };
636 636 this.dirty = true;
637 637 };
638 638
639 639 // Other cell functions: line numbers, ...
640 640
641 641 Notebook.prototype.cell_toggle_line_numbers = function() {
642 642 this.selected_cell().toggle_line_numbers()
643 643 };
644 644
645 645 // Kernel related things
646 646
647 647 Notebook.prototype.start_kernel = function () {
648 648 this.kernel = new IPython.Kernel();
649 649 var notebook_id = IPython.save_widget.get_notebook_id();
650 650 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
651 651 };
652 652
653 653
654 654 Notebook.prototype.restart_kernel = function () {
655 655 var that = this;
656 656 var notebook_id = IPython.save_widget.get_notebook_id();
657 657
658 658 var dialog = $('<div/>');
659 659 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
660 660 $(document).append(dialog);
661 661 dialog.dialog({
662 662 resizable: false,
663 663 modal: true,
664 664 title: "Restart kernel or continue running?",
665 665 buttons : {
666 666 "Restart": function () {
667 667 that.kernel.restart($.proxy(that.kernel_started, that));
668 668 $(this).dialog('close');
669 669 },
670 670 "Continue running": function () {
671 671 $(this).dialog('close');
672 672 }
673 673 }
674 674 });
675 675 };
676 676
677 677
678 678 Notebook.prototype.kernel_started = function () {
679 679 console.log("Kernel started: ", this.kernel.kernel_id);
680 680 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
681 681 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
682 682 };
683 683
684 684
685 685 Notebook.prototype.handle_shell_reply = function (e) {
686 686 reply = $.parseJSON(e.data);
687 687 var header = reply.header;
688 688 var content = reply.content;
689 689 var msg_type = header.msg_type;
690 690 // console.log(reply);
691 691 var cell = this.cell_for_msg(reply.parent_header.msg_id);
692 692 if (msg_type === "execute_reply") {
693 693 cell.set_input_prompt(content.execution_count);
694 694 cell.element.removeClass("running");
695 695 this.dirty = true;
696 696 } else if (msg_type === "complete_reply") {
697 697 cell.finish_completing(content.matched_text, content.matches);
698 698 };
699 699 var payload = content.payload || [];
700 700 this.handle_payload(cell, payload);
701 701 };
702 702
703 703
704 704 Notebook.prototype.handle_payload = function (cell, payload) {
705 705 var l = payload.length;
706 706 for (var i=0; i<l; i++) {
707 707 if (payload[i].source === 'IPython.zmq.page.page') {
708 708 if (payload[i].text.trim() !== '') {
709 709 IPython.pager.clear();
710 710 IPython.pager.expand();
711 711 IPython.pager.append_text(payload[i].text);
712 712 }
713 713 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
714 714 var index = this.find_cell_index(cell);
715 715 var new_cell = this.insert_code_cell_below(index);
716 716 new_cell.set_code(payload[i].text);
717 717 this.dirty = true;
718 718 }
719 719 };
720 720 };
721 721
722 722
723 723 Notebook.prototype.handle_iopub_reply = function (e) {
724 724 reply = $.parseJSON(e.data);
725 725 var content = reply.content;
726 726 // console.log(reply);
727 727 var msg_type = reply.header.msg_type;
728 728 var cell = this.cell_for_msg(reply.parent_header.msg_id);
729 if (!cell){
730 // message not from this notebook
731 console.log("Received IOPub message not caused by one of my cells");
732 return;
733 }
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