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